nixenvironment 0.0.70 → 0.0.71

Sign up to get free protection for your applications and to get access to all the features.
data/legacy/svncopy.pl DELETED
@@ -1,1134 +0,0 @@
1
- #! /usr/bin/perl
2
- #
3
- # svncopy.pl -- Utility script for copying with branching/tagging.
4
- #
5
- # This program is free software; you can redistribute it and/or modify it
6
- # under the terms of the GNU General Public License as published by the
7
- # Free Software Foundation; either version 2 of the License, or (at your
8
- # option) any later version.
9
- #
10
- # THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
11
- # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
12
- # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
13
- # NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
14
- # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
15
- # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
16
- # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
17
- # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
18
- # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
19
- # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20
- #
21
- # You should have received a copy of the GNU General Public License along
22
- # with this program; if not, write to the Free Software Foundation, Inc.,
23
- # 59 Temple Place - Suite 330, Boston MA 02111-1307 USA.
24
- #
25
- # This product makes use of software developed by
26
- # CollabNet (http://www.Collab.Net/), see http://subversion.tigris.org/.
27
- #
28
- # This software consists of voluntary contributions made by many
29
- # individuals. For exact contribution history, see the revision
30
- # history and logs, available at http://subversion.tigris.org/.
31
- #------------------------------------------------------------------------------
32
-
33
- #------------------------------------------------------------------------------
34
- #
35
- # This script copies one Subversion location to another, in the same way as
36
- # svn copy. Using the script allows proper branching and tagging of URLs
37
- # containing svn:externals definitions.
38
- #
39
- # For more information see the pod documentation at the foot of the file,
40
- # or run svncopy.pl -?.
41
- #
42
- #------------------------------------------------------------------------------
43
-
44
- #
45
- # Include files
46
- #
47
- use Cwd;
48
- use File::Temp 0.12 qw(tempdir tempfile);
49
- use Getopt::Long 2.25;
50
- use Pod::Usage;
51
- use URI 1.17;
52
-
53
- #
54
- # Global definitions
55
- #
56
-
57
- # Specify the location of the svn command.
58
- my $svn = 'svn';
59
-
60
- # Input parameters
61
- my $testscript = 0;
62
- my $verbose = 0;
63
- my $pin_externals = 0;
64
- my $update_externals = 0;
65
- my @sources;
66
- my $destination;
67
- my $message;
68
- my @svn_options = ();
69
-
70
- # Internal information
71
- my %externals_hash;
72
- my $temp_dir;
73
-
74
- # Error handling
75
- my @errors = ();
76
- my @warnings = ();
77
-
78
- # Testing-specific variables
79
- my $hideerrors = 0;
80
-
81
-
82
- #------------------------------------------------------------------------------
83
- # Main execution block
84
- #
85
-
86
- #
87
- # Process arguments
88
- #
89
- GetOptions( "pin-externals|tag|t" => \$pin_externals,
90
- "update-externals|branch|b" => \$update_externals,
91
- "message|m=s" => \$message,
92
- "revision|r=s" => \$revision,
93
- "verbose!" => \$verbose,
94
- "quiet|q" => sub { $verbose = 0; push( @svn_options, "--quiet" ) },
95
- "file|F=s" => sub { push( @svn_options, "--file", $_[1] ) },
96
- "username=s" => sub { push( @svn_options, "--username", $_[1] ) },
97
- "password=s" => sub { push( @svn_options, "--password", $_[1] ) },
98
- "no_auth_cache" => sub { push( @svn_options, "--no-auth-cache" ) },
99
- "force-log" => sub { push( @svn_options, "--force-log" ) },
100
- "encoding=s" => sub { push( @svn_options, "--encoding", $_[1] ) },
101
- "config-dir=s" => sub { push( @svn_options, "--config-dir", $_[1] ) },
102
- "help|?" => sub{ Usage() },
103
- ) or Usage();
104
-
105
- # Put in a signal handler to clean up any temporary directories.
106
- sub catch_signal {
107
- my $signal = shift;
108
- warn "$0: caught signal $signal. Quitting now.\n";
109
- exit 1;
110
- }
111
-
112
- $SIG{HUP} = \&catch_signal;
113
- $SIG{INT} = \&catch_signal;
114
- $SIG{TERM} = \&catch_signal;
115
- $SIG{PIPE} = \&catch_signal;
116
-
117
- #
118
- # Check our parameters
119
- #
120
- if ( @ARGV < 2 )
121
- {
122
- Usage( "Please specify source and destination" );
123
- exit 1;
124
- }
125
-
126
- #
127
- # Get source(s) and destination.
128
- #
129
- push ( @sources, shift( @ARGV ) );
130
- $destination = shift( @ARGV );
131
- while ( scalar( @ARGV ) )
132
- {
133
- push( @sources, $destination );
134
- $destination = shift( @ARGV );
135
- }
136
-
137
- #
138
- # Any validation errors? If so, bomb out.
139
- #
140
- if ( scalar( @errors ) > 0 )
141
- {
142
- print "\n", @errors;
143
- Usage();
144
- exit scalar( @errors );
145
- }
146
-
147
- #
148
- # Now do the main processing.
149
- # This will update @errors if anything goes wrong.
150
- #
151
- if ( !DoCopy( \@sources, $destination, $message ) )
152
- {
153
- print "\n*****************************************************************\n";
154
- print "Errors:\n";
155
- print @errors;
156
- }
157
-
158
- exit scalar( @errors );
159
-
160
-
161
- #------------------------------------------------------------------------------
162
- # Function: DoCopy
163
- #
164
- # Does the work of the copy.
165
- #
166
- # Parameters:
167
- # sources Reference to array containing source URLs
168
- # destination Destination URL
169
- # message Commit message to use
170
- #
171
- # Returns: 0 on error
172
- #
173
- # Updates @errors.
174
- #------------------------------------------------------------------------------
175
- sub DoCopy
176
- {
177
- my ( $sourceref, $destination, $message ) = @_;
178
- my @sources = @$sourceref;
179
- my $revstr = "";
180
- my $src;
181
- my $startdir = cwd;
182
- my $starterrors = scalar( @errors );
183
-
184
- print "\n=================================================================\n";
185
- $revstr = "\@$revision" if $revision;
186
- print "=== Copying from:\n";
187
- foreach $src ( @sources ) { print "=== $src$revstr\n"; }
188
- print "===\n";
189
- print "=== Copying to:\n";
190
- print "=== $destination\n";
191
- print "===\n";
192
- print "=== - branching (updating fully-contained svn:externals definitions)\n" if $update_externals;
193
- if ( $pin_externals )
194
- {
195
- my $revtext = $revision ? "revision $revision" : "current revision";
196
- print "=== - tagging (pinning all svn:externals definitions to $revtext)\n";
197
- }
198
- print "===\n" if ( $update_externals or $pin_externals );
199
-
200
- # Convert destination to URI
201
- $destination =~ s|/*$||;
202
- my $destination_uri = URI->new($destination);
203
-
204
- #
205
- # Generate a message if we don't have one.
206
- #
207
- unless ( $message )
208
- {
209
- $message = "svncopy.pl: Copied to '$destination'\n";
210
- foreach $src ( @sources )
211
- {
212
- $message .= " Copied from '$src'\n";
213
- }
214
- }
215
-
216
- #
217
- # Create a temporary directory to work in.
218
- #
219
- my ( $auto_temp_dir, $dest_dir ) =
220
- PrepareDirectory( $destination_uri, "svncopy.pl to '$destination'\n - creating intermediate directory" );
221
- $temp_dir = $auto_temp_dir->temp_dir();
222
- chdir( $temp_dir );
223
-
224
- foreach $src ( @sources )
225
- {
226
- # Convert source to URI
227
- $src =~ s|/*$||;
228
- my $source_uri = URI->new($src);
229
-
230
- #
231
- # Do the initial copy into our temporary. Note this will create a
232
- # subdirectory with the same name as the last directory in $source.
233
- #
234
- if ( !CopyToWorkDir( $src, $dest_dir ) )
235
- {
236
- error( "Copy failed" );
237
- return 0;
238
- }
239
- }
240
-
241
- #
242
- # Do any processing.
243
- #
244
- if ( $pin_externals or $update_externals )
245
- {
246
- if ( !UpdateExternals( $sourceref, $destination, $dest_dir, \$message ) )
247
- {
248
- error( "Couldn't update svn:externals" );
249
- return 0;
250
- }
251
- }
252
-
253
- #
254
- # And check in the new.
255
- #
256
- DoCommit( $dest_dir, $message ) or die "Couldn't commit\n";
257
-
258
- # Make sure we finish in the directory we started
259
- chdir( $startdir );
260
-
261
- print "=== ... copy complete\n";
262
- print "=================================================================\n";
263
-
264
- # Return whether there was an error.
265
- return ( scalar( @errors ) == $starterrors );
266
- }
267
-
268
-
269
- #------------------------------------------------------------------------------
270
- # Function: PrepareDirectory
271
- #
272
- # Prepares a temporary directory to work in.
273
- #
274
- # Parameters:
275
- # destination Destination URI
276
- # message Commit message
277
- #
278
- # Returns: temporary directory and subdirectory to work in
279
- #------------------------------------------------------------------------------
280
- sub PrepareDirectory
281
- {
282
- my ( $destination, $message ) = @_;
283
-
284
- my $auto_temp_dir = Temp::Delete->new();
285
- $temp_dir = $auto_temp_dir->temp_dir();
286
- info( "Using temporary directory $temp_dir\n" );
287
-
288
- #
289
- # Our working destination directory has the same name as the last directory
290
- # in the destination URI.
291
- #
292
- my @path_segments = grep { length($_) } $destination->path_segments;
293
- my $new_dir = pop( @path_segments );
294
- my $dest_dir = "$temp_dir/$new_dir";
295
-
296
- # Make sure the destination directory exists in Subversion.
297
- info( "Creating intermediate directories (if necessary)\n" );
298
- if ( !CreateSVNDirectories( $destination, $message ) )
299
- {
300
- error( "Couldn't create parent directories for '$destination'" );
301
- return;
302
- }
303
-
304
- # Check out the destination.
305
- info( "Checking out destination directory '$destination'\n" );
306
- if ( 0 != SVNCall( 'co', $destination, $dest_dir ) )
307
- {
308
- error( "Couldn't check out '$destination' into work directory." );
309
- return;
310
- }
311
-
312
- return ( $auto_temp_dir, $dest_dir );
313
- }
314
-
315
-
316
- #------------------------------------------------------------------------------
317
- # Function: CopyToWorkDir
318
- #
319
- # Does the svn copy into the temporary directory.
320
- #
321
- # Parameters:
322
- # source The URI to copy from
323
- # work_dir The working directory
324
- #
325
- # Returns: 1 on success
326
- #------------------------------------------------------------------------------
327
- sub CopyToWorkDir
328
- {
329
- my ( $source, $work_dir ) = @_;
330
- my $dest_dir = DestinationSubdir( $source, $work_dir );
331
- my @commandline = ();
332
-
333
- push( @commandline, "--revision", $revision ) if ( $revision );
334
-
335
- push( @commandline, $source, $work_dir );
336
-
337
- my $exit = SVNCall( "copy", @commandline );
338
-
339
- error( "$0: svn copy failed" ) if ( 0 != $exit );
340
-
341
- return ( 0 == $exit );
342
- }
343
-
344
-
345
- #------------------------------------------------------------------------------
346
- # Function: DestinationSubdir
347
- #
348
- # Returns the destination directory for a given source and a destination root
349
- # directory.
350
- #
351
- # Parameters:
352
- # source The URL to copy from
353
- # destination The working directory
354
- #
355
- # Returns: The relevant directory
356
- #------------------------------------------------------------------------------
357
- sub DestinationSubdir
358
- {
359
- my ( $source, $destination ) = @_;
360
- my $subdir;
361
-
362
- # Make sure source and destination are consistent about separator.
363
- # Note every function we call can handle Unix path format, so we
364
- # default to that.
365
- $source =~ s|\\|/|g;
366
- $destination =~ s|\\|/|g;
367
-
368
- # Find the last directory - that's the subdir we'll use in $destination
369
- if ( $source =~ m"/([^/]+)/*$" )
370
- {
371
- $subdir = $1;
372
- }
373
- else
374
- {
375
- $subdir = $source;
376
- }
377
- return "$destination/$subdir";
378
- }
379
-
380
-
381
- #------------------------------------------------------------------------------
382
- # Function: UpdateExternals
383
- #
384
- # Updates the svn:externals in the tree according to the --pin-externals or
385
- # --update_externals options.
386
- #
387
- # Parameters:
388
- # sourceref Ref to the URLs to copy from
389
- # destination The URL being copied to
390
- # work_dir The working directory
391
- # msgref Ref to message string to update with changes
392
- #
393
- # Returns: 1 on success
394
- #------------------------------------------------------------------------------
395
- sub UpdateExternals
396
- {
397
- my ( $sourceref, $destination, $work_dir, $msgref ) = @_;
398
- my @commandline = ();
399
- my $msg;
400
- my @dirfiles;
401
- my %extlist;
402
-
403
- # Check the externals on this directory and subdirectories
404
- info( "Checking '$work_dir'\n" );
405
- %extlist = GetRecursiveExternals( $work_dir );
406
-
407
- # And do the update
408
- while ( my ( $subdir, $exts ) = each ( %extlist ) )
409
- {
410
- my @externals = @$exts;
411
- if ( scalar( @externals ) )
412
- {
413
- UpdateExternalsOnDir( $sourceref, $destination, $subdir, $msgref, \@externals );
414
- }
415
- }
416
-
417
- return 1;
418
- }
419
-
420
-
421
- #------------------------------------------------------------------------------
422
- # Function: UpdateExternalsOnDir
423
- #
424
- # Updates the svn:externals in the tree according to the --pin-externals or
425
- # --update_externals options.
426
- #
427
- # Parameters:
428
- # sourceref Ref to the URLs to copy from
429
- # destination The URL being copied to
430
- # work_dir The working directory
431
- # externals Ref to the externals on the directory
432
- # msgref Ref to message string to update with changes
433
- #
434
- # Returns: 1 on success
435
- #------------------------------------------------------------------------------
436
- sub UpdateExternalsOnDir
437
- {
438
- my ( $sourceref, $destination, $work_dir, $msgref, $externalsref ) = @_;
439
- my @sources = @$sourceref;
440
- my @externals = @$externalsref;
441
- my @new_externals;
442
- my %changed;
443
-
444
- # Do any updating required
445
- foreach my $external ( @externals )
446
- {
447
- chomp( $external );
448
- next unless ( $external =~ m"^(\S+)(\s+)(?:-r\s*(\d+)\s+)?(.*)" );
449
- my ( $ext_dir, $spacing, $ext_rev, $ext_val ) = ( $1, $2, $3, $4 );
450
-
451
- info( " - Found $ext_dir => '$ext_val'" );
452
- info( " ($ext_rev)" ) if $ext_rev;
453
- info( "\n" );
454
-
455
- $externals_hash{ "$ext_val" } = $ext_rev;
456
-
457
- # Only update if it's not pinned to a version
458
- if ( !$ext_rev )
459
- {
460
- if ( $update_externals )
461
- {
462
- my $old_external = $external;
463
- foreach my $source ( @sources )
464
- {
465
- my $dest_dir = DestinationSubdir( $source, $destination );
466
- #info( "Checking against '$source'\n" );
467
- if ( $ext_val =~ s|^$source|$dest_dir| )
468
- {
469
- $external = "$ext_dir$spacing$ext_val";
470
- info( " - updated '$old_external' to '$external'\n" );
471
- $changed{$old_external} = $external;
472
- }
473
- }
474
- }
475
- elsif ( $pin_externals )
476
- {
477
- # Find the last revision of the destination and pin to that.
478
- my $old_external = $external;
479
- my $rev = LatestRevision( $ext_val, $revision );
480
- #info( "Pinning '$ext_val' to '$rev'\n" );
481
- $external = "$ext_dir -r $rev$spacing$ext_val";
482
- info( " - updated '$old_external' to '$external'\n" );
483
- $changed{$old_external} = $external;
484
- }
485
- }
486
- push( @new_externals, $external );
487
- }
488
-
489
- # And write the changes back
490
- if ( scalar( %changed ) )
491
- {
492
- # Update the commit log message
493
- my %info = SVNInfo( $work_dir );
494
- $$msgref .= "\n * $info{URL}: update svn:externals\n";
495
- while ( my ( $old, $new ) = each( %changed ) )
496
- {
497
- $$msgref .= " from '$old' to '$new'\n";
498
- info( " '$old' => '$new'\n" );
499
- }
500
-
501
- # And set the new externals
502
- my ($handle, $tmpfile) = tempfile( DIR => $temp_dir );
503
- print $handle join( "\n", @new_externals );
504
- close($handle);
505
- SVNCall( "propset", "--file", $tmpfile, "svn:externals", $work_dir );
506
- }
507
- }
508
-
509
-
510
- #------------------------------------------------------------------------------
511
- # Function: GetRecursiveExternals
512
- #
513
- # This function retrieves the svn:externals value from the
514
- # specified URL or location and subdirectories.
515
- #
516
- # Parameters:
517
- # location location of SVN object - file/dir or URL.
518
- #
519
- # Returns: hash
520
- #------------------------------------------------------------------------------
521
- sub GetRecursiveExternals
522
- {
523
- my ( $location ) = @_;
524
- my %retval;
525
- my $externals;
526
- my $subdir = ".";
527
-
528
- my ( $status, @externals ) = SVNCall( "propget", "-R", "svn:externals", $location );
529
-
530
- foreach my $external ( @externals )
531
- {
532
- chomp( $external );
533
-
534
- if ( $external =~ m"(.*) - (.*\s.*)" )
535
- {
536
- $subdir = $1;
537
- $external = $2;
538
- }
539
-
540
- push( @{$retval{$subdir}}, $external ) unless $external =~ m"^\s*$";
541
- }
542
-
543
- return %retval;
544
- }
545
-
546
-
547
- #------------------------------------------------------------------------------
548
- # Function: SVNInfo
549
- #
550
- # Gets the info about the given file.
551
- #
552
- # Parameters:
553
- # file The SVN object to query
554
- #
555
- # Returns: hash with the info
556
- #------------------------------------------------------------------------------
557
- sub SVNInfo
558
- {
559
- my $file = shift;
560
- my $old_verbose = $verbose;
561
- $verbose = 0;
562
- my ( $retval, @output ) = SVNCall( "info", $file );
563
- $verbose = $old_verbose;
564
- my %info;
565
-
566
- return if ( 0 != $retval );
567
-
568
- foreach my $line ( @output )
569
- {
570
- if ( $line =~ "^(.*): (.*)" )
571
- {
572
- $info{ $1 } = $2;
573
- }
574
- }
575
-
576
- return %info;
577
- }
578
-
579
-
580
- #------------------------------------------------------------------------------
581
- # Function: LatestRevision
582
- #
583
- # Returns the repository revision of the last change to the given object not
584
- # later than the given revision (i.e. it may return revision, but won't
585
- # return revision+1).
586
- #
587
- # Parameters:
588
- # source The URL to check
589
- # revision The revision of the URL to check from (if not supplied
590
- # defaults to last revision).
591
- #
592
- # Returns: The relevant revision number
593
- #------------------------------------------------------------------------------
594
- sub LatestRevision
595
- {
596
- my ( $source, $revision ) = @_;
597
- my $revtext = "";
598
-
599
- if ( $revision )
600
- {
601
- $revtext = "--revision $revision:0";
602
- }
603
-
604
- my $old_verbose = $verbose;
605
- $verbose = 0;
606
- my ( $retval, @output ) = SVNCall( "log -q", $revtext, $source );
607
- $verbose = $old_verbose;
608
-
609
- if ( 0 != $retval )
610
- {
611
- error( "LatestRevision: log -q on '$source' failed" );
612
- return -1;
613
- }
614
-
615
- #
616
- # The second line should give us the info we need: e.g.
617
- #
618
- # >svn log -q http://subversion/svn/scratch/ianb/svncopy-update/source/dirA
619
- # ------------------------------------------------------------------------
620
- # r1429 | ib | 2004-06-14 17:39:36 +0100 (Mon, 14 Jun 2004)
621
- # ------------------------------------------------------------------------
622
- # r1423 | ib | 2004-06-14 17:39:26 +0100 (Mon, 14 Jun 2004)
623
- # ------------------------------------------------------------------------
624
- # r1422 | ib | 2004-06-14 17:39:23 +0100 (Mon, 14 Jun 2004)
625
- # ------------------------------------------------------------------------
626
- # r1421 | ib | 2004-06-14 17:39:22 +0100 (Mon, 14 Jun 2004)
627
- # ------------------------------------------------------------------------
628
- #
629
- # The second line starts with the latest revision number.
630
- #
631
- if ( $output[1] =~ m"^r(\d+) \|" )
632
- {
633
- return $1;
634
- }
635
-
636
- error( "LatestRevision: log output not formatted as expected\n" );
637
-
638
- return -1;
639
- }
640
-
641
-
642
- #------------------------------------------------------------------------------
643
- # Function: DoCommit
644
- #
645
- # svn commits the temporary directory.
646
- #
647
- # Parameters:
648
- # work_dir The working directory
649
- # message Commit message
650
- #
651
- # Returns: non-zero on success
652
- #------------------------------------------------------------------------------
653
- sub DoCommit
654
- {
655
- my ( $work_dir, $message ) = @_;
656
- my @commandline = ();
657
-
658
- # Prepare a file containing the message
659
- my ($handle, $messagefile) = tempfile( DIR => $temp_dir );
660
- print $handle $message;
661
- close($handle);
662
- push( @commandline, "--file", $messagefile );
663
-
664
- push( @commandline, $work_dir );
665
-
666
- my ( $exit ) = SVNCall( "commit", @commandline );
667
-
668
- error( "$0: svn commit failed" ) if ( 0 != $exit );
669
-
670
- return ( 0 == $exit );
671
- }
672
-
673
-
674
- #------------------------------------------------------------------------------
675
- # Function: SVNCall
676
- #
677
- # Makes a call to subversion.
678
- #
679
- # Parameters:
680
- # command Subversion command
681
- # options Other options to pass to Subversion
682
- #
683
- # Returns: exit status, output from command
684
- #------------------------------------------------------------------------------
685
- sub SVNCall
686
- {
687
- my ( $command, @options ) = @_;
688
-
689
- # this block was added for the script to work fine with temporary
690
- # directory pathes which include space characters
691
- my $i=0; for(@options)
692
- {
693
- if(($options[$i] !~ /^".*"$/) && ($options[$i] =~ /\/.*/i))
694
- {
695
- $options[$i] = '"'.$_.'"';
696
- }
697
- $i++;
698
- }
699
-
700
- my @commandline = ( $svn, $command, @svn_options, @options );
701
-
702
- info( " > ", join( " ", @commandline ), "\n" );
703
-
704
- my @output = qx( @commandline 2>&1 );
705
-
706
- my $result = $?;
707
- my $exit = $result >> 8;
708
- my $signal = $result & 127;
709
- my $cd = $result & 128 ? "with core dump" : "";
710
- if ($signal or $cd)
711
- {
712
- error( "$0: 'svn $command' failed $cd: exit=$exit signal=$signal\n" );
713
- }
714
-
715
- if ( $exit > 0 )
716
- {
717
- info( join( "\n", @output ) );
718
- }
719
- if ( wantarray )
720
- {
721
- return ( $exit, @output );
722
- }
723
-
724
- return $exit;
725
- }
726
-
727
-
728
- #------------------------------------------------------------------------------
729
- # Function: FindRepositoryRoot
730
- #
731
- # Returns the root of the repository for a given URL. Do
732
- # this with the svn log command. Take the svn_url hostname and port
733
- # as the initial url and append to it successive portions of the final
734
- # path until svn log succeeds.
735
- #
736
- # Parameters:
737
- # URI URI within repository
738
- #
739
- # Returns: A URI for the root, or undefined on error
740
- #------------------------------------------------------------------------------
741
- sub FindRepositoryRoot
742
- {
743
- my $URI = shift;
744
- my $repos_root_uri;
745
- my $repos_root_uri_path;
746
- my $old_verbose = $verbose;
747
- $verbose = 0;
748
-
749
- info( "Finding the root URL of '$URI'.\n" );
750
-
751
- my $r = $URI->clone;
752
- my @path_segments = grep { length($_) } $r->path_segments;
753
- unshift(@path_segments, '');
754
- $r->path('');
755
- my @r_path_segments;
756
-
757
- while (@path_segments)
758
- {
759
- $repos_root_uri_path = shift @path_segments;
760
- push(@r_path_segments, $repos_root_uri_path);
761
- $r->path_segments(@r_path_segments);
762
- if ( SVNCall( 'log', '-r', 'HEAD', $r ) == 0 )
763
- {
764
- $repos_root_uri = $r;
765
- last;
766
- }
767
- }
768
-
769
- $verbose = $old_verbose;
770
-
771
- if ($repos_root_uri)
772
- {
773
- info( "Determined that the svn root URL is $repos_root_uri.\n\n" );
774
- return $repos_root_uri;
775
- }
776
- else
777
- {
778
- error( "$0: cannot determine root svn URL for '$URI'.\n" );
779
- return;
780
- }
781
- }
782
-
783
-
784
- #------------------------------------------------------------------------------
785
- # Function: CreateSVNDirectories
786
- #
787
- # Creates a directory in Subversion, including all intermediate directories.
788
- #
789
- # Parameters:
790
- # URI directory path to create.
791
- # message commit message (optional).
792
- #
793
- # Returns: 1 on success, 0 on error
794
- #------------------------------------------------------------------------------
795
- sub CreateSVNDirectories
796
- {
797
- my ( $URI, $message ) = @_;
798
- my $r = $URI->clone;
799
- my @path_segments = grep { length($_) } $r->path_segments;
800
- my @r_path_segments;
801
- unshift(@path_segments, '');
802
- $r->path('');
803
-
804
- my $found_root = 0;
805
- my $found_tail = 0;
806
-
807
- # Prepare a file containing the message
808
- my ($handle, $messagefile) = tempfile( DIR => $temp_dir );
809
- print $handle $message;
810
- close($handle);
811
- my @msgcmd = ( "--file", $messagefile );
812
-
813
- # We're going to get errors while we do this. Don't show the user.
814
- my $old_verbose = $verbose;
815
- $verbose = 0;
816
- # Find the repository root
817
- while (@path_segments)
818
- {
819
- my $segment = shift @path_segments;
820
- push( @r_path_segments, $segment );
821
- $r->path_segments( @r_path_segments );
822
- if ( !$found_root )
823
- {
824
- if ( SVNCall( 'log', '-r', 'HEAD', $r ) == 0 )
825
- {
826
- # We've found the root of the repository.
827
- $found_root = 1;
828
- }
829
- }
830
- elsif ( !$found_tail )
831
- {
832
- if ( SVNCall( 'log', '-r', 'HEAD', $r ) != 0 )
833
- {
834
- # We've found the first directory which doesn't exist.
835
- $found_tail = 1;
836
- }
837
- }
838
-
839
- if ( $found_tail )
840
- {
841
- # We're creating directories
842
- $verbose = $old_verbose;
843
- if ( 0 != SVNCall( 'mkdir', @msgcmd, $r ) )
844
- {
845
- error( "Couldn't create directory '$r'" );
846
- return 0;
847
- }
848
- }
849
- }
850
- $verbose = $old_verbose;
851
-
852
- return 1;
853
- }
854
-
855
-
856
- #------------------------------------------------------------------------------
857
- # Function: info
858
- #
859
- # Prints out an informational message in verbose mode
860
- #
861
- # Parameters:
862
- # @_ The message(s) to print
863
- #
864
- # Returns: none
865
- #------------------------------------------------------------------------------
866
- sub info
867
- {
868
- if ( $verbose )
869
- {
870
- print @_;
871
- }
872
- }
873
-
874
-
875
- #------------------------------------------------------------------------------
876
- # Function: error
877
- #
878
- # Prints out and logs an error message
879
- #
880
- # Parameters:
881
- # @_ The error messages
882
- #
883
- # Returns: none
884
- #------------------------------------------------------------------------------
885
- sub error
886
- {
887
- my $error;
888
-
889
- # This is used during testing
890
- if ( $hideerrors )
891
- {
892
- return;
893
- }
894
-
895
- # Now print out each error message and add it to the list.
896
- foreach $error ( @_ )
897
- {
898
- my $text = "svncopy.pl: $error\n";
899
- push( @errors, $text );
900
- if ( $verbose )
901
- {
902
- print $text;
903
- }
904
- }
905
- }
906
-
907
-
908
- #------------------------------------------------------------------------------
909
- # Function: Usage
910
- #
911
- # Prints out usage information.
912
- #
913
- # Parameters:
914
- # optional error message
915
- #
916
- # Returns: none
917
- #------------------------------------------------------------------------------
918
- sub Usage
919
- {
920
- my $msg;
921
- $msg = "\n*** $_[0] ***\n" if $_[0];
922
-
923
- pod2usage( { -message => $msg,
924
- -verbose => 0 } );
925
- }
926
-
927
-
928
- #------------------------------------------------------------------------------
929
- # This package exists just to delete the temporary directory.
930
- #------------------------------------------------------------------------------
931
- package Temp::Delete;
932
-
933
- use File::Temp 0.12 qw(tempdir);
934
-
935
- sub new
936
- {
937
- my $this = shift;
938
- my $class = ref($this) || $this;
939
- my $self = {};
940
- bless $self, $class;
941
-
942
- my $temp_dir = tempdir("svncopy_XXXXXXXXXX", TMPDIR => 1);
943
-
944
- $self->{tempdir} = $temp_dir;
945
-
946
- return $self;
947
- }
948
-
949
- sub temp_dir
950
- {
951
- my $self = shift;
952
- return $self->{tempdir};
953
- }
954
-
955
- sub DESTROY
956
- {
957
- my $self = shift;
958
- my $temp_dir = $self->{tempdir};
959
- if ( scalar( @errors ) )
960
- {
961
- print "Leaving $temp_dir for inspection\n";
962
- }
963
- else
964
- {
965
- info( "Cleaning up $temp_dir\n" );
966
- File::Path::rmtree([$temp_dir], 0, 0);
967
- }
968
- }
969
-
970
-
971
- #------------------------------------------------------------------------------
972
- # Documentation follows, in pod format.
973
- #------------------------------------------------------------------------------
974
- __END__
975
-
976
- =head1 NAME
977
-
978
- B<svncopy> - extended form of B<svn copy>
979
-
980
- =head1 SYNOPSIS
981
-
982
- B<svncopy.pl> [option ...] source [source ...] destination
983
-
984
- This script copies one Subversion location or set of locations to another,
985
- in the same way as B<svn copy>. Using the script allows more advanced operations,
986
- in particular allowing svn:externals to be dealt with properly for branching
987
- or tagging.
988
-
989
- Parameters:
990
- source Subversion item to copy from.
991
- Multiple sources can be given.
992
- destination Destination to copy to.
993
-
994
- Options:
995
- -t [--tag] : set svn:externals to current version
996
- [--pin-externals ]
997
- -b [--branch] : update fully contained svn:externals
998
- [--update-externals]
999
- -m [--message] arg : specify commit message ARG
1000
- -F [--file] arg : read data from file ARG
1001
- -r [--revision] arg : ARG (some commands also take ARG1:ARG2 range)
1002
- A revision argument can be one of:
1003
- NUMBER revision number
1004
- "{" DATE "}" revision at start of the date
1005
- "HEAD" latest in repository
1006
- "BASE" base rev of item's working copy
1007
- "COMMITTED" last commit at or before BASE
1008
- "PREV" revision just before COMMITTED
1009
- -q [--quiet] : print as little as possible
1010
- --username arg : specify a username ARG
1011
- --password arg : specify a password ARG
1012
- --no-auth-cache : do not cache authentication tokens
1013
- --force-log : force validity of log message source
1014
- --encoding arg : treat value as being in charset encoding ARG
1015
- --config-dir arg : read user config files from directory ARG
1016
- --[no]verbose : sets the script to give lots of output
1017
-
1018
- =head1 PARAMETERS
1019
-
1020
- =over
1021
-
1022
- =item B<source>
1023
-
1024
- The subversion item or items to copy from.
1025
-
1026
- =item B<destination>
1027
-
1028
- The destination URL to copy to.
1029
-
1030
- =back
1031
-
1032
- =head1 OPTIONS
1033
-
1034
- =over
1035
-
1036
- =item B<-t [--pin-externals or --tag]>
1037
-
1038
- Update any svn:externals to ensure they have a version number,
1039
- using the current destination version if none is already specified.
1040
- Useful for tagging operations.
1041
-
1042
- =item B<-b [--update-externals or --branch]>
1043
-
1044
- Update any unversioned svn:externals which point to a location
1045
- within one of the sources so that they point to the corresponding
1046
- location within the destination.
1047
-
1048
- Note: --pin-externals and --update-externals are mutually exclusive.
1049
-
1050
- =item B<-m [--message] arg>
1051
-
1052
- Specify commit message ARG
1053
-
1054
- =item B<-F [--file] arg>
1055
-
1056
- Read data from file ARG
1057
-
1058
- =item B<-r [--revision] arg>
1059
-
1060
- ARG (some commands also take ARG1:ARG2 range)
1061
- A revision argument can be one of:
1062
-
1063
- NUMBER revision number
1064
- "{" DATE "}" revision at start of the date
1065
- "HEAD" latest in repository
1066
- "BASE" base rev of item's working copy
1067
- "COMMITTED" last commit at or before BASE
1068
- "PREV" revision just before COMMITTED
1069
-
1070
- =item B<-q [--quiet]>
1071
-
1072
- Print as little as possible
1073
-
1074
- =item B<--username arg>
1075
-
1076
- Specify a username ARG
1077
-
1078
- =item B<--password arg>
1079
-
1080
- Specify a password ARG
1081
-
1082
- =item B<--no-auth-cache>
1083
-
1084
- Do not cache authentication tokens
1085
-
1086
- =item B<--force-log>
1087
-
1088
- Force validity of log message source
1089
-
1090
- =item B<--encoding arg>
1091
-
1092
- Treat value as being in charset encoding ARG
1093
-
1094
- =item B<--config-dir arg>
1095
-
1096
- Read user configuration files from directory ARG
1097
-
1098
- =item B<--[no]verbose>
1099
-
1100
- Sets the script to give lots of output when it runs.
1101
-
1102
- =item B<--help>
1103
-
1104
- Print a brief help message and exits.
1105
-
1106
- =back
1107
-
1108
- =head1 DESCRIPTION
1109
-
1110
- This script performs an B<svn copy> command. It allows extra processing to get
1111
- around the following limitations of B<svn copy>:
1112
-
1113
- svn:externals definitions are (in Subversion 1.0 and 1.1 at least) absolute paths.
1114
- This means that an B<svn copy> used as a branch or tag operation on a tree with
1115
- embedded svn:externals will not do what is expected. The svn:externals
1116
- will still point at the original location and will not be pinned down.
1117
-
1118
- B<svncopy --update-externals> (or B<svncopy --branch>) will update any
1119
- unversioned svn:externals in the destination tree which point at locations
1120
- within one of the source trees so that they point to the corresponding locations
1121
- within the destination tree instead. This effectively updates the reference to
1122
- point to the destination tree, and is the behaviour you want for branching.
1123
-
1124
- B<svncopy --pin-externals> (or B<svncopy --tag>) will update any unversioned
1125
- svn:externals in the destination tree to contain the current version of the
1126
- directory listed in the svn:externals definition. This effectively pins
1127
- the reference to the current version, and is the behaviour you want for tagging.
1128
-
1129
- Note: both forms of the command leave unchanged any svn:externals which
1130
- already contain a version number.
1131
-
1132
- =cut
1133
-
1134
- #------------------------------- END OF FILE ----------------------------------