cvsexportcommit: be graceful when "cvs status" reorders the arguments
authorJohannes Schindelin <Johannes.Schindelin@gmx.de>
Mon, 18 Feb 2008 17:55:22 +0000 (17:55 +0000)
committerJunio C Hamano <gitster@pobox.com>
Wed, 20 Feb 2008 06:44:20 +0000 (22:44 -0800)
In my use cases, "cvs status" sometimes reordered the passed filenames,
which often led to a misdetection of a dirty state (when it was in
reality a clean state).

I finally tracked it down to two filenames having the same basename.

So no longer trust the order of the results blindly, but actually check
the file name.

Since "cvs status" only returns the basename (and the complete path on the
server which is useless for our purposes), run "cvs status" several times
with lists consisting of files with unique (chomped) basenames.

Be a bit clever about new files: these are reported as "no file <blabla>",
so in order to discern it from existing files, prepend "no file " to the
basename.

In other words, one call to "cvs status" will not ask for two files
"blabla" (which does not yet exist) and "no file blabla" (which exists).

This patch makes cvsexportcommit slightly slower, when the list of changed
files has non-unique basenames, but at least it is accurate now.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
git-cvsexportcommit.perl
t/t9200-git-cvsexportcommit.sh

index d2e50c34292bc0ccb6e914ed595da9fd8a7e141d..9d142d08a5f59f15857453596d43ab9d8f987a25 100755 (executable)
@@ -198,15 +198,39 @@ if (@canstatusfiles) {
       my @updated = xargs_safe_pipe_capture([@cvs, 'update'], @canstatusfiles);
       print @updated;
     }
-    my @cvsoutput;
-    @cvsoutput = xargs_safe_pipe_capture([@cvs, 'status'], @canstatusfiles);
-    my $matchcount = 0;
-    foreach my $l (@cvsoutput) {
-        chomp $l;
-        if ( $l =~ /^File:/ and  $l =~ /Status: (.*)$/ ) {
-            $cvsstat{$canstatusfiles[$matchcount]} = $1;
-            $matchcount++;
+    # "cvs status" reorders the parameters, notably when there are multiple
+    # arguments with the same basename.  So be precise here.
+
+    my %added = map { $_ => 1 } @afiles;
+    my %todo = map { $_ => 1 } @canstatusfiles;
+
+    while (%todo) {
+      my @canstatusfiles2 = ();
+      my %fullname = ();
+      foreach my $name (keys %todo) {
+       my $basename = basename($name);
+
+       $basename = "no file " . $basename if (exists($added{$basename}));
+       chomp($basename);
+
+       if (!exists($fullname{$basename})) {
+         $fullname{$basename} = $name;
+         push (@canstatusfiles2, $name);
+         delete($todo{$name});
         }
+      }
+      my @cvsoutput;
+      @cvsoutput = xargs_safe_pipe_capture([@cvs, 'status'], @canstatusfiles2);
+      foreach my $l (@cvsoutput) {
+        chomp $l;
+        if ($l =~ /^File:\s+(.*\S)\s+Status: (.*)$/) {
+         if (!exists($fullname{$1})) {
+           print STDERR "Huh? Status reported for unexpected file '$1'\n";
+         } else {
+           $cvsstat{$fullname{$1}} = $2;
+         }
+       }
+      }
     }
 }
 
index a15222ced4d75e70f49df08159cff9dd8d4167a4..5c2ee237397a07e5e478b0f715798fe87905e7c1 100755 (executable)
@@ -246,4 +246,39 @@ test_expect_success \
        ;;
 esac
 
+test_expect_success 'check files before directories' '
+
+       echo Notes > release-notes &&
+       git add release-notes &&
+       git commit -m "Add release notes" release-notes &&
+       id=$(git rev-parse HEAD) &&
+       git cvsexportcommit -w "$CVSWORK" -c $id &&
+
+       echo new > DS &&
+       echo new > E/DS &&
+       echo modified > release-notes &&
+       git add DS E/DS release-notes &&
+       git commit -m "Add two files with the same basename" &&
+       id=$(git rev-parse HEAD) &&
+       git cvsexportcommit -w "$CVSWORK" -c $id &&
+       check_entries "$CVSWORK/E" "DS/1.1/|newfile5.txt/1.1/" &&
+       check_entries "$CVSWORK" "DS/1.1/|release-notes/1.2/" &&
+       diff -u "$CVSWORK/DS" DS &&
+       diff -u "$CVSWORK/E/DS" E/DS &&
+       diff -u "$CVSWORK/release-notes" release-notes
+
+'
+
+test_expect_success 'commit a file with leading spaces in the name' '
+
+       echo space > " space" &&
+       git add " space" &&
+       git commit -m "Add a file with a leading space" &&
+       id=$(git rev-parse HEAD) &&
+       git cvsexportcommit -w "$CVSWORK" -c $id &&
+       check_entries "$CVSWORK" " space/1.1/|DS/1.1/|release-notes/1.2/" &&
+       diff -u "$CVSWORK/ space" " space"
+
+'
+
 test_done