Search code examples
gitgit-rebasegit-merge-conflict

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


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?


Solution

  • 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'.