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} = {};