Git/Git 扩展中的“ squash”和“ fixup”有什么区别?

我已经使用 Git 扩展有一段时间了(它太棒了!) ,但我还没有找到一个简单的答案:

有时,在输入提交消息时,会出现输入错误。我的朋友向我展示了如何修复它(在 Git 扩展中) :

右键单击提交 > 高级 > 修复提交

enter image description here

然后我简单地勾选“修改”并重写我的消息,瞧! 我的提交消息被修复了。

然而,另一个选项“壁球提交”... 我一直想知道它是做什么的? !

我的问题是:

有人能简单地解释一下 Git/Git 扩展壁球承诺修复提交的确切区别吗?他们看起来有点... “相似”对我来说: enter image description here enter image description here

79726 次浏览

我不知道 Git 扩展具体用它来做什么,但是 git rebase有一个选项来自动压缩或修复压缩提交!或者装修!前缀:

   --autosquash, --no-autosquash
When the commit log message begins with "squash! ..." (or "fixup!
..."), and there is a commit whose title begins with the same ...,
automatically modify the todo list of rebase -i so that the commit
marked for squashing comes right after the commit to be modified,
and change the action of the moved commit from pick to squash (or
fixup).

Squash 和 fixup 之间的区别在于,在 rebase 期间,squash操作将提示您合并原始消息和 squash 提交的消息,而 fixup操作将保留原始消息并从 fixup 提交中丢弃消息。

简单地说,当重新设定一系列提交的基础时,每个标记为 squash的提交都给您机会将其消息作为 pickreword提交消息的一部分使用。

当您使用 fixup时,来自该提交的消息将被丢弃。

来自 Git-rebase 文档,“交互模式”部分:

如果您想将两个或多个提交合并为一个,请将第二个和随后的提交的命令“ pick”替换为“ squash”或“ fixup”。如果提交有不同的作者,则折叠的提交将归于第一次提交的作者。对于折叠的提交,建议的提交消息是将第一次提交的提交消息和使用“ squash”命令的提交消息连接起来,但是忽略了使用“ fixup”命令的提交消息。

我对 git 扩展进行了修改,但无法将许多提交压缩到一个扩展中。为此,我不得不求助于命令行,并发现 这篇文章很有帮助

git rebase -i Head~2

这是一个交互式 rebase,请注意:

  • 这里的 ~ 2是指您希望在这个操作中包含多少次提交,包括当前的头部
  • 你必须编辑随后的交互式编辑窗口,将第一项保留为“选择”,并将随后的行替换为“压扁”。如果这是不透明的,那么上面链接中的说明就清楚得多。

为什么不问问 Git 自己呢? 当你使用 git-bash重新设定基准时,它会显示:

pick 512b1d7 (some comment)
# Rebase 621b2e4..512b1d7 onto 621b2e4 (1 command)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
D:/code/fenixito-legacy-api/.git/rebase-merge/git-rebase-todo [unix] (11:57 23/10/2019)                                         1,1 start
"D:/code/xxx/.git/rebase-merge/git-rebase-todo" [UNIX] 27L, 1170C

所以你看:

S,squash = 使用 commit,但是要融入到以前的提交中

F,fixup = 像“ squash”一样,但是丢弃这个提交的日志消息

如果问题是在做 Git rebase ——交互式时,git 中 squashfixup的区别是什么,那么答案就是 提交消息

s, squash <commit> = 使用提交,但是要融入以前的提交

就像“南瓜”,但是丢弃这个提交的日志消息


例如:

pick 22a4667 father commit message
squash 46d7c0d child commit message # case 1
# fixup 46d7c0d child commit message # case 2

个案1重设基地后的 提交消息为:

father commit message


child commit message

而案例2中的提交消息是:

father commit message
# no sub messages

squashfixup都允许通过它们各自的 diff 引入代码更改(通常不会提到)

squashfixup之间的区别在于如何处理提交的 信息

fixup情况下,原始提交消息保持不变,并且忽略 fixup提交 信息中的任何文本(非正式地转储)。

squash的情况下,两个提交消息被一个接一个地合并(“合并”) ,并提供给用户的编辑器,这样它们就可以根据用户的需要合并/合并在一起。

最初的组合消息(压缩)将包含显示消息开始和结束位置的注释标记。可以应用多个 squashfixup

在讨论小代码更正(不需要更改消息)时,术语可能会令人困惑,因为通常有人会建议在实际上并不需要 squash的情况下将代码“压缩”进来。

除了以前的答案,我想强调的新选项,也可以与 fixup使用,进一步区分它与使用 squash

我正在使用 git version 2.32.1


让我们假设您正在将一个名为 my-new-feature的分支重定基于 main。 为了做到这一点,您正在使用命令 git rebase -i main进行交互式 rebase,同时您已经签出了 my-new-feature分支。

(如果您使用一个用于 Git 的 GUI,就像 OP 使用 Git 扩展一样,如果它们是最新的,那么它们也应该包含这些新选项)

运行 git rebase -i main将在编辑器中打开以下内容:

pick 996b0 work in progress
pick 59f3a feature finished


# Rebase b6914..59f3a onto b6914 (2 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
#                    commit's log message, unless -C is used, in which case
#                    keep only this commit's message; -c is same as -C but
#                    opens the editor
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified); use -c <commit> to reword the commit message

注意新的 -C | -c标志。

S,squash = 使用 commit,但是要融入到以前的提交中

F,fixup [-C |-c ] = 类似于“ squash”,但只保留前一个 提交的日志消息,除非使用-C,在这种情况下 只保留此提交的消息;-c 与-C 相同,但 打开编辑器


看看我们的两个承诺:

pick 996b0 work in progress
pick 59f3a feature finished

让我们假设您希望将它们融合在一起(可以使用 squashfixup完成) ,但是您只想保留第二个提交 feature finished的有用提交消息。


像过去一样使用 fixup:

pick 996b0 work in progress
f 59f3a feature finished

不会做我们想做的事。它将把提交融合在一起,但是留给我们的是 work in progress提交消息,这没有多大帮助。


我们可以这样使用 squash:

pick 996b0 work in progress
s 59f3a feature finished

这将打开一个新的编辑器,它将允许您手动注释掉第一个提交消息并持久化 feature finished提交消息。这确实有效,但是它强制您使用额外的手动步骤; 没人有时间做这个


这就是我们可以传递给 fixup的新选项非常方便的地方。

pick 996b0 work in progress
f -C 59f3a feature finished

这将把提交合并在一起,但是放弃 work in progress提交消息,只保留 feature finished提交消息。


这些新的选项使 fixup更加强大,并允许您在不需要时更少地依赖使用 squash