通过交错提交合并两个不同的 git 存储库

2023-12-20

我们有两个并行发展的存储库:一个用于我们项目的代码,另一个用于该项目的测试。我想将这两个存储库合并到一个存储库中,这样当我回顾历史时,我仍然有both目录结构。

假设我们当前的结构如下,其中project and tests是两个独立的 git 存储库:

project
    /src
    /include
tests
    /short
    /long

我希望最终得到一个包含两个目录的 git 存储库project and tests.

我不能简单地使用中描述的技术合并这两个存储库这个答案 https://stackoverflow.com/a/2235007/4177, this one https://stackoverflow.com/a/1768800/4177, or 这个网站 https://saintgimp.org/2013/01/22/merging-two-git-repositories-into-one-repository-without-losing-file-history/:它们会导致存储库在合并之前具有两个不同的历史记录,并且在检查过去的提交时,您可以选择src and include, or short and long,但你并不拥有当时出现的全部四个。

如果我签出在以下位置创建的提交project4个月前 我想看project/src and project/include正如他们出现在这次提交中的那样,但我也希望tests/short and test/long因为他们同时在(然后分开)test存储库。

我知道两个存储库之间的提交顺序仅取决于时间,并且可能不是很精确。但这对我来说已经足够好了。当然,我知道我无法保留每个存储库中的原始 git id。没关系,因为这两个存储库实际上是从另一个 RCS 新鲜导入的,因此任何地方都没有记录过 git id。

应该可以逐一检查每个存储库中的所有提交,按存储库中的时间排序,并提交生成的文件。是否已经有一个工具可以做到这一点?


编辑:对于基于日期的方法,该方法使这变得非常简单,但假设两个存储库之一将“控制”来自另一个存储库的提交,请参阅杰蒂尔的回答 https://stackoverflow.com/a/55886165/1256452。您最终会得到与“项目”历史记录完全匹配的提交历史记录,可能会压缩一些“测试”历史记录。如果您需要添加前缀,下面的答案更合适both历史集,或者想要交错它们(例如,需要对同一“项目”提交进行两个不同的“测试”更新)。


博士的答案 https://stackoverflow.com/a/55880876/1256452很好,但如果我自己做这件事并且想让它变得非常整洁和干净,我会使用不同的方法。

如果两个存储库的树不重叠,那么当然可以做到这一点 - 并且通过绕过通常的 Git 机制,直接进入底层git read-tree命令,您可以自动化它。 (这是哪里VonC最近的评论 https://stackoverflow.com/questions/55877484/is-committed-and-unmodified-the-same#comment98417233_55878249拒绝我关于 Git 和 Mercurial 非常相似的说法是正确的:如果您绕过顶级 Git 命令,您将获得在 Mercurial 中几乎无法轻松获得的东西。)

正如在博士的答案 https://stackoverflow.com/a/55880876/1256452,您可以通过组合两个存储库提交数据库来启动此过程git fetch。 (您可以在第三个存储库中执行此操作,我建议这样做,因为如果您决定要调整某些参数,或者通过将存储库 A 添加到存储库 B,或将存储库 B 添加到存储库,可以更轻松地从头开始重新启动该过程。回购协议 A.) 但在那之后,一切都出现了分歧。

您现在有两个不相交的提交 DAG:

        D--...--K
       /         \
A--B--C           M--N   <-- repoA/master
       \         /
        E--...--L

O--P--Q--...--Z   <-- repoB/master

(如果 repoA 和 repoB 都有多个分支提示,请绘制更合适的提交简化图。)

下一步是使用以下命令枚举两个不相交 DAG 中每一个中的所有提交git rev-list --topo-order --reverse以及您喜欢的任何其他排序选项。何时以及是否--topo-order是否必需取决于拓扑和其他排序信息,但通常您会希望父提交列在其任何子提交之前。

给定这两个提交哈希 ID 的线性化列表,您现在遇到了困难的部分:构建您希望提交的新组合树的图表。每一个new提交将通过组合两个旧图表中的每一个的一个提交来进行。如果其中一张图很复杂(如上面的 repoA),具有分支和合并,而另一张图则不是(如上面的 repoB),那么这可能会特别棘手。

我为此做了自己的设置,其中有一个非常简单的图表:

A--B   <-- A/master

O--P   <-- B/master

在我的简化设置中,我想对我的新主人进行第一次提交C结合了树A and O:

C   <-- master

然后我想做,作为我的第二次承诺master, 的组合A and P (not A and O并不是B and O要么),作为我的最后一次提交,组合B and P,这样我最终得到:

C--D--E   <-- master

with:
    C = A+O
    D = A+P
    E = B+P

因此,这里我们位于一个新的空存储库中,除了我们在项目 A 和 B 中读取的内容之外:

$ git log --all --graph --decorate --format='%h%d %s' --name-status | sed '/^[| ] $/d'
* 7b9921a (B/master) commit-P
| A B/another
* 51955b1 commit O
  A B/start
* 69597d3 (A/master) commit-B
| A A/new
* ff40069 commit-A
  A A/file

(我不小心没有用连字符连接提交 O,但是却用连字符连接了所有其他提交。sed在这种情况下,是删除一些对阅读没有真正帮助的空白行。)

$ git status
On branch master

No commits yet

nothing to commit (create/copy files and use "git add" to track)

现在我们使用一次一个来构建新的提交git read-tree填充索引以进行提交。我们从一个空索引开始(我们现在已经有了):

$ git status
On branch master

No commits yet

nothing to commit (create/copy files and use "git add" to track)

我们希望我们的第一个承诺能够合并A and O,现在让我们将这两个提交读入索引。如果我们必须向树添加一个前缀A我们可以在这里这样做:

$ git read-tree --prefix= ff40069
$ git ls-files --stage
100644 7a1c6130c652b6ea92f4d19183693727e32c9ac4 0       A/file
$ git read-tree --prefix= 51955b1
$ git ls-files --stage
100644 7a1c6130c652b6ea92f4d19183693727e32c9ac4 0       A/file
100644 f6284744575ecfc520293b33122d4a99548045e4 0       B/start

我们现在可以进行我们需要的提交:

$ git commit -m combine-A-and-O
[master (root-commit) 7c629d8] combine-A-and-O
 2 files changed, 2 insertions(+)
 create mode 100644 A/file
 create mode 100644 B/start

现在我们需要进行下一次提交,这意味着我们需要在索引中构建正确的树。为此,我们首先必须将其清理干净;否则下一个git read-tree --prefix将会失败并抱怨重叠文件Cannot bind.现在我们清空索引,然后读取提交 A 和 P:

$ git read-tree --empty
$ git read-tree --prefix= ff40069
$ git read-tree --prefix= 7b9921a

如果您愿意,您可以使用以下命令检查结果git ls-file --stage again:

$ git ls-files --stage
100644 7a1c6130c652b6ea92f4d19183693727e32c9ac4 0       A/file
100644 d7941926464291df213061d48784da98f8602d6c 0       B/another
100644 f6284744575ecfc520293b33122d4a99548045e4 0       B/start

无论如何,它们现在可以作为新的提交提交:

$ git commit -m 'combine A and P'
[master eb8fa3c] combine A and P
 1 file changed, 1 insertion(+)
 create mode 100644 B/another

(你现在可以看到我是如何得到不一致的连字符的:-))。最后,我们通过清空索引、读入两个所需的提交 (B+P) 并提交结果来重复该过程:

$ git read-tree --empty
$ git read-tree --prefix= A/master
$ git read-tree --prefix= B/master
$ git ls-files --stage
100644 7a1c6130c652b6ea92f4d19183693727e32c9ac4 0       A/file
100644 8e0c97794a6e80c2d371f9bd37174b836351f6b4 0       A/new
100644 d7941926464291df213061d48784da98f8602d6c 0       B/another
100644 f6284744575ecfc520293b33122d4a99548045e4 0       B/start
$ git commit -m 'combine B and P'
[master fad84f8] combine B and P
 1 file changed, 1 insertion(+)
 create mode 100644 A/new

(我在这里使用符号名称来获取最后两次提交,但哈希 ID 来自git rev-list当然会很好用。)我们现在可以看到这三个提交,全部都在master:

$ git log --decorate --oneline --graph
* fad84f8 (HEAD -> master) combine B and P
* eb8fa3c combine A and P
* 7c629d8 combine-A-and-O

现在可以安全删除A/master and B/master参考文献(和两个遥控器)。有一个特点:由于我们直接在索引中完成所有工作,而不用担心工作树,因此工作树仍然完全是空的:

$ ls
$ git status -s
 D A/file
 D A/new
 D B/another
 D B/start

为了最后解决这个问题,我们应该运行git checkout HEAD -- .:

$ git checkout HEAD -- .
$ git status -s
$ git status
On branch master
nothing to commit, working tree clean

如何编写自己的自动化脚本

在实践中,您可能想要使用git write-tree and git commit-tree, 而不是git commit,进行新的提交。您可以编写一个小脚本(用您喜欢的任何语言)来运行git rev-list收集要组合的提交的哈希 ID。脚本必须检查这些提交(例如,通过查看作者身份和日期、或文件内容等)来决定如何交织提交。然后,在做出有关交织以及提供哪些分支合并结构的决定后,脚本可以开始重复执行以下步骤的过程:

  • 清空索引。
  • 从 repo-A 的子图中的提交中拉入树,无论是什么--prefix选项是合适的——根据您的情况,这是--prefix=,即空字符串,但在其他情况下,它将是带有尾部斜杠的目录名称)。
  • 从 repo-B 的子图中的提交中拉入树,并使用另一个适当的--prefix,这样来自的条目之间就不会发生冲突A and B.
  • Use git write-tree写树。它的输出是下一步的树哈希 ID。
  • Use git commit-tree与适当的-p设置新提交的父级的参数。向其提供适当的(组合的或其他的)提交消息文本。使用环境变量GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, GIT_AUTHOR_DATE, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL, and GIT_COMMITTER_DATE控制作者和提交者的姓名和日期。输出来自git commit-tree是哈希 ID,它是某些后续提交的父级。

当整个事情结束后,last为任何特定分支或分支集所做的提交都是进入这些分支的哈希 ID,因此您现在可以运行:

git branch <name> <hash>

对于每个这样的哈希 ID。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

通过交错提交合并两个不同的 git 存储库 的相关文章

  • 无法从另一台计算机访问 git 分支

    基本上我看不到另一台计算机上的分支 我跑 git branch a 在家用电脑上我得到 C learn ror sample app filling in layout gt git branch a filling in layout m
  • git filter-repo:它可以在特定分支上使用吗?

    我正在读什么git filter repo可以做 因为我想用它做一个小实验 我有这个存储库 我只想从中获取一个目录的历史记录 比如说 master 但我不想为主人工作 我想创建一个新分支 例如filter repo test然后让git f
  • Windows 上的 git 忽略文件名大小写更改 [重复]

    这个问题在这里已经有答案了 我有一个reactjs应用程序 我正在将所有文件名标准化为小写以符合Nodejs 最佳实践 https devcenter heroku com articles node best practices stic
  • 如何将一个文件存储库上传到 Gist,并保留历史记录?

    我的计算机上有一个包含单个文件的 Git 存储库coins py 我如何才能将其作为 Github 上的要点来保存历史 这其实很简单 您可以将现有历史记录移动到 Gist 存储库中 就像将其移动到任何其他存储库中一样 创建要点 只需输入一些
  • Jenkins groovy - 如何从最新提交中检索标签?

    从中获取最新提交branchName 我们运行下面的代码 treeMapData git branch branchName credentialsId credential url gitLabServer projectName rep
  • gitattributes 中的`* text=auto eol=lf` 会做什么?

    我们的 gitattributes 文件中有这样的内容 text auto eol lf 我想准确理解这是做什么的 第一部分是text auto 来自文档 http git scm com docs gitattributes 这确保了所有
  • 如何在 git 交互式 rebase 中将提交与下一个提交合并?

    git rebase i允许通过以下方式将提交与前一个提交合并squash or fixup 这两个选项都需要至少一次提交pick ed 当一个人想要使用第一个提交但丢弃其提交消息时 情况怎么样 换句话说 如果我希望第一个提交与后续提交合并
  • 为什么我的存储库的 github 页面中没有出现问题选项卡?

    我想在 github 存储库中创建问题来纠正存储库中的技术债务 我看不到问题选项卡 对于其他存储库 问题选项卡显示在拉取请求选项卡下方 如何为我的存储库启用问题选项卡 您必须先启用问题 您可以通过点击来做到这一点Settings并启用 问题
  • 将远程更改合并到非当前分支的分支中

    我有多个分支 我想将远程更改合并到一个分支中不是我当前的分支 例如 git merge remote branch some other branch 仅当本地分支可以快速转发到远程头时 这才是可行的 在任何分支中 要从源获取远程分支并更新
  • 如何更改全局 git 设置以在拉取期间进行 git 合并

    目前 我的全局设置设置为在 git pull 期间执行变基操作 我希望它默认将其更改为 git merge 如何更改此设置 TL DR git config global pull rebase false 有点细节 Git 使用配置pul
  • 如何从 git 存储库中删除作者?

    如果我创建一个 Git 存储库并公开发布它 例如在 GitHub 等上 并且我收到存储库贡献者的请求 无论出于何种原因删除或隐藏他们的名字 有没有一种方法可以轻松做到这一点 基本上 我有这样的请求 可能想将他们的姓名和电子邮件地址替换为 匿
  • 无法在 git 上获取 Http 工作

    我在拇指驱动器上使用 gitbash 作为 git 我的防火墙阻止了我 并且想设置我的 git 以进行 http 访问 我使用 github 并且已经看到了有关如何执行此操作的各种信息 但我还不够了解 无法让它为我自己工作 我在 php i
  • 如何正确设置 Azure DevOps 和 GitHub 之间的双向同步

    我想通过执行以下操作在 Azure DevOps 和 GitHub 之间创建双向同步 使用 CI 触发器创建 Azure DevOps 管道 将更改从 Azure DevOps 存储库推送到 GitHub 中的分支 创建第二个管道 用于侦听
  • Git 只获取一个目录

    我有一名开发人员负责一个文件夹 另一名开发人员负责另一个文件夹 我想使用特定文件夹更新产品 我正在寻找类似的命令 cd myproject git pull myfolder 并期望只有 myfolder 会被更新 是否可以 好吧 我重新表
  • 撤消多个文件和文件夹“git add”[重复]

    这个问题在这里已经有答案了 我执行了 git add 现在我想恢复 git add 我怎样才能做到这一点 git reset 这相当于git reset HEAD 将取消 add 更常见的是 取消暂存 所有文件 In Git revert用
  • 未找到 Gradle DSL 方法:“versionCode()”

    构建我的 Android 项目时遇到问题 我使用Grgit https github com ajoberstar grgit填写versionCode and versionName在 gradle 中 一切工作正常 直到我将 Andro
  • 是否有 git-merge --dry-run 选项?

    我正在合并一个可能有很多冲突的远程分支 我怎么知道它是否会发生冲突 我没有看到任何类似的东西 dry run on git merge 如前所述 传入 no commit标志 但为了避免快进提交 也传入 no ff 像这样 git merg
  • 获取 git 存储库中每个文件的提交计数

    我正在寻找一种方法来查看有关 git 存储库中每个文件更改频率的统计信息 基本上 文件提交的频率实际上与以前的版本不同 此外 有没有办法获取文件上次更改的日期 我是一个 git 新手 还没有发现任何关于此的信息 任何帮助将不胜感激 这里有两
  • 配置 Eclipse/EGit 来跟踪上游存储库

    我正在使用 EGit 如新的 Eclipse 4 2 Juno 版本中提供的 我在 GitHub 上有一个存储库 是从另一个上游存储库分叉的 当我从 Github 上的存储库在 Eclipse 中创建项目时 它正确设置origin指向 Gi
  • Visual Studio 2017/2019/2022 gitsync/pull/push/fetch 操作卡住,并且无法停止

    我从 Visual Studio 中的 Git Changes 选项卡启动同步 获取 拉取或推送 但操作只是挂起 没有选项可以停止它 我必须点击 X 才能关闭 Visual Studio 如果操作是同步的 它会在其他所有操作上打开一个模式对

随机推荐