@@ -2226,55 +2226,222 @@ sub argsplit
22262226 }
22272227}
22282228
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
22312231{
2232- my $updater = shift ;
2232+ my ( $updater , $outNameMap , $outDirMap , $path , $isDir ) = @_ ;
22332233
2234- $state -> { args } = [] if ( scalar (@{ $state -> { args }}) == 1 and $state -> { args }[0] eq " . " );
2234+ my $fullPath = filecleanup( $path );
22352235
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;
22372242
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+ }
22392294
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+ }
22472301 }
22482302
2249- if ( scalar (@{$state -> {args }}) == 1 )
2303+ # What to do with it?
2304+ if (!$isDir )
22502305 {
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;
22552311
2256- foreach my $file ( @gethead )
2312+ if ( defined ( $revDirMap -> { $fullPath }) )
22572313 {
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 });
22612316 }
2317+ if ( defined ($otherRevDirMap ) &&
2318+ defined ($otherRevDirMap -> {$fullPath }) )
2319+ {
2320+ addDirMapFiles($updater ,$outNameMap ,$outDirMap ,
2321+ $otherRevDirMap -> {$fullPath });
2322+ }
2323+ }
2324+ }
22622325
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 )=@_ ;
22662332
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+ }
22682345
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
22702358 {
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" ;
22742361 }
22752362 }
22762363}
22772364
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+ }
22782445
22792446# # look up directory sticky tag, of either fullPath or a parent:
22802447sub getDirStickyInfo
@@ -2383,6 +2550,7 @@ sub getStickyTagOrDate
23832550sub statecleanup
23842551{
23852552 $state -> {files } = [];
2553+ $state -> {dirArgs } = {};
23862554 $state -> {args } = [];
23872555 $state -> {arguments } = [];
23882556 $state -> {entries } = {};
0 commit comments