git rebase -i -autosquash 冲突
git rebase -i -autosquash conflict
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
为什么修复应用不干净?
为什么修复也包含“2”,它不应该在修复引入的补丁中,而是在前一个提交的补丁中。
我认为问题出在变化的背景上。看看这个提交:
$ 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
看到了吗?补丁不适用,因为上下文不匹配。所以出现了冲突,需要手动修复。
当你将 bugger 线一分为二时,补丁类似于:
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
现在,虽然匹配不完美,但至少上下文的第一行未修改的行匹配,因此合并可以继续。
当您执行 --fixup
时,您正在乱序应用补丁,因此上下文已经消失。在第一种情况下,您的补丁应用如下:
- 在第 1 行插入
1
- 在第 2、3 行
1
之后插入 This is\naBUG
- 删除第
a BUG
行,第4行,This is
之后,替换为NOT a BUG
- 在第 2 行
1
之后、This is
之前插入 2
第 2、3 步非常明确。尽管行号与步骤 3 中的预期不同,但上下文清楚地表明了这一点。第二种情况,
- 在第 1 行插入
1
- 在第 2 行
1
之后插入 This is a BUG
- 删除行
This is a BUG
,在第 3 行 2
之后替换为 This is NOT a BUG
- 在第 2 行
1
之后、This is a BUG
之前插入 2
在这种情况下,补丁 #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 merge
或 git mergetool
手动修复冲突,当 rebase 失败时按照提示进行操作。
您可以通过添加 -s recursive -X theirs
或 -s recursive -X ours
来指定策略来使 rebase "succeed"。但是,由于上下文冲突,您的修正在这两种情况下都会被破坏。
git is giving me a major headache when using --fixup and --autosquash.
请注意还有另一种情况,在使用 git rebase --autosquash
时会让人头疼。
Git 2.20(2018 年第 4 季度)刚刚修复了一个与 autosquash 相关的错误:“git rebase -i"
没有正确清除状态文件 当运行 "squash/fixup" 中止,然后用户手动修改了提交 ,已更正。
参见 commit 10d2f35, commit 2f3eb68 (31 Aug 2018) by Johannes Schindelin (dscho
)。
(由 Junio C Hamano -- gitster
-- in commit 87ae8a1 合并,2018 年 9 月 24 日)
rebase -i --autosquash: demonstrate a problem skipping the last squash
The git commit --squash
command can be used not only to amend commit
messages and changes, but also to record notes for an upcoming rebase.
For example, when the author information of a given commit is incorrect, a user might call git commit --allow-empty -m "Fix author" --squash <commit>
, to remind them to fix that during the rebase.
When the editor would pop up, the user would simply delete the commit message to abort
the rebase at this stage, fix the author information, and continue with git rebase --skip
. (This is a real-world example from the rebase of Git for Windows onto v2.19.0-rc1.)
However, there is a bug in git rebase
that will cause the squash message not to be forgotten in this case. It will therefore be reused in the next fixup/squash chain (if any).
rebase -i
: be careful to wrap up fixup/squash chains
When an interactive rebase was stopped at the end of a fixup/squash chain, the user might have edited the commit manually before continuing (with either git rebase --skip
or git rebase --continue
, it does not really matter which).
We need to be very careful to wrap up the fixup/squash chain also in this scenario: otherwise the next fixup/squash chain would try to pick up where the previous one was left.
在 Git 2.27(2020 年第 2 季度)之前,“rebase -i
”在重新排列具有应用另一个修复(可能是也可能不是修复)的修复的序列时出现段错误- 又一步)。
参见 commit 02471e7 (09 May 2020) by Johannes Schindelin (dscho
)。
(由 Junio C Hamano -- gitster
-- in commit a2a0942 合并,2020 年 5 月 14 日)
rebase --autosquash
: fix a potential segfault
Reported-by: Paul Ganssle
Helped-by: Jeff King
Signed-off-by: Johannes Schindelin
When rearranging the todo
list so that the fixups/squashes are reordered just after the commits they intend to fix up, we use two arrays to maintain that list: next
and tail
.
The idea is that next[i]
, if set to a non-negative value, contains the index of the item that should be rearranged just after the i
th item.
To avoid having to walk the entire next
chain when appending another fixup/squash, we also store the end of the next
chain in tail[i]
.
The logic we currently use to update these array items is based on the assumption that given a fixup/squash item at index i
, we just found the index i2
indicating the first item in that fixup chain.
However, as reported by Paul Ganssle, that need not be true: the special form fixup! <commit-hash>
is allowed to point to another fixup commit in the middle of the fixup chain.
Example:
* 0192a To fixup
* 02f12 fixup! To fixup
* 03763 fixup! To fixup
* 04ecb fixup! 02f12
Note how the fourth commit targets the second commit, which is already a fixup that targets the first commit.
Previously, we would update next
and tail
under our assumption that every fixup!
commit would find the start of the fixup!
/squash!
chain.
This would lead to a segmentation fault because we would actually end up with a next[i]
pointing to a fixup!
but the corresponding tail[i]
pointing nowhere, which would the lead to a segmentation fault.
Let's fix this by inserting, rather than appending, the item.
In other words, if we make a given line successor of another line, we do not simply forget any previously set successor of the latter, but make it a successor of the former.
In the above example, at the point when we insert 04ecb just after 02f12, 03763 would already be recorded as a successor of 04ecb, and we now "squeeze in" 04ecb.
To complete the idea, we now no longer assume that next[i]
pointing to a line means that last[i]
points to a line, too.
Instead, we extend the concept of last
to cover also partial fixup!
/squash!
chains, i.e. chains starting in the middle of a larger such chain.
In the above example, after processing all lines, last[0]
(corresponding to 0192a) would point to 03763, which indeed is the end of the overall fixup!
chain, and last[1]
(corresponding to 02f12) would point to 04ecb (which is the last fixup!
targeting 02f12, but it has 03763 as successor, i.e. it is not the end of overall fixup!
chain).
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
为什么修复应用不干净?
为什么修复也包含“2”,它不应该在修复引入的补丁中,而是在前一个提交的补丁中。
我认为问题出在变化的背景上。看看这个提交:
$ 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
看到了吗?补丁不适用,因为上下文不匹配。所以出现了冲突,需要手动修复。
当你将 bugger 线一分为二时,补丁类似于:
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
现在,虽然匹配不完美,但至少上下文的第一行未修改的行匹配,因此合并可以继续。
当您执行 --fixup
时,您正在乱序应用补丁,因此上下文已经消失。在第一种情况下,您的补丁应用如下:
- 在第 1 行插入
1
- 在第 2、3 行
1
之后插入 - 删除第
a BUG
行,第4行,This is
之后,替换为NOT a BUG
- 在第 2 行
1
之后、This is
之前插入
This is\naBUG
2
第 2、3 步非常明确。尽管行号与步骤 3 中的预期不同,但上下文清楚地表明了这一点。第二种情况,
- 在第 1 行插入
1
- 在第 2 行
1
之后插入 - 删除行
This is a BUG
,在第 3 行2
之后替换为 - 在第 2 行
1
之后、This is a BUG
之前插入
This is a BUG
This is NOT a BUG
2
在这种情况下,补丁 #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 merge
或 git mergetool
手动修复冲突,当 rebase 失败时按照提示进行操作。
您可以通过添加 -s recursive -X theirs
或 -s recursive -X ours
来指定策略来使 rebase "succeed"。但是,由于上下文冲突,您的修正在这两种情况下都会被破坏。
git is giving me a major headache when using --fixup and --autosquash.
请注意还有另一种情况,在使用 git rebase --autosquash
时会让人头疼。
Git 2.20(2018 年第 4 季度)刚刚修复了一个与 autosquash 相关的错误:“git rebase -i"
没有正确清除状态文件 当运行 "squash/fixup" 中止,然后用户手动修改了提交 ,已更正。
参见 commit 10d2f35, commit 2f3eb68 (31 Aug 2018) by Johannes Schindelin (dscho
)。
(由 Junio C Hamano -- gitster
-- in commit 87ae8a1 合并,2018 年 9 月 24 日)
rebase -i --autosquash: demonstrate a problem skipping the last squash
The
git commit --squash
command can be used not only to amend commit messages and changes, but also to record notes for an upcoming rebase.For example, when the author information of a given commit is incorrect, a user might call
git commit --allow-empty -m "Fix author" --squash <commit>
, to remind them to fix that during the rebase. When the editor would pop up, the user would simply delete the commit message to abort the rebase at this stage, fix the author information, and continue withgit rebase --skip
. (This is a real-world example from the rebase of Git for Windows onto v2.19.0-rc1.)However, there is a bug in
git rebase
that will cause the squash message not to be forgotten in this case. It will therefore be reused in the next fixup/squash chain (if any).
rebase -i
: be careful to wrap up fixup/squash chainsWhen an interactive rebase was stopped at the end of a fixup/squash chain, the user might have edited the commit manually before continuing (with either
git rebase --skip
orgit rebase --continue
, it does not really matter which).We need to be very careful to wrap up the fixup/squash chain also in this scenario: otherwise the next fixup/squash chain would try to pick up where the previous one was left.
在 Git 2.27(2020 年第 2 季度)之前,“rebase -i
”在重新排列具有应用另一个修复(可能是也可能不是修复)的修复的序列时出现段错误- 又一步)。
参见 commit 02471e7 (09 May 2020) by Johannes Schindelin (dscho
)。
(由 Junio C Hamano -- gitster
-- in commit a2a0942 合并,2020 年 5 月 14 日)
rebase --autosquash
: fix a potential segfaultReported-by: Paul Ganssle
Helped-by: Jeff King
Signed-off-by: Johannes SchindelinWhen rearranging the
todo
list so that the fixups/squashes are reordered just after the commits they intend to fix up, we use two arrays to maintain that list:next
andtail
.The idea is that
next[i]
, if set to a non-negative value, contains the index of the item that should be rearranged just after thei
th item.To avoid having to walk the entire
next
chain when appending another fixup/squash, we also store the end of thenext
chain intail[i]
.The logic we currently use to update these array items is based on the assumption that given a fixup/squash item at index
i
, we just found the indexi2
indicating the first item in that fixup chain.However, as reported by Paul Ganssle, that need not be true: the special form
fixup! <commit-hash>
is allowed to point to another fixup commit in the middle of the fixup chain.Example:
* 0192a To fixup * 02f12 fixup! To fixup * 03763 fixup! To fixup * 04ecb fixup! 02f12
Note how the fourth commit targets the second commit, which is already a fixup that targets the first commit.
Previously, we would update
next
andtail
under our assumption that everyfixup!
commit would find the start of thefixup!
/squash!
chain.
This would lead to a segmentation fault because we would actually end up with anext[i]
pointing to afixup!
but the correspondingtail[i]
pointing nowhere, which would the lead to a segmentation fault.Let's fix this by inserting, rather than appending, the item.
In other words, if we make a given line successor of another line, we do not simply forget any previously set successor of the latter, but make it a successor of the former.
In the above example, at the point when we insert 04ecb just after 02f12, 03763 would already be recorded as a successor of 04ecb, and we now "squeeze in" 04ecb.
To complete the idea, we now no longer assume that
next[i]
pointing to a line means thatlast[i]
points to a line, too.
Instead, we extend the concept oflast
to cover also partialfixup!
/squash!
chains, i.e. chains starting in the middle of a larger such chain.In the above example, after processing all lines,
last[0]
(corresponding to 0192a) would point to 03763, which indeed is the end of the overallfixup!
chain, andlast[1]
(corresponding to 02f12) would point to 04ecb (which is the lastfixup!
targeting 02f12, but it has 03763 as successor, i.e. it is not the end of overallfixup!
chain).