为明确的合并建立“干净的” git历史记录

时间:2019-12-28 00:06:56

标签: git git-merge git-flow git-history

我有一个使用 GitFlow 的git存储库(即,它具有masterdeveloprelease-*feature-*分支)。但是,协作者尚未使用显式合并(即git merge --no-ff),例如git log --first-parent没有提供到目前为止的合并历史记录的简单汇总。

向前迈进,合作者将使用显式合并。但是,在执行此操作之前,我想确保历史记录是“干净的”,以便在调用git log --first-parent时不会显示以前的历史记录。但是,显然,我想在调用未过滤的git log时保持 actual 提交历史。

我的意愿是执行以下操作:

$ git checkout develop
$ git checkout --orphan CleanSlate
$ git rm . -r -f
$ git commit --allow-empty -m "Establish a clean slate for the develop branch"
$ git merge --no-ff --allow-unrelated-histories develop -m "Introduce all legacy files"
$ git checkout develop
$ git merge CleanSlate

基本上,我们的想法是:

  1. 建立一个没有历史记录的新(--orphan)分支
  2. 可选)从工作树中删除所有文件,以便我们不重新提交文件
  3. 建立初始提交,以便我们可以合并一些东西
  4. --no-ff分支执行显式合并(即develop),并确认不相关的历史记录
  5. 快速develop到我们刚刚执行的显式合并,以表示历史记录

我的问题:在将这种方法应用于生产环境之前,应该注意这种方法的后果吗?是否存在替代或更简单的方法来实现这种情况?

(在测试中,这似乎实现了我的目标,而对现有分支或工作流没有不利影响。但是,使用git时,我总是对不知道的东西保持警惕。)

1 个答案:

答案 0 :(得分:2)

我想我在这里理解了这个主意。

让我们一步一步地画出实际发生的情况。出于初始绘图的目的,假设分支develop终止于普通提交D

...--B--C--D   <-- develop

第一个命令似乎并不相关;第二个将我们带入一个未出生的(“孤立”)分支,第三个将索引和工作树清空:

$ git checkout develop
$ git checkout --orphan CleanSlate
$ git rm . -r -f

使第四个命令创建一个没有父项的空提交E

$ git commit --allow-empty -m "Establish a clean slate for the develop branch"

为我们提供了这张图:

          E   <-- CleanSlate (HEAD)

...--B--C--D   <-- develop

现在:

$ git merge --no-ff --allow-unrelated-histories develop -m "Introduce all legacy files"

merge命令进行新的合并提交;从逻辑上讲,F是下一个字母,但我陷入了诱惑,在这里将其称为M

          E--M   <-- CleanSlate (HEAD)
            /
...--B--C--D   <-- develop

重要的是,M first 父级是空的提交EM second 父级是提交D。因此,回到git log --first-parent的未来M将到达E,然后停下来。

最后两个命令将HEAD附加到develop并移动develop指向M

$ git checkout develop
$ git merge CleanSlate

给予:

          E--M   <-- develop (HEAD), CleanSlate
            /
...--B--C--D

(您现在可以安全删除名称CleanSlate。)

执行此操作的命令比较短

考虑此食谱(未经测试,但在发布之前我再次盯着它,看起来不错):

et=$(git hash-object -t tree /dev/null)
e=$(git commit-tree -m "dummy empty commit at which --first-parent stops" $et)
m=$(git commit-tree -p $e -p develop -m "begin strict no-ff merges" develop^{tree})
git checkout -B develop $m

使用两个-p(提交的父对象)参数,我们按照喜欢的顺序选择合并提交M的父哈希:第一个-p是被跟踪的父对象git log --first-parent和第二个-p是第二个父级,它使M成为合并提交。

存储在两个新提交中的实际(或快照)是$etempty tree)和develop^{tree}(提交快照)中的D)。现在,您可以根据需要轻松地选择使提交ED共享树。

最后一个git checkout -B develop使Git切换到提交M并指向名称develop。这是一个快速合并的事实,意味着您可以使用:

git checkout develop; git merge --ff-only $m

但是用一个更晦涩的命令来做到这一点。 ?注意:由于提交EM在保护develop之前没有保护它们的 name ,因此您必须在提交后的14天内完成最后一步开始该过程,以确保Git的垃圾收集器不会删除它们。

两种方法的结果相同。 Git的大部分内容与 commits 及其形成的图形有关。剩下的大部分是关于在遍历图表时使用名称(分支和/或标签名称和/或其他名称)来入门的。