nixenvironment 0.0.70 → 0.0.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 ----------------------------------