使用交互式 rebase 在分支中间修改提交需要解决冲突
Amended commit in middle of branch with interactive rebase requires conflict resolution
例如,master 分支有 init commit,其内容由以下人员创建:
echo "init" > data
我创建了功能分支:
git checkout -b feature
然后是串行提交的更改:
for newLine in feature1 feature2; do echo "$newLine" >> data; git commit -am "$newLine" ; done
我想更改 feature1 提交:
git rebase -i master
提交'edit' feature1,更改数据文件并继续变基:
sed -i 's/feature1/F/g' data; git add data; git rebase --continue
但是有冲突:
init
<<<<<<< HEAD
F
=======
feature1
feature2
>>>>>>> f4ac9dc... feature2
是否可以避免这种冲突。我只需要应用差异。结果将是:
init
F
feature2
不合适的解决方案:
git rebase -s recursive -X {ours,theirs} -i master
没有;至少,不可能(轻易)避免或忽略这种冲突。编辑太靠近了。
(以下是对 git 默认行为的描述。Leon 的回答展示了一种通过定义自定义合并工具来解决这些限制的方法。)
合并通过创建一个补丁(与 diff -u
一样)来表示一个分支中的更改,并将其应用到另一个分支(与 patch
一样)。为了应用补丁,context 必须相同。 (参见 )上下文,即 diff 中围绕实际删除和插入的几行,告诉修补工具它正在修补文件的正确部分。
Git 不会删除与原始文件中删除的行不完全匹配的行,并且不会插入与原始文件完全匹配的行之间的行。所以它不会在 'F' 之后插入 'feature2' 因为它在补丁中说 'feature2' 在 'feature1' 之后(而不是在 'F' 之后)。
合并时,每次冲突附近发生变化时,你都必须解决这个冲突。当您显然必须对连续几十次提交进行相同的更正时,这可能会变得乏味,但我不知道有什么好的解决方法。
在某些情况下,您可以使用不同的 merge strategy 让您的生活更轻松。请谨慎使用此功能,因为某些策略不如默认策略保守,并且可能很乐意破坏您的代码。无论如何,我认为没有可以解决示例问题的合并策略。
我能够想出一个自定义合并工具,它允许在以下假设下自动解决所描述场景的冲突:
修正变更和上游变更(如果有)仅限于更改的行(即没有添加或删除行)。
冲突完全是由于更改的对接(即更改不能重叠)。
然后,可以通过将以后的更改转换为 ed 脚本来解决此类冲突,该脚本通过绝对行号表示,并且 运行 该脚本位于文件的本地版本上。
下面的 dummymerge 脚本实现了这个想法。
必须在git注册如下:
git config mergetool.dummymerge.cmd 'dummymerge $BASE $LOCAL $REMOTE > $MERGED'
git config mergetool.dummymerge.trustExitCode true
使用它来自动解决描述类型的冲突,如下所示:
git mergetool -t dummymerge
虚拟合并
#!/bin/bash
myname=$(basename "[=12=]")
if [ $# -ne 3 ]
then
echo "Usage: $myname base_rev rev1 rev2"
exit 1
fi
diff -e "" ""|patch -e -o - ""
例如,master 分支有 init commit,其内容由以下人员创建:
echo "init" > data
我创建了功能分支:
git checkout -b feature
然后是串行提交的更改:
for newLine in feature1 feature2; do echo "$newLine" >> data; git commit -am "$newLine" ; done
我想更改 feature1 提交:
git rebase -i master
提交'edit' feature1,更改数据文件并继续变基:
sed -i 's/feature1/F/g' data; git add data; git rebase --continue
但是有冲突:
init
<<<<<<< HEAD
F
=======
feature1
feature2
>>>>>>> f4ac9dc... feature2
是否可以避免这种冲突。我只需要应用差异。结果将是:
init
F
feature2
不合适的解决方案:
git rebase -s recursive -X {ours,theirs} -i master
没有;至少,不可能(轻易)避免或忽略这种冲突。编辑太靠近了。
(以下是对 git 默认行为的描述。Leon 的回答展示了一种通过定义自定义合并工具来解决这些限制的方法。)
合并通过创建一个补丁(与 diff -u
一样)来表示一个分支中的更改,并将其应用到另一个分支(与 patch
一样)。为了应用补丁,context 必须相同。 (参见
Git 不会删除与原始文件中删除的行不完全匹配的行,并且不会插入与原始文件完全匹配的行之间的行。所以它不会在 'F' 之后插入 'feature2' 因为它在补丁中说 'feature2' 在 'feature1' 之后(而不是在 'F' 之后)。
合并时,每次冲突附近发生变化时,你都必须解决这个冲突。当您显然必须对连续几十次提交进行相同的更正时,这可能会变得乏味,但我不知道有什么好的解决方法。
在某些情况下,您可以使用不同的 merge strategy 让您的生活更轻松。请谨慎使用此功能,因为某些策略不如默认策略保守,并且可能很乐意破坏您的代码。无论如何,我认为没有可以解决示例问题的合并策略。
我能够想出一个自定义合并工具,它允许在以下假设下自动解决所描述场景的冲突:
修正变更和上游变更(如果有)仅限于更改的行(即没有添加或删除行)。
冲突完全是由于更改的对接(即更改不能重叠)。
然后,可以通过将以后的更改转换为 ed 脚本来解决此类冲突,该脚本通过绝对行号表示,并且 运行 该脚本位于文件的本地版本上。
下面的 dummymerge 脚本实现了这个想法。
必须在git注册如下:
git config mergetool.dummymerge.cmd 'dummymerge $BASE $LOCAL $REMOTE > $MERGED'
git config mergetool.dummymerge.trustExitCode true
使用它来自动解决描述类型的冲突,如下所示:
git mergetool -t dummymerge
虚拟合并
#!/bin/bash
myname=$(basename "[=12=]")
if [ $# -ne 3 ]
then
echo "Usage: $myname base_rev rev1 rev2"
exit 1
fi
diff -e "" ""|patch -e -o - ""