In order to preserve the conflict resolution history of merging from master branch to development branch, I do a commit of the state as-is, followed by another commit of conflict resolution.
In the first commit, I do nothing to resolve the conflict, so all the "<<<<<<<<=========>>>>>>>>" are kept in source code. In the second commit, I explicitly resolve the conflict.
The problem is, after I mark the file as resolved in the first commit, I don't know how to make a three-way merge on the second step. So how should I make git do a three-way merge on the files already marked merged?
答案 0 :(得分:1)
There's little to no value to doing things the way you are doing, so I recommend instead that you just complete the merge and commit it. Still, there is a way to do what you want. The problem is that you need to locate the correct three input files. The correct set of input files were computed and were available during the git merge
conflict, but are no longer available—you must re-compute them.
The three input files are:
--ours
or HEAD
or "local" version; and--theirs
or "remote" version.When you edited the work-tree file and saw:
auto-merged result text
<<<<<<< HEAD
some text
=======
different text
>>>>>>> their-branch
more auto-merged text
the text you were seeing was Git's best effort at combining these three inputs. The actual inputs were in the index at the time, as "stage 1", "stage 2", and "stage 3" respectively. If you ran git mergetool
, for instance, to complete the merge, the git mergetool
command used git checkout-index
to extract these three files (which it called BASE, LOCAL, and REMOTE).
When you committed the merge, Git erased the three inputs. They are no longer available. You can usually find them again. Here's where they came from:
The merge base file was extracted from the merge base commit.
To find the merge base commit hash ID, use git merge-base --all
. You'll also need the other two commit hash IDs. If the resulting merge base ID is, say, bbbbbbb
, you can then run:
git show bbbbbbb:path/to/file > file.base
to reconstruct the merge base copy of the file, in the current work-tree.
The left-hand-side file probably came out of the commit you were using at the time. (The exception to this rule occurs when you use a modified work-tree file as the left-hand-side file. In this case the left-hand-side version is not recoverable through Git at all.) So you'll need this hash ID. Let's say it's 1111111
; then:
git show 1111111:path/to/file > file.local
would re-create that input in the current work-tree.
Finally, the right-hand-side file came out of the commit you were merging. You'll need this last commit hash ID. Let's say it's 2222222
; then:
git show 2222222:path/to/file > file.remote
would re-create that input in the current work-tree.
All of this assumes that there were no file renames detected during the merge process. If there were such renames, choose the correct path names for each of the three input commits.
Once you have all three inputs, you can use the git merge-file
command to combine them, the same way git merge
would have. This will leave the merged result, complete with conflict markers, in one of those three files. See the linked documentation for details.
Finally, note that you can re-run the merge, by checking out the then-HEAD
commit as a detached HEAD. You can find both the then-HEAD and the other commit hash IDs by examining the merge commit: it has two parents, and those two parents are the left and right hand side commits respectively. So if the merge commit has hash ID $M
, then:
git checkout $M^1 # detach HEAD at left side commit
git merge $M^2 # re-execute the merge with right hand side commit
will repeat the work that git merge
did the first time, computing the same merge base and doing the same merge work and producing the same merge conflicts. See the gitrevisions documentation for more about the <revision>^1
and <revision>^2
notation. When the conflicts recur, the higher stage entries will be in your index once again.