You are viewing gitster

January 11th, 2009


Previous Entry Share Next Entry
11:51 pm - Fun with completing a merge
It probably is very stressful when your merge (either "git pull" from the origin, or "git merge anotherbranch" locally) results in a conflict for the first time.

First thing to notice is if the merge/pull did anything. Sometimes, people try to "git pull" or "git merge" while they have a lot of their own work uncommitted (aka "dirty work tree").

This is not CVS or Subversion where you "merge/update and then commit", risking your changes to be clobbered by humongous conflicts. You shouldn't be pulling or merging if you haven't got your own work into a good shape and committed them locally.

But novices can make this mistake, and "git pull" and "git merge" will stop without doing anything when your local uncommitted changes overlap with files that "git pull"/"git merge" may need to update. You can detect this situation by noticing messages like this:

error: Entry 'tree.c' not uptodate. Cannot merge.

In such a case, first clean up your act, and finish what you were doing before attempting a merge. It is a topic in another entry.

The topic of this entry, Completing a merge, is not about such a situation. What you would do after you really got into the merge and found that what you did and what they did conflict.

You will already have been told which paths conflicted, but if there were many, then

$ git ls-files -u

output will remind you.

Usually you will see three stages for each path, e.g.

 
100644cb61717685b09a2e409440206e27fce68831e04d1builtin-ls-tree.c
1006445b63e6eada5cd6de764acef694da624a70ce6dab2builtin-ls-tree.c
1006448a1db54f0f7bd803c4fd5537788c294c303c90c43builtin-ls-tree.c
 

but sometimes you will see only 2. stage #1 is the common ancestor, stage #2 is your history, and stage #3 is their history.

Here is a summary:
 
 
If you have...What happened is...
1 and 2 but not 3
They deleted but you modified
1 and 3 but not 2
They modified but you deleted
2 and 3 but not 1
You and they added the path independently
 
You can open such a conflicted file and will see lines delimited with <<<, === and >>> markers.  The lines between <<< and === are from your history, and the lines between === and >>> are from their history.  They show what each side modified starting from the common ancestor, and you often take the union of them, keeping the good bits of each side did.

It often suffices to view these conflicted lines to see what the best resolution is, but sometimes it is easier to see what each side did if you can see how the version in the common ancestor looked like.  You can ask git to use diff3 style:

$ git checkout --conflict=diff3 builtin-ls-tree.c

This inserts ||| marker before === marker.  The lines between <<< and ||| are yours, the lines between ||| and === are of the common ancestor.

After you fix up the file in your editor, save the result, and perform necessary testing to make sure the merge result is good.  Running "git diff" will show a combined diff that shows how your version and their version looked like and how your merge result now looks like.

$ git diff

When you are satisfied, then tell git that the contents in the file in the work tree is what you would want to record in the next commit you are going to create, by
 
$ git add builtin-ls-files.c

Note that if you saw only stage #1 and stage #3 (in other words, after you decided to remove that path, the other end kept modifying it), your merge resolution may be to delete it.  In such a case, obviously you do not want to say git add (which is about telling git that you want the contents in that path in the next commit).  You instead would want to tell git that you would want to remove that path from the next commit, so you would be saying git rm that_conflicted_file_that_should_be_removed here.

After repeating this for all the conflicted paths, you can commit the result.
 
$ git commit

and you are done.

One important lesson to take home is that git add is consistently the way to tell git that what you have in the specified path in the work tree is what you would want to record in the next commit you are about to create, (similarly, git rm is to tell git that you do not want that path to appear in the state the next commit records) and the conflict resolution is no exception.  There is no need to learn special command to mark a conflicted path resolved, and there is no such command as git resolved; you simply do not need one.

Tags:

(Leave a comment)

gitster's journal - Fun with completing a merge

> Recent Entries
> Archive
> Friends
> Profile

Links
Pages at the k.org
Gifts
貢ぎ物
The latest blog by Gitster

> Go to Top
LiveJournal.com