5

git 在使用 --fixup 和 --autosquash 时让我非常头疼。我想举两个例子,一个工作得很好,另一个是一团糟。(git 版本 2.6.2)

工作示例:

第一次提交:

$ git init
$ printf '1\n' > test.file
$ git add test.file
$ git commit -m 'Insert 1 --> first line'  
$ cat test.file
1

第二次提交(BUG):

$ printf 'This is\na BUG\n' >> test.file
$ git commit -am 'Added line 2 and 3 with BUG'
$ cat test.file
1  
This is  
a BUG

第三次提交:

$ sed -i '2i 2' test.file
$ git commit -am 'Insert 2 --> second line'
$ cat test.file
1  
2  
This is  
a BUG

第四次提交(修复):

$ sed -i 's/a BUG/NOT a BUG/' test.file
$ git add test.file
$ git log --oneline
b021696 Insert 2 --> second line  
2e18b8d Added line 2 and 3 with BUG  
d7b60a1 Insert 1 --> first line  
$ git commit --fixup HEAD~
$ cat test.file
1  
2  
This is  
NOT a BUG

变基:

$ git log --oneline
fe99989 fixup! Added line 2 and 3 with BUG  
b021696 Insert 2 --> second line  
2e18b8d Added line 2 and 3 with BUG  
d7b60a1 Insert 1 --> first line

$ git rebase -i --autosquash HEAD~3

[detached HEAD 6660b0e] Added line 2 and 3 with BUG
Date: Tue Nov 3 13:28:07 2015 +0100
1 file changed, 2 insertions(+)
Successfully rebased and updated refs/heads/master.

头痛示例:(唯一的区别是 BUGGY 提交是单行)

第一次提交:

$ git init
$ printf '1\n' > test.file
$ git add test.file
$ git commit -m 'Insert 1 --> first line'  
$ cat test.file
1

第二次提交(BUG):

$ printf 'This is a BUG\n' >> test.file
$ git commit -am 'Added line 2 with BUG'
$ cat test.file
1
This is a BUG

第三次提交:

$ sed -i '2i 2' test.file
$ git commit -am 'Insert 2 --> second line'
$ cat test.file
1  
2  
This is a BUG

第四次提交(修复):

$ sed -i 's/a BUG/NOT a BUG/' test.file
$ git add test.file
$ git log --oneline
2b83fe7 Insert 2 --> second line  
62cdd05 Added line 2 with BUG  
0ee3343 Insert 1 --> first line
$ git commit --fixup HEAD~
$ cat test.file
1  
2  
This is NOT a BUG

变基:

$ git log --oneline
c3d3db7 fixup! Added line 2 with BUG  
2b83fe7 Insert 2 --> second line  
62cdd05 Added line 2 with BUG  
0ee3343 Insert 1 --> first line
$ git rebase -i --autosquash HEAD~3
error: could not apply c3d3db7... fixup! Added line 2 with BUG

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".

Could not apply c3d3db78440e48c1bb637f78e0767520db65ea1e... fixup! Added line 2 with BUG

$ git status
interactive rebase in progress; onto 0ee3343
Last commands done (2 commands done):
   pick 62cdd05 Added line 2 with BUG
   fixup c3d3db7 fixup! Added line 2 with BUG
Next command to do (1 remaining command):
   pick 2b83fe7 Insert 2 --> second line
  (use "git rebase --edit-todo" to view and edit)
You are currently rebasing branch 'master' on '0ee3343'.
  (fix conflicts and then run "git rebase --continue")
  (use "git rebase --skip" to skip this patch)
  (use "git rebase --abort" to check out the original branch)

Unmerged paths:
  (use "git reset HEAD <file>..." to unstage)
  (use "git add <file>..." to mark resolution)

        both modified:   test.file

no changes added to commit (use "git add" and/or "git commit -a")  

$ cat test.file
1 
<<<<<<< HEAD
This is a BUG
======= 
2 
This is NOT a BUG
>>>>>>> c3d3db7... fixup! Added line 2 with BUG

为什么修复程序不能干净地应用?

为什么 fixup 还包含“2”,它不应该在 fixup 引入的补丁中,而是在前一次提交的补丁中。

4

3 回答 3

3

我认为问题在于变化的背景。看看这个提交:

$ git show c3d3db7
diff --git a/test.file b/test.file
index 7a103db..8c8e69a 100644
--- a/test.file
+++ b/test.file
@@ -1,3 +1,3 @@
 1
 2
-This is a BUG
+This is NOT a BUG

并且您想将此补丁应用于包含以下内容的文件:

1
This is a BUG

看?补丁不适用,因为上下文不匹配。因此出现了冲突,您必须手动修复它。


当您将臭虫线分成两部分时,补丁类似于:

diff --git a/test.file b/test.file
--- a/test.file
+++ b/test.file
@@ -1,3 +1,3 @@
 1
 2
 This is
-a BUG
+NOT a BUG

文件是:

1
This is
a BUG

现在,虽然匹配并不完美,但至少上下文中未修改的第一行匹配,因此合并可能会继续。

于 2015-11-03T14:45:04.083 回答
3

git 在使用 --fixup 和 --autosquash 时让我非常头疼。

注意还有另一种情况,使用时git rebase --autosquash,头痛。

Git 2.20(2018 年第四季度)刚刚修复了一个与 autosquash 相关的错误:“在中止运行“squash/fixup”然后用户手动修改提交时git rebase -i",没有正确清除状态文件,这已得到纠正。

请参阅Johannes Schindelin ( ) 的提交 10d2f35提交 2f3eb68(2018 年 8 月 31 日(由Junio C Hamano 合并——提交 87ae8a1中,2018 年 9 月 24 日)dschogitster

rebase -i --autosquash:演示跳过最后一个壁球的问题

git commit --squash命令不仅可以用于修改提交消息和更改,还可以为即将到来的 rebase 记录注释。

例如,当给定提交的作者信息不正确时,用户可能会调用git commit --allow-empty -m "Fix author" --squash <commit>,以提醒他们在 rebase 期间修复该问题。当编辑器弹出时,用户只需删除提交消息即可在此阶段中止 rebase,修复作者信息,然后继续git rebase --skip。(这是从 Git for Windows 到 v2.19.0-rc1 的 rebase 的真实示例。)

但是,有一个错误git rebase会导致在这种情况下不会忘记壁球消息。因此,它将在下一个修复/壁球链(如果有)中重用。

rebase -i:小心包装修复/壁球链

当交互式变基在修复/壁球链结束时停止时,用户可能在继续之前手动编辑了提交(使用git rebase --skipgit rebase --continue,哪个并不重要)。

在这种情况下,我们也需要非常小心地包装 fixup/squash 链:否则下一个 fixup/squash 链将尝试在前一个离开的地方拾取。


在 Git 2.27(2020 年第二季度)之前,“ rebase -i”在重新排列具有应用另一个修复(可能是也可能不是另一个步骤的修复)的修复的序列时出现段错误。

请参阅Johannes Schindelin ( ) 的提交 02471e7(2020 年 5 月 9 日(由Junio C Hamano 合并 -- --提交 a2a0942中,2020 年 5 月 14 日)dscho
gitster

rebase --autosquash:修复潜在的段错误

报告人:Paul Ganssle
帮助人:Jeff King
签字人:Johannes Schindelin

当重新排列todo列表以便在他们打算修复的提交之后重新排序修复/压缩时,我们使用两个数组来维护该列表:nexttail.

这个想法是next[i],如果设置为非负值,则包含应在i第 th 项之后重新排列的项的索引。

为了避免next在附加另一个修复/壁球时必须遍历整个链,我们还将next链的末端存储在tail[i].

我们目前用来更新这些数组项的逻辑是基于这样的假设,即在 index 处给定一个修复/压缩项i,我们刚刚找到i2指示该修复链中第一项的索引。

然而,正如 Paul Ganssle 所报道的,这不一定是真的:fixup! <commit-hash>允许特殊形式指向修复链中间的另一个修复提交。

例子:

* 0192a To fixup
* 02f12 fixup! To fixup
* 03763 fixup! To fixup
* 04ecb fixup! 02f12

请注意第四次提交如何针对第二次提交,这已经是针对第一次提交的修复。

以前,我们会更新nexttail假设每个fixup!提交都会找到fixup!/squash!链的开始。
这将导致分段错误,因为我们实际上最终会next[i]指向 afixup!但相应的tail[i]指向无处,这将导致分段错误。

让我们通过插入而不是附加项目来解决这个问题。

换句话说,如果我们将给定的行作为另一行的后继者,我们不会简单地忘记后者的任何先前设置的后继者,而是使其成为前者的后继者。

在上面的例子中,当我们在 02f12 之后插入 04ecb 时,03763 已经被记录为 04ecb 的后继,我们现在“挤入”04ecb。

为了完成这个想法,我们现在不再假设next[i]指向一条线意味着也last[i]指向一条线。
相反,我们扩展了 的概念last以涵盖部分fixup!/squash!链,即从更大的此类链的中间开始的链。

在上面的例子中,在处理完所有行之后,last[0](对应于 0192a)会指向 03763,这确实是整个fixup!链的末端,并且last[1](对应于 02f12)会指向 04ecb(这是最后一个fixup!定位 02f12,但它有 03763 作为后继,即它不是整个fixup!链的末端)。

于 2018-09-25T18:41:57.550 回答
2

当您执行 a--fixup时,您正在无序地应用补丁,因此上下文已经消失。在第一种情况下,您的补丁应用如下:

  1. 1在第 1 行插入
  2. This is\naBUG在第 2、3 行之后插入1
  3. 删除 line a BUG, on line 4, after This is, 替换为NOT a BUG
  4. 2在第 2 行之后1,之前插入This is

步骤 2、3 非常明确。即使行号与步骤 3 中的预期不同,上下文也清楚地表明了这一点。在第二种情况下,

  1. 1在第 1 行插入
  2. This is a BUG在第 2 行之后插入1
  3. 删除行This is a BUG,替换为This is NOT a BUG第 3 行之后的行2
  4. 2在第 2 行,之后1,之前插入This is a BUG

在这种情况下,补丁 #3 是不可能的,因为This is a BUG它没有出现在第 3 行并且它之前的行没有出现2。由于缺少上下文,Git 不假定在这种情况下第 2 行是正确的。

解决此问题的最简单方法是重新排列变基的顺序以反映您实际在做什么。而不是原来的顺序:

pick 5ef0459 Added line 2 with BUG
fixup ed5cd81 fixup! Added line 2 with BUG
pick 20e104e Insert 2 --> second line

切换最后两个元素为补丁提供所需的上下文:

pick 5ef0459 Added line 2 with BUG
pick 20e104e Insert 2 --> second line
fixup ed5cd81 fixup! Added line 2 with BUG

在这种情况下,您可能需要将-k标志添加到命令行以保留最后一次提交,该提交基本上是空的:

$ git rebase -i -k --autosquash HEAD~3
 Date: Tue Nov 3 10:45:40 2015 -0500
 1 file changed, 2 insertions(+), 1 deletion(-)
Successfully rebased and updated refs/heads/master.
$ cat test
1
2
This is NOT a BUG

另一种选择当然是使用git mergeor手动修复冲突git mergetool,在 rebase 失败时按照提示进行操作。

-s recursive -X theirs您可以通过添加或-s recursive -X ours指定策略使变基“成功” 。但是,由于上下文冲突,您的修复在这两种情况下都会被破坏。

于 2015-11-03T15:50:08.187 回答