Git 与 --squash 合并,同时保留每次提交的日志

Git merge with --squash while keeping log of every commit

初始场景:

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

合并后我想要什么 --squash:

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 .

这只是一个常规合并...

#!/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

您无法使用您显示的结构获得您想要的日志结果(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 master 进行代码考古学(即 "why was this line written this way"),因为所有更改都被粉碎到一个差异中,这使得将一行更改与特定提交消息相关联变得更加困难。由于 masterdevelopment 的历史是分离的,因此无法确保开发中的所有内容都进入 master,反之亦然(例如,对 master 的热修复)。

git log mastergit log development 提供的信息,而且更难理解masterdevelopment 没有关联,并且失去了保留合并历史记录的所有好处。维护这个复杂的设置没有意义。


而是使用git merge --no-ff合并feature分支(不是一个连续的"development"分支)并保留分支历史,便于考古。

              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 是一个功能分支,用于处理尚未合并的炸玉米饼。

git log --graph --decorate master 将显示 master 的完整历史,合并提交显示功能何时结束,以及说明分支历史的行。 GUI Git 历史工具,例如 GitX 是另一种以图表形式阅读历史的方法。

说到底,Git历史是一个图表。如果您学会了如何使用该图表,使用 Git 的生活将会容易得多。如果你试图让 Git 历史变成线性的,就像一大堆薄煎饼,你就违背了 Git 的要点,并为自己和其他人创造额外的工作。

好吧,不可能 完全 您所要求的,就像您合并分支一样,无论如何您都会在提交日志中看到来自两个分支的提交。你可能想要的是这样的东西:

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

您只需从 development 分支中挑选提交,压缩它们并在 master.

之上应用

最简单的方法是这样的:

git checkout development && git rebase -i master HEAD

然后用 squash 替换除第一个操作之外的所有操作。结果,您将获得一个分离的头,指向压缩的提交,应用在 master 之上。您只需将 master 重置为该提交即可。

我想,提交消息将不是您想要的,因为它将重新排序所有提交的提交消息,而不是它们的哈希值,但是您将能够使用 --exec选项。

一切都有点过于手动,但您可以编写所有内容并创建别名。我相信,这是使用 git 本身最接近的结果,而不是用某种高级编程语言重写所有内容。