How svn merge
Works
The svn merge
command is used in several different ways, but fundamentally it always does two things in sequence: 1) create a diff by comparing two sources, and 2) apply that diff to a target.
The two sources and the target are specified by command-line arguments, though in many cases one or more arguments can be omitted if merge
can determine implicitly what they're supposed to be.
In general, then, the command-line invocation has the form
$ svn merge [create diff] [apply to target]
In more detail,
1) [create diff]
svn merge
compares source A and source B to create a diff, which is the set of instructions required to transform source A into source B.
The sources can be any element of the fileset: a trunk or branch, a directory subtree, or an individual file. A source can be either an element of the working copy (in which case it's identified by its filepath) or an element of the repository proper (in which case it's identified by its repo URL). A source can refer to any revision of the repository; if a revision isn't specified, the most recent revision (HEAD) is typically assumed.
2) [apply to target]
Once the diff is created, merge
will then apply that set of instructions to the target. The target must be an element of a working copy; the merge
command is usually assumed to be executed from within this working copy.
Obviously, if the target is completely unrelated to the two sources used to create the diff, then you'll just get nonsense when the diff instructions are applied to it. The target must also be the same "type of thing" as the two sources; that is, if the diff compares two branches or branch/trunk, then the target should also be either a branch or trunk. If the two sources are files, then the target should be a file, and so on.
This is why merge
commands must be constructed carefully. Don't sweat it too much, though: since the target must be an element of your working copy, merge
can only affect the working copy, not the repo. As long as you don't commit
without checking the result of the merge
, it's easy enough to fix if you merge
incorrectly.
Reverse merges
A "reverse merge" to revert a file is a form of cherry-pick merge, which takes two different revisions of a single element as its two sources. Thus, we specify only one source name on the command line, accompanied by -r
or -c
flags to say which two revisions will be the sources.
The -r
flag and its argument specify a revision, either as an absolute number N
or as a range N:M
.
The -c
flag specifies the change made by a revision. This is useful in some cases, but not in this one because the difference between the current file (rev 30) and the desired file (rev 10) spans many changes over 20 different revisions - I don't want to revert a single change, but as many as twenty!
In terms of my hypothetical samplefile.txt
,
[create diff] = -r 10:30 <URL of repository>/path/to/samplefile.txt
produces the set of instructions that transforms samplefile.txt@10
to samplefile.txt@30
. That's not what I want, though - I want the opposite, the set of instructions that takes the current samplefile.txt@30
back to samplefile.txt@10
. This can be specified by reversing the rev numbers, i.e.
[create diff] = -r 30:10 <URL of repository>/path/to/samplefile.txt
This is what makes it a "reverse merge". (Note that the -r
flag has nothing to do with the word "reverse".)
My target is the file samplefile.txt
within my working copy, or
[apply to target] = WC_root/filepath/to/samplefile.txt
Since the diff is constructed to take samplefile.txt@30
-> samplefile.txt@10
, the copy in my working copy must be the latest version (rev 30). This is important to check, because I'd previously updated the file to rev 10 in an intuitive but for some reason futile attempt to commit that content back to the repository as revision 31. So always be sure to update
before executing a merge
command.
Reverting a file using a reverse merge
Putting [create diff]
and [apply to target]
together, the command to revert samplefile.txt
from its rev 30 content to its rev 10 content using a reverse merge is
$ svn update
$ svn merge -r 30:10 <URL of repository>/path/to/samplefile.txt WC_root/filepath/to/samplefile.txt
If everything checks out, and the current content of samplefile.txt
is indeed that of revision 10, then I now commit to the repo:
$ svn commit -m "reverting file to rev 10" WC_root/filepath/to/samplefile.txt
to lock in the file reversion for the benefit of the other developers.
References:
The best general documentation I could find about how the merge
command is defined is given in the SVN Book here:
Merge Syntax
The manual sections linked by @bahrep give several examples of how you might use merge
to accomplish different tasks. (I can't repeat the links here because that puts me over the 2-link limit.)
And of course there's svn help merge
, which will be much clearer after reading those links (and, hopefully, this answer).
Many thanks to @alroc and @bahrep for helping me sort this out!