How to redo merge conflict resolution after working with unrelated commits that came before it with rebase -ir?

坚强是说给别人听的谎言 提交于 2021-02-08 08:53:16

问题


For a simple example, let's say we have a repo where an incorrect line was added to a file a.

$ git init 
Initialized empty Git repository in /someplace/.git/
$ echo "a1\nb2" > a
$ echo b1 > b    
$ git add .
$ git commit -m init  
[master (root-commit) 917f3b9] init
 2 files changed, 3 insertions(+)
 create mode 100644 a
 create mode 100644 b

The branch master then forks off new branch f.

$ git branch f

In master, we then add another line to the incorrect file.

$ echo b3 >> a
$ git commit -am 'added b3 to a' 
[master e44bc9f] added b3 to a
 1 file changed, 1 insertion(+)

In branch f, we do something unrelated, commit it,

$ git checkout f
$ echo c1 > c 
$ git add .
$ git commit -m "added c"
  1 label onto
[f 7cb63b2] added c
 1 file changed, 1 insertion(+)
 create mode 100644 c

then we move the line incorrectly placed in file a to file b.

$ echo b2 >> b
$ sed -i '/b2/d' a
$ git commit -am "moved b2 to where it really belongs"
[f 32cee76] moved b2 to where it really belongs
 2 files changed, 1 insertion(+), 1 deletion(-)

Then we try to merge master to f but are met with the conflict that master added another incorrect line while f removed the first.

$ git merge master 
Auto-merging a
CONFLICT (content): Merge conflict in a
Automatic merge failed; fix conflicts and then commit the result.
$ cat a
a1
<<<<<<< HEAD
=======
b2
b3
>>>>>>> master

To resolve the conflict, we move the second incorrect line to the correct file, and commit the merge.

$ echo b3 >> b
$ sed -i '/HEAD/,$d' a
$ git commit -a --no-edit 
[f 28d8543] Merge branch 'master' into f

Then, we realize the commit about c should not be in this branch, so we "remove" it with rebase -ir and are met with the same conflict as earlier.

$ git rebase -ir $(git merge-base master @) # remove the commit about c
Auto-merging a
CONFLICT (content): Merge conflict in a
Could not apply 28d8543... onto # Merge branch 'master' into f
$ cat a
a1
<<<<<<< HEAD
=======
b2
b3
>>>>>>> refs/rewritten/onto

So, what's the simplest, most effective and versatile solution here to redo the previous resolution, taking into account what was added to b which does not appear in the conflict?


回答1:


There is a git utility for recording how a conflict is resolved so that you can replay it later. It's called git rerere.

It's really meant for a specific use case - and as that use case is predicated on assumptions I disagree with, I almost never use git rerere.

I would also say that with a good workflow, the scenario you describe should be infrequent if not nonexistent, so I personally wouldn't start using rerere to record every conflict resolution just on the off chance that I'll need it again later.

But... as that's the question you've presented, git rerere is probably the "answer'.




回答2:


The best solution that comes to mind so far (that really isn't that great) is forgoing git and making a patch of the merge conflict resolution:

$ cp -a . ../$(basename $PWD)~
$ echo b3 >> b
$ sed -i '/HEAD/,$d' a
$ diff -ru ../$(basename $PWD)~ .
diff -ru ../t~/a ./a
--- ../t~/a 2019-03-08 12:54:34.701197573 -0800
+++ ./a 2019-03-08 12:54:56.761197321 -0800
@@ -1,6 +1 @@
 a1
-<<<<<<< HEAD
-=======
-b2
-b3
->>>>>>> refs/rewritten/onto
diff -ru ../t~/b ./b
--- ../t~/b 2019-03-08 12:54:34.701197573 -0800
+++ ./b 2019-03-08 12:54:54.044530686 -0800
@@ -1,2 +1,3 @@
 b1
 b2
+b3

I can save that patch somewhere and apply it whenever I need to solve this again. The problem with this is managing these patches and having the discipline to either forsee when I'll need to redo the conflict resolution or always make a patch of every conflict resolution.

I would prefer a solution that uses git to manage this, though.




回答3:


git rerere or making a patch of refs/rewritten/onto is necessary because, with Git 2.23, refs/rewritten/onto will be removed in the particular case where a git rebase -ir is aborted.
"git rebase --abort" used to leave refs/rewritten/ when concluding "git rebase -r", which has been corrected.

See commit d559f50, commit 37e9ee5, commit d3fce47, commit 7372eae (14 May 2019) by Phillip Wood (phillipwood).
(Merged by Junio C Hamano -- gitster -- in commit 88f95e4, 09 Jul 2019)

rebase --abort/--quit: cleanup refs/rewritten

When rebase -r finishes it removes any refs under refs/rewritten that it has created.
However if the user aborts or quits the rebase refs are not removed. This can cause problems for future rebases.

For example I recently wanted to merge a updated version of a topic branch into an integration branch so ran rebase -ir and removed the picks and label for the topic branch from the todo list so that

merge -C <old-merge> topic

would pick up the new version of topic.
Unfortunately refs/rewritten/topic already existed from a previous rebase that had been aborted so the rebase just used the old topic, not the new one.

The logic for the non-interactive quit case is changed to ensure buf is always freed.



来源:https://stackoverflow.com/questions/55070965/how-to-redo-merge-conflict-resolution-after-working-with-unrelated-commits-that

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!