问题
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
: cleanuprefs/rewritten
When
rebase -r
finishes it removes any refs underrefs/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 thatmerge -C <old-merge> topic
would pick up the new version of topic.
Unfortunatelyrefs/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