在'git merge'之后保留提交历史记录

53

当我在两个不同的功能上工作时(它们是从master创建的两个不同分支),当我进行合并时,我会很烦恼因为我将没有提交历史记录。

我解释得更清楚一些。当我完成Branch-A的工作后,我将其合并到master中。如果我运行git log,我可以看到我在Branch-A上做的所有提交。

相反地,当我完成Branch-B的工作并尝试将其合并到master中(在Branch-A已经合并之后),我必须为合并指定一个提交消息(而对于第一个分支,我没有被要求输入任何信息)。 并且在将其合并到master后,如果我输入git log,我无法在master分支的历史记录中看到Branch-B的提交。

假设我有

**Branch A**

commit 09b2unfas9d781n2e
    Add more stuff

commit 8uj8masd89jas898a
    Add stuff

**Branch B**

commit 09b2unfas9d781n2e
    Add feature setting

commit 8uj8masd89jas898a
    Add feature

我完成了

**Master**

commit 6hf6h8hd871udjkdn
Merge: 09b2un 34osd6
    Merge branch 'Branch-B' into master

commit 09b2unfas9d781n2e
    Add more stuff

commit 8uj8masd89jas898a
    Add stuff

commit 34osd62dhc91123j8
    I'm a previous commit from 'master'.
    The last one before branching...

虽然我想要获得类似于以下的东西:

**Master**

commit 09b2unfas9d781n2e
    Add feature setting

commit 8uj8masd89jas898a
    Add feature

commit 09b2unfas9d781n2e
    Add more stuff

commit 8uj8masd89jas898a
    Add stuff

commit 34osd62dhc91123j8
    I'm a previous commit from 'master'.
    The last one before branching...

...能更准确地反映已提交的历史记录。

我不明白为什么我只能保留两个分支中的一个的历史记录。

如果没有那些隐藏/省略合并提交的 合并 提交,如何保持一切清晰呢?

2个回答

58

看起来第一次合并是一个快进合并,而第二次合并是一个三方合并。

A - B - C - D <-master
             \
              E - F - G <- branch-a

执行git merge(使用默认设置)将导致

A - B - C - D - E - F - G <- branch-a <-master

您也将没有机会编辑合并提交,因为它不存在。

当您的另一个分支与master分支(不仅是在其前面)发散时,进行三方合并

A - B - C - D  - E - F - G <-master
                  \
                   E1 - E2 <- branch-b
在这种情况下,Git 不能仅仅将主分支指针从 G 移动到 E2 ,因为这样会丢失在 FG 中所做的更改。当进行三方合并时,它会创建一个具有两个父级和提交消息的提交。现在,master 可以移动到此提交位置。(请注意,在此情况下,masterbranch-b 不指向相同的提交。)
A - B - C - D  - E - F - G - H <-master
                  \       /
                   E1 - E2 <- branch-b

如果你想拥有线性历史记录,那么你需要使用变基(rebase),但要注意的是,如果其他人看到了你的分支提交(commit),这可能会导致超出本回答范围的问题。使用变基将涉及两个步骤,变基(rebasing)和快进合并(fast-forward merge)。所以,与其先进行合并,不如在branch-b上执行以下命令:git rebase master。这将创建新的提交(commit),它们是旧的提交(commit)的副本,即相同的变更集(change-set)、作者信息和消息,但具有新的提交者信息和父级历史记录。 (我在插图中将这些提交称为E1'和E2',以指示它们只是副本。)旧的提交(commit)将存在,直到被垃圾收集,但除非查看引用日志(reflog),否则将无法访问。

A - B - C - D  - E - F - G <-master
                  \       \
                   E1 - E2 \ 
                            E1' - E2' <- branch-b

执行 git checkout master; git merge --ff-only branch-b 将把你的更改快速合并到主分支(master)中,从而使你获得线性历史记录。

A - B - C - D  - E - F - G - E1' -E2' <-master <- branch-b

3
当我在masterbranch-b中都有更改时,我需要将master合并到branch-b。如何重写branch-b,使其起始点为提交G而不是E?但是保留branch-b的提交历史,并将与之冲突的更改提交到branch-b。假设只有我知道branch-b存在。 - Artem P
5
没事了,确切的命令是 git rebase master - Artem P

11

使用rebase而不是merge。来自教程:

如果您检查已经变基的分支日志,它看起来像是一个线性的历史记录:即使最初是并行的,也似乎所有工作都是逐个完成的。

我想你的更改从Branch-B无法使用快进合并合并到master中。在这种情况下,会进行三方合并

Git不仅仅只是将分支指针向前移动,它还创建了一个新的快照来反映这个三方合并,并自动创建一个指向其的新提交。这称为合并提交,并且它有多个父级,是特殊的。

我会在将提交提交到master之前始终进行变基以保持线性历史记录。


1
我不知道为什么,但是 rebase 操作会改变 Git 的历史记录,可能会让其他人感到困惑! :) - Yan Foto
4
如果你仅在将本地工作分支合并到主分支之前对其进行变基,则不会产生混淆 - 因为没有人在使用你的本地分支,即使是在它快乐地加入主分支后,你自己也不会使用它。 - userfuser
2
@Kamafeather,你是对的!现在到了2022年,我被迫总是要进行变基。而且我的关于变基会让人困惑的论点一直都是错误的! - Yan Foto
2
❤️很高兴它有助于改进您的工作流程!它每天都帮助我整理和保留元数据和差异以便调试Git历史记录。尽管有时需要更多的工作或在重写历史时比较棘手,但我认为这是值得的;但你的观点也没错,我明白我的Git rebase工作流程可能会让人感到困惑(我记得当初作为新手时我也无法理解rebase)。对于初学者,我建议坚持使用“merge”,并且永远不要从我的WIP分支中选择提交(我将它们备份到origin,前缀为kama/WIP/feature-5,等待着谁碰它们就大喊大叫)。 - Kamafeather
1
最近我也滥用变基来重新排序/压缩/删除/修复以前的提交并进行更彻底的清理;尽管合并更加符合主流,但我喜欢变基的无政府主义力量!✊ - Kamafeather
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接