使用Git merge --squash合并分支同时保留每个提交的日志记录

7

初始情景:

A (master)
 \
 B - C  - D (development)

合并之后我想要的--压缩:

A     -     E (master/development)
 \         /
 B - C - D 

在分支master上,git log将会显示:
commit E
    Squashed commit of the following:
    commit D
    commit C
    commit B
commit A

请在分支development上持续进行开发:

A     -     E (master)
 \         / \         
 B - C - D    F - G - H (development)

再次合并并压缩:

再次合并并压缩:

A     -     E     -     I(master/development)
 \         / \         /
 B - C - D    F - G - H

在分支master上,执行git log命令会显示:

commit I
    Squashed commit of the following:
    commit H
    commit G
    commit F
commit E
    Squashed commit of the following:
    commit D
    commit C
    commit B
commit A

如果你在 development 分支上,执行 git log 命令会显示以下内容:

Commit I
Commit H
Commit G
Commit F
Commit E
Commit D
Commit C
Commit B
Commit A

我希望将合并压缩在master上的提交,同时保留development上的每个提交。

我不知道如何实现这一点。我的问题是我不知道如何使第一次合并中的D指向E,而I将包括B,C,D,E,F,G,H而不仅仅是F,G,H


1
你为什么想要这样做呢?这似乎会让历史更加复杂。而且你的日志结果也不会像你想象的那样。master分支的日志将显示所有提交记录。 - Schwern
3个回答

5
你展示的结构无法得到你想要的日志结果(git log --merges master 有点像),而且能够给你想要的日志的结构无意义,最终来看,这是一种与Git自相矛盾的工作方式。希望的结果是能够运行 git log master 并且只看到压缩后的提交记录,但这样做是不可行的。Git并不知道哪些提交记录是为了 master 而哪些是为了 development。现在我们来看一下提议的结构。
A     -     E     -     I [master] [development]
 \         / \         /
 B - C - D    F - G - H

此时,masterdevelopment指向相同的提交。就Git历史记录而言,它们是相同的。分支只是指向提交的标签。提交不记得它们所属的分支git log mastergit log development将产生相同的日志,显示从A到I的所有提交。E和I合并的提交日志将是冗余的。
您可以使用git log --merges master(或development)来以仅显示合并提交的方式获得所需的结果,但这会显示任何合并提交,甚至包括作为development的一部分完成的合并提交。因此,它并不真正起作用。
整个想法过于复杂,请继续阅读。
要获得所需的日志结果,您需要打破两个分支之间的关系,如下所示。
A     -     E     -     I [master]
 \                   
 B - C - D - F - G - H [development]

你可以通过一些方法让这种方式变得可行,但这样做并没有意义。git log master 包含与 git log development 完全相同的日志信息,所以它会和 git log development 一样长,但它们会被挤在一起。你不能使用 git log master 来进行代码考古(例如“为什么以这种方式编写了这一行”),因为更改都被合并到一个差异中,使得将更改行与特定提交消息关联变得更加困难。由于 masterdevelopment 的历史是不相关的,所以无法确定开发中的所有内容是否移植到了主分支,反之亦然(例如修复主分支的热补丁)。

git log master 提供的信息比 git log development 少,而且更难理解。 master 没有与 development 相关联,并失去了保留合并历史的所有好处。维护这种复杂的设置没有意义。


相反,使用 git merge --no-ff 合并功能 分支(不是连续的“开发”分支),并保留分支历史记录以便进行代码考古。

              G - J [feature/tacos]
             /
A     -     E     -     K [master]
 \         / \         /
 B - C - D    F - H - I

E和K是由git merge --no-ff生成的普通合并提交。没有连续的development分支,该分支由特性分支处理。特性分支是一次性使用的,并在合并后删除。特性分支的信息保存在合并提交中,git merge --no-ff保证了分支结构的完整性。feature/tacos是一个用于处理tacos的特性分支,尚未合并。 git log --graph --decorate master将显示master的完整历史记录,其中合并提交显示特性何时结束,以及展示分支历史的线条。像GitX这样的GUI Git历史记录工具是阅读历史记录的图表的另一种方式。
最终,Git历史记录就是一个图表。如果您学会如何使用该图表,使用Git的生活将变得更加容易。如果您试图使Git历史记录呈线性状态,就像一堆大烤饼,您就会打败Git的目的,并为自己和其他人增加额外的工作量。

1

好的,根据您的要求,是不可能完全得到您所要求的结果的,因为如果您合并分支,您的提交日志中将会看到来自两个分支的提交。您可能想要的是这样的:

A      -     E (master)
 \         
 B - C - D (development)

你只需从development分支中挑选所需的提交,将它们压缩并应用于master分支之上即可。
最简单的方法就是像下面这样做:
git checkout development && git rebase -i master HEAD

然后,您将除第一个之外的所有操作替换为squash。结果,您将获得一个指向已压缩提交的分离头,应用于master之上。您只需将master重置到此提交即可完成操作。
我猜,提交消息将不完全是您想要的,因为它将还原所有提交的提交消息,而不是它们的哈希值,但您可以使用--exec选项钩入提交消息生成。
一切都有点手动,但您可以编写脚本并创建别名。我相信,这是您可以使用git本身而不是在某种高级编程语言中重写所有内容的最接近方式。

0

这只是一个普通的合并...

#!/bin/sh
git init
touch a; git add a; git commit -m a
git checkout -b patch
touch b; git add b; git commit -m b
touch c; git add c; git commit -m c
git checkout master
git merge --no-ff patch
git log --graph --oneline

结果

*   a9fbaec Merge branch 'patch'
|\
| * fb7eac4 c
| * 94f44c1 b
|/
* 4de57a5 a

但是如果在master分支中提交没有被压缩,那么git log就会显示所有提交记录。 - Zizheng Wu
谢谢指出,我已经编辑了我的问题,希望现在更清晰了。 - Zizheng Wu

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