【问题标题】:Establishing a "clean" git history for explicit merges为显式合并建立“干净”的 git 历史记录
【发布时间】:2019-12-28 00:06:56
【问题描述】:

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

接下来,合作者将使用显式合并。但是,在他们这样做之前,我想确保历史记录是“干净的”,以便在调用 git log --first-parent 时不会显示以前的历史记录。但是,显然,我想在调用未过滤的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. develop 分支执行显式合并(即--no-ff),确认不相关的历史记录
  5. 快进develop 到我们刚刚执行的显式合并,它代表了历史

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

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

【问题讨论】:

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


    【解决方案1】:

    我想我理解这里的想法。

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

    重要的是,Mfirst 父级是空提交 EMsecond 父级是提交 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,按照我们喜欢的顺序:第一个-pgit 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 没有 name 保护它们,直到您像这样移动 develop,您必须在开始该过程的 14 天内完成最后一步,以使确保 Git 的垃圾收集器不会删除它们。

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

    【讨论】:

    • 我对@9​​87654366@ 的研究越多,我就越觉得它是一个很棒的工具。
    猜你喜欢
    • 2013-02-18
    • 2017-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多