Subversion Repositories DevTools

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
267 dpurdie 1
########################################################################
2
# Copyright (C) 2008 ERG Limited, All rights reserved
3
#
4
# Module name   : jats.sh
5
# Module type   : Makefile system
6
# Compiler(s)   : n/a
7
# Environment(s): jats
8
#
9
# Description   : JATS Subversion Interface Functions
10
#
11
#                 Requires a subversion client to be present on the machine
12
#                 Does require at least SubVersion 1.5
13
#                 Uses features not available in 1.4
14
#
15
#                 The package currently implements a set of functions
16
#                 There are some intentional limitations:
17
#                   1) Non recursive
18
#                   2) Errors terminate operation
19
#
20
#                 This package contains experimental argument passing
21
#                 processes. Sometimes use a hash of arguments
22
#
23
#......................................................................#
24
 
25
require 5.008_002;
26
use strict;
27
use warnings;
28
our $GBE_SVN_URL;
341 dpurdie 29
our $USER;
267 dpurdie 30
use JatsEnv;
31
 
32
package JatsSvn;
33
 
34
use JatsError;
35
use JatsSystem;
36
use JatsSvnCore qw(:All);
37
 
38
use File::Path;             # Instead of FileUtils
39
use File::Basename;
40
use Cwd;
41
 
42
 
43
# automatically export what we need into namespace of caller.
44
use Exporter();
45
our (@ISA, @EXPORT, %EXPORT_TAGS, @EXPORT_OK);
46
@ISA         = qw(Exporter JatsSvnCore);
47
 
48
@EXPORT      = qw(
49
                    NewSession
50
                    NewSessionByWS
51
                    NewSessionByUrl
52
 
53
                    SvnRmView
54
                    SvnIsaSimpleLabel
55
                    SvnComment
56
 
57
                    SvnUserCmd
58
                );
59
@EXPORT_OK =  qw(
60
                );
61
 
62
%EXPORT_TAGS = (All => [@EXPORT, @EXPORT_OK]);
63
 
64
#
65
#   Global Variables
66
#
67
 
68
#-------------------------------------------------------------------------------
69
# Function        : BEGIN
70
#
71
# Description     : Module Initialization
72
#                   Invoked by Perl as soon as possible
73
#                       Setup environment variables
74
#                       Calculate globals
75
#
76
# Inputs          : None
77
#
78
# Returns         : Nothing
79
#
80
sub BEGIN
81
{
82
    #
83
    #   Need to have a configured Repository
84
    #
85
    ::EnvImport ('GBE_SVN_URL' );
86
 
87
    #
88
    #   Remove trailing /s
89
    #
90
    $::GBE_SVN_URL =~ s~/*$~~;
91
 
92
    #
93
    #   Ensure that it is in valid format
94
    #   Three forms are supported
95
    #
96
    if ( $::GBE_SVN_URL =~ m{^svn://[^/]+$} ) {
97
        #
98
        #   Type is SVN server
99
        #   Protocol + server name
100
        #
101
    } elsif ( $::GBE_SVN_URL =~ m{^http://.+} ) {
102
        #
103
        #   Type is HTTP server
104
        #   Protocol + server name + path on server
105
        #
106
    } elsif ( $::GBE_SVN_URL =~ m{^file:///+[A-Z]:/} ) {
107
        #
108
        #   Type is local Repo (file)
109
        #   Windows absolute pathname
110
        #   file:///I:/path/...
111
        #
112
    } elsif ( $::GBE_SVN_URL =~ m{^file:///+[^/]} ) {
113
        #
114
        #   Type is local Repo (file)
115
        #   Unix absolute pathname
116
        #   file:///path/...
117
        #
118
    } else {
119
        Error ("GBE_SVN_URL format not understood","GBE_SVN_URL: $::GBE_SVN_URL");
120
    }
121
 
122
    #
123
    #   Add a trailing '/'
124
    #   This will make it easier to use
125
    #
126
    $::GBE_SVN_URL .= '/';
127
 
128
}
129
 
130
#-------------------------------------------------------------------------------
131
# Function        : SvnCo
132
#
133
# Description     : Create a workspace
134
#                   Can be used to extract files, without creating the
135
#                   subversion control files.
136
#
137
# Inputs          : $self               - Instance data
138
#                   $RepoPath           - Within the repository
139
#                   $Path               - Local path
140
#                   Options             - Options
141
#                           --Export    - Export Only
142
#
143
# Returns         : Nothing
144
#
145
sub SvnCo
146
{
147
    my ($self, $RepoPath, $path, @opts) = @_;
148
    my $export = grep (/^--Export/, @opts );
341 dpurdie 149
    Debug ("SvnCo", $RepoPath, $path);
267 dpurdie 150
 
151
    #
152
    #   Ensure that the output path does not exist
153
    #   Do not allow the user to create a local work space
154
    #   where one already exists
155
    #
156
    Error ("SvnCo: No PATH specified" ) unless ( $path );
157
    Error ("SvnCo: Target path already exists", "Path: " . $path ) if ( -e $path );
158
 
159
    #
160
    #   Build up the command line
161
    #
162
    my @args = $export ? 'export' : 'checkout';
163
    push @args, qw( --ignore-externals );
164
    push @args, $RepoPath, $path;
165
 
166
    my @co_list;
167
    if ( $self->SvnCmd ( @args,
168
                            {
169
                                'process' => \&ProcessCo,
170
                                'data' => \@co_list,
171
                                'credentials' => 1,
172
                                'nosavedata' => 1,
173
                            }
174
                       ) || @co_list )
175
    {
176
        #
177
        #   We have a checkout limitation
178
        #   Delete the workspace and then report the error
179
        #
180
        Verbose2 ("Remove WorkSpace: $path");
181
        rmtree( $path, IsVerbose(3) );
182
        Error ("Checking out Workspace", @{$self->{ERROR_LIST}}, @co_list );
183
    }
184
    return;
185
 
186
    #
187
    #   Internal routine to scan each the checkout
188
    #
189
    #   Due to the structure of a SubVersion repository it would be
190
    #   possible for a user to extract the entire repository. This is
191
    #   not good as the repo could be very very large
192
    #
193
    #   Assume that the structure of the repo is such that our
194
    #   user is not allowed to extract a directory tree that contains
195
    #   key paths - such as /tags/ as this would indicate that they are
196
    #   attempting to extract something that is not a package
197
    #
198
    #
199
    sub ProcessCo
200
    {
201
        my $self = shift;
202
        if (  m~((/)(tags|branches|trunk)(/|$))~ )
203
        {
204
            my $bad_dir = $1;
205
            push @{$self->{ERROR_LIST}}, "Checkout does not describe the root of a package. Contains: $bad_dir";
206
            return 1;
207
        }
208
 
209
        ##
210
        ##   Limit the size of the WorkSpace
211
        ##   This limit is a bit artificial, but does attempt to
212
        ##   limit views that encompass too much.
213
        ##
214
        #if ( $#{$self->{RESULT_LIST}} > 100 )
215
        #{
216
        #    Warning ("View is too large - DEBUG USE ONLY. WILL BE REMOVED" );
217
        #    push @{$self->{ERROR_LIST}}, "View too large";
218
        #    return 1;
219
        #}
220
    }
221
}
222
 
223
#-------------------------------------------------------------------------------
224
# Function        : SvnCi
225
#
226
# Description     : Check in the specified WorkSpace
227
#
228
# Inputs          : $self           - Instance data
229
#                   A hash of named arguments
230
#                       comment     - Commit comment
231
#
232
# Returns         : Tag of the checkin
233
#
234
sub SvnCi
235
{
236
    my $self = shift;
237
    my %opt = @_;
238
    my $status_url;
239
 
240
    Debug ("SvnCi");
241
    Error ("SvnCi: Odd number of args") unless ((@_ % 2) == 0);
242
 
243
    #
244
    #   Validate the source path
245
    #   Note: populates @{$self->{RESULT_LIST}} with 'info' commands
246
    #
247
    my $path = SvnValidateWs ($self, 'SvnCi');
248
 
249
    #
250
    #   Scan the @{$self->{RESULT_LIST}}, which has the results of an 'info'
251
    #   command the locate the URL.
252
    #
253
    #   This contains the target view space
254
    #   Sanity test. Don't allow Checkin to a /tags/ area
255
    #
256
    foreach ( @{$self->{RESULT_LIST}})
257
    {
258
        if ( m~^URL:\s+(.+)~ )
259
        {
260
            $status_url = $1;
261
            last;
262
        }
263
    }
264
 
265
    Error ("SvnCi: Cannot determine Repositoty URL")
266
        unless ( $status_url );
267
 
268
    Error ("SvnCi: Not allowed to commit to a 'tags' area", "URL: $status_url")
269
        if ( $status_url =~ m~/tags(/|$)~ );
270
 
271
    #
272
    #   Commit
273
    #
274
    $self->SvnCmd ( 'commit', $path
275
                    , '-m', SvnComment( $opt{'comment'}, 'Created by SvnCi' ),
276
                    , { 'credentials' => 1,
277
                        'process' => \&ProcessRevNo,
278
                        'error' => "SvnCi: Copy Error" }
279
                        );
280
    Error ("SvnCi: Cannot determine Revision Number", @{$self->{RESULT_LIST}})
281
        unless ( $self->{REVNO} );
282
 
283
    #
284
    #   Update the view
285
    #   Doing this so that the local view contains an up to date
286
    #   revision number. If not done, and a 'copy' is done based
287
    #   on this view then the branch information will indicate that
288
    #   the copy is based on an incorrect version.
289
    #   This can be confusing!
290
    #
291
    $self->SvnCmd ( 'update'   , $path
292
                        , '--ignore-externals'
293
                        , { 'credentials' => 1,
294
                            'error' => "SvnCi: Updating WorkSpace" }
295
                        );
296
    #
297
    #   Pass the updated revision number back to the user
298
    #
299
    $self->CalcRmReference($status_url);
300
    Message ("Tag is: " . $self->{RMREF} );
301
    return $self->{RMREF} ;
302
}
303
 
304
#-------------------------------------------------------------------------------
305
# Function        : SvnCreatePackage
306
#
307
# Description     : Create a package and any associated files
308
#
309
# Inputs          : $self        - Instance data
310
#                   A hash of named arguments
311
#                       package  - Name of the package
312
#                                  May include subdirs
313
#                       new      - True: Must not already exist
314
#                       replace  - True: Replace targets
315
#                       import   - DirTree to import
316
#                       label    - Tag for imported DirTree
317
#                       type     - Import TTB target
318
#
319
# Returns         : Revision of the copy
320
#
321
sub SvnCreatePackage
322
{
323
    my $self = shift;
324
    my %opt = @_;
325
    my $target;
326
 
327
    Debug ("SvnCreatePackage", @_);
328
    Error ("Odd number of args to SvnCreatePackage") unless ((@_ % 2) == 0);
329
    my %dirs = ( 'trunk/'       => 0,
330
                 'tags/'        => 0,
331
                 'branches/'    => 0 );
332
 
333
    #
334
    #   Sanity Tests
335
    #
336
    my $package = $self->Full || Error ("SvnCreatePackage: No package name provided");
337
    Error ("SvnCreatePackage: Invalid import path") if ( $opt{'import'} && ! -d $opt{'import'} );
338
    Error ("SvnCreatePackage: Tag without Import") if ( $opt{'label'} && ! $opt{'import'} );
339
    $opt{'label'} = SvnIsaSimpleLabel( $opt{'label'} ) if (  $opt{'label'} );
340
 
341
    #
342
    #   Package path cannot contain any of the keyword paths tags,trunk,branches
343
    #   as this would place a package with a package
344
    #
345
    Error ("Package path contains a reserved word ($1)", "Path: $package")
346
        if (  $package =~ m~/(tags|branches|trunk)(/|$)~ );
347
 
348
    #
349
    #   Package path cannot be pegged, or look like one
350
    #
351
    Error ("Package name contains a Peg ($1)", "Path: $package")
352
        if ( $package =~ m~.*(@\d+)$~ );
353
 
354
    #
355
    #   Determine TTB target
356
    #
357
    $opt{'type'} = 'trunk' unless ( $opt{'type'} );
358
    Error ("Invalid TTB type") unless ( $opt{'type'} =~ m{^(tags|branches|trunk)$} );
359
    Error ("Import without label") if ( $opt{'type'} ne  'trunk'  && ! $opt{'label'} );
360
 
361
    #
362
    #   Before we import data we must ensure that the targets do not exist
363
    #   Determine the import target(s)
364
    #
365
    my $import_target;
366
    my $copy_target;
367
 
368
    if ( $opt{'import'} )
369
    {
370
        #
371
        #   Primary target
372
        #   trunk, branck or tag
373
        #
374
        $import_target = $package . '/' . $opt{'type'};
375
        $import_target .= '/' .$opt{'label'} if ( $opt{'type'} ne 'trunk');
376
 
377
        $self->SvnValidateTarget( 'target'    => $import_target,
378
                                  'delete'    => $opt{'replace'},
379
                                  'available' => 1 );
380
 
381
        #
382
        #   Secondary target
383
        #   If primary is a trunk and a label is provided
384
        #
385
        if ( $opt{'type'} eq 'trunk' && $opt{'label'} )
386
        {
387
            $copy_target = $package . '/tags/' . $opt{'label'};
388
            $self->SvnValidateTarget( 'target'    => $copy_target,
389
                                      'delete'    => $opt{'replace'},
390
                                      'available' => 1 );
391
        }
392
    }
393
 
394
    #
395
    #   Probe to see if the package exists
396
    #
397
    my ( $ref_files, $ref_dirs, $ref_svn, $found ) = $self->SvnScanPath ( 'SvnCreatePackage', $package );
398
    if ( @$ref_dirs )
399
    {
400
        Error ("SvnCreatePackage: Package directory exists",
401
               "Cannot create a package here. Unexpected subdirectories:", @$ref_dirs);
402
    }
403
 
404
    if ( @$ref_files )
405
    {
406
        Warning ("SvnCreatePackage: Unexpected files found",
407
               "Path: $package",
408
               "Unexpected files found: @$ref_files");
409
    }
410
 
411
    if ( @$ref_svn )
412
    {
413
        #
414
        #   Do we need a new one
415
        #
416
        Error ("SvnCreatePackage: Package exists: $package") if $opt{'new'};
417
 
418
        #
419
        #   Some subversion files have been found here
420
        #   Create the rest
421
        #   Assume that the directory tree is good
422
        #
423
        #
424
        #   Some, or all, of the required package subdirectories exist
425
        #   Determine the new ones to created so that it can be done
426
        #   in an atomic step
427
        #
428
        delete $dirs{$_} foreach  ( @$ref_svn );
429
        if ( keys %dirs )
430
        {
431
            Warning ("SvnCreatePackage: Not all package subdirs present",
432
                     "Remaining dirs will be created",
433
                     "Found: @$ref_svn") if @$ref_svn;
434
        }
435
        else
436
        {
437
            Warning ("SvnCreatePackage: Package already present");
438
        }
439
    }
440
 
441
    #
442
    #   Create package directories that have not been discovered
443
    #       trunk
444
    #       branches
445
    #       tags
446
    #
447
    my @dirs;
448
    push @dirs, $package . '/' . $_ foreach ( keys %dirs );
449
    $target = $package . '/trunk';
450
 
451
    #
452
    #   Create missing directories - if any
453
    #
454
    if ( @dirs )
455
    {
456
        $self->SvnCmd ('mkdir', @dirs
457
                       , '-m', 'Created by SvnCreatePackage'
458
                       , '--parents'
459
                       , { 'credentials' => 1,
460
                           'error' => "SvnCreatePackage" } );
461
    }
462
 
463
    #
464
    #   Import data into the package if required
465
    #   Import data. Possible cases:
466
    #       - Import to trunk - and then tag it
467
    #       - Import to branches
468
    #       - Import to tags
469
    #
470
    if ( $import_target )
471
    {
472
        Verbose ("Importing directory into new package: $opt{'import'}");
473
 
474
        $target = $import_target;
475
        $self->{PRINTDATA} = 1;
476
        $self->SvnCmd ('import', $opt{'import'}
477
                        , $target
478
                        , '-m', 'Import by SvnCreatePackage'
479
                        , '--force'
480
                        , { 'credentials' => 1
481
                           ,'error' => "Import Incomplete"
482
                           ,'process' => \&ProcessRevNo
483
                          })
484
    }
485
 
486
    #
487
    #   If imported to the trunk AND a label is provided
488
    #   then tag the import as well.
489
    #   A simple URL copy
490
    #
491
    if ( $copy_target )
492
    {
493
        Verbose ("Labeling imported trunk: $opt{'label'} ");
494
        $target = $copy_target;
495
        $self->SvnCmd ('copy'  , $import_target
496
                        , $target
497
                        , '-m', 'Import tagged by SvnCreatePackage'
498
                        , { 'credentials' => 1
499
                          , 'process' => \&ProcessRevNo
500
                          , 'error' => "Import Incomplete" } );
501
    }
502
 
503
    #
504
    #   Pass the updated revision number back to the user
505
    #
506
    $self->CalcRmReference($target);
507
    Message ("Tag is: " . $self->{RMREF} );
508
    return $self->{RMREF} ;
509
}
510
 
511
#-------------------------------------------------------------------------------
512
# Function        : SvnRmView
513
#
514
# Description     : Remove a Subversion view
515
#                   Will run sanity checks and only remove the view if
516
#                   all is well
517
#
518
# Inputs          : A hash of named arguments
519
#                       path     - Path to local workspace
520
#                       modified - Array of files that are allowed to be modified
521
#                       force    - True: Force deletion
522
#
523
# Returns         :
524
#
525
sub SvnRmView
526
{
527
    my %opt = @_;
528
    Debug ("SvnRmView");
529
    Error ("Odd number of args to SvnRmView") unless ((@_ % 2) == 0);
530
 
531
    #
532
    #   Sanity test
533
    #
534
    my $path = $opt{'path'} || '';
535
    my $path_length = length ($path);
536
    Verbose2 ("Delete WorkSpace: $path");
537
 
538
    #
539
    #   If the path does not exist then assume that its already deleted
540
    #
541
    unless ( $path && -e $path )
542
    {
543
        Verbose2 ("SvnRmView: Path does not exist");
544
        return;
545
    }
546
 
547
    #
548
    #   Create information about the workspace
549
    #   This will also validate the path
550
    #
551
    my $session = NewSessionByWS ( $path );
552
 
553
    #
554
    #   Validate the path
555
    #
556
    $session->SvnValidateWs ($path, 'SvnRmView');
557
 
558
    #
559
    #   Ask subversion if there are any files to be updated
560
    #   Prevent deletion of a view that has modified files
561
    #
562
    unless ( $opt{'force'} )
563
    {
564
        $session->SvnWsModified ( 'cmd' => 'SvnRmView', %opt );
565
    }
566
 
567
    #
568
    #   Now we can delete it
569
    #
570
    Verbose2 ("Remove WorkSpace: $path");
571
    rmtree( $path, IsVerbose(3) );
572
}
573
 
574
 
575
#-------------------------------------------------------------------------------
576
# Function        : SvnCopyWs
577
#
578
# Description     : Copy a workspace to a new place in the repository
579
#                   Effectively 'label' the workspace
580
#
581
#                   It would appear that the 'copy' command is very clever
582
#                   If a version-controlled file has been changed
583
#                   in the source workspace, then it will automatically be
584
#                   copied. This is a trap.
585
#
586
#                   Only allow a 'copy' if there are no modified
587
#                   files in the work space (unless overridden)
588
#
589
#
590
# Inputs          : $self        - Instance data
591
#                   A hash of named arguments
592
#                       path     - Path to local workspace
593
#                       target   - Location within the repository to copy to
594
#                       comment  - Commit comment
595
#                       modified - Array of files that are allowed to
596
#                                  be modified in the workspace.
597
#                       noswitch  - True: Don't switch to the new URL
598
#                       replace   - True: Delete existing tag if present
599
#
600
# Returns         : Revision of the copy
601
#
602
sub SvnCopyWs
603
{
604
    my $self = shift;
605
    my %opt = @_;
606
    Debug ("SvnCopyWs");
607
    Error ("Odd number of args to SvnCopyWs") unless ((@_ % 2) == 0);
608
    Error ("SvnCopyWs: No Workspace" ) unless ( $self->{WS} );
609
 
610
    #
611
    #   Insert defaults
612
    #
613
    my $target = $opt{target} || Error ("SvnCopyWs: Target not specified" );
614
 
615
    #
616
    #   Validate the source path
617
    #
618
    my $path = SvnValidateWs ($self, 'SvnCopyWs');
619
 
620
    #
621
    #   Validate the target
622
    #   Cannot have a 'peg'
623
    #
624
    Error ("SvnCopyWs: Target contains a Peg: ($1)", $target)
625
        if ( $target =~ m~(@\d+)\s*$~ );
626
 
627
    #
628
    #   Examine the workspace and ensure that there are no modified
629
    #   files - unless they are expected
630
    #
631
    $self->SvnWsModified ( 'cmd' => 'SvnCopyWs', %opt );
632
 
633
    #
634
    #   Validate the repository
635
    #   Ensure that the target does not exist
636
    #   The target may be deleted if it exists and allowed by the user
637
    #
638
    $self->SvnValidateTarget ( 'cmd'    => 'SvnCopyWs',
639
                        'target' => $target,
640
                        'delete' => $opt{replace},
641
                        'comment' => 'Deleted by SvnCopyWs'
642
                        );
643
    #
644
    #   Copy source to destination
645
    #   It would appear that even though the source is a WorkSpace, the copy
646
    #   does not transfer data from the WorkSpace. It appears as though its all
647
    #   done on the server. This is good - and fast.
648
    #
649
    #   More over files that are modified in the local workspace will
650
    #   be copied and checked into the target.
651
    #
652
    if ( $self->SvnCmd ( 'cp'  , $path
653
                        , $target
654
                        , '--parents'
655
                        , '-m', SvnComment( $opt{'comment'}, 'Created by SvnCopyWs' ),
656
                        , { 'process' => \&ProcessRevNo,
657
                            'credentials' => 1, }
658
                        ) )
659
    {
660
        #
661
        #   Error in copy
662
        #   Attempt to delete the target. Don't worry if we can't do that
663
        #
664
        my @err1 = @{$self->{ERROR_LIST}};
665
        $self->SvnCmd ( 'delete'
666
                    , $target
667
                    , '-m', 'Deleted by SvnCopyWs after creation failure'
668
                    , { 'credentials' => 1, }
669
               );
670
        Error ("SvnCopyWs: Copy Error", @err1);
671
    }
672
 
673
    Error ("SvnCopyWs: Cannot determine Revision Number", @{$self->{RESULT_LIST}})
674
        unless ( $self->{REVNO} );
675
 
676
    Verbose2 ("Copy committed as revision: " . $self->{REVNO} );
677
 
678
    unless ( $opt{'noswitch'} )
679
    {
680
        #
681
        #   Switch to the new URL
682
        #   This will link the Workspace with the copy that we have just made
683
        #
684
        $self->SvnCmd ( 'switch', $target
685
                         , $path
686
                         , { 'credentials' => 1,
687
                             'error' => "SvnCopyWs: Cannot switch to new URL" }
688
               );
689
    }
690
 
691
    #
692
    #   Pass the updated revision number back to the user
693
    #
694
    $self->CalcRmReference($target );
695
    Message ("Tag is: " . $self->{RMREF} );
696
    return $self->{RMREF} ;
697
}
698
 
699
#-------------------------------------------------------------------------------
700
# Function        : SvnWsModified
701
#
702
# Description     : Test a Workspace for modified files
703
#                   Allow some files to be modified
704
#
705
# Inputs          : $self           - Instance data
706
#                   A hash of named arguments
707
#                       path        - Path to local workspace
708
#                       modified    - Files that are allowed to be modified
709
#                                     Relative to the 'path'
710
#                                     May be a single file or an array of files
711
#                       cmd         - Command name for error reporting
712
#
713
# Returns         :
714
#
715
sub SvnWsModified
716
{
717
    my $self = shift;
718
    my %opt = @_;
719
    Debug ("SvnWsModified");
720
    Error ("Odd number of args to SvnWsModified") unless ((@_ % 2) == 0);
721
 
722
    my $cmd = $opt{'cmd'} || 'SvnWsModified';
723
 
724
    #
725
    #   Validate the path
726
    #
727
    SvnValidateWs ($self, $cmd);
728
    my $path = $self->{WS};
729
    my $path_length = length ($path);
730
    Verbose2 ("Test Workspace for Modifications: $path");
731
 
732
    #
733
    #   Ask subversion if there are any files to be updated
734
    #
735
    $self->SvnCmd ('status', $path, {'error' => "Svn status command error"} );
736
 
737
    #
738
    #   Examine the list of modified files
739
    #
740
    if ( @{$self->{RESULT_LIST}} )
741
    {
742
        #
743
        #   Create a hash of file that are allowed to change
744
        #   These are files relative to the base of the view
745
        #
746
        #   The svn command has the 'path' prepended, so this
747
        #   will be removed as we process the commands
748
        #
749
        my %allowed;
750
        my @unexpected;
751
 
752
        if ( exists $opt{'modified'}  )
753
        {
754
            $allowed{'/' . $_} = 1 foreach ( ref ($opt{'modified'}) ? @{$opt{'modified'}} : $opt{'modified'}  );
755
        }
756
 
757
        #
758
        #   Process the list of modified files
759
        #   Do this even if we aren't allowed modified files as we
760
        #   still need to examine the status and kill off junk entries
761
        #   ie: ?, I, ! and ~
762
        #
763
        #    First column: Says if item was added, deleted, or otherwise changed
764
        #      ' ' no modifications
765
        #      'A' Added
766
        #      'C' Conflicted
767
        #      'D' Deleted
768
        #      'I' Ignored
769
        #      'M' Modified
770
        #      'R' Replaced
771
        #      'X' item is unversioned, but is used by an externals definition
772
        #      '?' item is not under version control
773
        #      '!' item is missing (removed by non-svn command) or incomplete
774
        #      '~' versioned item obstructed by some item of a different kind
775
        #
776
        foreach my $entry ( @{$self->{RESULT_LIST}} )
777
        {
778
            #
779
            #   Extract filename from line
780
            #       First 7 chars are status
781
            #       Remove WS path too
782
            #
783
            my $file = substr ( $entry, 7 + $path_length );
784
            next if ( $allowed{$file} );
785
 
786
            #
787
            #   Examine the first char and rule out funny things
788
            #
789
            my $f1 =  substr ($entry, 0,1 );
790
            next if ( $f1 =~ m{[?I!~]} );
791
            push @unexpected, $entry;
792
        }
793
        Error ("Workspace contains unexpected modified files", @unexpected)
794
            if ( @unexpected );
795
    }
796
}
797
 
798
#-------------------------------------------------------------------------------
799
# Function        : SvnListPackages
800
#
801
# Description     : Determine a list of packages within the repo
802
#                   This turns out to be a very slow process
803
#                   so don't use it unless you really really need to
804
#
805
# Inputs          : $repo       - Name of the repository
806
#
807
# Returns         : 
808
#
809
sub SvnListPackages
810
{
811
    my ($repo) = @_;
812
 
813
    my @path_list = $repo;
814
    my @list;
815
    my $scanned = 0;
816
    Debug ("SvnListPackages");
817
    while ( @path_list )
818
    {
819
        my $path = shift @path_list;
820
        $scanned++;
821
print "Reading: $path\n";
822
        my ( $ref_files, $ref_dirs, $ref_svn, $found ) = SvnScanPath ( 'Listing Packages', $path );
823
 
824
        #
825
        #   If there are Subversion dirs (ttb) in this directory
826
        #   then this is a package. Add to list
827
        #
828
        push @list, $path if ( @$ref_svn );
829
 
830
        #
831
        #   Add subdirs to the list of paths to explore
832
        #
833
        foreach  ( @$ref_dirs )
834
        {
835
            chop;                                 # Remove trailing '/'
836
            push @path_list, $path . '/' . $_;    # Extend the path
837
        }
838
    }
839
 
840
    Message ("Found:", @list );
841
    Message ("Dirs Scanned: $scanned");
842
    Message ("Packages Found: $#list");
843
}
844
 
845
#-------------------------------------------------------------------------------
846
# Function        : ListLabels
847
#
848
# Description     : List labels within a given package
849
#
850
# Inputs          : $self               - Instance data
851
#                   $path               - path to label source
852
#
853
# Returns         : Ref to an array
854
#
855
sub ListLabels
856
{
857
    my ($self, $path) = @_;
858
    Debug ("ListLabels");
859
 
860
    my ( $ref_files, $ref_dirs, $ref_svn, $found ) = $self->SvnScanPath ( 'Listing Versions', $path );
861
 
862
    Error ("List: Path not found: $path") unless ( $found );
863
 
864
    #
865
    #   Dont report files - just directories
866
    #
867
    return $ref_dirs;
868
}
869
 
870
 
871
#-------------------------------------------------------------------------------
872
# Function        : SvnLocateWsRoot
873
#
874
# Description     : Given a WorkSpace, determine the root of the work space
875
#                   This is not as simple as you might think
876
#
877
#                   Algorithm
878
#                       svn ls ..
879
#                       Am I in the parent directory
880
#                       Repeat
881
#
882
# Inputs          : $self               - Instance data
883
#                   $test               - True: Don't die on error
884
#
885
# Returns         : Root of workspace as an absolute address
886
#                   Will not return if there is an error
887
#
888
sub SvnLocateWsRoot
889
{
890
    my ($self, $test) = @_;
891
    my @path;
892
    my $path = $self->{WS};
893
 
894
    Debug ("SvnLocateWsRoot");
895
    Error ("SvnLocateWsRoot: No Workspace") unless ( $path  );
896
    Verbose2 ("SvnLocateWsRoot: Start in $path");
897
 
898
    #
899
    #   Validate the source path
900
    #
901
    if ( SvnValidateWs ($self, 'SvnLocateWsRoot', $test) )
902
    {
903
        return undef;
904
    }
905
 
906
    #
907
    #   Need to sanitize the users path to ensure that the following
908
    #   algorithm works. Need:
909
    #       1) Absolute Path
910
    #       2) Not ending in '/'
911
    #
912
 
913
    #
914
    #   If we have a relative path then prepend the current directory
915
    #   An absolute path is:
916
    #           /aaa/aa/aa
917
    #       or  c:/aaa/aa/aa
918
    #
919
    $path = getcwd() . '/' . $path
920
        unless ( $path =~ m~^/|\w:/~  );
921
 
922
    #
923
    #   Walk the bits and remove ".." directories
924
    #       Done by pushing non-.. elements and poping last entry for .. elements.
925
    #   Have a leading "/" which is good.
926
    #
927
    #   Create a array of directories in the path
928
    #   Split on one or more \ or / separators
929
    #
930
    foreach ( split /[\\\/]+/ , $path )
931
    {
932
        next if ( $_ eq '.' );
933
        unless ( $_ eq '..' )
934
        {
935
            push @path, $_;
936
        }
937
        else
938
        {
939
            Error ("SvnLocateWsRoot: Bad Pathname: $path")
940
                if ( $#path <= 0 );
941
            pop @path;
942
        }
943
    }
944
 
945
    Verbose2 ("Clean absolute path elements: @path");
946
    PATH_LOOP:
947
    while ( @path )
948
    {
949
        #
950
        #   This directory element. Append / to assist in compare
951
        #   Determine parent path
952
        #
953
        my $name = pop (@path) . '/';
954
        my $parent = join ('/', @path );
955
 
956
        #
957
        #   Examine the parent directory
958
        #   Get a list of all elements in the parent
959
        #   Need to ensure that this directory is one of them
960
        #
961
        #   Ignore any errors - assume that they are because the
962
        #   parent is not a part of the work space. This will terminate the
963
        #   search.
964
        #
965
        $self->SvnCmd ('list', $parent, '--depth', 'immediates' );
966
        foreach my $entry ( @{$self->{RESULT_LIST}} )
967
        {
968
            next PATH_LOOP
969
                if ( $entry eq $name );
970
        }
971
 
972
        #
973
        #   Didn't find 'dir' in directory svn listing of parent
974
        #   This parent is not a part of the same WorkSpace as 'dir'
975
        #   We have a winner.
976
        #
977
        chop $name;                         #   Chop the '/' previously added
978
        $self->{WS} = $parent . '/' . $name;
979
        return $self->{WS};
980
    }
981
 
982
    #
983
    #   Shouldn't get this far
984
    #
985
    Error ("SvnLocateWsRoot: Root not found");
986
}
987
 
988
#-------------------------------------------------------------------------------
989
# Function        : SvnValidateWs
990
#
991
# Description     : Validate the path to a working store
992
#
993
# Inputs          : $self           - Instance data
994
#                   $user           - Optional prefix for error messages
995
#                   $test           - True: Just test, Else Error
996
#
997
# Returns         : Will not return if not a workspace
998
#                   Returns the users path
999
#                   @{$self->{RESULT_LIST}} will be populated with info about the
1000
#                   item requested as per an 'info' call
1001
#
1002
sub SvnValidateWs
1003
{
1004
    my ($self, $user, $test) = @_;
1005
    Debug ("SvnValidateWs");
1006
 
1007
    $user = "Invalid Subversion Workspace" unless ( $user );
1008
    my $path = $self->{WS} ;
1009
 
1010
    #
1011
    #   Only validate it one
1012
    #
1013
    return $path if ( $self->{WS_VALIDATED} );
1014
 
1015
    #
1016
    #   Validate the source path
1017
    #   Must exist and must be a directory
1018
    #
1019
    if ( ! $path ) {
1020
        @{$self->{ERROR_LIST}} = "$user: No path specified";
1021
 
1022
    } elsif ( ! -e $path ) {
1023
        @{$self->{ERROR_LIST}} = "$user: Path does not exist: $path";
1024
 
1025
    } elsif ( ! -d $path ) {
1026
        @{$self->{ERROR_LIST}} = "$user: Path is not a directory";
1027
    } else {
1028
        #
1029
        #   Determine the source path is an fact a view
1030
        #   The info command can do this. Use depth empty to limit the work done
1031
        #
1032
        $self->SvnCmd ('info', $path, '--depth', 'empty' );
1033
 
1034
        #
1035
        #   Error. Prepend nice message
1036
        #
1037
        unshift @{$self->{ERROR_LIST}}, "$user: Path is not a WorkSpace: $path"
1038
            if ( @{$self->{ERROR_LIST}} );
1039
    }
1040
 
1041
    #
1042
    #   Figure out what to do
1043
    #
1044
    if ( $test )
1045
    {
1046
        return @{$self->{ERROR_LIST}};
1047
    }
1048
    else
1049
    {
1050
        Error @{$self->{ERROR_LIST}} if @{$self->{ERROR_LIST}};
1051
        $self->{WS_VALIDATED} = 1;
1052
        return $path;
1053
    }
1054
}
1055
 
1056
#-------------------------------------------------------------------------------
1057
# Function        : SvnValidatePackageRoot
1058
#
1059
# Description     : Validate a package root
1060
#
1061
# Inputs          : $self           - Instance data
1062
#
1063
# Returns         : Will only return if valid
1064
#                   Returns a cleaned package root
1065
#
1066
sub SvnValidatePackageRoot
1067
{
1068
    my ($self) = @_;
1069
    Debug ("SvnValidatePackageRoot");
1070
    my $url = $self->Full || Error ("SvnValidatePackageRoot: No URL");
1071
 
1072
    Error ("Package path contains a reserved word ($self->{TAGTYPE})", "Path: $url")
1073
        if (  $self->{TAGTYPE} );
1074
 
1075
    Error ("Package name contains a Peg ($self->{PEG})", "Path: $url")
1076
        if ( $self->{PEG} );
1077
 
1078
    #
1079
    #   Ensure that the target path does exist
1080
    #   Moreover it needs to be a directory and it should have a
1081
    #   a ttb structure
1082
    #
1083
    my ( $ref_files, $ref_dirs, $ref_svn, $found ) = $self->SvnScanPath ( 'Package Base Test', $url );
1084
 
1085
    #
1086
    #   Examine the results to see if we have a valid package base
1087
    #
1088
    Error ("Package Base Test: Not a valid package") unless ( $found );
1089
 
1090
    #
1091
    #   Extra bits found
1092
    #   Its not the root of a package
1093
    #
1094
    if ( @$ref_files )
1095
    {
1096
        Warning ("Package Base Test: Files exists",
1097
               "Unexpected files found:", @$ref_files );
1098
    }
1099
 
1100
    #
1101
    #   Need a truck directory
1102
    #   If we don't have a truck we don't have a package
1103
    #
1104
    my $trunk_found = grep ( /trunk\//, @$ref_svn );
1105
    Error ("Invalid Package Base. Does not contain a 'trunk' directory")
1106
        unless ( $trunk_found );
1107
 
1108
    return $url;
1109
}
1110
 
1111
 
1112
#-------------------------------------------------------------------------------
1113
# Function        : SvnIsaSimpleLabel
1114
#
1115
# Description     : Check a label
1116
#                       Must not contain a PEG
1117
#                       Must not contain invalid characters (@ or /)
1118
#                       Must not contain a :: sequence (will confuse other tools)
1119
#                       Handle special label of TIMESTAMP
1120
#
1121
# Inputs          : $label          - to test
1122
#
1123
# Returns         : Will not return on error
1124
#                   Returns label on success
1125
#
1126
sub SvnIsaSimpleLabel
1127
{
1128
    my ($label) = @_;
1129
    Debug ("SvnIsaSimpleLabel, $label");
1130
 
1131
    Error ("No label provided") unless ( $label );
1132
    Error ("Invalid label. Peg (\@nnn) is not allowed: \"$label\"" ) if ( $label =~ m~@\d+$~ );
1133
    Error ("Invalid label. Package Path is not allowed: \"$label\"" ) if ( $label =~ m~/~ );
1134
    Error ("Invalid label. Invalid Characters: \"$label\"" ) unless ( $label =~ m~^[-.:0-9a-zA-Z_]+$~ );
1135
    Error ("Invalid label. Double :: not allowed: \"$label\"" ) if ( $label =~m~::~ );
1136
 
1137
    #
1138
    #   Allow for a label of TIMESTAMP and have it expand
341 dpurdie 1139
    #   Create a label based on users name and a date-time hat can be sorted
267 dpurdie 1140
    #
1141
    if ( $label eq 'TIMESTAMP' )
1142
    {
341 dpurdie 1143
        ::EnvImport ('USER' );
1144
        my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
1145
        $label = sprintf("%s_%4.4u.%2.2u.%2.2u.%2.2u%2.2u%2.2u",
1146
            $::USER, $year+1900, $mon+1, $mday, $hour, $min, $sec );
267 dpurdie 1147
    }
1148
    return $label;
1149
}
1150
 
1151
#-------------------------------------------------------------------------------
1152
# Function        : NewSession
1153
#
1154
# Description     : Create a new empty SvnSession Class
1155
#
1156
# Inputs          : None
1157
#
1158
# Returns         : Class
1159
#
1160
sub NewSession
1161
{
1162
    Debug ("NewSession");
1163
    my $self  = SvnSession();
1164
 
1165
    #
1166
    #   Document Class Variables
1167
    #
1168
    $self->{URL} = '';                  # Repo URL prefix
1169
    $self->{WS}  = '';                  # Users WorkSpace
1170
    $self->{NAMEDSERVER} = '';          # User specified server
1171
    $self->{PROTOCOL} = '';             # Named Access Protocol
1172
    $self->{PKGROOT} = '';              # Package root
1173
 
1174
    #
1175
    #   Create a class
1176
    #   Bless my self
1177
    #
1178
    bless ($self, __PACKAGE__);
1179
    return $self;
1180
}
1181
 
1182
#-------------------------------------------------------------------------------
1183
# Function        : NewSessionByWS
1184
#
1185
# Description     : Establish a new SVN Session based on a Workspace
1186
#                   Given a workspace path determine the SvnServer and other
1187
#                   relevent information.
1188
#
1189
#                   Requires some rules
1190
#                       * The package is rooted within a 'ttb'
1191
#
1192
# Inputs          : $path                   - Path to WorkSpace
1193
#                   $test                   - No Error on no WS
1194
#
1195
# Returns         : Ref to Session Information
1196
#
1197
sub NewSessionByWS
1198
{
1199
    my ($path, $test) = @_;
1200
    Debug ("NewSessionByWS", @_);
1201
 
1202
    #
1203
    #   Create a basic Session
1204
    #   Populate it with information that is known
1205
    #
1206
    my $self = NewSession();
1207
    $self->{WS} = $path;
1208
 
1209
    #
1210
    #   Validate the path provided
1211
    #   In the process populate the @{$self->{RESULT_LIST}} with information
1212
    #   about the workspace.
1213
    #
1214
    if ($self->SvnValidateWs ( undef, 1) )
1215
    {
1216
        return $self if ( $test );
1217
        Error ( @{$self->{ERROR_LIST}} );
1218
    }
1219
 
1220
    #
1221
    #   Extract useful info
1222
    #       URL: svn://auperaws996vm21/test/MixedView/trunk
1223
    #       Repository Root: svn://auperaws996vm21/test
1224
    #
1225
    my $url;
1226
    my $reporoot;
1227
 
1228
    foreach ( @{$self->{RESULT_LIST}} )
1229
    {
1230
        $url = $1 if ( m~^URL:\s+(.+)~ );
1231
        $reporoot = $1 if ( m~^Repository Root:\s+(.+)~ );
1232
        last if ( $url && $reporoot );
1233
    }
1234
    Error ("JatsSvn Internal error. Can't parse info")
1235
        unless ( $url && $reporoot );
1236
 
1237
    #
1238
    #   Need the length of the path to the repository
1239
    #   but not the name of the repostory itself.
1240
    #
1241
    #   Remove that from the head of the URL to give a
1242
    #   path within the repository, that includes the repos name
1243
    #
1244
    $reporoot = (fileparse( $reporoot ))[1];
1245
    $url = substr ($url, length ($reporoot));
1246
    $self->{WSURL} = $url;
1247
    chop $reporoot;
1248
 
1249
    Verbose2 ("SvnLocatePackageRoot: $reporoot, $url" );
1250
 
1251
    #
1252
    #   Remove anything after a ttb ( truck, tags, branch ) element
1253
    #   This will be the root of the package within the repo
1254
    #
1255
    if (  $url =~ m~(.+)/((tags|branches|trunk)(/|$).*)~ )
1256
    {
1257
        $url = $1;
1258
        $self->{WSTYPE} = $3;
1259
    }
1260
    else
1261
    {
1262
        Error ("SvnLocatePackageRoot. Non standard repository format",
1263
               "Url: $url");
1264
    }
1265
 
1266
    #
1267
    #   Insert known information
1268
    #
1269
    $self->{URL} = $reporoot . '/';
1270
    $self->{PKGROOT} = $url;
1271
 
1272
    #
1273
    #   Create useful information
1274
    #
1275
    SplitPackageUrl($self);
1276
    return $self;
1277
}
1278
 
1279
#-------------------------------------------------------------------------------
1280
# Function        : NewSessionByUrl
1281
#
1282
# Description     : Establish a new SVN Session based on a user URL
1283
#
1284
# Inputs          : $uurl                   - Users URL
1285
#                   $session                - Optional: Existing session
1286
#
1287
# Returns         : Ref to Session Information
1288
#
1289
sub NewSessionByUrl
1290
{
1291
    my ($uurl, $self ) = @_;
1292
    Debug ("NewSessionByUrl", @_);
1293
    Error ("No Repostory Path specified") unless ( $uurl );
1294
 
1295
    #
1296
    #   Create a basic Session
1297
    #   Populate it with information that is known
1298
    #
1299
    $self = NewSession() unless ( $self );
1300
 
1301
    #
1302
    #   Examine the URL and determine if we have a FULL Url or
1303
    #   a path within the 'default' server
1304
    #
341 dpurdie 1305
    if ( defined $::GBE_SVN_URL && $uurl =~ m~^$::GBE_SVN_URL(.*)~ )
267 dpurdie 1306
    {
341 dpurdie 1307
        $uurl = $1;
1308
    }
1309
 
1310
    if ( $uurl =~ m~^((file|http|https|svn):///?([^/]+)/)(.+)~ )
1311
    {
267 dpurdie 1312
        #       http://server/
341 dpurdie 1313
        #       https://server/
267 dpurdie 1314
        #       svn://server/
1315
        #       file://This/Isa/Bad/Guess
1316
        #
1317
        $self->{NAMEDSERVER} = 1;
1318
        $self->{PROTOCOL} = $2;
1319
 
1320
        $self->{URL} = $1;
1321
        $self->{PKGROOT} = $4;
1322
    }
1323
    else
1324
    {
1325
        #
1326
        #   User default (site configured) Repo Root
1327
        #
1328
        Error ("No site repository configured",
1329
               "Configure GBE_SVN_URL" ) unless ( $::GBE_SVN_URL );
1330
 
1331
        $self->{URL} = $::GBE_SVN_URL;
1332
        $self->{PKGROOT} = $uurl;
1333
    }
1334
 
1335
    #
1336
    #   Create useful information
1337
    #
1338
    SplitPackageUrl($self);
1339
    return $self;
1340
}
1341
 
1342
#-------------------------------------------------------------------------------
1343
# Function        : SplitPackageUrl
1344
#
1345
# Description     : Slip the package URL into a few useful bits
1346
#
1347
# Inputs          : $self           - Instance data
1348
#
1349
# Returns         : Nothing
1350
#
1351
sub SplitPackageUrl
1352
{
1353
    my ($self) = @_;
1354
    Debug ("SplitPackageUrl", @_);
1355
 
1356
    #
1357
    #   Remove any protocol that may be present
1358
    #       http://server/
341 dpurdie 1359
    #       https://server/
267 dpurdie 1360
    #       svn://server/
1361
    #       file://This/Isa/Bad/Guess
1362
    #
341 dpurdie 1363
    if ( $self->{URL} =~ m~^(file|http|https|svn)://([^/]+)~ )
267 dpurdie 1364
    {
1365
        $self->{PROTOCOL} = $1;
1366
        $self->{SERVER} = $2;
1367
    }
1368
 
1369
    if ( $self->{PKGROOT} =~ m~(.*)(@\d+)$~ )
1370
    {
1371
        $self->{PEG} = $2;
1372
    }
1373
 
1374
    #
1375
    #   Determine TTB type
1376
    #   Need to handle
1377
    #       .../trunk
1378
    #       .../trunk@nnnnn
1379
    #       .../tags/version@nnnnn
1380
    #       .../branches/version@nnnnn
1381
    #
1382
    #
1383
    if (  $self->{PKGROOT} =~ m~/?(.*)/(tags|branches|trunk)(/|$|@)(.*)$~ )
1384
    {
1385
        $self->{PATH}         = $1;
1386
        $self->{TAGTYPE}      = $2;
1387
        $self->{VERSION}      = $4;
1388
    }
1389
    else
1390
    {
1391
        $self->{PATH} = $self->{PKGROOT};
1392
    }
1393
 
1394
    DebugDumpData ('SplitPackageUrl', $self ) if ( IsDebug(2) );
1395
}
1396
 
1397
#-------------------------------------------------------------------------------
1398
# Function        : Full
1399
#                   FullWs
1400
#                   Repo
1401
#                   Peg
1402
#                   Type
1403
#                   WsType
1404
#                   Path
1405
#                   Version
1406
#                   RmRef
1407
#                   Url
1408
#
1409
# Description     : Accessor functions
1410
#
1411
# Inputs          : $self       - Instance data
1412
#                                 self (is $_[0])
1413
#
1414
# Returns         : Data Item
1415
#
1416
sub Full    { return $_[0]->{URL} . $_[0]->{PKGROOT} ; }
1417
sub FullWs  { return $_[0]->{URL} . $_[0]->{WSURL} ; }
1418
sub Peg     { return $_[0]->{PEG} ; }
1419
sub Type    { return $_[0]->{TAGTYPE} || '' ; }
1420
sub WsType  { return $_[0]->{WSTYPE}  || '' ; }
1421
sub Path    { return $_[0]->{PATH} ; }
1422
sub Version { return $_[0]->{VERSION} ; }
1423
sub RmRef   { return $_[0]->{RMREF} ; }
1424
 
1425
#-------------------------------------------------------------------------------
341 dpurdie 1426
# Function        : Url
1427
#
1428
# Description     : Accessor function
1429
#                   Return URL or prepend URL to user tag
1430
#                   If user tag has a protocol, then don't prepend
1431
#
1432
# Inputs          : $self           - Class ref
1433
#                   $utag           - Option user suffix
1434
#
1435
# Returns         : Well formed Url
1436
#
1437
sub Url
1438
{
1439
    my ( $self, $utag ) = @_;
1440
    if ( $utag =~ m~^(file|http|https|svn)://([^/]+)~ )
1441
    {
1442
        return $utag;
1443
    }
1444
    return $self->{URL} . ($utag || '') ;
1445
}
1446
 
1447
 
1448
#-------------------------------------------------------------------------------
267 dpurdie 1449
# Function        : Print
1450
#
1451
# Description     : Debug display the URL
1452
#
1453
# Inputs          : $self           - Instance data
1454
#                   $header
1455
#                   $indent
1456
#
1457
# Returns         : Nothing
1458
#
1459
sub Print
1460
{
1461
    my ($self, $header, $indent) = @_;
1462
    print "$header\n" if $header;
1463
    $indent = 4 unless ( defined $indent );
1464
    $indent = ' ' x $indent;
1465
 
1466
 
1467
    print $indent . "PROTOCOL:" . $self->{PROTOCOL} . "\n";
1468
    print $indent . "SERVER  :" . $self->{SERVER} . "\n";
1469
    print $indent . "URL     :" . $self->{URL} . "\n";
1470
    print $indent . "PKGROOT :" . $self->{PKGROOT} . "\n";
1471
    print $indent . "PATH    :" . $self->{PATH} . "\n";
1472
    print $indent . "TAGTYPE :" . $self->{TAGTYPE} . "\n";
1473
    print $indent . "VERSION :" . $self->{VERSION} . "\n";
1474
    print $indent . "PEG     :" . $self->{PEG} . "\n";
1475
    print $indent . "FULL    :" . $self->Full . "\n";
1476
}
1477
 
1478
#-------------------------------------------------------------------------------
1479
# Function        : BranchName
1480
#
1481
# Description     : Create a full URL to a branch or tag based on the
1482
#                   current entry
1483
#
1484
#                   URL must have a TTB format
1485
#
1486
# Inputs          : $self           - Instance data
1487
#                   $branch         - Name of the branch
1488
#                   $type           - Optional branch type
1489
#
1490
# Returns         : Full URL name to the new branch
1491
#
1492
sub BranchName
1493
{
1494
    my ($self, $branch, $type ) = @_;
1495
    Debug ( "BranchName", $branch );
1496
 
1497
    $type = 'branches' unless ( $type );
1498
    my $root = $self->{PKGROOT};
1499
 
1500
    $root =~ s~/(tags|branches|trunk)(/|$|@).*~~;
1501
 
1502
    return $self->{URL} . $root . '/' . $type . '/' . $branch;
1503
}
1504
 
1505
#------------------------------------------------------------------------------
1506
1;