如何在 Git 中压缩提交?
Posted
技术标签:
【中文标题】如何在 Git 中压缩提交?【英文标题】:How to squash commits in Git? 【发布时间】:2017-10-16 16:40:28 【问题描述】:我正在尝试压缩我的提交并将我的分支合并到 master 作为一个单一的提交。这就是我正在尝试的。我切换到 master 分支,然后我做
git merge --squash <branch_name>
我明白了
Automatic merge went well; stopped before committing as requested
Squash commit -- not updating HEAD
之后我提交。但是,这就是我得到的
$ git commit -m "Resolved"
On branch master
nothing to commit, working tree clean
由于某种原因,这些更改没有反映出来,我收到的消息没有提交。我已经在堆栈上浏览了许多帖子和问题,但直到现在都没有任何帮助。
【问题讨论】:
您说 我切换到 master 分支,然后 [运行git merge --squash <branch_name>
] 但随后您将 git commit
故障输出状态显示为 On branch sample
。那不是master
。有些东西不匹配。如果你正在做你告诉我们你正在做的事情,它会说On branch master
。
@torek 这是问题中的错字。我已经更正了。
Squash my last X commits together using Git的可能重复
【参考方案1】:
我并不完全清楚你正在合并什么提交,因此你为什么会得到这个结果。您可以通过以下方式向自己澄清第一个问题(它对我没有太大帮助,因为我没有提交):
git log --decorate --oneline --graph --boundary master...<branch-name>
(注意这里的三个点)。这将显示您现在在 master
上的提交,以及您将从 <branch-name>
通过这两个分支的合并基础提交引入的提交。
不过,无论如何,我可以做出一个很好的猜测,因为git merge
的工作方式是将这个合并基础与两个分支提示进行比较。这是在进行合并之前的示例图片段(此时,这是常规合并还是壁球合并都没有关系):
...--B--C--D <-- master (HEAD)
\
E--F--G <-- feature
每个大写字母代表一个提交(其真实 ID 是 Git 哈希 ID,例如 a9f3c72
或其他)。 merge base 这里的提交是提交B
:它是提交链的位置,同时从master
和feature
开始并向后工作(在此图中向左),首先走到一起。换句话说,提交B
是both 分支master
和 分支feature
上的最新提交。这就是使它成为合并基础提交的原因。
Git 现在实际上将运行:
git diff B D # see what "we" did on branch master
git diff B G # see what "they" did on branch feature
然后,Git 必须合并这些更改:如果我们更改 README
以在末尾添加一行,Git 应该将这个额外的行添加到末尾。如果他们以某种方式更改了foo.py
(可能是添加了一行并删除了另一行),Git 应该将他们的更改更改为foo.py
。但是,如果我们都做完全相同的事情,Git 应该只获取该更改的一个副本。例如,如果我们将master
上的相同更改为foo.py
,我们毕竟不需要他们的更改:它已被我们的更改所涵盖。 p>
假设我们更改了README
,我们和他们都修复了foo.py
中的相同内容,但他们也更改了doc.txt
和main.py
。所以我们的最后一组更改是保留我们在README
中添加的行,保留我们的foo.py
更改,并选择doc.txt
和main.py
更改。效果是 Git 将所有这些应用到合并基础提交 B
的内容。 这为我们提供了新提交 H
的内容。(请注意 H
中的内容,因为它可能会再次困扰我们。)Git 更新索引(下一次提交的位置make go) 和工作树(我们可以在其中看到将要提交或已提交的内容)到此新内容,准备提交。
现在常规 vs 壁球合并突然变得很重要,因为如果 Git 要进行 常规 合并提交,它会这样做:
...--B--C--D---H <-- master (HEAD)
\ /
E--F--G <-- feature
这个新的merge commit H
结合了所有在提交C-D
中完成的工作和所有在提交E-F-G
中完成的工作,将指向both em> 提交D
,master
的前一个提示,并提交G
,feature
的前一个也是当前的提示。
如果 Git 要进行 squash 提交,但是,它会在说完之后停止:
Automatic merge went well; stopped before committing as requested
Squash commit -- not updating HEAD
$
它让我们做出提交。一旦我们做出这个提交,我们就会得到新的提交 H
,但是这一次它没有指向 both D
和 G
。这一次,新的提交 H
指向 only 到 D
:
...--B--C--D---H <-- master (HEAD)
\
E--F--G <-- feature
假设这一切都按照它应该的方式工作,我们确实实际上是提交H
。这就产生了我认为最有可能的情况。
可能的情况
让我们看看如果我们再次运行git merge --squash feature
会发生什么。
Git 和以前一样开始,通过找到合并基础:分支 master
和 feature
的连接点。再次提交 B
。
现在 Git 区分两个分支提示。这次master
的tip是H
,所以两个diff是:
git diff B H
git diff B G
Git 现在会合并这些更改。这一次,我们更改了README
、foo.py
、doc.txt
和main.py
。 (请记住,这些是我们所说的通过合并所有内容而得到的更改。)同时它们(在feature
中)以与我们相同的方式更改了foo.py
,以与我们相同的方式更改了doc.txt
,并更改了main.py
和我们一样。
因此,Git 接受了我们所有的更改,而不是他们的。结果完全匹配提交H
。 Git 现在停止并显示与以前相同的消息。
这一次,当我们运行时:
git commit
为了完成任务,Git 将我们的索引(我们为提交暂存的内容)与我们的HEAD
提交进行比较,发现它们完全、完全、100% 相同。 我们已经完成了来自feature
的所有工作。Git 说“没有什么要提交”和“工作树干净”,因为没有什么要提交并且工作树与索引匹配。
可能性较小的可能性
我们可以在这里获得相同效果的另一种方法是,如果不首先进行壁球提交H
,则提交系列E-F-G
“撤消自身”足以使其无关紧要。例如,假设F
是对foo.py
的匹配更改(它可能是提交C
的副本),但提交G
是提交E
的恢复。现在,从 B
到 G
的更改总和不再涉及 doc.txt
和 main.py
, 包含在我们原来的 B
-to-D
更改中。 git merge --squash
已提交合并,但同样对最终的源树没有影响。我们的索引和工作树将匹配提交 G
和 git commit
根本不会做出新的提交 H
。
就“提交差异”而言,这与以前的情况相同:在另一个分支上引入的任何更改(如果有),我们已经有了。但这次我们没有通过 squash-merging 获得它:反正我们已经拥有它了。
【讨论】:
【参考方案2】:如果您想压缩提交,请转到 HEAD(last) 提交,然后:
git reset --soft HEAD~2 && git commit
输入此命令后,您将被要求将提交消息写入新提交。 此命令将使用您编写的新提交消息将您的最后两个提交压缩为一个。
然后合并你的分支,它是如何描述的here。
【讨论】:
以上是关于如何在 Git 中压缩提交?的主要内容,如果未能解决你的问题,请参考以下文章