Subversion Repositories DevTools

Rev

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