通过交错提交合并两个不同的 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 存储库 的相关文章

  • 从 master 更改为新的默认分支 git

    这是一个场景 我们有一个默认的分支 Master 我们以此为基础 创建分支并向上推等等 我们现在创建了一个Develop分支Master并将其设置为默认开发分支 我想知道的是 我现在如何知道我的 git pull 命令是否通过命令行请求默认
  • 如何标记单个文件?或如何下载特定文件? - 胃肠道

    我想向文件添加标签以轻松下载该文件而不是整个分支 如何下载GIT中的特定文件 我不知道有什么方法可以标记特定文件git 对于第二个问题 如何下载特定文件git 仅当您的意思是 同时下载 时 以下内容才有效in a git存储库 这就是我理解
  • Git 中的“分支提示”是什么?

    我正在学习 Git 并阅读专业 Git 书籍 https git scm com book en v2 书中和 Stack Overflow 上有时会使用术语 分支提示 但我找不到它的含义 分支提示是分支上的最后一次提交或最近一次提交 基本
  • 合并之间的 git rebase 会导致完全不相关的文件发生冲突

    我有一个大型 Git 存储库 几个月前引入了一个错误 我想bisect它 首先引入一个过去的提交 存储库 然后重播合并 做rebase到新的 commit 如下图所示 据我了解 由于合并 Git 似乎无法正常工作 预期的 但我想更好地了解为
  • 如何在本地快速拉取拉取请求

    在合并拉取请求之前 我想在本地快速拉取请求并运行测试并测试一些内容 我还不想点击 gihub 合并拉取请求 我以为滑轮会有帮助http ejohn org blog pulley http ejohn org blog pulley 但我收
  • RuntimeError:模型类 django_messages.models.Message 未声明显式 app_label 并且不在 INSTALLED_APPS 中的应用程序中

    我正在尝试使用https github com arneb django messages https github com arneb django messages打包我的消息传递内容并尝试了以下操作 pip install git h
  • 当出现错误时如何删除远程分支?

    我尝试过以下命令 但失败了 git push origin next remote error denying ref deletion for refs heads next To blah git remote rejected nex
  • 返回到存储库中的特定修订后提交并推送更改?

    我们需要及时返回到某个特定的提交 一些意外的改变是为了掌握 尝试恢复它挖得太深 所以 master 的状态很糟糕 现在我们希望master回到66ada4cc61d62afc 根据git 恢复到某个提交 https stackoverflo
  • 如何通过哈希显示提交的日期和时间

    I used git reflog识别我创建特定分支时的哈希值 我得到了哈希值fe1ddcdef 我还没有将此分支推送到远程 我现在正在尝试查找日期和时间fe1ddcdef发生 git reflog只告诉我 fe1ddcdef HEAD 1
  • 如何查看 github 的 SSH 密钥?

    我最近收到一封来自 Github 的电子邮件 要求我检查我的 SSH 密钥 请检查您的钥匙并确保您 认出他们 如果您有任何疑问 请拒绝钥匙并 上传新密钥 如何在 ubuntu 11 10 上使用 git 检查我的密钥 您可以按照 GitHu
  • Eclipse 与外部 src 和 Web 内容文件夹链接或映射

    首先这不是问题但是我遇到的问题的解决方案浪费了4 5个小时来找到解决方案 请让我知道是否有更好的替代方法来使用 eclipse 项目管理外部 src 文件夹 我正在使用版本控制系统 GIT 来管理我的项目 还使用外部 Git 客户端 Sou
  • TeamCity 将功能分支推送到主分支

    有没有办法将成功构建的功能分支推送到另一个分支 我想要这样的东西 Git 存储库 Gitorious GitHub 等 分支机构 master 当前项目的代码 质量保证 代码等待 QA 的分支 功能分支 许多远程分支 开发人员可以在其中开发
  • Git 扩展 - 无法在 Windows 上推送到网络驱动器中的 git bare 存储库

    我正在 Windows 上学习 git 我已经安装了 Git 扩展 版本 2 47 3 并使用了它 我在我的 C 单元中创建了一个裸存储库 作为中央存储库 并在硬盘中的其他任何位置创建了个人存储库 我对硬盘中的这两个存储库进行提交 推送和拉
  • 未能将一些参考推送至 [email protected]

    当我尝试推送到 Heroku 存储库时收到此错误 我已经设置了autocrlf false在gitconfig中 但这个问题仍然存在 我也尝试过这个解决方案here https stackoverflow com questions 566
  • Git post-receive - 如何检查推送的分支是否与主分支合并

    在我们的团队中 我们通常将所有任务推送到单独的分支中 然后发布经理审查这些分支并将它们合并到 主 分支中 有时团队成员忘记将他们的分支与主分支合并 在推送之前 所以我想做的是 在用户推送后输出一条消息 请与主分支合并 我想我需要检查一些内容
  • 在 Azure DevOps 中为 Wix MSI 文件生成 GUID

    我正在为 Web 服务器应用程序和 Sitecore 前端应用程序设置 Wix 安装程序 我的问题并非特定于 Web 服务器或 Sitecore 我的问题是 Wix 以及如何使用它进行持续交付 1 Wix 需要每个文件和产品本身的 GUID
  • 如何签出仅在“git ls-remote”中列出的分支?

    我遇到了无法切换到仅列出的分支的情况git ls remote 这是详细信息 我分叉了一个 github repoA 作为 repoB 创建了自己的分支并将其推送到 ComputerA 中的 repoB 在 ComputerB 中 我将分叉
  • 自动同步两个 git 存储库

    是否可以保持同步两个 Github 存储库 远程 的特定文件夹 有两个 github 存储库 repoA 和 repoB 这两个存储库都有名为 ABC 的文件夹以及其他独特的文件夹 如果repoA的文件夹ABC中的任何文件有更新 我想自动更
  • 清理远程 Git 分支

    我已经将 SVN 存储库移至 Git 可能由于多次克隆 我现在只剩下一堆看起来像这样的分支 BranchA origin BranchA remotes BranchA remotes origin BranchA remotes orig
  • 如何获取 git 存储库中所有文件的计数?

    如何获取 git 存储库中当前所有文件的计数 您可以使用以下命令获取 git 存储库中所有跟踪文件的计数 git ls files wc l 命令分解 The git ls files命令本身打印出存储库中所有跟踪文件的列表 每行一个 Th

随机推荐