4.9. Resolve Conflicts (Merging Changes of Others)
We've already seen how svn status -u
can predict conflicts. Suppose you run svn update
and some interesting things occur:
$ svn update
U INSTALL
G README
C bar.c
Updated to revision 46.
The U
and G
codes are no cause for concern; those files cleanly absorbed changes from the repository. The files marked with U
contained no local changes but were Updated
with changes from the repository. The G
stands for merGed
, which means that the file had local changes to begin with, but the changes coming from the repository didn't overlap with the local changes.
But the C
stands for conflict. This means that the changes from the server overlapped with your own, and now you have to manually choose between them.
Whenever a conflict occurs, three things typically occur to assist you in noticing and resolving that conflict:
-
Subversion prints a C
during the update, and remembers that the file is in a state of conflict.
-
If Subversion considers the file to be mergeable, it places conflict markers — special strings of text which delimit the sides of the conflict—into the file to visibly demonstrate the overlapping areas. (Subversion uses the svn:mime-type property to decide if a file is capable of contextual, line-based merging.)
-
For every conflicted file, Subversion places three extra unversioned files in your working copy:
-
filename.mine
-- this is your file as it existed in your working copy before you updated your working copy — that is, without conflict markers. This file has only your latest changes in it. (If Subversion considers the file to be unmergeable, then the .mine
file isn't created, since it would be identical to the working file.)
-
filename.rOLDREV
-- this is the file that was the BASE
revision before you updated your working copy. That is, the file that you checked out before you made your latest edits. OLDREV
is the revision number of the file in your .svn
directory.
-
filename.rNEWREV
-- this is the file that your Subversion client just received from the server when you updated your working copy. This file corresponds to the HEAD
revision of the repository. NEWREV
is the revision number of the repository HEAD
.
For example, Sally makes changes to the file sandwich.txt
in the repository. Harry has just changed the file in his working copy and checked it in. Sally updates her working copy before checking in and she gets a conflict:
$ svn update
C sandwich.txt
Updated to revision 2.
$ ls -1
sandwich.txt
sandwich.txt.mine
sandwich.txt.r1
sandwich.txt.r2
At this point, Subversion does not allow Sally to commit the file sandwich.txt
until the three temporary files are removed:
$ svn commit -m "Add a few more things"
svn: Commit failed (details follow):
svn: Aborting commit: '/home/sally/svn-work/sandwich.txt' remains in conflict
To resolve a conflict do one of three things:
-
Merge the conflicted text by hand (by examining and editing the conflict markers within the file).
-
Copy one of the temporary files on top of the working file.
-
Run svn revert FILENAME
to throw away all of the local changes.
Once the conflict is resolved, let Subversion know by running svn resolved
. This removes the three temporary files and Subversion no longer considers the file to be in a state of conflict.
$ svn resolved sandwich.txt
Resolved conflicted state of 'sandwich.txt'
Merging conflicts by hand can be quite intimidating the first time you attempt it, but with a little practice, it can become as easy as falling off a bike.
Here's an example. Due to a miscommunication, you and Sally, your collaborator, both edit the file sandwich.txt
at the same time. Sally commits her changes, and when you go to update your working copy, you get a conflict and you're going to have to edit sandwich.txt
to resolve the conflicts. First, let's take a look at the file:
$ cat sandwich.txt
Top piece of bread
Mayonnaise
Lettuce
Tomato
Provolone
<<<<<<< .mine
Salami
Mortadella
Prosciutto
=======
Sauerkraut
Grilled Chicken
>>>>>>> .r2
Creole Mustard
Bottom piece of bread
The strings of less-than signs, equal signs, and greater-than signs are conflict markers, and are not part of the actual data in conflict. You generally want to ensure that those are removed from the file before your next commit. The text between the first two sets of markers is composed of the changes you made in the conflicting area:
<<<<<<< .mine
Salami
Mortadella
Prosciutto
=======
The text between the second and third sets of conflict markers is the text from Sally's commit:
=======
Sauerkraut
Grilled Chicken
>>>>>>> .r2
Usually you won't want to just delete the conflict markers and Sally's changes -- she's going to be awfully surprised when the sandwich arrives and it's not what she wanted. So this is where you pick up the phone or walk across the office and explain to Sally that you can't get sauerkraut from an Italian deli. Once you've agreed on the changes to check in, edit your file and remove the conflict markers.
Top piece of bread
Mayonnaise
Lettuce
Tomato
Provolone
Salami
Mortadella
Prosciutto
Creole Mustard
Bottom piece of bread
Now run svn resolved
, and you're ready to commit your changes:
$ svn resolved sandwich.txt
$ svn commit -m "Go ahead and use my sandwich, discarding Sally's edits."
Note that svn resolved
, unlike most of the other commands we deal with in this chapter, requires an argument. In any case, you want to be careful and only run svn resolved
when you're certain that you've fixed the conflict in your file -- once the temporary files are removed, Subversion lets you commit the file even if it still contains conflict markers.
If you ever get confused while editing the conflicted file, you can always consult the three files that Subversion creates for you in your working copy -- including your file as it was before you updated. You can even use a third-party interactive merging tool to examine those three files.