Resolve Conflicts (Merging Others’ Changes)

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 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 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)ostpone

Leave the file in a conflicted state for you to resolve after your update is complete.

(d)iff

Display the differences between the base revision and the conflicted file itself in unified diff format.

(e)dit

Open the file in conflict with your favorite editor, as set in the environment variable EDITOR.

(r)esolved

After 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)ull

Discard the newly received changes from the server and use only your local changes for the file under review.

(t)heirs-(f)ull

Discard your local changes to the file under review and use only the newly received changes from the server.

(l)aunch

Launch an external program to perform the conflict resolution. This requires a bit of preparation beforehand.

(h)elp

Show 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.

Viewing conflict differences interactively

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.

Resolving conflict differences interactively

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).

Postponing conflict resolution

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 conflict. 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:

    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, 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, it’s the file that you checked out before you made your latest edits.

    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.

    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

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.

Discarding your changes in favor of a newly fetched revision

If you get a conflict and decide that you want to throw out your changes, you can run svn resolve --accept theirs-full CONFLICTED-PATH and Subversion will discard your edits and remove the temporary files:

$ 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'

Punting: using svn revert

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.



[6] And if you ask them for it, they may very well ride you out of town on a rail.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset