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 Conflict discovered in 'bar.c'. Select: (p) postpone, (df) diff-full, (e) edit, (h) help for more options:
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 U
pdated with changes from the
repository. The G
stands for merG
ed,
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 next two lines are part of a feature (new in Subversion
1.5) called interactive conflict resolution.
This means that the changes from the server overlapped with your own,
and you have the opportunity to resolve this conflict. The most commonly
used options are displayed, but you can see all of the options by typing
h
:
... (p) postpone - mark the conflict to be resolved later (df) diff-full - show all changes made to merged file (e) edit - change merged file in an editor (r) resolved - accept merged version of file (mf) mine-full - accept my version of entire file (ignore their changes) (tf) theirs-full - accept their version of entire file (lose my changes) (l) launch - launch external tool to resolve conflict (h) help - show this list
Let’s briefly review each of these options before we go into detail on what each option means:
p
)ostponeLeave the file in a conflicted state for you to resolve after your update is complete.
d
)iffDisplay the differences between the base revision and the conflicted file itself in unified diff format.
e
)ditOpen the file in conflict with your favorite editor, as
set in the environment variable EDITOR
.
r
)esolvedAfter editing a file, tell svn that you’ve resolved the conflicts in the file and that it should accept the current contents—basically, that you’ve “resolved” the conflict.
m
)ine-(f
)ullDiscard the newly received changes from the server and use only your local changes for the file under review.
t
)heirs-(f
)ullDiscard your local changes to the file under review and use only the newly received changes from the server.
l
)aunchLaunch an external program to perform the conflict resolution. This requires a bit of preparation beforehand.
h
)elpShow the list of all possible commands you can use in interactive conflict resolution.
We’ll cover these commands in more detail now, grouping them together by related functionality.
Before deciding how to attack a conflict interactively, odds are
that you’d like to see exactly what is in conflict, and the diff command
(d
) is what you’ll use for this:
... Select: (p) postpone, (df) diff-full, (e) edit, (h)elp for more options : d --- .svn/text-base/sandwich.txt.svn-base Tue Dec 11 21:33:57 2007 +++ .svn/tmp/tempfile.32.tmp Tue Dec 11 21:34:33 2007 @@ -1 +1,5 @@ -Just buy a sandwich. +<<<<<<< .mine +Go pick up a cheesesteak. +======= +Bring me a taco! +>>>>>>> .r32 ...
The first line of the diff content shows the previous contents
of the working copy (the BASE
revision); the next content line is your change; and the last
content line is the change that was just received from the server
(usually the HEAD
revision). With this information in
hand, you’re ready to move on to the next action.
There are four different ways to resolve conflicts interactively—two of which allow you to selectively merge and edit changes, and two of which allow you to simply pick a version of the file and move along.
If you wish to choose some combination of your local changes,
you can use the “edit” command (e
) to
manually edit the file with conflict markers in a text editor
(determined by the EDITOR
environment
variable). Editing the file by hand in your favorite text editor is a
somewhat low-tech way of remedying conflicts (see Merging conflicts by hand for a walkthrough), so some
people like to use fancy graphical merge tools instead.
To use a merge tool, you need to either set the SVN_MERGE
environment variable or define the merge-tool-cmd
option in your Subversion
configuration file (see Configuration Options
for more details). Subversion will pass four arguments to the merge
tool: the BASE
revision of the
file, the revision of the file received from the server as part of the
update, the copy of the file containing your local edits, and the
merged copy of the file (which contains conflict markers). If your
merge tool is expecting arguments in a different order or format,
you’ll need to write a wrapper script for Subversion to invoke. After
you’ve edited the file, if you’re satisfied with the changes you’ve
made, you can tell Subversion that the edited file is no longer in
conflict by using the “resolve” command (r
).
If you decide that you don’t need to merge any changes but just
want to accept one version of the file or the other, you can either
choose your changes (a.k.a. “mine”) by using the
“mine-full” command (mf
) or choose
theirs by using the “theirs-full” command (tf
).
This may sound like an appropriate section for avoiding marital
disagreements, but it’s actually still about Subversion, so read on.
If you’re doing an update and encounter a conflict that you’re not
prepared to review or resolve, you can type p
to
postpone resolving a conflict on a file-by-file basis when you run
svn update
. If you’re running an update and don’t
want to resolve any conflicts, you can pass the --non-interactive
option to
svn update, and any file in conflict will be marked with a
C
automatically.
The C
stands
for c
onflict. This
means that the changes from the server overlapped with your own, and
now you have to manually choose between them after the update has
completed. When you postpone a conflict resolution, svn typically does three things 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 that delimit the
“sides” of the conflict—into the file to visibly
demonstrate the overlapping areas. (Subversion uses the svn:mime-type
property to decide whether a file is capable of contextual,
line-based merging. See File Content Type to learn
more.)
For every conflicted file, Subversion places three extra unversioned files in your working copy:
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, the .mine file isn’t created, since it would be identical to the working file.)
OLDREV
This is the file that was the BASE
revision before you updated
your working copy. That is, it’s the file that you checked
out before you made your latest edits.
NEWREV
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.
Here, OLDREV
is the revision
number of the file in your .svn directory, and
NEWREV
is the revision number of the
repository HEAD
.
For example, Sally makes changes to the file sandwich.txt, but does not yet commit those changes. Meanwhile, Harry commits changes to that same file. Sally updates her working copy before committing and she gets a conflict, which she postpones:
$ svn update Conflict discovered in 'sandwich.txt'. Select: (p) postpone, (df) diff-full, (e) edit, (h)elp for more options : p C sandwich.txt Updated to revision 2. $ ls -1 sandwich.txt sandwich.txt.mine sandwich.txt.r1 sandwich.txt.r2
At this point, Subversion will 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
If you’ve postponed a conflict, you need to resolve the conflict
before Subversion will allow you to commit your changes. You’ll do
this with the svn resolve command
and one of several arguments to the --accept
option.
If you want to choose the version of the file that you last
checked out before making your edits, choose the
base
argument.
If you want to choose the version that contains only your edits,
choose the mine-full
argument.
If you want to choose the version that your most recent update
pulled from the server (and thus discard your edits entirely), choose
the theirs-full
argument.
However, if you want to pick and choose from your changes and
the changes that your update fetched from the server, merge the
conflicted text “by hand” (by examining and editing the
conflict markers within the file), and then choose the
working
argument.
svn resolve removes the three temporary files and accepts the version of
the file that you specified with the --accept
option,
and Subversion no longer considers the file to be in a state of
conflict:
$ svn resolve --accept working 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. You’re going to have to edit sandwich.txt to resolve the conflict. First, 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, equals 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 markers 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. 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.[6] Once you’ve agreed on the changes you will commit, 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 use svn resolve, and you’re ready to commit your changes:
$ svn resolve --accept working sandwich.txt Resolved conflicted state of 'sandwich.txt' $ svn commit -m "Go ahead and use my sandwich, discarding Sally's edits."
Note that svn resolve, unlike most of the other commands we deal with in this chapter, requires that you explicitly list any filenames that you wish to resolve. In any case, you want to be careful and use svn resolve only when you’re certain that you’ve fixed the conflict in your file—once the temporary files are removed, Subversion will let 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.
If you get a conflict and decide that you want to throw out your
changes, you can run svn resolve --accept theirs-full
and Subversion
will discard your edits and remove the temporary files:CONFLICTED-PATH
$ svn update Conflict discovered in 'sandwich.txt'. Select: (p) postpone, (df) diff-full, (e) edit, (h) help for more options: p C sandwich.txt Updated to revision 2. $ ls sandwich.* sandwich.txt sandwich.txt.mine sandwich.txt.r2 sandwich.txt.r1 $ svn resolve --accept theirs-full sandwich.txt Resolved conflicted state of 'sandwich.txt'
If you decide that you want to throw out your changes and start your edits again (whether this occurs after a conflict or at any time), just revert your changes:
$ svn revert sandwich.txt Reverted 'sandwich.txt' $ ls sandwich.* sandwich.txt
Note that when you revert a conflicted file, you don’t have to use svn resolve.