Subversion Repositories DevTools

Rev

Rev 2048 | Rev 2354 | Go to most recent revision | Details | Compare with Previous | 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
369 dpurdie 59
                    SvnPaths
267 dpurdie 60
                );
61
@EXPORT_OK =  qw(
62
                );
63
 
64
%EXPORT_TAGS = (All => [@EXPORT, @EXPORT_OK]);
65
 
66
#
67
#   Global Variables
68
#
69
 
70
#-------------------------------------------------------------------------------
71
# Function        : SvnCo
72
#
73
# Description     : Create a workspace
74
#                   Can be used to extract files, without creating the
75
#                   subversion control files.
76
#
1345 dpurdie 77
# Inputs          : $self                   - Instance data
78
#                   $RepoPath               - Within the repository
79
#                   $Path                   - Local path
1356 dpurdie 80
#                   Hash of Options
81
#                           export          - Bool: Export Only
82
#                           force           - Bool: Force export to overwrite
83
#                           print           - Bool: Don't print files exported
84
#                           pretext=aa      - Text: Display before operation
267 dpurdie 85
#
86
# Returns         : Nothing
87
#
88
sub SvnCo
89
{
1356 dpurdie 90
    my $self = shift;
91
    my $RepoPath = shift;
92
    my $path = shift;
93
    my %opt = @_;
94
 
341 dpurdie 95
    Debug ("SvnCo", $RepoPath, $path);
1356 dpurdie 96
    Error ("SvnCi: Odd number of args") unless ((@_ % 2) == 0);
267 dpurdie 97
 
1356 dpurdie 98
    #
99
    #   Set some defaults
100
    #
101
    my $cmd = $opt{export} ? 'export' : 'checkout';
102
    my $print = exists $opt{print} ? $opt{print} : 1;
103
    $self->{CoText} =  $opt{pretext} || 'Extracting';
1345 dpurdie 104
 
267 dpurdie 105
    #
106
    #   Ensure that the output path does not exist
107
    #   Do not allow the user to create a local work space
108
    #   where one already exists
109
    #
110
    Error ("SvnCo: No PATH specified" ) unless ( $path );
1356 dpurdie 111
    Error ("SvnCo: Target path already exists", "Path: " . $path ) if ( ! $opt{force} && -e $path  );
267 dpurdie 112
 
113
    #
114
    #   Build up the command line
115
    #
1345 dpurdie 116
    my @args = $cmd;
267 dpurdie 117
    push @args, qw( --ignore-externals );
1356 dpurdie 118
    push @args, qw( --force ) if ( $opt{force} );
267 dpurdie 119
    push @args, $RepoPath, $path;
120
 
121
    my @co_list;
122
    if ( $self->SvnCmd ( @args,
123
                            {
124
                                'process' => \&ProcessCo,
125
                                'data' => \@co_list,
126
                                'credentials' => 1,
127
                                'nosavedata' => 1,
1348 dpurdie 128
                                'printdata' => $print,
267 dpurdie 129
                            }
130
                       ) || @co_list )
131
    {
132
        #
133
        #   We have a checkout limitation
134
        #   Delete the workspace and then report the error
135
        #
385 dpurdie 136
        #   Note: For some reason a simple rmtree doesn't work
137
        #         Nor does glob show all the directories
138
        #
267 dpurdie 139
        Verbose2 ("Remove WorkSpace: $path");
140
        rmtree( $path, IsVerbose(3) );
385 dpurdie 141
        rmtree( $path, IsVerbose(3) );
267 dpurdie 142
        Error ("Checking out Workspace", @{$self->{ERROR_LIST}}, @co_list );
143
    }
144
    return;
145
 
146
    #
147
    #   Internal routine to scan each the checkout
148
    #
149
    #   Due to the structure of a SubVersion repository it would be
150
    #   possible for a user to extract the entire repository. This is
151
    #   not good as the repo could be very very large
152
    #
153
    #   Assume that the structure of the repo is such that our
154
    #   user is not allowed to extract a directory tree that contains
155
    #   key paths - such as /tags/ as this would indicate that they are
156
    #   attempting to extract something that is not a package
157
    #
158
    #
159
    sub ProcessCo
160
    {
161
        my $self = shift;
1270 dpurdie 162
        my $data = shift;
163
 
164
        if ( $self->{PRINTDATA} )
267 dpurdie 165
        {
1270 dpurdie 166
            #
167
            #   Pretty display for user
1349 dpurdie 168
            #   Hide some noise, but not much
1270 dpurdie 169
            #
1349 dpurdie 170
            unless ( $data =~ m~^Export complete.~ )
171
            {
172
                Information1 ( $self->{CoText} . ': ' . $data);
173
            }
1270 dpurdie 174
        }
175
 
176
        if (  $data =~ m~((/)(tags|branches|trunk)(/|$))~ )
177
        {
267 dpurdie 178
            my $bad_dir = $1;
179
            push @{$self->{ERROR_LIST}}, "Checkout does not describe the root of a package. Contains: $bad_dir";
180
            return 1;
181
        }
182
 
183
        ##
184
        ##   Limit the size of the WorkSpace
185
        ##   This limit is a bit artificial, but does attempt to
186
        ##   limit views that encompass too much.
187
        ##
188
        #if ( $#{$self->{RESULT_LIST}} > 100 )
189
        #{
190
        #    Warning ("View is too large - DEBUG USE ONLY. WILL BE REMOVED" );
191
        #    push @{$self->{ERROR_LIST}}, "View too large";
192
        #    return 1;
193
        #}
194
    }
195
}
196
 
197
#-------------------------------------------------------------------------------
1343 dpurdie 198
# Function        : SvnSwitch
199
#
200
# Description     : Switches files and directories
201
#
202
# Inputs          : $self               - Instance data
203
#                   $RepoPath           - Within the repository
204
#                   $Path               - Local path
205
#                   Options             - Options
206
#                           --NoPrint   - Don't print files exported
2053 dpurdie 207
#                           --KeepWs    - Don't delete the WorkSpace on error
1343 dpurdie 208
#
209
# Returns         : Nothing
210
#
211
sub SvnSwitch
212
{
213
    my ($self, $RepoPath, $path, @opts) = @_;
1348 dpurdie 214
    my $printdata = ! grep (/^--NoPrint/, @opts );
2053 dpurdie 215
    my $keepWs = grep (/^--KeepWs/, @opts );
1343 dpurdie 216
    Debug ("SvnSwitch", $RepoPath, $path);
217
 
218
    #
219
    #   Build up the command line
220
    #
221
    my @sw_list;
222
    if ( $self->SvnCmd ( 'switch', $RepoPath, $path,
223
                            {
224
                                'process' => \&ProcessSwitch,
225
                                'data' => \@sw_list,
226
                                'credentials' => 1,
227
                                'nosavedata' => 1,
1348 dpurdie 228
                                'printdata' => $printdata,
1343 dpurdie 229
                            }
230
                       ) || @sw_list )
231
    {
232
        #
233
        #   We have a switch problem
234
        #   Delete the workspace and then report the error
235
        #
236
        #   Note: For some reason a simple rmtree doesn't work
237
        #         Nor does glob show all the directories
238
        #
2053 dpurdie 239
        unless ( $keepWs )
240
        {
241
            Verbose2 ("Remove WorkSpace: $path");
242
            rmtree( $path, IsVerbose(3) );
243
            rmtree( $path, IsVerbose(3) );
244
            Error ("Switch elements", @{$self->{ERROR_LIST}}, @sw_list );
245
        }
246
        Warning("Switch error: Workspace state unkwown", @{$self->{ERROR_LIST}}, @sw_list);
1343 dpurdie 247
    }
248
    return;
249
 
250
    #
251
    #   Internal routine to scan each line of the Switch output
252
    #   Use to provide a nice display
253
    #
254
    sub ProcessSwitch
255
    {
256
        my $self = shift;
257
        my $data = shift;
258
 
259
        if ( $self->{PRINTDATA} )
260
        {
261
            #
262
            #   Pretty display for user
263
            #
264
            Information1 ("Switching : $data");
265
        }
266
    }
267
}
268
 
269
#-------------------------------------------------------------------------------
267 dpurdie 270
# Function        : SvnCi
271
#
272
# Description     : Check in the specified WorkSpace
273
#
274
# Inputs          : $self           - Instance data
275
#                   A hash of named arguments
276
#                       comment     - Commit comment
379 dpurdie 277
#                       allowSame   - Allow no change to the workspace
267 dpurdie 278
#
279
# Returns         : Tag of the checkin
280
#
281
sub SvnCi
282
{
283
    my $self = shift;
284
    my %opt = @_;
285
    my $status_url;
379 dpurdie 286
    my $ws_rev;
267 dpurdie 287
 
288
    Debug ("SvnCi");
289
    Error ("SvnCi: Odd number of args") unless ((@_ % 2) == 0);
290
 
291
    #
292
    #   Validate the source path
1328 dpurdie 293
    #   Note: populates %{$self->{InfoWs}} with 'info' data
267 dpurdie 294
    #
295
    my $path = SvnValidateWs ($self, 'SvnCi');
296
 
297
    #
1328 dpurdie 298
    #   Examine %{$self->{InfoWs}}, which has the results of an 'info'
267 dpurdie 299
    #   command the locate the URL.
300
    #
301
    #   This contains the target view space
302
    #   Sanity test. Don't allow Checkin to a /tags/ area
303
    #
1328 dpurdie 304
    $status_url = $self->{InfoWs}{URL};
305
    $ws_rev = $self->{InfoWs}{Revision};
379 dpurdie 306
 
267 dpurdie 307
    Error ("SvnCi: Cannot determine Repositoty URL")
308
        unless ( $status_url );
309
 
310
    Error ("SvnCi: Not allowed to commit to a 'tags' area", "URL: $status_url")
311
        if ( $status_url =~ m~/tags(/|$)~ );
312
 
313
    #
314
    #   Commit
1328 dpurdie 315
    #   Will modify Repo, so kill the cached Info
316
    #   Will only be a real issue if we tag in the same session
267 dpurdie 317
    #
1328 dpurdie 318
    delete $self->{'InfoWs'};
319
    delete $self->{'InfoRepo'};
320
 
267 dpurdie 321
    $self->SvnCmd ( 'commit', $path
322
                    , '-m', SvnComment( $opt{'comment'}, 'Created by SvnCi' ),
323
                    , { 'credentials' => 1,
324
                        'process' => \&ProcessRevNo,
379 dpurdie 325
                        'error' => "SvnCi: Copy Error",
326
                         }
267 dpurdie 327
                        );
379 dpurdie 328
 
329
    #
330
    #   No error and no commit
331
    #   Workspace was not changed, may be allowed
332
    #
333
    if ( ! $self->{REVNO} && $opt{allowSame} )
334
    {
335
        Warning ("SvnCi: Workspace matches Repository. No commit");
336
        $self->{REVNO} = $ws_rev;
337
    }
338
 
267 dpurdie 339
    Error ("SvnCi: Cannot determine Revision Number", @{$self->{RESULT_LIST}})
340
        unless ( $self->{REVNO} );
341
 
342
    #
343
    #   Update the view
344
    #   Doing this so that the local view contains an up to date
345
    #   revision number. If not done, and a 'copy' is done based
346
    #   on this view then the branch information will indicate that
347
    #   the copy is based on an incorrect version.
348
    #   This can be confusing!
349
    #
350
    $self->SvnCmd ( 'update'   , $path
351
                        , '--ignore-externals'
352
                        , { 'credentials' => 1,
353
                            'error' => "SvnCi: Updating WorkSpace" }
354
                        );
355
    #
356
    #   Pass the updated revision number back to the user
357
    #
358
    $self->CalcRmReference($status_url);
379 dpurdie 359
    Message ("Commit Tag is: " . $self->{RMREF} );
267 dpurdie 360
    return $self->{RMREF} ;
361
}
362
 
363
#-------------------------------------------------------------------------------
364
# Function        : SvnCreatePackage
365
#
366
# Description     : Create a package and any associated files
367
#
368
# Inputs          : $self        - Instance data
369
#                   A hash of named arguments
1348 dpurdie 370
#                       package     - Name of the package
371
#                                     May include subdirs
372
#                       new         - True: Must not already exist
373
#                       replace     - True: Replace targets
374
#                       import      - DirTree to import
375
#                       label       - Tag for imported DirTree
376
#                       type        - Import TTB target
377
#                       printdata   - True: Print extracted files (default)
267 dpurdie 378
#
1348 dpurdie 379
#
267 dpurdie 380
# Returns         : Revision of the copy
381
#
382
sub SvnCreatePackage
383
{
384
    my $self = shift;
385
    my %opt = @_;
386
    my $target;
387
 
388
    Debug ("SvnCreatePackage", @_);
389
    Error ("Odd number of args to SvnCreatePackage") unless ((@_ % 2) == 0);
390
    my %dirs = ( 'trunk/'       => 0,
391
                 'tags/'        => 0,
392
                 'branches/'    => 0 );
393
 
394
    #
1348 dpurdie 395
    #   Sanity Tests and defaul values
267 dpurdie 396
    #
397
    my $package = $self->Full || Error ("SvnCreatePackage: No package name provided");
398
    Error ("SvnCreatePackage: Invalid import path") if ( $opt{'import'} && ! -d $opt{'import'} );
399
    Error ("SvnCreatePackage: Tag without Import") if ( $opt{'label'} && ! $opt{'import'} );
400
    $opt{'label'} = SvnIsaSimpleLabel( $opt{'label'} ) if (  $opt{'label'} );
1348 dpurdie 401
    $opt{'printdata'} = 1 unless ( exists $opt{'printdata'} );
267 dpurdie 402
 
403
    #
404
    #   Package path cannot contain any of the keyword paths tags,trunk,branches
405
    #   as this would place a package with a package
406
    #
407
    Error ("Package path contains a reserved word ($1)", "Path: $package")
408
        if (  $package =~ m~/(tags|branches|trunk)(/|$)~ );
409
 
410
    #
411
    #   Package path cannot be pegged, or look like one
412
    #
413
    Error ("Package name contains a Peg ($1)", "Path: $package")
414
        if ( $package =~ m~.*(@\d+)$~ );
415
 
416
    #
417
    #   Determine TTB target
1356 dpurdie 418
    #   The TTB type for branches and tags also conatins the branch or tag
267 dpurdie 419
    #
420
    $opt{'type'} = 'trunk' unless ( $opt{'type'} );
1347 dpurdie 421
    if ( $opt{'type'} =~ m~^(tags|branches|trunk)(/|$)(.*)~ ) {
422
        Error ("SvnCreatePackage: TTB type ($1) must be followed by a path element")
423
            if ( (($1 eq 'tags') or ($1 eq 'branches' )) && ! $3  );
424
        Error ('SvnCreatePackage: TTB type of trunk must not be followed by a path element: ' . $opt{'type'})
425
            if ( ($1 eq 'trunk') && $3  );
426
    } else {
427
        Error ("SvnCreatePackage: Invalid TTB Type: " . $opt{'type'} );
428
    }
267 dpurdie 429
 
430
    #
431
    #   Before we import data we must ensure that the targets do not exist
432
    #   Determine the import target(s)
433
    #
434
    my $import_target;
435
    my $copy_target;
436
 
1356 dpurdie 437
    $self->{DEVBRANCH} = 'trunk';
267 dpurdie 438
    if ( $opt{'import'} )
439
    {
440
        #
441
        #   Primary target
442
        #   trunk, branck or tag
443
        #
444
        $import_target = $package . '/' . $opt{'type'};
1347 dpurdie 445
        $self->{DEVBRANCH} = $opt{'type'} ;
267 dpurdie 446
 
447
        $self->SvnValidateTarget( 'target'    => $import_target,
448
                                  'delete'    => $opt{'replace'},
449
                                  'available' => 1 );
450
 
451
        #
452
        #   Secondary target
1347 dpurdie 453
        #   Are we tagging the import too
267 dpurdie 454
        #
1347 dpurdie 455
        if ( $opt{'label'} )
267 dpurdie 456
        {
457
            $copy_target = $package . '/tags/' . $opt{'label'};
458
            $self->SvnValidateTarget( 'target'    => $copy_target,
459
                                      'delete'    => $opt{'replace'},
460
                                      'available' => 1 );
461
        }
462
    }
463
 
464
    #
465
    #   Probe to see if the package exists
466
    #
467
    my ( $ref_files, $ref_dirs, $ref_svn, $found ) = $self->SvnScanPath ( 'SvnCreatePackage', $package );
468
    if ( @$ref_dirs )
469
    {
470
        Error ("SvnCreatePackage: Package directory exists",
471
               "Cannot create a package here. Unexpected subdirectories:", @$ref_dirs);
472
    }
473
 
474
    if ( @$ref_files )
475
    {
476
        Warning ("SvnCreatePackage: Unexpected files found",
477
               "Path: $package",
478
               "Unexpected files found: @$ref_files");
479
    }
480
 
481
    if ( @$ref_svn )
482
    {
483
        #
484
        #   Do we need a new one
485
        #
486
        Error ("SvnCreatePackage: Package exists: $package") if $opt{'new'};
487
 
488
        #
489
        #   Some subversion files have been found here
490
        #   Create the rest
491
        #   Assume that the directory tree is good
492
        #
493
        #
494
        #   Some, or all, of the required package subdirectories exist
495
        #   Determine the new ones to created so that it can be done
496
        #   in an atomic step
497
        #
498
        delete $dirs{$_} foreach  ( @$ref_svn );
499
        if ( keys %dirs )
500
        {
501
            Warning ("SvnCreatePackage: Not all package subdirs present",
502
                     "Remaining dirs will be created",
503
                     "Found: @$ref_svn") if @$ref_svn;
504
        }
505
        else
506
        {
507
            Warning ("SvnCreatePackage: Package already present");
508
        }
509
    }
510
    #
511
    #   Create package directories that have not been discovered
512
    #       trunk
513
    #       branches
514
    #       tags
515
    #
516
    my @dirs;
517
    push @dirs, $package . '/' . $_ foreach ( keys %dirs );
518
    $target = $package . '/trunk';
519
 
520
    #
521
    #   Create missing directories - if any
522
    #
523
    if ( @dirs )
524
    {
525
        $self->SvnCmd ('mkdir', @dirs
379 dpurdie 526
                       , '-m', $self->Path() . ': Created by SvnCreatePackage'
267 dpurdie 527
                       , '--parents'
385 dpurdie 528
                       , { 'credentials' => 1
529
                           ,'error' => "SvnCreatePackage"
530
                           ,'process' => \&ProcessRevNo
531
                         } );
267 dpurdie 532
    }
533
 
534
    #
535
    #   Import data into the package if required
536
    #   Import data. Possible cases:
537
    #       - Import to trunk - and then tag it
538
    #       - Import to branches
539
    #       - Import to tags
540
    #
541
    if ( $import_target )
542
    {
543
        Verbose ("Importing directory into new package: $opt{'import'}");
544
 
545
        $target = $import_target;
1348 dpurdie 546
        $self->{PRINTDATA} = $opt{'printdata'};
267 dpurdie 547
        $self->SvnCmd ('import', $opt{'import'}
548
                        , $target
549
                        , '-m', 'Import by SvnCreatePackage'
550
                        , '--force'
551
                        , { 'credentials' => 1
552
                           ,'error' => "Import Incomplete"
553
                           ,'process' => \&ProcessRevNo
1348 dpurdie 554
                           ,'printdata' => $opt{'printdata'}
267 dpurdie 555
                          })
556
    }
557
 
558
    #
559
    #   If imported to the trunk AND a label is provided
560
    #   then tag the import as well.
561
    #   A simple URL copy
562
    #
563
    if ( $copy_target )
564
    {
565
        Verbose ("Labeling imported trunk: $opt{'label'} ");
566
        $target = $copy_target;
567
        $self->SvnCmd ('copy'  , $import_target
568
                        , $target
569
                        , '-m', 'Import tagged by SvnCreatePackage'
570
                        , { 'credentials' => 1
571
                          , 'process' => \&ProcessRevNo
572
                          , 'error' => "Import Incomplete" } );
573
    }
574
 
575
    #
1356 dpurdie 576
    #   If we have done very little then we won't know the version
577
    #   of the repo. Need to force it
578
    #
579
    unless ( $self->{REVNO} || $self->{WSREVNO} )
580
    {
581
        $self->SvnInfo( $package, 'InfoRepo' );
582
        $self->{REVNO}  = $self->{'InfoRepo'}{'Last Changed Rev'} || Error ("SvnCreatePackage: Bad info for Repository");
583
    }
584
 
585
 
586
    #
267 dpurdie 587
    #   Pass the updated revision number back to the user
588
    #
589
    $self->CalcRmReference($target);
1356 dpurdie 590
    Message ("Create Package Rm Ref : " . $self->RmRef);
591
    Message ("Create Package Vcs Tag: " . $self->SvnTag);
267 dpurdie 592
    return $self->{RMREF} ;
593
}
594
 
595
#-------------------------------------------------------------------------------
596
# Function        : SvnRmView
597
#
598
# Description     : Remove a Subversion view
599
#                   Will run sanity checks and only remove the view if
600
#                   all is well
601
#
602
# Inputs          : A hash of named arguments
603
#                       path     - Path to local workspace
604
#                       modified - Array of files that are allowed to be modified
605
#                       force    - True: Force deletion
606
#
607
# Returns         :
608
#
609
sub SvnRmView
610
{
611
    my %opt = @_;
612
    Debug ("SvnRmView");
613
    Error ("Odd number of args to SvnRmView") unless ((@_ % 2) == 0);
614
 
615
    #
616
    #   Sanity test
617
    #
618
    my $path = $opt{'path'} || '';
619
    my $path_length = length ($path);
620
    Verbose2 ("Delete WorkSpace: $path");
621
 
622
    #
623
    #   If the path does not exist then assume that its already deleted
624
    #
625
    unless ( $path && -e $path )
626
    {
627
        Verbose2 ("SvnRmView: Path does not exist");
628
        return;
629
    }
630
 
631
    #
632
    #   Create information about the workspace
633
    #   This will also validate the path
634
    #
361 dpurdie 635
    my $session = NewSessionByWS ( $path, 0, 1 );
267 dpurdie 636
 
637
    #
638
    #   Validate the path
639
    #
640
    $session->SvnValidateWs ($path, 'SvnRmView');
641
 
642
    #
643
    #   Ask subversion if there are any files to be updated
644
    #   Prevent deletion of a view that has modified files
645
    #
646
    unless ( $opt{'force'} )
647
    {
648
        $session->SvnWsModified ( 'cmd' => 'SvnRmView', %opt );
649
    }
650
 
651
    #
652
    #   Now we can delete it
653
    #
654
    Verbose2 ("Remove WorkSpace: $path");
655
    rmtree( $path, IsVerbose(3) );
656
}
657
 
658
 
659
#-------------------------------------------------------------------------------
660
# Function        : SvnCopyWs
661
#
662
# Description     : Copy a workspace to a new place in the repository
663
#                   Effectively 'label' the workspace
664
#
665
#                   It would appear that the 'copy' command is very clever
666
#                   If a version-controlled file has been changed
667
#                   in the source workspace, then it will automatically be
668
#                   copied. This is a trap.
669
#
670
#                   Only allow a 'copy' if there are no modified
671
#                   files in the work space (unless overridden)
672
#
1328 dpurdie 673
#                   Only allow a 'copy' if the local workspace is
674
#                   up to date with respect with the repo. It possible
675
#                   to do a 'commit' and then a 'copy' (tag) and have
676
#                   unexpected results as the workspace has not been
677
#                   updated. This is a trap.
267 dpurdie 678
#
1328 dpurdie 679
#
267 dpurdie 680
# Inputs          : $self        - Instance data
681
#                   A hash of named arguments
682
#                       path     - Path to local workspace
683
#                       target   - Location within the repository to copy to
684
#                       comment  - Commit comment
685
#                       modified - Array of files that are allowed to
686
#                                  be modified in the workspace.
385 dpurdie 687
#                       noswitch        - True: Don't switch to the new URL
688
#                       replace         - True: Delete existing tag if present
689
#                       allowLocalMods  - True: Allow complex tagging
1343 dpurdie 690
#                       noupdatecheck   - True: Do not check that the WS is up to date
267 dpurdie 691
#
692
# Returns         : Revision of the copy
693
#
694
sub SvnCopyWs
695
{
696
    my $self = shift;
697
    my %opt = @_;
385 dpurdie 698
    my $rv;
267 dpurdie 699
    Debug ("SvnCopyWs");
700
    Error ("Odd number of args to SvnCopyWs") unless ((@_ % 2) == 0);
701
    Error ("SvnCopyWs: No Workspace" ) unless ( $self->{WS} );
702
 
703
    #
704
    #   Insert defaults
705
    #
706
    my $target = $opt{target} || Error ("SvnCopyWs: Target not specified" );
707
 
708
    #
709
    #   Validate the source path
710
    #
711
    my $path = SvnValidateWs ($self, 'SvnCopyWs');
712
 
713
    #
714
    #   Validate the target
715
    #   Cannot have a 'peg'
716
    #
717
    Error ("SvnCopyWs: Target contains a Peg: ($1)", $target)
718
        if ( $target =~ m~(@\d+)\s*$~ );
719
 
720
    #
1328 dpurdie 721
    #   Ensure the Workspace is up to date
722
    #       Determine the state of the Repo and the Workspace
723
    #
1343 dpurdie 724
    unless ( $opt{noupdatecheck} )
725
    {
726
        $self->SvnInfo( $self->{WS} , 'InfoWs' );
727
        $self->SvnInfo( $self->FullWs, 'InfoRepo' );
1328 dpurdie 728
 
1343 dpurdie 729
        my $wsLastChangedRev = $self->{'InfoWs'}{'Last Changed Rev'} || Error ("SvnCopyWs: Bad info for Workspace");
730
        my $repoLastChangedRev = $self->{'InfoRepo'}{'Last Changed Rev'} || Error ("SvnCopyWs: Bad info for Repository");
1328 dpurdie 731
 
1343 dpurdie 732
        Verbose("WS Rev  : $wsLastChangedRev");
733
        Verbose("Repo Rev: $repoLastChangedRev");
734
        Error ('SvnCopyWs: The repository has been modified since the workspace was last updated.',
735
               'Possibly caused by a commit without an update.',
736
               'Update the workspace and try again.',
737
               "Last Changed Rev. Repo: $repoLastChangedRev. Ws:$wsLastChangedRev") if ( $repoLastChangedRev > $wsLastChangedRev );
738
    }
1328 dpurdie 739
 
740
    #
267 dpurdie 741
    #   Examine the workspace and ensure that there are no modified
742
    #   files - unless they are expected
743
    #
744
    $self->SvnWsModified ( 'cmd' => 'SvnCopyWs', %opt );
385 dpurdie 745
 
267 dpurdie 746
    #
747
    #   Validate the repository
748
    #   Ensure that the target does not exist
749
    #   The target may be deleted if it exists and allowed by the user
750
    #
751
    $self->SvnValidateTarget ( 'cmd'    => 'SvnCopyWs',
752
                        'target' => $target,
753
                        'delete' => $opt{replace},
754
                        'comment' => 'Deleted by SvnCopyWs'
755
                        );
369 dpurdie 756
 
267 dpurdie 757
    #
758
    #   Copy source to destination
1328 dpurdie 759
    #   Assuming the WorkSpace is up to date then, even though the source is a
760
    #   WorkSpace, the copy does not transfer data from the WorkSpace.
761
    #   It appears as though its all done on the server. This is good - and fast.
267 dpurdie 762
    #
1328 dpurdie 763
    #   If the Workspace is not up to date, then files that SVN thinks have not
764
    #   been transferred will be transferred - hence the need to update after
765
    #   a commit.
766
    #
385 dpurdie 767
    #   Moreover, files that are modified in the local workspace will
768
    #   be copied and checked into the target, but this is not nice.
267 dpurdie 769
    #
385 dpurdie 770
    $rv = $self->SvnCmd ( 'cp'  , $path
267 dpurdie 771
                        , $target
772
                        , '--parents'
773
                        , '-m', SvnComment( $opt{'comment'}, 'Created by SvnCopyWs' ),
774
                        , { 'process' => \&ProcessRevNo,
1348 dpurdie 775
                            'credentials' => 1,
776
                            'printdata' => 1,
777
                             }
385 dpurdie 778
                        );
779
    if ($rv)
267 dpurdie 780
    {
781
        #
782
        #   Error in copy
783
        #   Attempt to delete the target. Don't worry if we can't do that
784
        #
785
        my @err1 = @{$self->{ERROR_LIST}};
786
        $self->SvnCmd ( 'delete'
787
                    , $target
788
                    , '-m', 'Deleted by SvnCopyWs after creation failure'
789
                    , { 'credentials' => 1, }
790
               );
791
        Error ("SvnCopyWs: Copy Error", @err1);
792
    }
793
 
794
    Error ("SvnCopyWs: Cannot determine Revision Number", @{$self->{RESULT_LIST}})
795
        unless ( $self->{REVNO} );
796
 
797
    Verbose2 ("Copy committed as revision: " . $self->{REVNO} );
798
 
799
    unless ( $opt{'noswitch'} )
800
    {
801
        #
802
        #   Switch to the new URL
803
        #   This will link the Workspace with the copy that we have just made
804
        #
805
        $self->SvnCmd ( 'switch', $target
806
                         , $path
807
                         , { 'credentials' => 1,
808
                             'error' => "SvnCopyWs: Cannot switch to new URL" }
809
               );
810
    }
811
 
812
    #
813
    #   Pass the updated revision number back to the user
814
    #
1347 dpurdie 815
    $self->CalcRmReference($target);
353 dpurdie 816
    #Message ("Tag is: " . $self->{RMREF} );
267 dpurdie 817
    return $self->{RMREF} ;
818
}
819
 
820
#-------------------------------------------------------------------------------
821
# Function        : SvnWsModified
822
#
823
# Description     : Test a Workspace for modified files
824
#                   Allow some files to be modified
825
#
826
# Inputs          : $self           - Instance data
827
#                   A hash of named arguments
1422 dpurdie 828
#                       path            - Path to local workspace
829
#                       modifiedRoot    - Alternate base for files
830
#                       modified        - Files that are allowed to be modified
831
#                                         Relative to the 'path' or 'modifiedRoot'
832
#                                         May be a single file or an array of files
833
#                       allowLocalMods  - Only warn about local mods
834
#                       cmd             - Command name for error reporting
267 dpurdie 835
#
836
# Returns         :
837
#
838
sub SvnWsModified
839
{
840
    my $self = shift;
841
    my %opt = @_;
842
    Debug ("SvnWsModified");
843
    Error ("Odd number of args to SvnWsModified") unless ((@_ % 2) == 0);
844
 
845
    my $cmd = $opt{'cmd'} || 'SvnWsModified';
846
 
847
    #
848
    #   Validate the path
849
    #
850
    SvnValidateWs ($self, $cmd);
851
    my $path = $self->{WS};
1422 dpurdie 852
    my $modifiedRoot = $opt{'modifiedRoot'} || $path;
853
    my $path_length = length ($modifiedRoot);
267 dpurdie 854
    Verbose2 ("Test Workspace for Modifications: $path");
855
 
856
    #
857
    #   Ask subversion if there are any files to be updated
858
    #
859
    $self->SvnCmd ('status', $path, {'error' => "Svn status command error"} );
860
 
861
    #
862
    #   Examine the list of modified files
863
    #
864
    if ( @{$self->{RESULT_LIST}} )
865
    {
866
        #
351 dpurdie 867
        #   Create a hash of files that are allowed to change
267 dpurdie 868
        #   These are files relative to the base of the view
869
        #
870
        #   The svn command has the 'path' prepended, so this
871
        #   will be removed as we process the commands
872
        #
873
        my %allowed;
874
        my @unexpected;
875
 
876
        if ( exists $opt{'modified'}  )
877
        {
878
            $allowed{'/' . $_} = 1 foreach ( ref ($opt{'modified'}) ? @{$opt{'modified'}} : $opt{'modified'}  );
879
        }
880
 
881
        #
882
        #   Process the list of modified files
883
        #   Do this even if we aren't allowed modified files as we
884
        #   still need to examine the status and kill off junk entries
885
        #   ie: ?, I, ! and ~
886
        #
887
        #    First column: Says if item was added, deleted, or otherwise changed
888
        #      ' ' no modifications
889
        #      'A' Added
890
        #      'C' Conflicted
891
        #      'D' Deleted
892
        #      'I' Ignored
893
        #      'M' Modified
894
        #      'R' Replaced
895
        #      'X' item is unversioned, but is used by an externals definition
896
        #      '?' item is not under version control
897
        #      '!' item is missing (removed by non-svn command) or incomplete
898
        #      '~' versioned item obstructed by some item of a different kind
899
        #
900
        foreach my $entry ( @{$self->{RESULT_LIST}} )
901
        {
902
            #
903
            #   Extract filename from line
351 dpurdie 904
            #       First 8 chars are status
267 dpurdie 905
            #       Remove WS path too
906
            #
1328 dpurdie 907
            if ( length $entry >= 8 + $path_length)
908
            {
909
                my $file = substr ( $entry, 8 + $path_length );
910
                next if ( $allowed{$file} );
911
            }
267 dpurdie 912
 
913
            #
914
            #   Examine the first char and rule out funny things
915
            #
916
            my $f1 =  substr ($entry, 0,1 );
917
            next if ( $f1 =~ m{[?I!~]} );
918
            push @unexpected, $entry;
919
        }
385 dpurdie 920
 
921
        if ( @unexpected )
922
        {
923
            if ( $opt{allowLocalMods} ) {
924
                Message ("Workspace contains locally modified files:", @unexpected);
925
            } else {
926
                Error ("Workspace contains unexpected modified files", @unexpected);
927
            }
928
        }
267 dpurdie 929
    }
930
}
931
 
932
#-------------------------------------------------------------------------------
933
# Function        : SvnListPackages
934
#
935
# Description     : Determine a list of packages within the repo
936
#                   This turns out to be a very slow process
937
#                   so don't use it unless you really really need to
938
#
1341 dpurdie 939
# Inputs          : $self       - Instance data
940
#                   $repo       - Name of the repository
941
#                   Last argument may be a hash of options.
942
#                           Progress    - True: Show progress
943
#                           Show        - >1 : display matched Tags and stats
944
#                                         >2 : display Packages
945
#                           Tag         - Enable Tag Matching
946
#                                         Value is the tag to match
267 dpurdie 947
#
1341 dpurdie 948
# Returns         : Ref to an array of all packages
949
#                   Ref to an array of all packahes with matched tag
267 dpurdie 950
#
951
sub SvnListPackages
952
{
1341 dpurdie 953
    #
954
    #   Extract arguments and options
955
    #   If last argument is a hesh, then its a hash of options
956
    #
957
    my $opt;
958
    $opt = pop @_
959
        if (@_ > 0 and UNIVERSAL::isa($_[-1],'HASH'));
267 dpurdie 960
 
1341 dpurdie 961
    my ($self, $repo) = @_;
962
 
963
    my @path_list = '';
267 dpurdie 964
    my @list;
1341 dpurdie 965
    my @mlist;
267 dpurdie 966
    my $scanned = 0;
967
    Debug ("SvnListPackages");
968
    while ( @path_list )
969
    {
970
        my $path = shift @path_list;
1341 dpurdie 971
        if ( $opt->{Progress} )
972
        {
973
            Message ("Reading: " . ( $path || 'RepoRoot') );
974
        }
267 dpurdie 975
        $scanned++;
1341 dpurdie 976
        my ( $ref_files, $ref_dirs, $ref_svn, $found ) = $self->SvnScanPath ( 'Listing Packages', join( '/', $repo, $path) );
267 dpurdie 977
 
978
        #
979
        #   If there are Subversion dirs (ttb) in this directory
980
        #   then this is a package. Add to list
981
        #
982
        push @list, $path if ( @$ref_svn );
983
 
984
        #
985
        #   Add subdirs to the list of paths to explore
986
        #
987
        foreach  ( @$ref_dirs )
988
        {
1341 dpurdie 989
            chop;                                   # Remove trailing '/'
990
            push @path_list, $path ? join('/', $path , $_) : $_; # Extend the path
267 dpurdie 991
        }
992
    }
993
 
1341 dpurdie 994
    if ( $opt->{Tag} )
995
    {
996
        my $tag = $opt->{Tag};
997
        foreach my $path ( sort @list )
998
        {
999
            Message ("Testing: $path") if ( $opt->{Progress} );
1000
            if ( $self->SvnTestPath ( 'Listing Packages', join('/', $repo, $path, 'tags', $tag) ) )
1001
            {
1002
                push @mlist, $path;
1003
            }
1004
        }
1005
    }
1006
 
1007
    if ( $opt->{Show} )
1008
    {
1009
        Message ("Found Tags:", @mlist );
1010
        Message ("Found Packages:", @list ) if  $opt->{Show} > 2;
1011
        Message ("Tags Found: " . scalar @mlist );
1012
        Message ("Packages Found: " . scalar @list );
1013
        Message ("Dirs Scanned: $scanned");
1014
    }
1015
 
1016
    return \@list, \@mlist;
267 dpurdie 1017
}
1018
 
1019
#-------------------------------------------------------------------------------
1020
# Function        : ListLabels
1021
#
1022
# Description     : List labels within a given package
1023
#
1024
# Inputs          : $self               - Instance data
1025
#                   $path               - path to label source
1026
#
1027
# Returns         : Ref to an array
1028
#
1029
sub ListLabels
1030
{
1031
    my ($self, $path) = @_;
1032
    Debug ("ListLabels");
1033
 
1034
    my ( $ref_files, $ref_dirs, $ref_svn, $found ) = $self->SvnScanPath ( 'Listing Versions', $path );
1035
 
1036
    Error ("List: Path not found: $path") unless ( $found );
1037
 
1038
    #
1039
    #   Dont report files - just directories
1040
    #
1041
    return $ref_dirs;
1042
}
1043
 
1044
 
1045
#-------------------------------------------------------------------------------
1046
# Function        : SvnLocateWsRoot
1047
#
1048
# Description     : Given a WorkSpace, determine the root of the work space
1049
#                   This is not as simple as you might think
1050
#
1051
#                   Algorithm
1052
#                       svn ls ..
1053
#                       Am I in the parent directory
1054
#                       Repeat
1055
#
369 dpurdie 1056
#                   Updates 'WS' and 'WSURL'
1057
#
267 dpurdie 1058
# Inputs          : $self               - Instance data
1059
#                   $test               - True: Don't die on error
1060
#
1061
# Returns         : Root of workspace as an absolute address
1062
#                   Will not return if there is an error
1063
#
1064
sub SvnLocateWsRoot
1065
{
1066
    my ($self, $test) = @_;
1067
    my @path;
1068
    my $path = $self->{WS};
1399 dpurdie 1069
    my $found;
267 dpurdie 1070
 
1071
    Debug ("SvnLocateWsRoot");
1072
    Error ("SvnLocateWsRoot: No Workspace") unless ( $path  );
1073
    Verbose2 ("SvnLocateWsRoot: Start in $path");
1074
 
1075
    #
1076
    #   Validate the source path
1077
    #
1078
    if ( SvnValidateWs ($self, 'SvnLocateWsRoot', $test) )
1079
    {
1080
        return undef;
1081
    }
1082
 
1083
    #
1399 dpurdie 1084
    #   Under Subversion 1.7 the process is a lot easier
267 dpurdie 1085
    #
1399 dpurdie 1086
    if ( exists $self->{'InfoWs'}{'Working Copy Root Path'} )
1087
    {
1088
        #
1089
        #   WS is now known
1090
        #
1091
        $self->{WS} = $self->{'InfoWs'}{'Working Copy Root Path'};
267 dpurdie 1092
 
1399 dpurdie 1093
        #
1094
        #   Calculate WSURL
1095
        #
2048 dpurdie 1096
        $self->{WSURL} = join('/', $self->{PKGROOT}, $self->{DEVBRANCH})
1097
            if ($self->{DEVBRANCH});
1399 dpurdie 1098
        $found = 1;
1099
    }
1100
    else
267 dpurdie 1101
    {
1399 dpurdie 1102
        # Preversion 1.7
1103
        Warning ("Using svn < 1.7. This is not recommended");
267 dpurdie 1104
 
1399 dpurdie 1105
        #
1106
        #   Need to sanitize the users path to ensure that the following
1107
        #   algorithm works. Need:
1108
        #       1) Absolute Path
1109
        #       2) Not ending in '/'
1110
        #
369 dpurdie 1111
 
267 dpurdie 1112
        #
1399 dpurdie 1113
        #   If we have a relative path then prepend the current directory
1114
        #   An absolute path is:
1115
        #           /aaa/aa/aa
1116
        #       or  c:/aaa/aa/aa
267 dpurdie 1117
        #
1399 dpurdie 1118
        $path = getcwd() . '/' . $path
1119
            unless ( $path =~ m~^/|\w:/~  );
267 dpurdie 1120
 
1121
        #
1399 dpurdie 1122
        #   Walk the bits and remove ".." directories
1123
        #       Done by pushing non-.. elements and poping last entry for .. elements.
1124
        #   Have a leading "/" which is good.
267 dpurdie 1125
        #
1399 dpurdie 1126
        #   Create a array of directories in the path
1127
        #   Split on one or more \ or / separators
267 dpurdie 1128
        #
1399 dpurdie 1129
        foreach ( split /[\\\/]+/ , $path )
267 dpurdie 1130
        {
1399 dpurdie 1131
            next if ( $_ eq '.' );
1132
            unless ( $_ eq '..' )
1133
            {
1134
                push @path, $_;
1135
            }
1136
            else
1137
            {
1138
                Error ("SvnLocateWsRoot: Bad Pathname: $path")
1139
                    if ( $#path <= 0 );
1140
                pop @path;
1141
            }
267 dpurdie 1142
        }
1143
 
1144
        #
1399 dpurdie 1145
        #   Need to adjust the WSURL too
1146
        #   Break into parts and pop them off as we go
1147
        #   Add a dummy one to allow for the first iteration
267 dpurdie 1148
        #
1399 dpurdie 1149
        my @wsurl = (split (/[\\\/]+/ , $self->{WSURL}), 'Dummy');
369 dpurdie 1150
 
1399 dpurdie 1151
        Verbose2 ("Clean absolute path elements: @path");
1152
        PATH_LOOP:
1153
        while ( @path )
1154
        {
1155
            #
1156
            #   This directory element. Append / to assist in compare
1157
            #   Determine parent path
1158
            #
1159
            my $name = pop (@path) . '/';
1160
            my $parent = join ('/', @path );
1161
            pop @wsurl;
369 dpurdie 1162
 
1399 dpurdie 1163
            #
1164
            #   Examine the parent directory
1165
            #   Get a list of all elements in the parent
1166
            #   Need to ensure that this directory is one of them
1167
            #
1168
            #   Ignore any errors - assume that they are because the
1169
            #   parent is not a part of the work space. This will terminate the
1170
            #   search.
1171
            #
1172
            $self->SvnCmd ('list', $parent, '--depth', 'immediates' );
1173
            foreach my $entry ( @{$self->{RESULT_LIST}} )
1174
            {
1175
                next PATH_LOOP
1176
                    if ( $entry eq $name );
1177
            }
1178
 
1179
            #
1180
            #   Didn't find 'dir' in directory svn listing of parent
1181
            #   This parent is not a part of the same WorkSpace as 'dir'
1182
            #   We have a winner.
1183
            #
1184
            chop $name;                         #   Chop the '/' previously added
1185
            $self->{WS} = $parent . '/' . $name;
1186
 
1187
            #
1188
            #   Reform the WSURL. Elements have been removed as we tested up the
1189
            #   path
1190
            #
1191
            $self->{WSURL} = join '/', @wsurl;
1192
            $found = 1;
1193
            last;
1194
        }
267 dpurdie 1195
    }
1196
 
1197
    #
1198
    #   Shouldn't get this far
1199
    #
1399 dpurdie 1200
    Error ("SvnLocateWsRoot: Root not found")
1201
        unless ( $found );
1202
 
1203
    #
1204
    #   Refresh Info
1205
    #   Must kill cached copy
1206
    #
1207
    delete $self->{'InfoWs'};
1208
    $self->SvnInfo($self->{WS}, 'InfoWs');
1209
    return $self->{WS};
1210
 
267 dpurdie 1211
}
1212
 
1213
#-------------------------------------------------------------------------------
1214
# Function        : SvnValidateWs
1215
#
1216
# Description     : Validate the path to a working store
1217
#
1218
# Inputs          : $self           - Instance data
1219
#                   $user           - Optional prefix for error messages
1220
#                   $test           - True: Just test, Else Error
1221
#
1222
# Returns         : Will not return if not a workspace
1223
#                   Returns the users path
1328 dpurdie 1224
#                   Populates the hash: $self->{InfoWs}
267 dpurdie 1225
#
1226
sub SvnValidateWs
1227
{
1228
    my ($self, $user, $test) = @_;
1229
    Debug ("SvnValidateWs");
1230
 
1231
    $user = "Invalid Subversion Workspace" unless ( $user );
1328 dpurdie 1232
    my $path = $self->{WS};
267 dpurdie 1233
 
1234
    #
1328 dpurdie 1235
    #   Only validate it once
267 dpurdie 1236
    #
1237
    return $path if ( $self->{WS_VALIDATED} );
1238
 
1239
    #
1240
    #   Validate the source path
1241
    #   Must exist and must be a directory
1242
    #
1243
    if ( ! $path ) {
1244
        @{$self->{ERROR_LIST}} = "$user: No path specified";
1245
 
1246
    } elsif ( ! -e $path ) {
1247
        @{$self->{ERROR_LIST}} = "$user: Path does not exist: $path";
1248
 
1249
    } elsif ( ! -d $path ) {
1250
        @{$self->{ERROR_LIST}} = "$user: Path is not a directory";
1251
    } else {
1252
        #
1253
        #   Determine the source path is an fact a view
1254
        #   The info command can do this. Use depth empty to limit the work done
1255
        #
1328 dpurdie 1256
        $self->SvnInfo($path, 'InfoWs');
267 dpurdie 1257
 
1258
        #
1259
        #   Error. Prepend nice message
1260
        #
1261
        unshift @{$self->{ERROR_LIST}}, "$user: Path is not a WorkSpace: $path"
1262
            if ( @{$self->{ERROR_LIST}} );
1263
    }
1264
 
1265
    #
1266
    #   Figure out what to do
1267
    #
1268
    if ( $test )
1269
    {
1270
        return @{$self->{ERROR_LIST}};
1271
    }
1272
    else
1273
    {
1274
        Error @{$self->{ERROR_LIST}} if @{$self->{ERROR_LIST}};
1275
        $self->{WS_VALIDATED} = 1;
1276
        return $path;
1277
    }
1278
}
1279
 
1280
#-------------------------------------------------------------------------------
1281
# Function        : SvnValidatePackageRoot
1282
#
1283
# Description     : Validate a package root
1284
#
1285
# Inputs          : $self           - Instance data
1286
#
1287
# Returns         : Will only return if valid
1288
#                   Returns a cleaned package root
1289
#
1290
sub SvnValidatePackageRoot
1291
{
379 dpurdie 1292
    my ($self, $warning_only) = @_;
267 dpurdie 1293
    Debug ("SvnValidatePackageRoot");
1294
    my $url = $self->Full || Error ("SvnValidatePackageRoot: No URL");
1295
 
1296
    Error ("Package path contains a reserved word ($self->{TAGTYPE})", "Path: $url")
1297
        if (  $self->{TAGTYPE} );
1298
 
1299
    Error ("Package name contains a Peg ($self->{PEG})", "Path: $url")
1300
        if ( $self->{PEG} );
1301
 
1302
    #
1303
    #   Ensure that the target path does exist
1304
    #   Moreover it needs to be a directory and it should have a
1305
    #   a ttb structure
1306
    #
1307
    my ( $ref_files, $ref_dirs, $ref_svn, $found ) = $self->SvnScanPath ( 'Package Base Test', $url );
1308
 
1309
    #
379 dpurdie 1310
    #   Only looking for package path
1311
    #
1312
    if ( !$found && $warning_only )
1313
    {
1314
        return $url;
1315
    }
1316
 
1317
    #
267 dpurdie 1318
    #   Examine the results to see if we have a valid package base
1319
    #
1320
    Error ("Package Base Test: Not a valid package") unless ( $found );
1321
 
1322
    #
1323
    #   Extra bits found
1324
    #   Its not the root of a package
1325
    #
1326
    if ( @$ref_files )
1327
    {
1328
        Warning ("Package Base Test: Files exists",
1329
               "Unexpected files found:", @$ref_files );
1330
    }
1331
 
1332
    #
1333
    #   Need a truck directory
1334
    #   If we don't have a truck we don't have a package
1335
    #
1336
    my $trunk_found = grep ( /trunk\//, @$ref_svn );
1337
    Error ("Invalid Package Base. Does not contain a 'trunk' directory")
1338
        unless ( $trunk_found );
1339
 
1340
    return $url;
1341
}
1342
 
1343
 
1344
#-------------------------------------------------------------------------------
1345
# Function        : SvnIsaSimpleLabel
1346
#
1347
# Description     : Check a label
1348
#                       Must not contain a PEG
1349
#                       Must not contain invalid characters (@ or /)
1350
#                       Must not contain a :: sequence (will confuse other tools)
1351
#                       Handle special label of TIMESTAMP
1352
#
1353
# Inputs          : $label          - to test
1354
#
1355
# Returns         : Will not return on error
1356
#                   Returns label on success
1357
#
1358
sub SvnIsaSimpleLabel
1359
{
1360
    my ($label) = @_;
1361
    Debug ("SvnIsaSimpleLabel, $label");
1362
 
1363
    Error ("No label provided") unless ( $label );
1364
    Error ("Invalid label. Peg (\@nnn) is not allowed: \"$label\"" ) if ( $label =~ m~@\d+$~ );
1365
    Error ("Invalid label. Package Path is not allowed: \"$label\"" ) if ( $label =~ m~/~ );
383 dpurdie 1366
    Error ("Invalid label. Invalid Start Character: \"$label\"" ) unless ( $label =~ m~^[0-9a-zA-Z]~ );
1367
    Error ("Invalid label. Invalid End Character: \"$label\"" ) unless ( $label =~ m~[0-9a-zA-Z]$~ );
267 dpurdie 1368
    Error ("Invalid label. Invalid Characters: \"$label\"" ) unless ( $label =~ m~^[-.:0-9a-zA-Z_]+$~ );
1369
    Error ("Invalid label. Double :: not allowed: \"$label\"" ) if ( $label =~m~::~ );
1370
 
1371
    #
1372
    #   Allow for a label of TIMESTAMP and have it expand
383 dpurdie 1373
    #   Create a label based on users name and a date-time that can be sorted
267 dpurdie 1374
    #
1375
    if ( $label eq 'TIMESTAMP' )
1376
    {
341 dpurdie 1377
        ::EnvImport ('USER' );
1378
        my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
1379
        $label = sprintf("%s_%4.4u.%2.2u.%2.2u.%2.2u%2.2u%2.2u",
1380
            $::USER, $year+1900, $mon+1, $mday, $hour, $min, $sec );
267 dpurdie 1381
    }
1382
    return $label;
1383
}
1384
 
1385
#-------------------------------------------------------------------------------
1386
# Function        : NewSession
1387
#
1388
# Description     : Create a new empty SvnSession Class
1389
#
1390
# Inputs          : None
1391
#
1392
# Returns         : Class
1393
#
1394
sub NewSession
1395
{
1396
    Debug ("NewSession");
1397
    my $self  = SvnSession();
1398
 
1399
    #
1400
    #   Document Class Variables
1401
    #
1402
    $self->{URL} = '';                  # Repo URL prefix
1403
    $self->{WS}  = '';                  # Users WorkSpace
1404
    $self->{PROTOCOL} = '';             # Named Access Protocol
1405
    $self->{PKGROOT} = '';              # Package root
1406
 
1407
    #
1408
    #   Create a class
1409
    #   Bless my self
1410
    #
1411
    bless ($self, __PACKAGE__);
1412
    return $self;
1413
}
1414
 
1415
#-------------------------------------------------------------------------------
1416
# Function        : NewSessionByWS
1417
#
1418
# Description     : Establish a new SVN Session based on a Workspace
1419
#                   Given a workspace path determine the SvnServer and other
1420
#                   relevent information.
1421
#
1422
#                   Requires some rules
1423
#                       * The package is rooted within a 'ttb'
1424
#
1425
# Inputs          : $path                   - Path to WorkSpace
1426
#                   $test                   - No Error on no WS
361 dpurdie 1427
#                   $slack                  - Less stringent
267 dpurdie 1428
#
1429
# Returns         : Ref to Session Information
1430
#
1431
sub NewSessionByWS
1432
{
361 dpurdie 1433
    my ($path, $test, $slack) = @_;
267 dpurdie 1434
    Debug ("NewSessionByWS", @_);
1435
 
1436
    #
1437
    #   Create a basic Session
1438
    #   Populate it with information that is known
1439
    #
1440
    my $self = NewSession();
1441
    $self->{WS} = $path;
1442
 
1443
    #
1444
    #   Validate the path provided
1328 dpurdie 1445
    #   In the process populate $self->{InfoWs} with info about the workspace.
267 dpurdie 1446
    #
1447
    if ($self->SvnValidateWs ( undef, 1) )
1448
    {
1449
        return $self if ( $test );
1450
        Error ( @{$self->{ERROR_LIST}} );
1451
    }
1452
 
1453
    #
1454
    #   Extract useful info
1455
    #       URL: svn://auperaws996vm21/test/MixedView/trunk
1456
    #       Repository Root: svn://auperaws996vm21/test
1457
    #
1328 dpurdie 1458
    my $url = $self->{'InfoWs'}{'URL'};
1459
    my $reporoot = $self->{'InfoWs'}{'Repository Root'};
1460
    my $repoVersion = $self->{'InfoWs'}{'Revision'};
1347 dpurdie 1461
    my $devBranch;
267 dpurdie 1462
 
1463
    Error ("JatsSvn Internal error. Can't parse info")
1464
        unless ( $url && $reporoot );
1465
 
1466
    #
1467
    #   Need the length of the path to the repository
1468
    #   but not the name of the repostory itself.
1469
    #
1470
    #   Remove that from the head of the URL to give a
1471
    #   path within the repository, that includes the repos name
1472
    #
1473
    $reporoot = (fileparse( $reporoot ))[1];
1474
    $url = substr ($url, length ($reporoot));
1475
    $self->{WSURL} = $url;
1476
    chop $reporoot;
1477
 
1478
    Verbose2 ("SvnLocatePackageRoot: $reporoot, $url" );
1479
 
1480
    #
1481
    #   Remove anything after a ttb ( truck, tags, branch ) element
1482
    #   This will be the root of the package within the repo
1483
    #
1484
    if (  $url =~ m~(.+)/((tags|branches|trunk)(/|$).*)~ )
1485
    {
1486
        $url = $1;
1487
        $self->{WSTYPE} = $3;
1347 dpurdie 1488
        if ( $3 eq 'trunk' ) {
1489
            $devBranch = $3;
1490
        } elsif ( $3 eq 'branches' ) {
2025 dpurdie 1491
            my @bits = split('/', $2);
1492
            $devBranch = join('/', @bits[0 .. 1]);
1347 dpurdie 1493
        }
267 dpurdie 1494
    }
1495
    else
1496
    {
361 dpurdie 1497
        #
1498
        #   If we are being slack (ie deleting the workspace)
1499
        #   Then generate a warning, not an error
1500
        #
1501
        my $fnc = $slack ? \&Warning : \&Error;
1502
        $fnc->("SvnLocatePackageRoot. Non standard repository format",
1503
               "Url must contain 'tags' or 'branches' or 'trunk'",
267 dpurdie 1504
               "Url: $url");
361 dpurdie 1505
        $self->{WSTYPE} = 'trunk';
267 dpurdie 1506
    }
1507
 
1508
    #
1509
    #   Insert known information
1510
    #
1511
    $self->{URL} = $reporoot . '/';
1512
    $self->{PKGROOT} = $url;
369 dpurdie 1513
    $self->{WSREVNO} = $repoVersion;
1347 dpurdie 1514
    $self->{DEVBRANCH} = $devBranch;
267 dpurdie 1515
 
1516
    #
1517
    #   Create useful information
1518
    #
1519
    SplitPackageUrl($self);
1520
    return $self;
1521
}
1522
 
1523
#-------------------------------------------------------------------------------
1524
# Function        : NewSessionByUrl
1525
#
1526
# Description     : Establish a new SVN Session based on a user URL
1527
#
1528
# Inputs          : $uurl                   - Users URL
361 dpurdie 1529
#                   $ttb_test               - Test and warn for TTB structure
267 dpurdie 1530
#                   $session                - Optional: Existing session
1531
#
1532
# Returns         : Ref to Session Information
1533
#
1534
sub NewSessionByUrl
1535
{
361 dpurdie 1536
    my ($uurl, $ttb_test, $self ) = @_;
267 dpurdie 1537
    Debug ("NewSessionByUrl", @_);
1538
    Error ("No Repostory Path specified") unless ( $uurl );
1539
 
1540
    #
1541
    #   Create a basic Session
1542
    #   Populate it with information that is known
1543
    #
1544
    $self = NewSession() unless ( $self );
1545
 
1546
    #
369 dpurdie 1547
    #   Examine the URL and convert a Repository Path into a URL
353 dpurdie 1548
    #   as provided by configuration information within the environment
267 dpurdie 1549
    #
361 dpurdie 1550
    ($self->{URL}, $self->{PKGROOT} ) = SvnPath2Url ($uurl);
1551
 
1552
    #
1553
    #   Create useful information
1554
    #
1555
    SplitPackageUrl($self);
1556
 
1557
    #
1558
    #   Warn of non-standard URLs
1559
    #   These may create problems latter
1560
    #
1561
    if ( $ttb_test )
1562
    {
1563
        Warning("Non standard repository format",
1564
                 "Url should contain 'tags' or 'branches' or 'trunk'",
1565
                 "Url: $self->{PKGROOT}") unless $self->{TAGTYPE};
1566
    }
1567
 
1568
    return $self;
1569
}
1570
 
1571
#-------------------------------------------------------------------------------
1572
# Function        : SvnPath2Url
1573
#
1574
# Description     : Convert a repository path to a Full Url
1575
#                   Also handles Full Url
1576
#
1577
# Inputs          : $rpath             - Repository Path
1578
#                                        May be a full URL
1579
#
1580
# Returns         : List context
1581
#                   Two items that can be joined
1582
#                   URL                - URL
1583
#                   PKG_ROOT           - Package Root
1584
#
1585
#                   Scalar context: Joined URL and Package Root
1586
#                                   Fully formed URL
1587
#
1588
sub SvnPath2Url
1589
{
1590
    my ($rpath) = @_;
1591
    my $processed = 0;
1592
    my $url;
1593
    my $pkgroot;
1594
 
1595
    #
1596
    #   Examine the argument and convert a Repository Path into a URL
1597
    #   as provided by configuration information within the environment
1598
    #
1599
    $rpath =~ m~(.+?)/(.*)~;
1600
    my $fe = $1 || $rpath;
353 dpurdie 1601
    my $rest = $2 || '';
1602
    if ( $SVN_URLS{$fe} )
267 dpurdie 1603
    {
361 dpurdie 1604
        $url = $SVN_URLS{$fe};
1605
        $pkgroot = $rest;
353 dpurdie 1606
        $processed = 1;
341 dpurdie 1607
    }
1608
 
353 dpurdie 1609
    if ( ! $processed )
341 dpurdie 1610
    {
267 dpurdie 1611
        #
353 dpurdie 1612
        #   Examine the URL and determine if we have a FULL Url or
1613
        #   a path within the 'default' server
1614
        #
1615
        foreach my $key ( @SVN_URLS_LIST )
1616
        {
361 dpurdie 1617
            if ( $rpath =~ m~^$SVN_URLS{$key}(.*)~ )
353 dpurdie 1618
            {
361 dpurdie 1619
                $url = $SVN_URLS{$key};
1620
                $pkgroot = $1;
353 dpurdie 1621
                $processed = 1;
1622
                last;
1623
            }
1624
        }
1625
    }
267 dpurdie 1626
 
353 dpurdie 1627
    #
1628
    #   Last attempt
1629
    #   Treat as a raw URL - some operations won't be allowed
1630
    #
1631
    if ( ! $processed )
267 dpurdie 1632
    {
361 dpurdie 1633
        if ( $rpath =~ m~^((file|http|https|svn):///?([^/]+)/)(.+)~ )
353 dpurdie 1634
        {
1635
            #       http://server/
1636
            #       https://server/
1637
            #       svn://server/
1638
            #       file://This/Isa/Bad/Guess
1639
            #
361 dpurdie 1640
            $url = $1;
1641
            $pkgroot = $4;
353 dpurdie 1642
        }
369 dpurdie 1643
        elsif ($SVN_URLS{''} )
353 dpurdie 1644
        {
1270 dpurdie 1645
            if ( exists $ENV{'GBE_ABT'} && $ENV{'GBE_ABT'})
1646
            {
1647
                Error ("Attempt to use default repository within automated build", "Path: " . $rpath);
1648
            }
361 dpurdie 1649
            $url = $SVN_URLS{''};
1650
            $pkgroot = $rpath;
353 dpurdie 1651
        }
1652
        else
1653
        {
1654
            #
1655
            #   User default (site configured) Repo Root
1656
            #
1657
            Error ("No site repository configured for : $fe",
1658
                   "Configure GBE_SVN_URL_" . uc($fe) );
1659
        }
267 dpurdie 1660
    }
1661
 
1662
    #
361 dpurdie 1663
    #   May want two elements, may want one
267 dpurdie 1664
    #
361 dpurdie 1665
    return $url, $pkgroot if ( wantarray );
1666
    return $url . $pkgroot;
267 dpurdie 1667
}
1668
 
369 dpurdie 1669
#-------------------------------------------------------------------------------
1670
# Function        : SvnPaths
1671
#
1672
# Description     : Extract SVN path conversion information
1673
#
1674
# Inputs          : Nothing
1675
#
1676
# Returns         : Two refs
1677
#                   Hash of SVN URLS
1678
#                   Array for search order
1679
#
1680
sub SvnPaths
1681
{
1682
    return \%SVN_URLS, \@SVN_URLS_LIST;
1683
}
361 dpurdie 1684
 
267 dpurdie 1685
#-------------------------------------------------------------------------------
1686
# Function        : SplitPackageUrl
1687
#
1347 dpurdie 1688
# Description     : Split the package URL into a few useful bits
267 dpurdie 1689
#
1690
# Inputs          : $self           - Instance data
1691
#
1692
# Returns         : Nothing
1693
#
1694
sub SplitPackageUrl
1695
{
1696
    my ($self) = @_;
353 dpurdie 1697
    Debug ("SplitPackageUrl", $self->{URL}, $self->{PKGROOT});
267 dpurdie 1698
 
1699
    #
1700
    #   Remove any protocol that may be present
1701
    #       http://server/
341 dpurdie 1702
    #       https://server/
267 dpurdie 1703
    #       svn://server/
1704
    #       file://This/Isa/Bad/Guess
1705
    #
341 dpurdie 1706
    if ( $self->{URL} =~ m~^(file|http|https|svn)://([^/]+)~ )
267 dpurdie 1707
    {
1708
        $self->{PROTOCOL} = $1;
1709
        $self->{SERVER} = $2;
1710
    }
1711
 
1712
    if ( $self->{PKGROOT} =~ m~(.*)(@\d+)$~ )
1713
    {
1714
        $self->{PEG} = $2;
1715
    }
1716
 
1717
    #
1718
    #   Determine TTB type
1719
    #   Need to handle
1720
    #       .../trunk
1721
    #       .../trunk@nnnnn
1722
    #       .../tags/version@nnnnn
1723
    #       .../branches/version@nnnnn
1724
    #
1725
    #
1726
    if (  $self->{PKGROOT} =~ m~/?(.*)/(tags|branches|trunk)(/|$|@)(.*)$~ )
1727
    {
1728
        $self->{PATH}         = $1;
1729
        $self->{TAGTYPE}      = $2;
1730
        $self->{VERSION}      = $4;
1731
    }
1732
    else
1733
    {
1734
        $self->{PATH} = $self->{PKGROOT};
1735
    }
1736
 
1737
    DebugDumpData ('SplitPackageUrl', $self ) if ( IsDebug(2) );
1738
}
1739
 
1740
#-------------------------------------------------------------------------------
1741
# Function        : Full
1742
#                   FullWs
1743
#                   Repo
1744
#                   Peg
1745
#                   Type
1746
#                   WsType
1747
#                   Path
1748
#                   Version
1749
#                   RmRef
385 dpurdie 1750
#                   RmPath
267 dpurdie 1751
#
1752
# Description     : Accessor functions
1753
#
1754
# Inputs          : $self       - Instance data
1755
#                                 self (is $_[0])
1756
#
1757
# Returns         : Data Item
1758
#
369 dpurdie 1759
sub Full        { return $_[0]->{URL} . $_[0]->{PKGROOT} ; }
1760
sub FullWs      { return $_[0]->{URL} . $_[0]->{WSURL} ; }
1761
sub FullWsRev   { return $_[0]->{URL} . $_[0]->{WSURL} . '@' . $_[0]->{WSREVNO} ; }
1343 dpurdie 1762
sub FullPath    { return $_[0]->{URL} . $_[0]->{PATH} ; }
369 dpurdie 1763
sub Peg         { return $_[0]->{PEG} ; }
1347 dpurdie 1764
sub DevBranch   { return $_[0]->{DEVBRANCH} || '' ; }
369 dpurdie 1765
sub Type        { return $_[0]->{TAGTYPE} || '' ; }
1766
sub WsType      { return $_[0]->{WSTYPE}  || '' ; }
1767
sub Path        { return $_[0]->{PATH} ; }
1768
sub Version     { return $_[0]->{VERSION} ; }
1769
sub RmRef       { return $_[0]->{RMREF} ; }
385 dpurdie 1770
sub RmPath      { my $path = $_[0]->{RMREF}; $path =~ s~@.*?$~~ ;return  $path; }
1347 dpurdie 1771
sub SvnTag      { return $_[0]->{SVNTAG} || '' ; }
267 dpurdie 1772
 
1773
#-------------------------------------------------------------------------------
1774
# Function        : Print
1775
#
1776
# Description     : Debug display the URL
1777
#
1778
# Inputs          : $self           - Instance data
1779
#                   $header
1780
#                   $indent
1781
#
1782
# Returns         : Nothing
1783
#
1784
sub Print
1785
{
1786
    my ($self, $header, $indent) = @_;
1787
    print "$header\n" if $header;
1788
    $indent = 4 unless ( defined $indent );
1789
    $indent = ' ' x $indent;
1790
 
1791
 
1347 dpurdie 1792
    print $indent . "PROTOCOL :" . $self->{PROTOCOL} . "\n";
1793
    print $indent . "SERVER   :" . $self->{SERVER} . "\n";
1794
    print $indent . "URL      :" . $self->{URL} . "\n";
1795
    print $indent . "PKGROOT  :" . $self->{PKGROOT} . "\n";
1796
    print $indent . "PATH     :" . $self->{PATH} . "\n";
1797
    print $indent . "TAGTYPE  :" . ($self->{TAGTYPE} || '') . "\n";
1798
    print $indent . "VERSION  :" . ($self->{VERSION} || '') . "\n";
1799
    print $indent . "PEG      :" . ($self->{PEG} || '') . "\n";
1800
    print $indent . "DEVBRANCH:" . ($self->{DEVBRANCH} || '') . "\n";
1801
    print $indent . "SVNTAG   :" . ($self->{SVNTAG} || '') . "\n";
1343 dpurdie 1802
#    print $indent . "FULL    :" . $self->Full . "\n";
1803
 
1804
    print $indent . "Full         :" . $self->Full . "\n";
2048 dpurdie 1805
    print $indent . "FullWs       :" . $self->FullWs    . "\n";
1343 dpurdie 1806
#    print $indent . "FullWsRev    :" . $self->FullWsRev . "\n";
1807
    print $indent . "FullPath     :" . $self->FullPath  . "\n";
1808
    print $indent . "Peg          :" . $self->Peg       . "\n";
1347 dpurdie 1809
    print $indent . "DevBranch    :" . $self->DevBranch . "\n";
1343 dpurdie 1810
    print $indent . "Type         :" . $self->Type      . "\n";
1811
    print $indent . "WsType       :" . $self->WsType    . "\n";
1812
    print $indent . "Path         :" . $self->Path      . "\n";
1813
    print $indent . "Version      :" . $self->Version   . "\n";
1814
    print $indent . "RmRef        :" . ($self->RmRef || '') . "\n";
1815
#    print $indent . "RmPath       :" . ($self->RmPath|| '') . "\n";
267 dpurdie 1816
}
1817
 
1818
#-------------------------------------------------------------------------------
1819
# Function        : BranchName
1820
#
1821
# Description     : Create a full URL to a branch or tag based on the
1822
#                   current entry
1823
#
1824
#                   URL must have a TTB format
1825
#
1826
# Inputs          : $self           - Instance data
1827
#                   $branch         - Name of the branch
1828
#                   $type           - Optional branch type
1829
#
1830
# Returns         : Full URL name to the new branch
1831
#
1832
sub BranchName
1833
{
1834
    my ($self, $branch, $type ) = @_;
1835
    Debug ( "BranchName", $branch );
1836
 
1837
    $type = 'branches' unless ( $type );
1838
    my $root = $self->{PKGROOT};
1839
 
1840
    $root =~ s~/(tags|branches|trunk)(/|$|@).*~~;
1841
 
1842
    return $self->{URL} . $root . '/' . $type . '/' . $branch;
1843
}
1844
 
379 dpurdie 1845
#-------------------------------------------------------------------------------
1846
# Function        : setRepoProperty
1847
#
1848
# Description     : Sets a Repository property
1849
#                   This may well fail unless the Repo is setup to allow such
1850
#                   chnages and the user is allowed to make such changes
1851
#
1852
# Inputs          : $name
1853
#                   $value
1341 dpurdie 1854
#                   $allowError     - Support for bad repositories
379 dpurdie 1855
#
1341 dpurdie 1856
# Returns         : 0 - Change made
1857
#                   Will not return on error
379 dpurdie 1858
#
1859
sub setRepoProperty
1860
{
1341 dpurdie 1861
    my ($self, $name, $value, $allowError ) = @_;
1862
    my $retval = 0;
1863
 
379 dpurdie 1864
    Debug ( "setRepoProperty", $name, $value );
1865
    #
1866
    #   Ensure that the Repo version is known
1867
    #   This should be set by a previous operation
1868
    #
1869
    unless ( defined $self->{REVNO} )
1870
    {
1871
        Error ("setRepoProperty. Release Revision Number not known");
1872
    }
1873
 
1874
    #
1875
    #   Execute the command
1876
    #
1877
    if ( $self->SvnCmd ( 'propset' , $name, '--revprop', '-r',  $self->{REVNO}, $value, $self->Full,
1878
                            {
1879
                                'credentials' => 1,
1880
                                'nosavedata' => 1,
1881
                            }
1882
                       ) )
1883
    {
1884
        #
1885
        #   Property NOT set
1886
        #
1341 dpurdie 1887
        if ( $allowError )
1888
        {
1889
            Warning ("setRepoProperty: $name - FAILED");
1890
            $retval = 1;
1891
        }
1892
        else
1893
        {
1894
            Error ("setRepoProperty: $name - FAILED");
1895
        }
379 dpurdie 1896
    }
1341 dpurdie 1897
 
1898
    return $retval;
379 dpurdie 1899
}
1900
 
1341 dpurdie 1901
#-------------------------------------------------------------------------------
1902
# Function        : backTrackSvnLabel
1903
#
1904
# Description     : Examine a Svn Tag and backtrack until we find the branch
1905
#                   that was used to create the label
1906
#
1907
# Inputs          : $self                   - Instance Data
1908
#                   $src_label              - Label to process
1909
#                                             Label within the current instance
1349 dpurdie 1910
#                   A hash of named arguments
1911
#                       data                - Scalar ref. Hash of good stuff returned
1912
#                       printdata           - Print RAW svn data
1913
#                       onlysimple          - Do not do exhaustive scan
1356 dpurdie 1914
#                       savedevbranch       - Save Dev Branch in session
1341 dpurdie 1915
#
1916
# Returns         : Branch from which the label was taken
1917
#                   or the label prefixed with 'tags'.
1918
#
1919
sub backTrackSvnLabel
1920
{
1349 dpurdie 1921
    my $self = shift;
1922
    my $src_label = shift;
1923
    my %opt = @_;
1924
    my $branch;
1341 dpurdie 1925
 
1349 dpurdie 1926
    Debug ("backTrackSvnLabel");
1927
    Error ("backTrackSvnLabel: Odd number of args") unless ((@_ % 2) == 0);
1928
 
1341 dpurdie 1929
    #
1349 dpurdie 1930
    #   May need to read and process data twice
1931
    #   First   - stop on copy. May it fast
1932
    #   Second  - all the log.
1341 dpurdie 1933
 
1934
    #
1935
    #   extract data
1936
    #
1349 dpurdie 1937
    foreach my $mode ( '--stop-on-copy', '' )
1938
    {
1939
        #   Init stored data
1940
        #   Used to communicate with callback function(s)
1941
        #
1942
        Information ("backTrackSvnLabel: Performing exhaustive search") unless $mode;
1943
        $self->{btData} = ();
1944
        $self->{btData}{results}{base} = $self->FullPath();
1945
        $self->{btData}{results}{label} = $src_label;
1946
        $self->{btData}{results}{changeSets} = 0;
1356 dpurdie 1947
        $self->{btData}{results}{distance} = 0;
1341 dpurdie 1948
 
1349 dpurdie 1949
        #
1950
        #   Linux does not handle empty arguments in the same
1951
        #   manner as windows. Solution: pass an array
1952
        #
1953
        my @mode;
1954
        push @mode, $mode if ( $mode);
1380 dpurdie 1955
        my $spath = $self->FullPath() . '/' . $src_label;
1341 dpurdie 1956
 
1380 dpurdie 1957
        Verbose2("backTrackSvnLabel. Log from $spath");
1349 dpurdie 1958
        $self->SvnCmd ( 'log', '-v', '--xml', '-q'
1959
                        , @mode
1380 dpurdie 1960
                        , $spath
1349 dpurdie 1961
                        , { 'credentials' => 1,
1962
                            'process' => \&ProcessBackTrack,
1963
                            'printdata' => $opt{printdata},
1964
                            'nosavedata' => 1,
1965
                             }
1966
                            );
1341 dpurdie 1967
 
1349 dpurdie 1968
        last if ( $self->{btData}{good} );
1969
        last if ( $opt{onlysimple} );
1341 dpurdie 1970
    }
1343 dpurdie 1971
 
1341 dpurdie 1972
    #
1349 dpurdie 1973
    #   Did not backtrack to a branch (or trunk)
1974
    #   Return the users label
1341 dpurdie 1975
    #
1349 dpurdie 1976
    unless ( $self->{btData}{good} )
1341 dpurdie 1977
    {
1348 dpurdie 1978
        $branch = $src_label;
1341 dpurdie 1979
    }
1349 dpurdie 1980
    else
1981
    {
1982
        $branch = $self->{btData}{results}{devBranch};
1356 dpurdie 1983
        if ( $opt{savedevbranch} )
1984
        {
1985
            $self->{btData}{results}{devBranch} =~ m~^(.*?)(@|$)~;
1986
            $self->{DEVBRANCH} = $1;
1987
        }
1988
 
1349 dpurdie 1989
    }
1341 dpurdie 1990
 
1344 dpurdie 1991
    #
1349 dpurdie 1992
    #   Return data to the user
1344 dpurdie 1993
    #
1349 dpurdie 1994
    if ( my $refData = $opt{data} )
1995
    {
1996
        Error ('Internal: backTrackSvnLabel. Arg to "data" must be ref to a scalar')
1997
            unless ( ref($refData) eq 'SCALAR' );
1998
        $$refData = $self->{btData}{results};
1999
    }
2000
 
2001
    #
2002
    #   Clean up the data
2003
    #
1344 dpurdie 2004
    delete $self->{btData};
1341 dpurdie 2005
    return $branch;
2006
}
2007
 
2008
#-------------------------------------------------------------------------------
2009
# Function        : ProcessBackTrack
2010
#
2011
# Description     :
2012
#                   Parse
2013
#                       <logentry
2014
#                          revision="24272">
2015
#                       <author>bivey</author>
2016
#                       <date>2005-07-25T15:45:35.000000Z</date>
2017
#                       <paths>
2018
#                       <path
2019
#                          prop-mods="false"
2020
#                          text-mods="false"
2021
#                          kind="dir"
2022
#                          copyfrom-path="/enqdef/branches/Stockholm"
2023
#                          copyfrom-rev="24271"
2024
#                          action="A">/enqdef/tags/enqdef_24.0.1.sls</path>
2025
#                       </paths>
2026
#                       <msg>COTS/enqdef: Tagged by Jats Svn Import</msg>
2027
#                       </logentry>
2028
#
2029
#
2030
#                   Uses:   $self->{btData}     - Scratch Data
2031
#
2032
# Inputs          : $self           - Class Data
2033
#                   $line           - Input data to parse
2034
#
2035
# Returns         : 0 - Do not terminate input command
2036
#
2037
sub  ProcessBackTrack
2038
{
2039
    my ($self, $line ) = @_;
2040
    Message ( $line ) if $self->{PRINTDATA};
2041
 
1349 dpurdie 2042
    $line =~ s~\s+$~~;
2043
    next unless ( $line );
2044
#    Debug0('', $line);
2045
 
2046
    my $workSpace =  \%{$self->{btData}};
2047
    if ( $line =~ m~<logentry$~ ) {
2048
        $workSpace->{mode} = 'l';
2049
        $workSpace->{rev} = 0;
2050
        $workSpace->{changesSeen} = 0;
2051
 
2052
    } elsif ( $line =~ m~</logentry>$~ ) {
2053
        $workSpace->{mode} = '';
2054
 
2055
        #
2056
        #   End of a <logenty>
2057
        #   See if we have a result - a dev branch not copied from a tag
2058
        #
2059
        if ( exists $workSpace->{devBranch} )
2060
        {
2061
            $workSpace->{results}{distance}++;
2062
            $workSpace->{devBranch} =~ m~/((tags|branches|trunk)(/|\@).*)~;
2063
            my $devBranch = $1;
2064
 
2065
            push @{$workSpace->{results}{paths}}, $devBranch;
2066
            unless ( $devBranch =~ m ~^tags~ )
2067
            {
2068
                $workSpace->{results}{devBranch} = $devBranch;
2069
                $workSpace->{results}{isaBranch} = 1;
2070
                $workSpace->{good} = 1;
2071
                return 1;
2072
            }
2073
        }
2074
 
2075
    } elsif ( $line =~ m~<path$~ ) {
2076
        $workSpace->{mode} = 'p';
2077
        Error ('Path without Rev') unless ( $workSpace->{rev} );
2078
 
2079
    } elsif ( $line =~ m~</paths>$~ ) {
2080
        $workSpace->{mode} = '';
1344 dpurdie 2081
    }
1349 dpurdie 2082
    return 0 unless ( $workSpace->{mode} );
1344 dpurdie 2083
 
1349 dpurdie 2084
    if ( $workSpace->{mode} eq 'l' )
2085
    {
2086
        #
2087
        #   Processing logentry data
2088
        #       Only need the rev
2089
        #
2090
        $workSpace->{rev} = $1
2091
            if ( $line =~ m~revision=\"(\d+)\"~ );
2092
 
2093
    } elsif ( $workSpace->{mode} eq 'p' ) {
2094
        #
2095
        #   Processing Paths
2096
        #       Entries appear to be in a random order
2097
        #       Not always the same order
2098
        #
2099
        my $end = 0;
2100
        if ( $line =~ s~\s*(.+?)="(.+)">(.*)</path>$~~ )
2101
        {
2102
            #
2103
            #   Last entry has two items
2104
            #       Attribute
2105
            #       Data Item
2106
            #
2107
            $end = 1;
2108
            $workSpace->{path}{$1} = $2;
2109
            $workSpace->{path}{DATA} = $3;
2110
        }
2111
        elsif ($line =~ m~\s*(.*?)="(.*)"~ )
2112
        {
2113
            $workSpace->{path}{$1} = $2;
2114
        }
2115
#        else
2116
#        {
2117
#            Warning ("Cannot decode XML log: $line");
2118
#        }
2119
 
2120
        if ( $end )
2121
        {
1356 dpurdie 2122
            #
2123
            #   If the Repo is created by a pre 1.6 SVN, then kind will be
2124
            #   empty. Have a guess.
2125
            #
2126
            if ( $workSpace->{path}{'kind'} eq '' )
1349 dpurdie 2127
            {
1356 dpurdie 2128
                if ( exists $workSpace->{path}{'copyfrom-path'} ) {
2129
                    $workSpace->{path}{'kind'} = 'dir';
2130
                } else {
2131
                    $workSpace->{path}{'kind'} = 'file';
2132
                }
2133
            }
2134
 
2135
            if ( $workSpace->{path}{'kind'} eq 'dir' &&  exists $workSpace->{path}{'copyfrom-path'} )
2136
            {
1349 dpurdie 2137
                my $srev = $workSpace->{path}{'copyfrom-rev'};
1356 dpurdie 2138
                my $from = $workSpace->{path}{'copyfrom-path'};
2139
                if ( $from =~ m~/trunk$~ || $from =~ m~/branches/[^/]+$~ )
2140
                {
2141
                    $workSpace->{devBranch} = $from . '@' . $srev;
2142
                }
1349 dpurdie 2143
            }
2144
 
2145
            elsif ( $workSpace->{path}{'kind'} eq 'file' )
2146
            {
2147
                #
2148
                #   Track files that have been changed between tag and branch
2149
                #   The log is presented as newest first
2150
                #   The files have a tag-name component.
2151
                #       Remove the tag name - so that we can compare files
2152
                #       Save the first instance of changed files
2153
                #           Others will be in older versions
2154
                #           and thus of no interest
2155
                #
2156
                #   Count the chnage sets that have changes
2157
                #   Having changes in multiple change sets indicates
2158
                #   development on a /tags/ - which is BAD
2159
                #
2160
                $workSpace->{path}{'DATA'} =~ m~(.+)/((tags|branches|trunk)(/|$).*)~;
2161
                my $file =  $2;
2162
                my $full = $file;
2163
                $file =~ s~^tags/(.+?)/~~;
2164
 
2165
                if ( ! exists $workSpace->{files}{$file}  )
2166
                {
2167
                    push @{$workSpace->{results}{files}}, $full . '@' . $workSpace->{rev};
2168
                }
2169
                $workSpace->{files}{$file}++;
2170
                $workSpace->{firstFile} = $file unless ( defined $workSpace->{firstFile} );
2171
 
2172
                unless ( $workSpace->{changesSeen} )
2173
                {
2174
                    unless( $workSpace->{firstFile} eq $file )
2175
                    {
2176
                        $workSpace->{results}{changeSets}++;
2177
                        $workSpace->{changesSeen}++;
2178
                    }
2179
                }
2180
 
2181
                if ( scalar keys %{$workSpace->{files}} > 1 )
2182
                {
2183
                    $workSpace->{results}{multipleChanges} = 1;
2184
                    Verbose ("backTrackSvnLabel: Changes in multiple versions");
2185
                }
2186
            }
2187
 
2188
            delete $workSpace->{path};
2189
        }
1341 dpurdie 2190
    }
2191
 
2192
    #
2193
    #   Return 0 to keep on going
2194
    return 0;
2195
}
2196
 
267 dpurdie 2197
#------------------------------------------------------------------------------
2198
1;