From 38bcd31a5879847d259a75fbcab9a7977ff63f1e Mon Sep 17 00:00:00 2001 From: Damien Diederen Date: Thu, 27 Mar 2008 23:17:26 +0100 Subject: [PATCH 1/7] cvsserver: Respond to the 'editors' and 'watchers' commands These commands list users editing and watching locked files. This trivial implementation always returns an empty response, since git-cvsserver does not implement file locking. Without this, TkCVS hangs at startup, waiting forever for a response. Signed-off-by: Damien Diederen Signed-off-by: Junio C Hamano --- git-cvsserver.perl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 7f632af20d..2fe0a8a3cb 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -73,8 +73,8 @@ 'status' => \&req_status, 'admin' => \&req_CATCHALL, 'history' => \&req_CATCHALL, - 'watchers' => \&req_CATCHALL, - 'editors' => \&req_CATCHALL, + 'watchers' => \&req_EMPTY, + 'editors' => \&req_EMPTY, 'annotate' => \&req_annotate, 'Global_option' => \&req_Globaloption, #'annotate' => \&req_CATCHALL, @@ -199,6 +199,11 @@ sub req_CATCHALL $log->warn("Unhandled command : req_$cmd : $data"); } +# This method invariably succeeds with an empty response. +sub req_EMPTY +{ + print "ok\n"; +} # Root pathname \n # Response expected: no. Tell the server which CVSROOT to use. Note that From 23b7180fdcd46556038241cd0388eca092fc55e1 Mon Sep 17 00:00:00 2001 From: Damien Diederen Date: Thu, 27 Mar 2008 23:17:42 +0100 Subject: [PATCH 2/7] cvsserver: Only print the file part of the filename in status header The "File:" header of CVS status output only includes the basename of the file, even when generating a recursive listing; do the same. Signed-off-by: Damien Diederen Signed-off-by: Junio C Hamano --- git-cvsserver.perl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 2fe0a8a3cb..444ec0db79 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -1471,8 +1471,10 @@ sub req_status $status ||= "Unknown"; + my ($filepart) = filenamesplit($filename); + print "M ===================================================================\n"; - print "M File: $filename\tStatus: $status\n"; + print "M File: $filepart\tStatus: $status\n"; if ( defined($state->{entries}{$filename}{revision}) ) { print "M Working revision:\t" . $state->{entries}{$filename}{revision} . "\n"; From 852b921c78ca33606600d7fd45e573a8435dbcb8 Mon Sep 17 00:00:00 2001 From: Damien Diederen Date: Thu, 27 Mar 2008 23:17:53 +0100 Subject: [PATCH 3/7] cvsserver: Do not include status output for subdirectories if -l is passed This effectively implements the -l switch by pruning the entries whose filenames contain a path separator. It was previously ignored. Without this, TkCVS includes strange "ghost" entries in its directory listings. Signed-off-by: Damien Diederen Signed-off-by: Junio C Hamano --- git-cvsserver.perl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 444ec0db79..89a4dac291 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -1428,6 +1428,8 @@ sub req_status { $filename = filecleanup($filename); + next if exists($state->{opt}{l}) && index($filename, '/', length($state->{prependdir})) >= 0; + my $meta = $updater->getmeta($filename); my $oldmeta = $meta; From dded801a7ba8bb91e73cfea2f37242a2aebbc5db Mon Sep 17 00:00:00 2001 From: Damien Diederen Date: Thu, 27 Mar 2008 23:18:02 +0100 Subject: [PATCH 4/7] cvsserver: Add a few tests for 'status' command Signed-off-by: Damien Diederen Signed-off-by: Junio C Hamano --- t/t9400-git-cvsserver-server.sh | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh index b91b151417..616832407f 100755 --- a/t/t9400-git-cvsserver-server.sh +++ b/t/t9400-git-cvsserver-server.sh @@ -420,4 +420,36 @@ test_expect_success 'cvs update (merge no-op)' \ GIT_CONFIG="$git_config" cvs -Q update && diff -q merge ../merge' +#------------ +# CVS STATUS +#------------ + +cd "$WORKDIR" +test_expect_success 'cvs status' ' + mkdir status.dir && + echo Line > status.dir/status.file && + echo Line > status.file && + git add status.dir status.file && + git commit -q -m "Status test" && + git push gitcvs.git >/dev/null && + cd cvswork && + GIT_CONFIG="$git_config" cvs update && + GIT_CONFIG="$git_config" cvs status | grep "^File: status.file" >../out && + test $(wc -l <../out) = 2 +' + +cd "$WORKDIR" +test_expect_success 'cvs status (nonrecursive)' ' + cd cvswork && + GIT_CONFIG="$git_config" cvs status -l | grep "^File: status.file" >../out && + test $(wc -l <../out) = 1 +' + +cd "$WORKDIR" +test_expect_success 'cvs status (no subdirs in header)' ' + cd cvswork && + GIT_CONFIG="$git_config" cvs status | grep ^File: >../out && + ! grep / <../out +' + test_done From e78f69a3f2e580e71a44b91fa99185bc6f92db29 Mon Sep 17 00:00:00 2001 From: Damien Diederen Date: Thu, 27 Mar 2008 23:18:12 +0100 Subject: [PATCH 5/7] cvsserver: Implement update -p (print to stdout) Cvs update -p -r is the documented way to retrieve a specific revision of a file (similar to git show :). Without this patch, the -p flag is ignored and status output is produced, causing clients to interpret it as the contents of the file. TkCVS uses update -p as a basis for implementing its various "View" and "Diff" commands. Signed-off-by: Damien Diederen Signed-off-by: Junio C Hamano --- git-cvsserver.perl | 47 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 89a4dac291..49c0ba2593 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -963,6 +963,17 @@ sub req_update $meta = $updater->getmeta($filename); } + # If -p was given, "print" the contents of the requested revision. + if ( exists ( $state->{opt}{p} ) ) { + if ( defined ( $meta->{revision} ) ) { + $log->info("Printing '$filename' revision " . $meta->{revision}); + + transmitfile($meta->{filehash}, { print => 1 }); + } + + next; + } + if ( ! defined $meta ) { $meta = { @@ -1096,9 +1107,9 @@ sub req_update my $file_local = $filepart . ".mine"; system("ln","-s",$state->{entries}{$filename}{modified_filename}, $file_local); my $file_old = $filepart . "." . $oldmeta->{revision}; - transmitfile($oldmeta->{filehash}, $file_old); + transmitfile($oldmeta->{filehash}, { targetfile => $file_old }); my $file_new = $filepart . "." . $meta->{revision}; - transmitfile($meta->{filehash}, $file_new); + transmitfile($meta->{filehash}, { targetfile => $file_new }); # we need to merge with the local changes ( M=successful merge, C=conflict merge ) $log->info("Merging $file_local, $file_old, $file_new"); @@ -1550,14 +1561,14 @@ sub req_diff print "E File $filename at revision 1.$revision1 doesn't exist\n"; next; } - transmitfile($meta1->{filehash}, $file1); + transmitfile($meta1->{filehash}, { targetfile => $file1 }); } # otherwise we just use the working copy revision else { ( undef, $file1 ) = tempfile( DIR => $TEMP_DIR, OPEN => 0 ); $meta1 = $updater->getmeta($filename, $wrev); - transmitfile($meta1->{filehash}, $file1); + transmitfile($meta1->{filehash}, { targetfile => $file1 }); } # if we have a second -r switch, use it too @@ -1572,7 +1583,7 @@ sub req_diff next; } - transmitfile($meta2->{filehash}, $file2); + transmitfile($meta2->{filehash}, { targetfile => $file2 }); } # otherwise we just use the working copy else @@ -1585,7 +1596,7 @@ sub req_diff { ( undef, $file2 ) = tempfile( DIR => $TEMP_DIR, OPEN => 0 ); $meta2 = $updater->getmeta($filename, $wrev); - transmitfile($meta2->{filehash}, $file2); + transmitfile($meta2->{filehash}, { targetfile => $file2 }); } # We need to have retrieved something useful @@ -2014,14 +2025,17 @@ sub revparse return undef; } -# This method takes a file hash and does a CVS "file transfer" which transmits the -# size of the file, and then the file contents. -# If a second argument $targetfile is given, the file is instead written out to -# a file by the name of $targetfile +# This method takes a file hash and does a CVS "file transfer". Its +# exact behaviour depends on a second, optional hash table argument: +# - If $options->{targetfile}, dump the contents to that file; +# - If $options->{print}, use M/MT to transmit the contents one line +# at a time; +# - Otherwise, transmit the size of the file, followed by the file +# contents. sub transmitfile { my $filehash = shift; - my $targetfile = shift; + my $options = shift; if ( defined ( $filehash ) and $filehash eq "deleted" ) { @@ -2043,11 +2057,20 @@ sub transmitfile if ( open my $fh, '-|', "git-cat-file", "blob", $filehash ) { - if ( defined ( $targetfile ) ) + if ( defined ( $options->{targetfile} ) ) { + my $targetfile = $options->{targetfile}; open NEWFILE, ">", $targetfile or die("Couldn't open '$targetfile' for writing : $!"); print NEWFILE $_ while ( <$fh> ); close NEWFILE or die("Failed to write '$targetfile': $!"); + } elsif ( defined ( $options->{print} ) && $options->{print} ) { + while ( <$fh> ) { + if( /\n\z/ ) { + print 'M ', $_; + } else { + print 'MT text ', $_, "\n"; + } + } } else { print "$size\n"; print while ( <$fh> ); From 6e8937a084927867df4d90c75b2619f2b4b0df18 Mon Sep 17 00:00:00 2001 From: Damien Diederen Date: Thu, 27 Mar 2008 23:18:23 +0100 Subject: [PATCH 6/7] cvsserver: Add test for update -p Signed-off-by: Damien Diederen Signed-off-by: Junio C Hamano --- t/t9400-git-cvsserver-server.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh index 616832407f..166b43f783 100755 --- a/t/t9400-git-cvsserver-server.sh +++ b/t/t9400-git-cvsserver-server.sh @@ -420,6 +420,24 @@ test_expect_success 'cvs update (merge no-op)' \ GIT_CONFIG="$git_config" cvs -Q update && diff -q merge ../merge' +cd "$WORKDIR" +test_expect_success 'cvs update (-p)' ' + touch really-empty && + echo Line 1 > no-lf && + echo -n Line 2 >> no-lf && + git add really-empty no-lf && + git commit -q -m "Update -p test" && + git push gitcvs.git >/dev/null && + cd cvswork && + GIT_CONFIG="$git_config" cvs update && + rm -f failures && + for i in merge no-lf empty really-empty; do + GIT_CONFIG="$git_config" cvs update -p "$i" >$i.out + diff $i.out ../$i >>failures 2>&1 + done && + test -z "$(cat failures)" +' + #------------ # CVS STATUS #------------ From c1bc30614ad0a9b63f4f798f0aa71d376a095938 Mon Sep 17 00:00:00 2001 From: Damien Diederen Date: Thu, 27 Mar 2008 23:18:35 +0100 Subject: [PATCH 7/7] cvsserver: Use the user part of the email in log and annotate results Generate the CVS author names by taking the first eight characters of the user part of the email address. The resulting names are more likely to make sense (or at least reduce ambiguities) in "corporate" environments. Signed-off-by: Damien Diederen Signed-off-by: Junio C Hamano --- git-cvsserver.perl | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 49c0ba2593..dcca4e7173 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -1728,8 +1728,7 @@ sub req_log print "M revision 1.$revision->{revision}\n"; # reformat the date for log output $revision->{modified} = sprintf('%04d/%02d/%02d %s', $3, $DATE_LIST->{$2}, $1, $4 ) if ( $revision->{modified} =~ /(\d+)\s+(\w+)\s+(\d+)\s+(\S+)/ and defined($DATE_LIST->{$2}) ); - $revision->{author} =~ s/\s+.*//; - $revision->{author} =~ s/^(.{8}).*/$1/; + $revision->{author} = cvs_author($revision->{author}); print "M date: $revision->{modified}; author: $revision->{author}; state: " . ( $revision->{filehash} eq "deleted" ? "dead" : "Exp" ) . "; lines: +2 -3\n"; my $commitmessage = $updater->commitmessage($revision->{commithash}); $commitmessage =~ s/^/M /mg; @@ -1844,8 +1843,7 @@ sub req_annotate unless ( defined ( $metadata->{$commithash} ) ) { $metadata->{$commithash} = $updater->getmeta($filename, $commithash); - $metadata->{$commithash}{author} =~ s/\s+.*//; - $metadata->{$commithash}{author} =~ s/^(.{8}).*/$1/; + $metadata->{$commithash}{author} = cvs_author($metadata->{$commithash}{author}); $metadata->{$commithash}{modified} = sprintf("%02d-%s-%02d", $1, $2, $3) if ( $metadata->{$commithash}{modified} =~ /^(\d+)\s(\w+)\s\d\d(\d\d)/ ); } printf("M 1.%-5d (%-8s %10s): %s\n", @@ -2139,6 +2137,16 @@ sub kopts_from_path } } +# Generate a CVS author name from Git author information, by taking +# the first eight characters of the user part of the email address. +sub cvs_author +{ + my $author_line = shift; + (my $author) = $author_line =~ /<([^>@]{1,8})/; + + $author; +} + package GITCVS::log; ####