cvsserver: Add version awareness to argsfromdir
Signed-off-by: Matthew Ogilvie <mmogilvi_git@miniinfo.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Matthew Ogilvie committed
Oct 13, 2012 at 23:42 UTC
d66e8f8cf33909fe43f58ca5d87de8624986425c
1 file changed
+198
-30
git-cvsserver.perl
+198
-30
@@ -2226,55 +2226,222 @@ sub argsplit
2226
}
2227
}
2228
2229
-# This method uses $state->{directory} to populate $state->{args} with a list of filenames
2230
-sub argsfromdir
2229
+# Used by argsfromdir
2230
+sub expandArg
2231
{
2232
- my $updater = shift;
2232
+ my ($updater,$outNameMap,$outDirMap,$path,$isDir) = @_;
2233
2234
- $state->{args} = [] if ( scalar(@{$state->{args}}) == 1 and $state->{args}[0] eq "." );
2234
+ my $fullPath = filecleanup($path);
2235
2236
- return if ( scalar ( @{$state->{args}} ) > 1 );
2236
+ # Is it a directory?
2237
+ if( defined($state->{dirMap}{$fullPath}) ||
2238
+ defined($state->{dirMap}{"$fullPath/"}) )
2239
+ {
2240
+ # It is a directory in the user's sandbox.
2241
+ $isDir=1;
2242
2238
- my @gethead = @{$updater->gethead};
2243
+ if(defined($state->{entries}{$fullPath}))
2244
+ {
2245
+ $log->fatal("Inconsistent file/dir type");
2246
+ die "Inconsistent file/dir type";
2247
+ }
2248
+ }
2249
+ elsif(defined($state->{entries}{$fullPath}))
2250
+ {
2251
+ # It is a file in the user's sandbox.
2252
+ $isDir=0;
2253
+ }
2254
+ my($revDirMap,$otherRevDirMap);
2255
+ if(!defined($isDir) || $isDir)
2256
+ {
2257
+ # Resolve version tree for sticky tag:
2258
+ # (for now we only want list of files for the version, not
2259
+ # particular versions of those files: assume it is a directory
2260
+ # for the moment; ignore Entry's stick tag)
2261
+
2262
+ # Order of precedence of sticky tags:
2263
+ # -A [head]
2264
+ # -r /tag/
2265
+ # [file entry sticky tag, but that is only relevant to files]
2266
+ # [the tag specified in dir req_Sticky]
2267
+ # [the tag specified in a parent dir req_Sticky]
2268
+ # [head]
2269
+ # Also, -r may appear twice (for diff).
2270
+ #
2271
+ # FUTURE: When/if -j (merges) are supported, we also
2272
+ # need to add relevant files from one or two
2273
+ # versions specified with -j.
2274
+
2275
+ if(exists($state->{opt}{A}))
2276
+ {
2277
+ $revDirMap=$updater->getRevisionDirMap();
2278
+ }
2279
+ elsif( defined($state->{opt}{r}) and
2280
+ ref $state->{opt}{r} eq "ARRAY" )
2281
+ {
2282
+ $revDirMap=$updater->getRevisionDirMap($state->{opt}{r}[0]);
2283
+ $otherRevDirMap=$updater->getRevisionDirMap($state->{opt}{r}[1]);
2284
+ }
2285
+ elsif(defined($state->{opt}{r}))
2286
+ {
2287
+ $revDirMap=$updater->getRevisionDirMap($state->{opt}{r});
2288
+ }
2289
+ else
2290
+ {
2291
+ my($sticky)=getDirStickyInfo($fullPath);
2292
+ $revDirMap=$updater->getRevisionDirMap($sticky->{tag});
2293
+ }
2294
2240
- # push added files
2241
- foreach my $file (keys %{$state->{entries}}) {
2242
- if ( exists $state->{entries}{$file}{revision} &&
2243
- $state->{entries}{$file}{revision} eq '0' )
2244
- {
2245
- push @gethead, { name => $file, filehash => 'added' };
2246
- }
2295
+ # Is it a directory?
2296
+ if( defined($revDirMap->{$fullPath}) ||
2297
+ defined($otherRevDirMap->{$fullPath}) )
2298
+ {
2299
+ $isDir=1;
2300
+ }
2301
}
2302
2249
- if ( scalar(@{$state->{args}}) == 1 )
2303
+ # What to do with it?
2304
+ if(!$isDir)
2305
{
2251
- my $arg = $state->{args}[0];
2252
- $arg .= $state->{prependdir} if ( defined ( $state->{prependdir} ) );
2253
-
2254
- $log->info("Only one arg specified, checking for directory expansion on '$arg'");
2306
+ $outNameMap->{$fullPath}=1;
2307
+ }
2308
+ else
2309
+ {
2310
+ $outDirMap->{$fullPath}=1;
2311
2256
- foreach my $file ( @gethead )
2312
+ if(defined($revDirMap->{$fullPath}))
2313
{
2258
- next if ( $file->{filehash} eq "deleted" and not defined ( $state->{entries}{$file->{name}} ) );
2259
- next unless ( $file->{name} =~ /^$arg\// or $file->{name} eq $arg );
2260
- push @{$state->{args}}, $file->{name};
2314
+ addDirMapFiles($updater,$outNameMap,$outDirMap,
2315
+ $revDirMap->{$fullPath});
2316
}
2317
+ if( defined($otherRevDirMap) &&
2318
+ defined($otherRevDirMap->{$fullPath}) )
2319
+ {
2320
+ addDirMapFiles($updater,$outNameMap,$outDirMap,
2321
+ $otherRevDirMap->{$fullPath});
2322
+ }
2323
+ }
2324
+}
2325
2263
- shift @{$state->{args}} if ( scalar(@{$state->{args}}) > 1 );
2264
- } else {
2265
- $log->info("Only one arg specified, populating file list automatically");
2326
+# Used by argsfromdir
2327
+# Add entries from dirMap to outNameMap. Also recurse into entries
2328
+# that are subdirectories.
2329
+sub addDirMapFiles
2330
+{
2331
+ my($updater,$outNameMap,$outDirMap,$dirMap)=@_;
2332
2267
- $state->{args} = [];
2333
+ my($fullName);
2334
+ foreach $fullName (keys(%$dirMap))
2335
+ {
2336
+ my $cleanName=$fullName;
2337
+ if(defined($state->{prependdir}))
2338
+ {
2339
+ if(!($cleanName=~s/^\Q$state->{prependdir}\E//))
2340
+ {
2341
+ $log->fatal("internal error stripping prependdir");
2342
+ die "internal error stripping prependdir";
2343
+ }
2344
+ }
2345
2269
- foreach my $file ( @gethead )
2346
+ if($dirMap->{$fullName} eq "F")
2347
+ {
2348
+ $outNameMap->{$cleanName}=1;
2349
+ }
2350
+ elsif($dirMap->{$fullName} eq "D")
2351
+ {
2352
+ if(!$state->{opt}{l})
2353
+ {
2354
+ expandArg($updater,$outNameMap,$outDirMap,$cleanName,1);
2355
+ }
2356
+ }
2357
+ else
2358
{
2271
- next if ( $file->{filehash} eq "deleted" and not defined ( $state->{entries}{$file->{name}} ) );
2272
- next unless ( $file->{name} =~ s/^$state->{prependdir}// );
2273
- push @{$state->{args}}, $file->{name};
2359
+ $log->fatal("internal error in addDirMapFiles");
2360
+ die "internal error in addDirMapFiles";
2361
}
2362
}
2363
}
2364
2365
+# This method replaces $state->{args} with a directory-expanded
2366
+# list of all relevant filenames (recursively unless -d), based
2367
+# on $state->{entries}, and the "current" list of files in
2368
+# each directory. "Current" files as determined by
2369
+# either the requested (-r/-A) or "req_Sticky" version of
2370
+# that directory.
2371
+# Both the input args and the new output args are relative
2372
+# to the cvs-client's CWD, although some of the internal
2373
+# computations are relative to the top of the project.
2374
+sub argsfromdir
2375
+{
2376
+ my $updater = shift;
2377
+
2378
+ # Notes about requirements for specific callers:
2379
+ # update # "standard" case (entries; a single -r/-A/default; -l)
2380
+ # # Special case: -d for create missing directories.
2381
+ # diff # 0 or 1 -r's: "standard" case.
2382
+ # # 2 -r's: We could ignore entries (just use the two -r's),
2383
+ # # but it doesn't really matter.
2384
+ # annotate # "standard" case
2385
+ # log # Punting: log -r has a more complex non-"standard"
2386
+ # # meaning, and we don't currently try to support log'ing
2387
+ # # branches at all (need a lot of work to
2388
+ # # support CVS-consistent branch relative version
2389
+ # # numbering).
2390
+#HERE: But we still want to expand directories. Maybe we should
2391
+# essentially force "-A".
2392
+ # status # "standard", except that -r/-A/default are not possible.
2393
+ # # Mostly only used to expand entries only)
2394
+ #
2395
+ # Don't use argsfromdir at all:
2396
+ # add # Explicit arguments required. Directory args imply add
2397
+ # # the directory itself, not the files in it.
2398
+ # co # Obtain list directly.
2399
+ # remove # HERE: TEST: MAYBE client does the recursion for us,
2400
+ # # since it only makes sense to remove stuff already in
2401
+ # # the sandobx?
2402
+ # ci # HERE: Similar to remove...
2403
+ # # Don't try to implement the confusing/weird
2404
+ # # ci -r bug er.."feature".
2405
+
2406
+ if(scalar(@{$state->{args}})==0)
2407
+ {
2408
+ $state->{args} = [ "." ];
2409
+ }
2410
+ my %allArgs;
2411
+ my %allDirs;
2412
+ for my $file (@{$state->{args}})
2413
+ {
2414
+ expandArg($updater,\%allArgs,\%allDirs,$file);
2415
+ }
2416
+
2417
+ # Include any entries from sandbox. Generally client won't
2418
+ # send entries that shouldn't be used.
2419
+ foreach my $file (keys %{$state->{entries}})
2420
+ {
2421
+ $allArgs{remove_prependdir($file)} = 1;
2422
+ }
2423
+
2424
+ $state->{dirArgs} = \%allDirs;
2425
+ $state->{args} = [
2426
+ sort {
2427
+ # Sort priority: by directory depth, then actual file name:
2428
+ my @piecesA=split('/',$a);
2429
+ my @piecesB=split('/',$b);
2430
+
2431
+ my $count=scalar(@piecesA);
2432
+ my $tmp=scalar(@piecesB);
2433
+ return $count<=>$tmp if($count!=$tmp);
2434
+
2435
+ for($tmp=0;$tmp<$count;$tmp++)
2436
+ {
2437
+ if($piecesA[$tmp] ne $piecesB[$tmp])
2438
+ {
2439
+ return $piecesA[$tmp] cmp $piecesB[$tmp]
2440
+ }
2441
+ }
2442
+ return 0;
2443
+ } keys(%allArgs) ];
2444
+}
2445
2446
## look up directory sticky tag, of either fullPath or a parent:
2447
sub getDirStickyInfo
@@ -2383,6 +2550,7 @@ sub getStickyTagOrDate
2550
sub statecleanup
2551
{
2552
$state->{files} = [];
2553
+ $state->{dirArgs} = {};
2554
$state->{args} = [];
2555
$state->{arguments} = [];
2556
$state->{entries} = {};