Subversion notes
Just a collection of SVN stuff that I've found useful... I've tried to make all the links relative to v1.8. Manual here. Basic work cycle described here.
Page Contents
TODOs
http://stackoverflow.com/questions/14889395/concrete-sample-merges-of-git-that-wont-work-in-svn< http://svn.apache.org/repos/asf/subversion/trunk/notes/skip-deltas https://stackoverflow.com/questions/6296284/svn-list-files-committed-for-a-revision http://svnbook.red-bean.com/en/1.7/svn.ref.svn.c.mergeinfo.html https://pythonconquerstheuniverse.wordpress.com/2011/03/16/learning-subversion-the-mystery-of-svn-2/ http://svnbook.red-bean.com/en/1.7/svn.branchmerge.basicmerging.html#svn.branchmerge.basicmerging.mergeinfo
Install On Ubuntu Or Upgrade
sudo apt-get install subversion --upgrade
Diffs
On The Fly Diff Tool
To use a specific diff tool from the command line use:
svn diff some.file --diff-cmd meld svn diff some.file --diff-cmd kdiff3 etc...
Make SVN use P4Merge For Diff
I really like the Perforce merge and diff tools so after a bit of searching around now use the following script on my Linux box
#!/bin/sh LEFT_FILE_DESCR=$3 RIGHT_FILE_DESCR=$5 LEFT=$6 RIGHT=$7 echo STARTING DIFF echo LEFT IS "$LEFT_FILE_DESCR" echo RIGHT IS "$RIGHT_FILE_DESCR" p4merge -nl "$LEFT_FILE_DESCR" -nr "$RIGHT_FILE_DESCR" "$LEFT" "$RIGHT"
Save the above script somewhere on your PATH. I called it p4_diff_helper.sh. Then in the subversion config file (~/.subversion/config for linux and %appdata%\subversion\config for windows) add in the diff command under the [helpers] section.
[helpers] <snip> ### Set diff-cmd to the absolute path of your 'diff' program. ### diff-cmd = diff_program (diff, gdiff, etc.) ### diff-cmd = kdiff3 diff-cmd = p4_diff_helper.sh
This took me quite a while the first time round, reading on various threads, then I found out that the "Using External Differencing and Merge Tools" section of the Red Book had the answer all along, so use that link for more information :)
Doing the same for a merge is even easier:
#!/bin/sh BASE=$1 THEIRS=$2 MINE=$3 MERGED=$4 WCPATH=$5 p4merge "$BASE" "$MINE" "$THEIRS" "$MERGED"
Again, assuming you save the above on the PATH as p4_merge_helper.sh, modify the subversion config file (~/.subversion/config for linux and %appdata%\subversion\config for windows) add in the merge command under the [helpers] section.
[helpers] <snip> merge-tool-cmd = p4_merge_helper.sh
SVN Log
Only Report Changes After Branch
snv log --stop-on-copy
Get More Merge Information
Use the -g flag (and also useful is the verbosity flag -v) to get more information in the log dump. The -g flag adds extra information into the trace for instances where merges where made back into the line, or where the line was copied out to another branch.
Properties
Too see changes in properties use the following command:
svn log -v --diff --depth=empty .
Grep the output for "Modified: svn:externals"
SVN Ignore Files
The following will ignore all *.orig files from the current directory and all subdirectories
svn propset svn:ignore '*.orig' . --recursive
It's usually a good idea to use svn propedit
instread of propset
if you want
to ignore many things. To use this make sure you have set your SVN_EDITOR
environment
variable to, for example, gvim -f
on Windows or gvim --no-fork
on Linux.
From SVN 1.8 onwards there is a new property svn:global-ignores
:
global-ignores ... is a whitespace-delimited collection of file patterns. The Subversion client checks these patterns against the names of the files that are candidates for addition to version control, as well as to unversioned files that the svn status command notices. If any file's name matches one of the patterns, Subversion will basically act as if the file didn't exist at all...
Diffs and Revision Keywords
Normally, having looked at the log you would diff 2 revisions by executing:
svn diff -r aaa:bbb
However, for some easy tasks, there are special revision keywords that can be used [Ref] when referring to a working copy path:
HEAD
- The latest commit.BASE
- The revision number of an item in a working copy. If the item has been locally modified, this refers to the way the item appears without those local modifications.COMMITTED
- The most recent revision prior to, or equal to,BASE
, in which an item changed.PREV
-COMMITTED - 1
.
For example:
$ svn diff -r PREV:COMMITTED foo.c # shows the last change committed to foo.c
Branch, Checkout, Merge and Reintegrate
Branching & Checkout
From SVN manual: Subversion has no internal concept of a branch
- it knows only how to make copies ... that happens to carry
some extra historical information.
To branch off, for example trunk, to new workshpace:
svn cp svn+ssh://myserver.com/trunk/src svn+ssh://myserver.com/0_developers/my_name/branch_name -m "a description" cd /where/you/want/to/checkout/to svn co svn+ssh://myserver.com/0_developers/my_name/branch_name
Checkout At Specific Revision
svn up -r1234
Merging
To update my local branch with changes from trunk make sure branch is cleann no local modifications and up to date.
cd /where/you/want/to/checkout/to svn merge ^/trunk/src
Note that this has only affected your working copy! The changes have not been submitted to the repository yet. For that you must do an svn commit. This is also known as a sync merge.
Useful tips from the Red Book...
svn mergeinfo --show-revs=merged ^/trunk ^/branches/0_developers/my_name/branch_name svn mergeinfo --show-revs=eligible ^/trunk ^/branches/0_developers/my_name/branch_name
The first line reports a summary of changelists where trunk was merged to branch. The second line reports a summary of changelists where trunk has not yet been merged to branch
If a merge doesn't work out and you have not yet committed it, to revert the merge just use:
svn revert -R .
Reintegrating
To re-integrate to trunk, first make sure dev-line is up to date by merging in from trunk again and committing any changes resulting from the merge back to the repo.
Don't forget the --dry-run flag... tries the operation, shows you the output, but make no actual changes. It's a nice way to see what your merge will do before you actually do it!
Pre v1.8
cd /where/truck/is/checked/out svn up svn merge --reintegrate ^/0_developers/my_name/branch_name ... test ... svn commit -m "message"
For subversion < v1.8, the --reintegrate option is critical for reintegrating changes from a branch back into its original line of development so that only changes unique to your branch are copied back!
Pre v1.8: The [--reintegrate] option is critical for reintegrating changes from a branch back into its original line of developmentādon't forget it! ... By specifying the --reintegrate option, you're asking Subversion to carefully replicate only those changes unique to your branch.
-- See
"Svn Red Bean".
Pre v1.8: Once you have reintegrated a branch into trunk, it is no longer "alive". It
is generally unfit for futher use after it has been reintegrated!
-- See
"Keeping a Reintegrated Branch Alive"
for further information.
It is possible, pre 1.8, to keep the re-integrated branch alive. But, you must do this as soon as you have done the original reintegrate!
SVN 1.8 onwards
Joy! None of the above problems occur in v1.8... it is is smarter! Merging
back into trunk lines is now called an "automatic reintegrate".
You do not need to use --reintegrate and the branch
remains useable after the merge...
Your trunk working copy cannot have any local edits, switched paths, or contain a mixture of revisions so commit any pending edits and then svn update the branch.
cd /where/truck/is/checked/out svn up svn merge ^/0_developers/my_name/branch_name ... test ... svn commit -m "message"
Resolving Conflicts
This section of the Red Book covers this.
This SO thread discusses the difference between left, right, working etc in merge conflicts.
Evil Twins
Evil twins are two files with the same name and location in two different branches that do not have a common ancestor. For example, if B was a branch of A and in A I create the file dummy.txt, then file-copy (not svn cp) that file into B and then try to reintegrate B to A, SVN will throw a wobbly.
Why is this? It is basically because SVN has absolutely no way to know how to merge these files. Who's correct? A or B? The files might look similar, but may not be even related! Had there been a common ancestor at least SVN would know they're related and could have a stab at a merge.
Then this is the case it is best to pospone resolution of the conflict and compare the two files. Your only option will probably to be to accept the working copy as the file to keep. Then you will have to manually edit that working copy file to reflect the changes that you want to keep between the evil twins.
To do this you use...
svn resolve --accept='working' file-name
This removes the conflict marker and says that the working copy contains the resolved file. (At this point, note however that it does NOT!).
Now you need to manually resolve the changes. Edit the working copy or overwrite it. Then when you commit the working copy will become the updated file in A. Note, that if you use B further you still face the same problem on the next merge. The best thing to do would be to remove the bopy in B and replace it with a branch from A.
See Merge History
Use the command svn mergeinfo src_branch targ_branch. It will print an ascii-art graph of the merge history between the two branches.
See also these tips in merge section.
Rollback To A Previous Revision
From your repo directory use the following, which is described in the svb redbook here.
svn update svn merge -r <newer-rev>:<older-rev> <filename or . for current dir> svn commit
As the manual says....
You can use svn merge to "undo" the change in your working copy, and then commit the local modification to the repository. All you need to do is to specify a reverse difference. (You can do this by specifying --revision 392:391, or by an equivalent --change -392.)
SVN Properties - Recursive
Arg! Setting the ignore property recursively is deceptive. The problem and several really good answers are found in this SO thread.
Another useful command is svn plist -v branch to see all the properties set on a directory or file.
Externals
Can kinda emulate Perforce client specs, but either way are pretty useful
if you want your development line to include other bits of code from
different development lines. I.e. ...contruct a working copy that
is made out of a number of different checkouts
. When a branch
has externals, after the branch is checked out or updated, subversion
will also automatically chekout/update the external items into the
working copy.
To set up externals use the svn property svn:externals. As the manual points out, it is a multiline property so best use svn propedit as oppsed to svn propset.
The manual also recommends using explicit revisions in your external definitions so that the snapshot of the external soruce is well controlled and you don't get unexpected updates to external code that you may not control. Note that this does mean, however, that you will not be able to modify the external.
If you do modify an external, you must explicity commit the external. Doing a commit on the "mother" checkout will not recurse into the externals.
If the external definition references something in the same repository it is also good practice to use paths relative to the root of the repository. I.e. paths prefixed with "^/".
The format for the svn:externals property is this:
^/path/to/external[@revision] local/path
Or...
[-r revision] ^/path/to/external local/path
WARNING: When you do an svn diff
you will not see diffs for externals. To get these diffs you will have to do an svn status
and
grep for "^M":
svn status | grep "^\s*[MAD]" | sed -e "s/^M\s*//g" | while read fn; do svn diff "$fn"; done
WARNING: Just like for diffs, you generally do not affect or get information on externals.
Any other command other than the svn checkout
, svn update
, svn switch
, and svn status
commands, will usually skip externals. This means that when you merge from one branch to another, externals are not merged! You have to do
these seperately!
This can lead to problems if, for example, you haven't checked out your development branch from the root (perhaps its a massive repo and you didn't want to check it all out). This is shown below and assumes the externals are relative paths inside the one repo that do not speciify specific revisions.
The solution would be to checkout the development branch at its root, rather than a subdirectory, as shown above. However, if the real repo is really massive you might not want to do that do a sparse checkout with only the modified externals will do.
To quickly see all the externals in a repo you could try something like the following:
find . -type d -a -not -path \*/.svn\* -print | \ while read f; do \ svn propget svn:externals "$f" >.svnproptmp 2>&1 && \ echo "$f" && \ cat -b .svnproptmp && \ rm .svnproptmp; \ done
My Repository Moved!
Recently a repository I had checked out was moved for various reasons. I still had some
files in my checkout that were modified so I didn't want to have to re-checkout from
the new location and manually add these edits. Solution was the command
svn relocate
.
svn relocate FROM-PREFIX TO-PREFIX
Sparse Checkouts
See docs.
When dealing with really large repositories, you may not need to check out the entire repository. If this is the case you can use a "sparse" checkout. To do this type:
svn checkout --depth=immediates https://your.repo/root checkout_dir
This will only check out the immediate files at the root of your repository and will not recurse into subdirectories. This means that files in the root directory are checked out and directories in the root are created, but their contents are not filled.
For directories that you need, you can then change into them (maybe applying the sparse checkout recursively to selctively get subdirectories), and type:
svn update --set-depth=infinity
Other targets of use:
--depth empty
Include only the immediate target of the operation, not any of its file or directory children.
--depth files
Include the immediate target of the operation and any of its immediate file children.
--depth immediates
Include the immediate target of the operation and any of its immediate file or directory children. The directory children will themselves be empty.
--depth infinity
Include the immediate target, its file and directory children, its children's children, and so on to full recursion.
Misc
Generate And Restore A Patch File
Useful when uploading diffs to ReviewBoard for example. Use the following:
svn diff --internal-diff --patch-compatible -rX:Y > patch.diff
Note the use of the --internal-diff. If your SVN config file specifies a diff tool other than the internal SVN diff tool your patch won't be generated. This option ensures that the SVN internal diff tool is used to the patch file comes out right. Also notice --patch-compatible. This will make sure new, previously non-existent, files are added to the diff. (You might then have to edit the resulting patch file to replace the text (nonexistent) with (revision 0)).
To restore a patch do:
patch < patch.diff
Check Your SSH Connection
If your are having trouble with your SSH connection, you can check that your keys are correctly loaded using the following command (on Linux with OpenSSH) to list your loaded keys...
ssh-add -L
If you're still having problems look in ~/.subversion/config and go to the [tunnels] section. In the SSH command take out the -q option to get better debug messages to the console.
OR... even better just type...
export SVN_SSH="ssh -v "
...either on the same line as your svn command to just apply it to that command, or on a line by its own to make the setting apply to all svn commands. (Note the trailing space after the -v).
Add all untracked files
svn add $(svn status | grep ? | awk '{print $2}')
Diff all changes in changeset
svn diff -c <changeset> [filename]
This is the same as running the following:
svn diff -r rev-1:rev [filename]
If filename is ommitted all changes in that changeset are examined
Nice Diff Syntax For Same-File-Different-Branch
Useful command example is:
svn diff ^/branches/{PATH_1,PATH_2}/some/repo/path
Where:
- ^/ is a shortcut for the root of the current repository, and
- xx{A,B}yy expands to xxAyy <space> xxByy
Therefore the whole line expands to the following.
svn diff <root>/branches/PATH_1/some/repo/path <root>/branches/PATH_2/some/repo/path
This is really a linux variable expansion, it's not something SVN actually offers.
Diff Two Paths But Select Exactly What To Diff
I found it really useful to take a summary of SVN diffs, delete the stuff I wasn't interested in and then only diff the remaining when considering two paths, one being a branch of the other...
Lets say that branch 1 was at this location: svn+ssh://your.server.xxx/subversion/branches/branch_A"
And lets say that branch 2 was at this location: svn+ssh://your.server.xxx/subversion/branches/branch_B/some/other/path"
Then I could use the following script. It would summarise the differences to a file. I could then cull any files I didn't want to diff for whatever reason, save the file, exit the editor and then diff what I'd left in the file...
#!/bin/bash svnroot="svn+ssh://your.server.xxx/subversion/branches/" b1="branch_A" b2="branch_B/some/other/path" diffFile=$(mktemp) echo "Diffing to $diffFile" echo svn diff --summarize $svnroot{$b1,$b2} svn diff --summarize $svnroot{$b1,$b2} | tee $diffFile echo "Firing up you editor" gvim -f $diffFile while IFS='' read -r line || [[ -n "$line" ]] do line=$(echo $line | awk '{print $2}') line=${line:${#svnroot}} echo $line if [ "${line::${#b1}}" == "$b1" ] then base=${line:${#b1}} echo -e "\n\n" echo "DIFFING $svnroot{$b1,$b2}$base" svn diff $svnroot{$b1,$b2}$base elif [ "${line::${#b2}}" == "$b2" ] then base=${line:${#b2}} echo -e "\n\n" echo "DIFFING $svnroot{$b1,$b2}$base" svn diff $svnroot{$b1,$b2}$base else echo "WARNING Skipping line '$line'" fi done < $diffFile echo "The diff file is $diffFile. Delete it if you wish or copy it to save" echo "it somewhere safe..."