How to maintain a branch
This page explains some procedures which are helpful for those maintaining development branches. Make sure you have already read and understood the SVN introduction for CVS users before starting this document! Maintainers who don't work with branches might want to skim through this document anyway, since it might contain some SVN uses which could be interesting to them.
Creating a branch
Usually, a branch is created out of the current HEAD. The easiest and fastest way to do this is to remotely copy the whole tree:
$ svn copy -m "creating reload-rewrite branch" \ svn+ssh://email@example.com/svn/gcc/trunk \ svn+ssh://firstname.lastname@example.org/svn/gcc/branches/reload-rewrite
Notice that you need to specify a commit message (either on the command line, or in the editor), since there is no separate commit step involved. After this operation, the new repository revision number will be displayed: that's the branchpoint.
If, for some reason, you want to branch the trunk but *not* at the current HEAD (say, it's broken, or contains things you're not ready to work on right now), you can specify the trunk revision number with -r:
$ svn copy -r53420 -m "creating reload-rewrite branch" \ svn+ssh://email@example.com/svn/gcc/trunk \ svn+ssh://firstname.lastname@example.org/svn/gcc/branches/reload-rewrite
If you have a working copy which you want to be the basis for the branch (it contains local modifies which you want to be in the branch), you can do a local-to-remote copy:
$ svn copy -m "creating reload-rewrite branch" . \ svn+ssh://email@example.com/svn/gcc/branches/reload-rewrite
Notice that this copy encapsulates a commit operation.
Inspecting the history
When you run svn log on a branch, the log will go through the branchpoint. In fact svn realizes that your files are just copies of the trunk, so you can go through the history back to the original file creation (in trunk). Many times, though, one wants to check for the history of the branch *only*. To do this, svn log has a cute option called --stop-on-copy which stops the log at the first copy operation (being in reverse chronological order, that's actually the last copy in our time), which should be the branch creation.
Keeping track of merges: svn 1.5 merge support
The preferred solution to maintain a branch is to use svn 1.5, which has introduced the proper merge support. Note that due to some bugs you must be using at least version 1.5.5 of the svn client. Also, never ever invoke svn merge with pre-1.5 svn client on your branch after you started peforming merges this way.
Beware that some of the scenarios described below were not tested on GCC repository, thus it might happen that you will run into some svn bug.
How do I perform a merge from trunk?
To perform a merge from trunk to the branch, from a working copy directory of the branch do
$ svn merge svn+ssh://firstname.lastname@example.org/svn/gcc/trunk Merging rXXXX through rYYYY into '.': M . D gcc/reload.c D gcc/reload1.c . . . ... check that everything is fine ... $ svn commit -m "Merged revisions rXXXX-YYYY to the branch"
Note that svn merge does not perform a commit for you; rather, it is up to you to check that merged state is OK and then proceed with the commit.
How do I merge the branch to trunk?
This part has been untested. Please try it and update Wiki as necessary.
First, you have to perform trunk to branch merge as described above. Then do the same the thing the other way, but this time with --reintegrate option:
$ cd branch-working-directory $ svn merge svn+ssh://email@example.com/svn/gcc/trunk ... $ svn commit $ cd ../trunk-working-directory $ svn update $ svn merge --reintegrate svn+ssh://firstname.lastname@example.org/svn/gcc/branches/your-branch ... Committed revision 123456. $ svn commit
At this point, if you wish to use the branch for further development, you must delete it and re-create again with svn rm and svn cp. Alternatively you can do
$ svn merge --record-only -c 123456 svn+ssh://email@example.com/svn/gcc/trunk
This ensures that Subversion does not try to merge back to the branch the revision where the branch was merged to the trunk. This process may be repeated as many times as necessary.
How do I find out which trunk revisions are already merged?
It is stored in the svn:mergeinfo property. So you can do
$ svn propget svn:mergeinfo . /trunk:XXXXX-YYYYY
How do I find out which trunk revisions are available for merge?
Do the following:
$ svn mergeinfo svn+ssh://firstname.lastname@example.org/svn/gcc/trunk --show-revs eligible r123456 r123458 . . . r123999
I already have a branch which I maintain with svnmerge.py! What do I do?
This part has been untested. Please try it and update Wiki as necessary.
Both svnmerge.py and svn 1.5 merge record merge information in directory properties. Please use this tool, included with Subversion, to convert merge information from between them.
I already have a branch which I have been merging manually! What do I do?
You are on your own here. Please try it and update Wiki as necessary.
Use --record-only option of svn merge to let svn know up to which revision you have performed merges yourself.
Keeping track of merges: automatic solution using svnmerge.py
svnmerge.py is a contrib tool (maintained by Giovanni Bajo, shipped with SVN 1.3) which lets you automate the merge process, by recording the merge history (that is, which revisions have been already merged) in a property (properties are special versioned metadata that SVN stores in repository). The tool tool tracks each individual change (fine-grained) and will never reapply an already applied change, thus allowing for safe cherry-picking of patches. Fair warning: pay attention to not confuse svnmerge the tool with svn merge the command!
After branch creation, checkout a copy of the branch and initialize auto merge tracking by running this command in a branch working copy:
$ svnmerge.py init
svnmerge never does the commits for you: it just modifies your working copy and lets you review and commit the changes. After this command, you will see that only metadata is changed:
$ svn status # the 'M' in the second column means metadata modified M . $ svn diff Property changes on: . ___________________________________________________________________ Name: svnmerge-integrated + /trunk:1-63560
The property says that the branch already integrates all the commits in the trunk up to revision 63530, which is the branch point: svnmerge automatically computed it by looking at the branch history. As log message, svnmerge created a file called svnmerge-commit-message.txt which is not directly usable because it will trigger bug mails for PR fixing bugs:
$ <edit svnmerge-commit-message.txt to remove lines referring to PR's, or just remove all the copied merge logs> $ svn commit -F svnmerge-commit-message.txt $ rm svnmerge-commit-message.txt
A few days pass by, and you want to merge new trunk commits into your branch.
If you want to briefly review which changes happened, svnmerge avail displays a list of the new revisions available in trunk, in form of revision numbers, logs, or diffs
$ svnmerge.py avail 63561-63570,63572,63575,63580-63590 $ svnmerge.py avail --log [... log of changes ...] $ svnmerge.py avail --diff [... diff of changes ...]
Notice that it's normal for available revision numbers not be fully consecutive, because svnmerge avail will display only commits which affect the trunk. The other commits not displayed in the list are probably commits in other parts of the repository (branches, tags, etc.), and you're not interested in those.
To do the actual merge, just run:
$ svnmerge.py merge
and that's it! It'll merge all the available changes into you working copy, and let you fix the conflicts, review the changes and do the commit. As usual, svnmerge-commit-message.txt will be written for you to use and it will contain also the original logs of the merged changes.
svnmerge-commit-message.txt is not an automatically generated, appropriate commit message and is not intended as the text that should be inserted verbatim in the svn commit message for the branch merge. Any GCC Bugzilla PRs in the logs will re-trigger automatic mail messages, which probably is neither intended nor wanted. You therefore want to remove the lines which refer to the PR!
To do partial merges or cherry-picking, you can pass -r/--revision to svnmerge merge and name the single commit (or range of commits). If you name revision N svnmerge will merge the commit N (that is the changes between revision N-1 and N .
As expected, after you merge one or multiple revisions, they will disappear from the list of available revisions (the output of svnmerge avail . svnmerge records all the merge operations, so that it will never try to merge a previously-merged commit again, even if you specify it (either directly or in a range).
Also, if you review the changes available for merges with svnmerge avail before doing the actual merge, you may notice some commits that you do not want to merge for some reason. You can let svnmerge know this using svnmerge block so that it'll remember not to merge those commits in your branch (nor show them as available anymore). Of couse, svnmerge unblock can be used to undo the block (and svnmerge avail --blocked shows the list of blocked revisions).
Keeping track of merges: manual solution with tags
To keep track of the last-merge happened from the trunk, one good solution is to use tags exactly like we used to do with CVS. The only difference here is that tags operations are versioned: so you don't want and don't need to create one different tag *per* merge, you can just work with a single tag.
After branch creation, create a tag which records the branchpoint:
$ svn cp -rBRANCHPOINT -m "record merge point" \ svn+ssh://email@example.com/svn/gcc/trunk\ svn+ssh://firstname.lastname@example.org/svn/gcc/tags/reload-rewrite-last-merge
(remember to substitute BRANCHPOINT with the actual revision number) Notice that I used the -rREV form for the trunk: in fact, you want to make sure you tag exactly the revision that you branched from. In fact, between the first svn copy command to create the branch, and the second svn copy command to create the tag, somebody else could have done a commit to the trunk, which we do not want to lose.
To merge, follow these steps.
Decide the new merge point. svn info svn://gcc.gnu.org/svn/gcc/trunk || grep Revision will tell you the last revision in trunk, but maybe you want to merge up to an earlier version (for some reason). Let's call the new merge point revision NEW_MERGE_REV
- Merge the changes in your working copy. From the branch working copy, run:
$ svn log --stop-on-copy \ svn+ssh://email@example.com/svn/gcc/tags/reload-rewrite-last-merge Note the revision number of the last log message it gives, which is to the left of the author. $ svn merge -r<last revision number it gave - 1>:NEW_MERGE_REV \ svn+ssh://firstname.lastname@example.org/svn/gcc/trunk
Review and fix the merge. You can use svn status and svn diff as usual. If things get messy, you can revert all the changes with svn revert -R . and start again (maybe with a different NEW_MERGE_REV .
Commit the changes as usual, with svn commit
- Record the new branch point:
$ svn rm -m "remove old merge tag" \ svn+ssh://email@example.com/svn/gcc/tags/reload-rewrite-last-merge $ svn cp -rNEW_MERGE_REV -m "record new merge point" \ svn+ssh://firstname.lastname@example.org/svn/gcc/trunk \ svn+ssh://email@example.com/svn/gcc/tags/reload-rewrite-last-merge \