| Introduction |
| ============ |
| |
| This Perl script copies one Subversion location or set of locations to another, |
| in the same way as svn copy. Using the script allows more advanced operations, |
| in particular allowing svn:externals to be dealt with properly for branching |
| or tagging. |
| |
| Command Line Options |
| ==================== |
| |
| Run the script with no command line arguments to see all the command |
| line options it takes. |
| |
| Dependencies |
| ============ |
| |
| This script depends on module File::Temp. This became part of the standard |
| Perl distribution in 5.8.0. If you have an earlier version of Perl, you can |
| download it from CPAN at http://search.cpan.org/search?module=File::Temp . |
| It works with Perl versions from 5.005 onwards. |
| |
| Overview |
| ======== |
| |
| This script performs an svn copy command. It allows extra processing to get |
| around the following limitations of svn copy: |
| |
| svn:externals definitions are (in Subversion 1.0 and 1.1 at least) absolute |
| paths. This means that an svn copy used as a branch or tag operation on a |
| tree with embedded svn:externals will not do what is expected. The |
| svn:externals will still point at the original location and will not be |
| pinned down. |
| |
| Installation |
| ============ |
| |
| Rename svncopy.pl.in to svncopy.pl. |
| |
| Locate the following lines in the script: |
| |
| # Specify the location of the svn command. |
| my $svn = '@SVN_BINDIR@/svn'; |
| |
| Replace @SVN_BINDIR@ with the path to your svn executable. This should be the |
| command you have to type to use the svn command line. If svn is on the path, |
| you can just set this line to: |
| |
| my $svn = 'svn'; |
| |
| Alternatively, if you have to type /usr/local/bin/svn, you should set it to: |
| |
| my $svn = '/usr/local/bin/svn'; |
| |
| Branching |
| ========= |
| |
| svncopy --update-externals (or svncopy --branch) will update any unversioned |
| svn:externals in the destination tree which point at locations within one of |
| the source trees so that they point to the corresponding locations within the |
| destination tree instead. This effectively updates the reference to |
| point to the destination tree, and is the behaviour you want for branching. |
| |
| Tagging |
| ======= |
| |
| svncopy --pin-externals (or svncopy --tag) will update any unversioned |
| svn:externals in the destination tree to contain the current version of the |
| directory listed in the svn:externals definition. This effectively pins |
| the reference to the current version, and is the behaviour you want for tagging. |
| |
| Note: both forms of the command leave unchanged any svn:externals which |
| already contain a version number. |
| |
| Examples |
| ======== |
| |
| These examples assume the following repository layout: |
| |
| Path Last mod or svn:externals target |
| ---- -------------------------------- |
| trunk/ 5195 |
| common/ 5192 |
| common1.c 5192 |
| common2.c 4997 |
| inc/ 4986 |
| common1.h 4331 |
| common2.h 4986 |
| proj_foo/ 5003 |
| foo1.c 5001 |
| foo2.c 4995 |
| X common -r 4997 http://svn/repos/trunk/common |
| X inc http://svn/repos/trunk/inc |
| proj_bar/ 5195 |
| bar1.c 5054 |
| bar2.c 5195 |
| bar2.h 5195 |
| X common http://svn/repos/trunk/common |
| X inc http://svn/repos/trunk/inc |
| X public http://someserver/repos/public |
| |
| i.e. both proj_foo and proj_bar have svn:externals set to: |
| |
| common http://svn/repos/trunk/common |
| inc http://svn/repos/trunk/inc |
| |
| with proj_foo having pinned common to version 4997. |
| |
| Example 1 - using svn copy to tag (what not to do) |
| -------------------------------------------------- |
| |
| This is the naive way of creating a tag. |
| |
| $ svn copy http://svn/repos/trunk/proj_bar \ |
| http://svn/repos/tags/proj_bar/release_3.2 |
| |
| Result: |
| |
| trunk/ |
| [ as above] |
| tags/ |
| proj_bar/ |
| release_3.2/ |
| bar1.c |
| bar2.c |
| bar2.h |
| X common http://svn/repos/trunk/common |
| X inc http://svn/repos/trunk/inc |
| X public http://someserver/repos/public |
| |
| The svn:externals are still pointing to the head revisions in trunk. Any |
| changes in trunk/common, trunk/inc or trunk/project/inc will modify the |
| subdirectories in tags/proj_bar/release_3.2. This is not the desired effect. |
| |
| Example 2 - using svn copy to branch (what not to do) |
| ----------------------------------------------------- |
| |
| This is the naive way of creating a branch. |
| |
| $ svn copy http://svn/repos/trunk/proj_bar \ |
| http://svn/repos/branches/proj_bar/3.2_bugfix |
| |
| Result: |
| |
| trunk/ |
| [ as above] |
| branches/ |
| proj_bar/ |
| 3.2_bugfix/ |
| proj_bar/ |
| bar1.c |
| bar2.c |
| bar2.h |
| X common http://svn/repos/trunk/common |
| X inc http://svn/repos/trunk/inc |
| X public http://someserver/repos/public |
| |
| The svn:externals are still pointing to the head revisions in trunk. Any |
| changes in trunk/common, trunk/inc or trunk/project/inc will modify the |
| subdirectories in branches/proj_bar/3.2_bugfix/proj_bar. Worse, any |
| changes in these subdirectories will get propagated back to trunk. Again, |
| this is not the desired effect. |
| |
| Example 3 - tagging properly |
| ---------------------------- |
| |
| Using the script allows tags to be created which won't change. |
| |
| $ perl svncopy.pl --tag http://svn/repos/trunk/proj_bar \ |
| http://svn/repos/tags/proj_bar/release_3.2 |
| |
| Result: |
| |
| trunk/ |
| [ as above] |
| tags/ |
| proj_bar/ |
| release_3.2/ |
| proj_bar/ |
| bar1.c |
| bar2.c |
| bar2.h |
| X common -r 5192 http://svn/repos/trunk/common |
| X inc -r 4986 http://svn/repos/trunk/inc |
| X public -r 17753 http://someserver/repos/public |
| |
| The svn:externals are pinned to the latest repository version containing a |
| modification to the corresponding directories. The contents of the externals |
| will not change. |
| |
| Example 4 - tagging retrospectively |
| ----------------------------------- |
| |
| If you want to create a tag, but changes have been made since the version you |
| want to tag, all is not lost. Pass the revision number and the tag will be |
| done from there. E.g. if proj_foo should have been tagged at version 5001 |
| (when common was at 4997), the following command will do the trick: |
| |
| $ perl svncopy.pl --tag --revision 5001 http://svn/repos/trunk/proj_foo \ |
| http://svn/repos/tags/proj_foo/release_2.7 |
| |
| Result: |
| |
| trunk/ |
| [ as above] |
| tags/ |
| proj_foo/ |
| release_2.7/ |
| proj_foo/ |
| foo1.c |
| foo2.c |
| X common -r 4997 http://svn/repos/trunk/common |
| X inc -r 4986 http://svn/repos/trunk/inc |
| |
| The svn:externals are pinned to the latest repository version containing a |
| modification to the corresponding directories prior to revision 5002. |
| The contents of the externals will not change. |
| |
| Example 5 - branching properly |
| ------------------------------ |
| |
| Using the script allows branches to be created which are really independent. |
| |
| $ perl svncopy.pl --branch http://svn/repos/trunk \ |
| http://svn/repos/branches/3.2_bugfix |
| |
| Result: |
| |
| trunk/ |
| [ as above] |
| branches/ |
| 3.2_bugfix/ |
| trunk/ |
| common/ |
| common1.c |
| common2.c |
| inc/ |
| common1.h |
| common2.h |
| proj_foo/ |
| foo1.c |
| foo2.c |
| X common -r 4997 http://svn/repos/trunk/common |
| X inc http://svn/repos/branches/3.2_bugfix/trunk/inc |
| proj_bar/ |
| bar1.c |
| bar2.c |
| bar2.h |
| X common http://svn/repos/branches/3.2_bugfix/trunk/common |
| X inc http://svn/repos/branches/3.2_bugfix/trunk/inc |
| X public http://someserver/repos/public |
| |
| The svn:externals are now pointing to the corresponding directories in the |
| branch. The subdirectories in the branch will be unaffected by changes |
| in trunk, and similarly trunk will not be affected by changes in the branch. |
| |
| Note: proj_foo/common was pinned to revision 4997 in trunk. Because of this |
| the script has left it unchanged. |
| |
| Example 6 - branching part of a tree |
| ------------------------------------ |
| |
| If you don't want to branch the whole tree, you can just branch the directories |
| which contain your project: |
| |
| $ perl svncopy.pl --branch http://svn/repos/trunk/common \ |
| http://svn/repos/trunk/inc \ |
| http://svn/repos/trunk/proj_bar \ |
| http://svn/repos/branches/3.2_bugfix |
| |
| Result: |
| |
| trunk/ |
| [ as above] |
| branches/ |
| 3.2_bugfix/ |
| common/ |
| common1.c |
| common2.c |
| inc/ |
| common1.h |
| common2.h |
| proj_bar/ |
| bar1.c |
| bar2.c |
| bar2.h |
| X common http://svn/repos/branches/3.2_bugfix/common |
| X inc http://svn/repos/branches/3.2_bugfix/inc |
| X public http://someserver/repos/public |
| |
| The svn:externals are now pointing to the corresponding directories in |
| the branch, as in Example 4. |
| |
| Note: you *must* branch all affected directories simultaneously. If you |
| branch them one at a time, the script will not know which externals refer |
| to other components of the same project, and will leave them unchanged. |
| |
| Testing svncopy.pl |
| ================== |
| |
| svncopy.pl comes with a script to do some basic testing, called testsvncopy.pl. |
| Installation is similar to svncopy.pl - update @SVN_BINDIR@. You also have to |
| supply a scratch repository location for the test script to use. Either update |
| @SVN_TEST_REPOSITORY@ in testsvncopy.pl.in or pass in the location using the |
| --test-repository parameter when running the script. |