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