Index: contrib/client-side/svncopy/svncopy.pl.in =================================================================== --- contrib/client-side/svncopy/svncopy.pl.in (revision 37924) +++ contrib/client-side/svncopy/svncopy.pl.in (working copy) @@ -213,27 +213,56 @@ } } + $multiple_sources = scalar( @sources ) > 1; + info( "multiple_sources: " . ( $multiple_sources ? "Yes" : "No" ) . "\n" ); + # # Create a temporary directory to work in. # my ( $auto_temp_dir, $dest_dir ) = - PrepareDirectory( $destination_uri, "svncopy.pl to '$destination'\n - creating intermediate directory" ); + PrepareDirectory( $destination_uri, "svncopy.pl to '$destination'\n - creating intermediate directory", $multiple_sources ); $temp_dir = $auto_temp_dir->temp_dir(); chdir( $temp_dir ); - foreach $src ( @sources ) + if ( $multiple_sources ) { + # + # We've already created the SVN destination and the workspace, + # so copy each of the sources into the workspace. + # + foreach $src ( @sources ) + { + # Convert source to URI + $src =~ s|/*$||; + my $source_uri = URI->new($src); + + # + # Do the initial copy into our temporary. Note this will create a + # subdirectory with the same name as the last directory in $source. + # + if ( !CopyToWorkDir( $src, $dest_dir ) ) + { + error( "Copy failed" ); + return 0; + } + } + } + else + { + # + # We just have one source, so copy it directly to the + # destination URL, then check the result out into a workspace so + # that we can update the externals. + # + # Convert source to URI + $src = $sources[0]; $src =~ s|/*$||; my $source_uri = URI->new($src); - # - # Do the initial copy into our temporary. Note this will create a - # subdirectory with the same name as the last directory in $source. - # - if ( !CopyToWorkDir( $src, $dest_dir ) ) + if ( !CopyAndCheckout( $source_uri, $destination_uri, $dest_dir ) ) { - error( "Copy failed" ); + error( "Copy/checkout failed" ); return 0; } } @@ -279,7 +308,7 @@ #------------------------------------------------------------------------------ sub PrepareDirectory { - my ( $destination, $message ) = @_; + my ( $destination, $message, $multiple_sources ) = @_; my $auto_temp_dir = Temp::Delete->new(); $temp_dir = $auto_temp_dir->temp_dir(); @@ -293,22 +322,31 @@ my $new_dir = pop( @path_segments ); my $dest_dir = "$temp_dir/$new_dir"; - # Make sure the destination directory exists in Subversion. - info( "Creating intermediate directories (if necessary)\n" ); - if ( !CreateSVNDirectories( $destination, $message ) ) + # + # If we've got multiple sources, go ahead and create the SVN + # directory and create a working copy now. If we have a single source, + # we'll create the SVN directory and workspace in + # CopyAndCheckout(). + # + if ( $multiple_sources ) { - error( "Couldn't create parent directories for '$destination'" ); - return; - } + # Make sure the destination directory exists in Subversion. + info( "Creating intermediate directories (if necessary)\n" ); + if ( !CreateSVNDirectories( $destination, $message ) ) + { + error( "Couldn't create parent directories for '$destination'" ); + return; + } - # Check out the destination. - info( "Checking out destination directory '$destination'\n" ); - if ( 0 != SVNCall( 'co', $destination, $dest_dir ) ) - { - error( "Couldn't check out '$destination' into work directory." ); - return; + # Check out the destination. + info( "Checking out destination directory '$destination'\n" ); + if ( 0 != SVNCall( 'co', $destination, $dest_dir ) ) + { + error( "Couldn't check out '$destination' into work directory." ); + return; + } } - + return ( $auto_temp_dir, $dest_dir ); } @@ -343,6 +381,54 @@ #------------------------------------------------------------------------------ +# Function: CopyAndCheckout +# +# Does the svn copy for a single source, then checks out the copy into +# the working directory. +# +# Parameters: +# source The URI to copy from +# destination The URI to copy to +# work_dir The working directory +# +# Returns: 1 on success +#------------------------------------------------------------------------------ +sub CopyAndCheckout +{ + my ( $source, $destination, $work_dir ) = @_; + + + # Prepare a file containing the message + my ($handle, $messagefile) = tempfile( DIR => $temp_dir ); + print $handle $message; + close($handle); + + # Create commandline to copy source URI to destination URI + my @commandline = (); + push( @commandline, "--file", $messagefile ); + push( @commandline, "--revision", $revision ) if ( $revision ); + push( @commandline, $source, $destination ); + + info( "Copying source URI to destination URI\n" ); + if ( 0 != SVNCall( 'copy', @commandline ) ) + { + error( "Couldn't copy '$source' to '$destination'." ); + return; + } + + # Check out working copy of destination + info( "Checking out destination directory '$work_dir'\n" ); + if ( 0 != SVNCall( 'co', $destination, $work_dir ) ) + { + error( "Couldn't check out '$destination' into work directory." ); + return; + } + + return 1; +} + + +#------------------------------------------------------------------------------ # Function: DestinationSubdir # # Returns the destination directory for a given source and a destination root Index: contrib/client-side/svncopy/testsvncopy.pl.in =================================================================== --- contrib/client-side/svncopy/testsvncopy.pl.in (revision 37924) +++ contrib/client-side/svncopy/testsvncopy.pl.in (working copy) @@ -174,7 +174,7 @@ { sources => [ "$testURL/source/dirA/dir1", ], pin => 0, update => 1, - ext_dir => "dir1", + ext_dir => "", expected_externals => [ "DIR2 $testURL/source/dirA/dir2", "DIR2Pin -r __PINREV__ $testURL/source/dirA/dir2", @@ -182,14 +182,13 @@ "DIR4 $testURL/wibble/dirA/dir2" ], expected_tree => [ - "dir1/", ], }, # Updating a tree - enclosed should change, unless pinned { sources => [ "$testURL/source/dirA", ], pin => 0, update => 1, - ext_dir => "dirA/dir1", + ext_dir => "dir1", expected_externals => [ "DIR2 $testURL/dest/dirA/dir2", "DIR2Pin -r __PINREV__ $testURL/source/dirA/dir2", @@ -197,16 +196,15 @@ "DIR4 $testURL/wibble/dirA/dir2" ], expected_tree => [ - "dirA/", - "dirA/dir1/", - "dirA/dir2/", + "dir1/", + "dir2/", ], }, # Updating with no update - no change { sources => [ "$testURL/source/dirA", ], pin => 0, update => 0, - ext_dir => "dirA/dir1", + ext_dir => "dir1", expected_externals => [ "DIR2 $testURL/source/dirA/dir2", "DIR2Pin -r __PINREV__ $testURL/source/dirA/dir2", @@ -214,9 +212,8 @@ "DIR4 $testURL/wibble/dirA/dir2" ], expected_tree => [ - "dirA/", - "dirA/dir1/", - "dirA/dir2/", + "dir1/", + "dir2/", ], }, # Updating with two sources @@ -240,7 +237,7 @@ { sources => [ "$testURL/source/dirA/dir1", ], pin => 1, update => 0, - ext_dir => "dir1", + ext_dir => "", expected_externals => [ "DIR2 -r __REV__ $testURL/source/dirA/dir2", "DIR2Pin -r __PINREV__ $testURL/source/dirA/dir2", @@ -248,14 +245,13 @@ "DIR4 -r __REV__ $testURL/wibble/dirA/dir2", ], expected_tree => [ - "dir1/", ], }, # Pinning a tree { sources => [ "$testURL/source/dirA", ], pin => 1, update => 0, - ext_dir => "dirA/dir1", + ext_dir => "dir1", expected_externals => [ "DIR2 -r __REV__ $testURL/source/dirA/dir2", "DIR2Pin -r __PINREV__ $testURL/source/dirA/dir2", @@ -263,9 +259,8 @@ "DIR4 -r __REV__ $testURL/wibble/dirA/dir2", ], expected_tree => [ - "dirA/", - "dirA/dir1/", - "dirA/dir2/", + "dir1/", + "dir2/", ], }, # Pinning with two sources Index: contrib/client-side/svncopy/svncopy.README =================================================================== --- contrib/client-side/svncopy/svncopy.README (revision 37924) +++ contrib/client-side/svncopy/svncopy.README (working copy) @@ -147,13 +147,12 @@ 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 + 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 @@ -176,13 +175,12 @@ 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 + 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 @@ -206,11 +204,10 @@ 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 + 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. @@ -230,25 +227,24 @@ [ 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 + 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