1

我有一个使用GitFlow的 git 存储库(即,它有masterdeveloprelease-*feature-*分支)。然而,合作者并没有使用显式合并(即git merge --no-ff),因此 eggit log --first-parent没有提供迄今为止的合并历史的简单汇总。

展望未来,合作者将使用显式合并。但是,在他们这样做之前,我想确保历史记录是“干净的”,以便在调用git log --first-parent. 但是,显然,我想在调用 unfiltered 时保持实际git log的提交历史。

我的倾向是执行以下操作:

$ 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-ffdevelop
  5. 快进develop到我们刚刚执行的显式合并,以便表示历史

我的问题:在将其应用于生产环境之前,我应该了解这种方法的后果吗?是否有替代或更简单的方法更适合完成此类场景?

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

4

1 回答 1

3

我想我理解这里的想法。

让我们一步一步画出实际发生的情况。出于初始绘图的目的,假设分支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"

合并命令进行新的合并提交;逻辑上F是下一个字母,但我陷入了诱惑并M在这里调用它:

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

重要的是,第一个父级M是空提交E。的第二个父级M是 commit Dgit log --first-parent所以一个回到过去的未来M会到达E然后停止。

最后两个命令附加HEADdevelop并移动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合并提交的第二个父级。

存储在两个新提交中的实际$et(或快照)分别是(空树)和develop^{tree}(提交的快照)中的树D。如果您愿意,您现在可以轻松地选择让提交E共享树。D

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

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

但这是通过一个更晦涩的命令来实现的。注意:由于提交E并且在您像这样移动之前M没有名称保护它们develop,因此您必须在开始该过程的 14 天内完成最后一步,以确保 Git 的垃圾收集器不会删除它们。

结果是一样的。Git 的大部分内容都是关于提交和它们形成的图表。其余的大部分是关于在遍历图表时使用名称(分支和/或标签名称和/或其他名称)开始的。

于 2019-12-28T01:26:31.990 回答