From 2c3af7e748206f385c85d35b902bce522107360b Mon Sep 17 00:00:00 2001 From: Matthew Ogilvie Date: Sat, 13 Oct 2012 23:42:24 -0600 Subject: [PATCH 01/18] cvsserver: factor out git-log parsing logic Some field conversion was already duplicated, and more calls will be added soon. Signed-off-by: Matthew Ogilvie Signed-off-by: Junio C Hamano --- git-cvsserver.perl | 176 +++++++++++++++++++++++++++------------------ 1 file changed, 105 insertions(+), 71 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index c5ebfa063..dca0ed62f 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -3157,45 +3157,10 @@ sub update push @git_log_params, $self->{module}; } # git-rev-list is the backend / plumbing version of git-log - open(GITLOG, '-|', 'git', 'rev-list', @git_log_params) or die "Cannot call git-rev-list: $!"; - - my @commits; - - my %commit = (); - - while ( ) - { - chomp; - if (m/^commit\s+(.*)$/) { - # on ^commit lines put the just seen commit in the stack - # and prime things for the next one - if (keys %commit) { - my %copy = %commit; - unshift @commits, \%copy; - %commit = (); - } - my @parents = split(m/\s+/, $1); - $commit{hash} = shift @parents; - $commit{parents} = \@parents; - } elsif (m/^(\w+?):\s+(.*)$/ && !exists($commit{message})) { - # on rfc822-like lines seen before we see any message, - # lowercase the entry and put it in the hash as key-value - $commit{lc($1)} = $2; - } else { - # message lines - skip initial empty line - # and trim whitespace - if (!exists($commit{message}) && m/^\s*$/) { - # define it to mark the end of headers - $commit{message} = ''; - next; - } - s/^\s+//; s/\s+$//; # trim ws - $commit{message} .= $_ . "\n"; - } - } - close GITLOG; - - unshift @commits, \%commit if ( keys %commit ); + open(my $gitLogPipe, '-|', 'git', 'rev-list', @git_log_params) + or die "Cannot call git-rev-list: $!"; + my @commits=readCommits($gitLogPipe); + close $gitLogPipe; # Now all the commits are in the @commits bucket # ordered by time DESC. for each commit that needs processing, @@ -3294,7 +3259,7 @@ sub update } # convert the date to CVS-happy format - $commit->{date} = "$2 $1 $4 $3 $5" if ( $commit->{date} =~ /^\w+\s+(\w+)\s+(\d+)\s+(\d+:\d+:\d+)\s+(\d+)\s+([+-]\d+)$/ ); + my $cvsDate = convertToCvsDate($commit->{date}); if ( defined ( $lastpicked ) ) { @@ -3303,7 +3268,7 @@ sub update while ( ) { chomp; - unless ( /^:\d{6}\s+\d{3}(\d)\d{2}\s+[a-zA-Z0-9]{40}\s+([a-zA-Z0-9]{40})\s+(\w)$/o ) + unless ( /^:\d{6}\s+([0-7]{6})\s+[a-f0-9]{40}\s+([a-f0-9]{40})\s+(\w)$/o ) { die("Couldn't process git-diff-tree line : $_"); } @@ -3313,11 +3278,7 @@ sub update # $log->debug("File mode=$mode, hash=$hash, change=$change, name=$name"); - my $git_perms = ""; - $git_perms .= "r" if ( $mode & 4 ); - $git_perms .= "w" if ( $mode & 2 ); - $git_perms .= "x" if ( $mode & 1 ); - $git_perms = "rw" if ( $git_perms eq "" ); + my $dbMode = convertToDbMode($mode); if ( $change eq "D" ) { @@ -3327,11 +3288,11 @@ sub update revision => $head->{$name}{revision} + 1, filehash => "deleted", commithash => $commit->{hash}, - modified => $commit->{date}, + modified => $cvsDate, author => $commit->{author}, - mode => $git_perms, + mode => $dbMode, }; - $self->insert_rev($name, $head->{$name}{revision}, $hash, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms); + $self->insert_rev($name, $head->{$name}{revision}, $hash, $commit->{hash}, $cvsDate, $commit->{author}, $dbMode); } elsif ( $change eq "M" || $change eq "T" ) { @@ -3341,11 +3302,11 @@ sub update revision => $head->{$name}{revision} + 1, filehash => $hash, commithash => $commit->{hash}, - modified => $commit->{date}, + modified => $cvsDate, author => $commit->{author}, - mode => $git_perms, + mode => $dbMode, }; - $self->insert_rev($name, $head->{$name}{revision}, $hash, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms); + $self->insert_rev($name, $head->{$name}{revision}, $hash, $commit->{hash}, $cvsDate, $commit->{author}, $dbMode); } elsif ( $change eq "A" ) { @@ -3355,11 +3316,11 @@ sub update revision => $head->{$name}{revision} ? $head->{$name}{revision}+1 : 1, filehash => $hash, commithash => $commit->{hash}, - modified => $commit->{date}, + modified => $cvsDate, author => $commit->{author}, - mode => $git_perms, + mode => $dbMode, }; - $self->insert_rev($name, $head->{$name}{revision}, $hash, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms); + $self->insert_rev($name, $head->{$name}{revision}, $hash, $commit->{hash}, $cvsDate, $commit->{author}, $dbMode); } else { @@ -3382,7 +3343,7 @@ sub update die("Couldn't process git-ls-tree line : $_"); } - my ( $git_perms, $git_type, $git_hash, $git_filename ) = ( $1, $2, $3, $4 ); + my ( $mode, $git_type, $git_hash, $git_filename ) = ( $1, $2, $3, $4 ); $seen_files->{$git_filename} = 1; @@ -3392,18 +3353,10 @@ sub update $head->{$git_filename}{mode} ); - if ( $git_perms =~ /^\d\d\d(\d)\d\d/o ) - { - $git_perms = ""; - $git_perms .= "r" if ( $1 & 4 ); - $git_perms .= "w" if ( $1 & 2 ); - $git_perms .= "x" if ( $1 & 1 ); - } else { - $git_perms = "rw"; - } + my $dbMode = convertToDbMode($mode); # unless the file exists with the same hash, we need to update it ... - unless ( defined($oldhash) and $oldhash eq $git_hash and defined($oldmode) and $oldmode eq $git_perms ) + unless ( defined($oldhash) and $oldhash eq $git_hash and defined($oldmode) and $oldmode eq $dbMode ) { my $newrevision = ( $oldrevision or 0 ) + 1; @@ -3412,13 +3365,13 @@ sub update revision => $newrevision, filehash => $git_hash, commithash => $commit->{hash}, - modified => $commit->{date}, + modified => $cvsDate, author => $commit->{author}, - mode => $git_perms, + mode => $dbMode, }; - $self->insert_rev($git_filename, $newrevision, $git_hash, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms); + $self->insert_rev($git_filename, $newrevision, $git_hash, $commit->{hash}, $cvsDate, $commit->{author}, $dbMode); } } close FILELIST; @@ -3431,10 +3384,10 @@ sub update $head->{$file}{revision}++; $head->{$file}{filehash} = "deleted"; $head->{$file}{commithash} = $commit->{hash}; - $head->{$file}{modified} = $commit->{date}; + $head->{$file}{modified} = $cvsDate; $head->{$file}{author} = $commit->{author}; - $self->insert_rev($file, $head->{$file}{revision}, $head->{$file}{filehash}, $commit->{hash}, $commit->{date}, $commit->{author}, $head->{$file}{mode}); + $self->insert_rev($file, $head->{$file}{revision}, $head->{$file}{filehash}, $commit->{hash}, $cvsDate, $commit->{author}, $head->{$file}{mode}); } } # END : "Detect deleted files" @@ -3472,6 +3425,87 @@ sub update $self->{dbh}->commit() or die "Failed to commit changes to SQLite"; } +sub readCommits +{ + my $pipeHandle = shift; + my @commits; + + my %commit = (); + + while ( <$pipeHandle> ) + { + chomp; + if (m/^commit\s+(.*)$/) { + # on ^commit lines put the just seen commit in the stack + # and prime things for the next one + if (keys %commit) { + my %copy = %commit; + unshift @commits, \%copy; + %commit = (); + } + my @parents = split(m/\s+/, $1); + $commit{hash} = shift @parents; + $commit{parents} = \@parents; + } elsif (m/^(\w+?):\s+(.*)$/ && !exists($commit{message})) { + # on rfc822-like lines seen before we see any message, + # lowercase the entry and put it in the hash as key-value + $commit{lc($1)} = $2; + } else { + # message lines - skip initial empty line + # and trim whitespace + if (!exists($commit{message}) && m/^\s*$/) { + # define it to mark the end of headers + $commit{message} = ''; + next; + } + s/^\s+//; s/\s+$//; # trim ws + $commit{message} .= $_ . "\n"; + } + } + + unshift @commits, \%commit if ( keys %commit ); + + return @commits; +} + +sub convertToCvsDate +{ + my $date = shift; + # Convert from: "git rev-list --pretty" formatted date + # Convert to: "the format specified by RFC822 as modified by RFC1123." + # Example: 26 May 1997 13:01:40 -0400 + if( $date =~ /^\w+\s+(\w+)\s+(\d+)\s+(\d+:\d+:\d+)\s+(\d+)\s+([+-]\d+)$/ ) + { + $date = "$2 $1 $4 $3 $5"; + } + + return $date; +} + +sub convertToDbMode +{ + my $mode = shift; + + # NOTE: The CVS protocol uses a string similar "u=rw,g=rw,o=rw", + # but the database "mode" column historically (and currently) + # only stores the "rw" (for user) part of the string. + # FUTURE: It might make more sense to persist the raw + # octal mode (or perhaps the final full CVS form) instead of + # this half-converted form, but it isn't currently worth the + # backwards compatibility headaches. + + $mode=~/^\d\d(\d)\d{3}$/; + my $userBits=$1; + + my $dbMode = ""; + $dbMode .= "r" if ( $userBits & 4 ); + $dbMode .= "w" if ( $userBits & 2 ); + $dbMode .= "x" if ( $userBits & 1 ); + $dbMode = "rw" if ( $dbMode eq "" ); + + return $dbMode; +} + sub insert_rev { my $self = shift; From 1899cbc5b2e2cb3e7c98cceed818d8adfb242ec2 Mon Sep 17 00:00:00 2001 From: Matthew Ogilvie Date: Sat, 13 Oct 2012 23:42:25 -0600 Subject: [PATCH 02/18] cvsserver: cleanup extra slashes in filename arguments Signed-off-by: Matthew Ogilvie Signed-off-by: Junio C Hamano --- git-cvsserver.perl | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index dca0ed62f..1d929dfac 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -2309,6 +2309,9 @@ sub filenamesplit return ( $filepart, $dirpart ); } +# Cleanup various junk in filename (try to canonicalize it), and +# add prependdir to accomodate running CVS client from a +# subdirectory (so the output is relative to top directory of the project). sub filecleanup { my $filename = shift; @@ -2320,11 +2323,36 @@ sub filecleanup return undef; } + if($filename eq ".") + { + $filename=""; + } $filename =~ s/^\.\///g; + $filename =~ s%/+%/%g; $filename = $state->{prependdir} . $filename; + $filename =~ s%/$%%; return $filename; } +# Remove prependdir from the path, so that is is relative to the directory +# the CVS client was started from, rather than the top of the project. +# Essentially the inverse of filecleanup(). +sub remove_prependdir +{ + my($path) = @_; + if(defined($state->{prependdir}) && $state->{prependdir} ne "") + { + my($pre)=$state->{prependdir}; + $pre=~s%/$%%; + if(!($path=~s%^\Q$pre\E/?%%)) + { + $log->fatal("internal error missing prependdir"); + die("internal error missing prependdir"); + } + } + return $path; +} + sub validateGitDir { if( !defined($state->{CVSROOT}) ) From 51a7e6dbc9b871ddfd73752e1a1b254e38e4336b Mon Sep 17 00:00:00 2001 From: Matthew Ogilvie Date: Sat, 13 Oct 2012 23:42:26 -0600 Subject: [PATCH 03/18] cvsserver: define a tag name character escape mechanism CVS tags are officially only allowed to use [-_0-9A-Za-f]. Git refs commonly uses other characters, especially [./]. Such characters need to be escaped from CVS in order to be referenced. This just defines functions to escape/unescape names. The functions are not used yet. Signed-off-by: Matthew Ogilvie Signed-off-by: Junio C Hamano --- git-cvsserver.perl | 91 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 1d929dfac..4eecc0bc8 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -3807,6 +3807,97 @@ sub gethistorydense return $result; } +=head2 escapeRefName + +Apply an escape mechanism to compensate for characters that +git ref names can have that CVS tags can not. + +=cut +sub escapeRefName +{ + my($self,$refName)=@_; + + # CVS officially only allows [-_A-Za-z0-9] in tag names (or in + # many contexts it can also be a CVS revision number). + # + # Git tags commonly use '/' and '.' as well, but also handle + # anything else just in case: + # + # = "_-s-" For '/'. + # = "_-p-" For '.'. + # = "_-u-" For underscore, in case someone wants a literal "_-" in + # a tag name. + # = "_-xx-" Where "xx" is the hexadecimal representation of the + # desired ASCII character byte. (for anything else) + + if(! $refName=~/^[1-9][0-9]*(\.[1-9][0-9]*)*$/) + { + $refName=~s/_-/_-u--/g; + $refName=~s/\./_-p-/g; + $refName=~s%/%_-s-%g; + $refName=~s/[^-_a-zA-Z0-9]/sprintf("_-%02x-",$1)/eg; + } +} + +=head2 unescapeRefName + +Undo an escape mechanism to compensate for characters that +git ref names can have that CVS tags can not. + +=cut +sub unescapeRefName +{ + my($self,$refName)=@_; + + # see escapeRefName() for description of escape mechanism. + + $refName=~s/_-([spu]|[0-9a-f][0-9a-f])-/unescapeRefNameChar($1)/eg; + + # allowed tag names + # TODO: Perhaps use git check-ref-format, with an in-process cache of + # validated names? + if( !( $refName=~m%^[^-][-a-zA-Z0-9_/.]*$% ) || + ( $refName=~m%[/.]$% ) || + ( $refName=~/\.lock$/ ) || + ( $refName=~m%\.\.|/\.|[[\\:?*~]|\@\{% ) ) # matching } + { + # Error: + $log->warn("illegal refName: $refName"); + $refName=undef; + } + return $refName; +} + +sub unescapeRefNameChar +{ + my($char)=@_; + + if($char eq "s") + { + $char="/"; + } + elsif($char eq "p") + { + $char="."; + } + elsif($char eq "u") + { + $char="_"; + } + elsif($char=~/^[0-9a-f][0-9a-f]$/) + { + $char=chr(hex($char)); + } + else + { + # Error case: Maybe it has come straight from user, and + # wasn't supposed to be escaped? Restore it the way we got it: + $char="_-$char-"; + } + + return $char; +} + =head2 in_array() from Array::PAT - mimics the in_array() function From 658b57ad523e0108bafe5f5d56dbb99c0f3ef95c Mon Sep 17 00:00:00 2001 From: Matthew Ogilvie Date: Sat, 13 Oct 2012 23:42:27 -0600 Subject: [PATCH 04/18] cvsserver: add misc commit lookup, file meta data, and file listing functions These will be used soon, but not yet. PERFORMANCE NOTE: getMetaFromCommithash() does not scale well as currently implemented. See comment for possible optimization strategies. Fortunately, it will only be used in cases that would not have worked at all before this change. Signed-off-by: Matthew Ogilvie Signed-off-by: Junio C Hamano --- git-cvsserver.perl | 366 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 365 insertions(+), 1 deletion(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 4eecc0bc8..16e2e1ff9 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -2986,6 +2986,9 @@ sub new die "Git repo '$self->{git_path}' doesn't exist" unless ( -d $self->{git_path} ); + # Stores full sha1's for various branch/tag names, abbreviations, etc: + $self->{commitRefCache} = {}; + $self->{dbdriver} = $cfg->{gitcvs}{$state->{method}}{dbdriver} || $cfg->{gitcvs}{dbdriver} || "SQLite"; $self->{dbname} = $cfg->{gitcvs}{$state->{method}}{dbname} || @@ -3446,7 +3449,7 @@ sub update ); } # invalidate the gethead cache - $self->{gethead_cache} = undef; + $self->clearCommitRefCaches(); # Ending exclusive lock here @@ -3648,6 +3651,169 @@ sub gethead return $tree; } +=head2 getAnyHead + +Returns a reference to an array of getmeta structures, one +per file in the specified tree hash. + +=cut + +sub getAnyHead +{ + my ($self,$hash) = @_; + + if(!defined($hash)) + { + return $self->gethead(); + } + + my @files; + { + open(my $filePipe, '-|', 'git', 'ls-tree', '-z', '-r', $hash) + or die("Cannot call git-ls-tree : $!"); + local $/ = "\0"; + @files=<$filePipe>; + close $filePipe; + } + + my $tree=[]; + my($line); + foreach $line (@files) + { + $line=~s/\0$//; + unless ( $line=~/^(\d+)\s+(\w+)\s+([a-zA-Z0-9]+)\t(.*)$/o ) + { + die("Couldn't process git-ls-tree line : $_"); + } + + my($mode, $git_type, $git_hash, $git_filename) = ($1, $2, $3, $4); + push @$tree, $self->getMetaFromCommithash($git_filename,$hash); + } + + return $tree; +} + +=head2 getRevisionDirMap + +A "revision dir map" contains all the plain-file filenames associated +with a particular revision (treeish), organized by directory: + + $type = $out->{$dir}{$fullName} + +The type of each is "F" (for ordinary file) or "D" (for directory, +for which the map $out->{$fullName} will also exist). + +=cut + +sub getRevisionDirMap +{ + my ($self,$ver)=@_; + + if(!defined($self->{revisionDirMapCache})) + { + $self->{revisionDirMapCache}={}; + } + + # Get file list (previously cached results are dependent on HEAD, + # but are early in each case): + my $cacheKey; + my (@fileList); + if( !defined($ver) || $ver eq "" ) + { + $cacheKey=""; + if( defined($self->{revisionDirMapCache}{$cacheKey}) ) + { + return $self->{revisionDirMapCache}{$cacheKey}; + } + + my @head = @{$self->gethead()}; + foreach my $file ( @head ) + { + next if ( $file->{filehash} eq "deleted" ); + + push @fileList,$file->{name}; + } + } + else + { + my ($hash)=$self->lookupCommitRef($ver); + if( !defined($hash) ) + { + return undef; + } + + $cacheKey=$hash; + if( defined($self->{revisionDirMapCache}{$cacheKey}) ) + { + return $self->{revisionDirMapCache}{$cacheKey}; + } + + open(my $filePipe, '-|', 'git', 'ls-tree', '-z', '-r', $hash) + or die("Cannot call git-ls-tree : $!"); + local $/ = "\0"; + while ( <$filePipe> ) + { + chomp; + unless ( /^(\d+)\s+(\w+)\s+([a-zA-Z0-9]+)\t(.*)$/o ) + { + die("Couldn't process git-ls-tree line : $_"); + } + + my($mode, $git_type, $git_hash, $git_filename) = ($1, $2, $3, $4); + + push @fileList, $git_filename; + } + close $filePipe; + } + + # Convert to normalized form: + my %revMap; + my $file; + foreach $file (@fileList) + { + my($dir) = ($file=~m%^(?:(.*)/)?([^/]*)$%); + $dir='' if(!defined($dir)); + + # parent directories: + # ... create empty dir maps for parent dirs: + my($td)=$dir; + while(!defined($revMap{$td})) + { + $revMap{$td}={}; + + my($tp)=($td=~m%^(?:(.*)/)?([^/]*)$%); + $tp='' if(!defined($tp)); + $td=$tp; + } + # ... add children to parent maps (now that they exist): + $td=$dir; + while($td ne "") + { + my($tp)=($td=~m%^(?:(.*)/)?([^/]*)$%); + $tp='' if(!defined($tp)); + + if(defined($revMap{$tp}{$td})) + { + if($revMap{$tp}{$td} ne 'D') + { + die "Weird file/directory inconsistency in $cacheKey"; + } + last; # loop exit + } + $revMap{$tp}{$td}='D'; + + $td=$tp; + } + + # file + $revMap{$dir}{$file}='F'; + } + + # Save in cache: + $self->{revisionDirMapCache}{$cacheKey}=\%revMap; + return $self->{revisionDirMapCache}{$cacheKey}; +} + =head2 getlog See also gethistorydense(). @@ -3742,6 +3908,204 @@ sub getmeta return $meta; } +sub getMetaFromCommithash +{ + my $self = shift; + my $filename = shift; + my $revCommit = shift; + + # NOTE: This function doesn't scale well (lots of forks), especially + # if you have many files that have not been modified for many commits + # (each git-rev-parse redoes a lot of work for each file + # that theoretically could be done in parallel by smarter + # graph traversal). + # + # TODO: Possible optimization strategies: + # - Solve the issue of assigning and remembering "real" CVS + # revision numbers for branches, and ensure the + # data structure can do this efficiently. Perhaps something + # similar to "git notes", and carefully structured to take + # advantage same-sha1-is-same-contents, to roll the same + # unmodified subdirectory data onto multiple commits? + # - Write and use a C tool that is like git-blame, but + # operates on multiple files with file granularity, instead + # of one file with line granularity. Cache + # most-recently-modified in $self->{commitRefCache}{$revCommit}. + # Try to be intelligent about how many files we do with + # one fork (perhaps one directory at a time, without recursion, + # and/or include directory as one line item, recurse from here + # instead of in C tool?). + # - Perhaps we could ask the DB for (filename,fileHash), + # and just guess that it is correct (that the file hadn't + # changed between $revCommit and the found commit, then + # changed back, confusing anything trying to interpret + # history). Probably need to add another index to revisions + # DB table for this. + # - NOTE: Trying to store all (commit,file) keys in DB [to + # find "lastModfiedCommit] (instead of + # just files that changed in each commit as we do now) is + # probably not practical from a disk space perspective. + + # Does the file exist in $revCommit? + # TODO: Include file hash in dirmap cache. + my($dirMap)=$self->getRevisionDirMap($revCommit); + my($dir,$file)=($filename=~m%^(?:(.*)/)?([^/]*$)%); + if(!defined($dir)) + { + $dir=""; + } + if( !defined($dirMap->{$dir}) || + !defined($dirMap->{$dir}{$filename}) ) + { + my($fileHash)="deleted"; + + my($retVal)={}; + $retVal->{name}=$filename; + $retVal->{filehash}=$fileHash; + + # not needed and difficult to compute: + $retVal->{revision}="0"; # $revision; + $retVal->{commithash}=$revCommit; + #$retVal->{author}=$commit->{author}; + #$retVal->{modified}=convertToCvsDate($commit->{date}); + #$retVal->{mode}=convertToDbMode($mode); + + return $retVal; + } + + my($fileHash)=safe_pipe_capture("git","rev-parse","$revCommit:$filename"); + chomp $fileHash; + if(!($fileHash=~/^[0-9a-f]{40}$/)) + { + die "Invalid fileHash '$fileHash' looking up" + ." '$revCommit:$filename'\n"; + } + + # information about most recent commit to modify $filename: + open(my $gitLogPipe, '-|', 'git', 'rev-list', + '--max-count=1', '--pretty', '--parents', + $revCommit, '--', $filename) + or die "Cannot call git-rev-list: $!"; + my @commits=readCommits($gitLogPipe); + close $gitLogPipe; + if(scalar(@commits)!=1) + { + die "Can't find most recent commit changing $filename\n"; + } + my($commit)=$commits[0]; + if( !defined($commit) || !defined($commit->{hash}) ) + { + return undef; + } + + # does this (commit,file) have a real assigned CVS revision number? + my $tablename_rev = $self->tablename("revision"); + my $db_query; + $db_query = $self->{dbh}->prepare_cached( + "SELECT * FROM $tablename_rev WHERE name=? AND commithash=?", + {},1); + $db_query->execute($filename, $commit->{hash}); + my($meta)=$db_query->fetchrow_hashref; + if($meta) + { + $meta->{revision} = "1.$meta->{revision}"; + return $meta; + } + + # fall back on special revision number + my($revision)=$commit->{hash}; + $revision=~s/(..)/'.' . (hex($1)+100)/eg; + $revision="2.1.1.2000$revision"; + + # meta data about $filename: + open(my $filePipe, '-|', 'git', 'ls-tree', '-z', + $commit->{hash}, '--', $filename) + or die("Cannot call git-ls-tree : $!"); + local $/ = "\0"; + my $line; + $line=<$filePipe>; + if(defined(<$filePipe>)) + { + die "Expected only a single file for git-ls-tree $filename\n"; + } + close $filePipe; + + chomp $line; + unless ( $line=~m/^(\d+)\s+(\w+)\s+([a-zA-Z0-9]+)\t(.*)$/o ) + { + die("Couldn't process git-ls-tree line : $line\n"); + } + my ( $mode, $git_type, $git_hash, $git_filename ) = ( $1, $2, $3, $4 ); + + # save result: + my($retVal)={}; + $retVal->{name}=$filename; + $retVal->{revision}=$revision; + $retVal->{filehash}=$fileHash; + $retVal->{commithash}=$revCommit; + $retVal->{author}=$commit->{author}; + $retVal->{modified}=convertToCvsDate($commit->{date}); + $retVal->{mode}=convertToDbMode($mode); + + return $retVal; +} + +=head2 lookupCommitRef + +Convert tag/branch/abbreviation/etc into a commit sha1 hash. Caches +the result so looking it up again is fast. + +=cut + +sub lookupCommitRef +{ + my $self = shift; + my $ref = shift; + + my $commitHash = $self->{commitRefCache}{$ref}; + if(defined($commitHash)) + { + return $commitHash; + } + + $commitHash=safe_pipe_capture("git","rev-parse","--verify","--quiet", + $self->unescapeRefName($ref)); + $commitHash=~s/\s*$//; + if(!($commitHash=~/^[0-9a-f]{40}$/)) + { + $commitHash=undef; + } + + if( defined($commitHash) ) + { + my $type=safe_pipe_capture("git","cat-file","-t",$commitHash); + if( ! ($type=~/^commit\s*$/ ) ) + { + $commitHash=undef; + } + } + if(defined($commitHash)) + { + $self->{commitRefCache}{$ref}=$commitHash; + } + return $commitHash; +} + +=head2 clearCommitRefCaches + +Clears cached commit cache (sha1's for various tags/abbeviations/etc), +and related caches. + +=cut + +sub clearCommitRefCaches +{ + my $self = shift; + $self->{commitRefCache} = {}; + $self->{revisionDirMapCache} = undef; + $self->{gethead_cache} = undef; +} + =head2 commitmessage this function takes a commithash and returns the commit message for that commit From eb5dcb2c0290aa0cdbbd3f6c9ddfa77f251deacf Mon Sep 17 00:00:00 2001 From: Matthew Ogilvie Date: Sat, 13 Oct 2012 23:42:28 -0600 Subject: [PATCH 05/18] cvsserver: implement req_Sticky and related utilities Nothing sets sticky yet, or uses the values set by this, but soon... Signed-off-by: Matthew Ogilvie Signed-off-by: Junio C Hamano --- git-cvsserver.perl | 171 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 169 insertions(+), 2 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 16e2e1ff9..53d8de7f8 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -60,6 +60,7 @@ 'Valid-responses' => \&req_Validresponses, 'valid-requests' => \&req_validrequests, 'Directory' => \&req_Directory, + 'Sticky' => \&req_Sticky, 'Entry' => \&req_Entry, 'Modified' => \&req_Modified, 'Unchanged' => \&req_Unchanged, @@ -470,11 +471,19 @@ sub req_Directory { $log->info("Setting prepend to '$state->{path}'"); $state->{prependdir} = $state->{path}; + my %entries; foreach my $entry ( keys %{$state->{entries}} ) { - $state->{entries}{$state->{prependdir} . $entry} = $state->{entries}{$entry}; - delete $state->{entries}{$entry}; + $entries{$state->{prependdir} . $entry} = $state->{entries}{$entry}; } + $state->{entries}=\%entries; + + my %dirMap; + foreach my $dir ( keys %{$state->{dirMap}} ) + { + $dirMap{$state->{prependdir} . $dir} = $state->{dirMap}{$dir}; + } + $state->{dirMap}=\%dirMap; } if ( defined ( $state->{prependdir} ) ) @@ -482,9 +491,60 @@ sub req_Directory $log->debug("Prepending '$state->{prependdir}' to state|directory"); $state->{directory} = $state->{prependdir} . $state->{directory} } + + if ( ! defined($state->{dirMap}{$state->{directory}}) ) + { + $state->{dirMap}{$state->{directory}} = + { + 'names' => {} + #'tagspec' => undef + }; + } + $log->debug("req_Directory : localdir=$data repository=$repository path=$state->{path} directory=$state->{directory} module=$state->{module}"); } +# Sticky tagspec \n +# Response expected: no. Tell the server that the directory most +# recently specified with Directory has a sticky tag or date +# tagspec. The first character of tagspec is T for a tag, D for +# a date, or some other character supplied by a Set-sticky +# response from a previous request to the server. The remainder +# of tagspec contains the actual tag or date, again as supplied +# by Set-sticky. +# The server should remember Static-directory and Sticky requests +# for a particular directory; the client need not resend them each +# time it sends a Directory request for a given directory. However, +# the server is not obliged to remember them beyond the context +# of a single command. +sub req_Sticky +{ + my ( $cmd, $tagspec ) = @_; + + my ( $stickyInfo ); + if($tagspec eq "") + { + # nothing + } + elsif($tagspec=~/^T([^ ]+)\s*$/) + { + $stickyInfo = { 'tag' => $1 }; + } + elsif($tagspec=~/^D([0-9.]+)\s*$/) + { + $stickyInfo= { 'date' => $1 }; + } + else + { + die "Unknown tag_or_date format\n"; + } + $state->{dirMap}{$state->{directory}}{stickyInfo}=$stickyInfo; + + $log->debug("req_Sticky : tagspec=$tagspec repository=$state->{repository}" + . " path=$state->{path} directory=$state->{directory}" + . " module=$state->{module}"); +} + # Entry entry-line \n # Response expected: no. Tell the server what version of a file is on the # local machine. The name in entry-line is a name relative to the directory @@ -511,6 +571,8 @@ sub req_Entry tag_or_date => $data[5], }; + $state->{dirMap}{$state->{directory}}{names}{$data[1]} = 'F'; + $log->info("Received entry line '$data' => '" . $state->{directory} . $data[1] . "'"); } @@ -2213,6 +2275,110 @@ sub argsfromdir } } + +## look up directory sticky tag, of either fullPath or a parent: +sub getDirStickyInfo +{ + my($fullPath)=@_; + + $fullPath=~s%/+$%%; + while($fullPath ne "" && !defined($state->{dirMap}{"$fullPath/"})) + { + $fullPath=~s%/?[^/]*$%%; + } + + if( !defined($state->{dirMap}{"$fullPath/"}) && + ( $fullPath eq "" || + $fullPath eq "." ) ) + { + return $state->{dirMap}{""}{stickyInfo}; + } + else + { + return $state->{dirMap}{"$fullPath/"}{stickyInfo}; + } +} + +# Resolve precedence of various ways of specifying which version of +# a file you want. Returns undef (for default head), or a ref to a hash +# that contains "tag" and/or "date" keys. +sub resolveStickyInfo +{ + my($filename,$stickyTag,$stickyDate,$reset) = @_; + + # Order of precedence of sticky tags: + # -A [head] + # -r /tag/ + # [file entry sticky tag] + # [the tag specified in dir req_Sticky] + # [the tag specified in a parent dir req_Sticky] + # [head] + + my $result; + if($reset) + { + # $result=undef; + } + elsif( defined($stickyTag) && $stickyTag ne "" ) + # || ( defined($stickyDate) && $stickyDate ne "" ) # TODO + { + $result={ 'tag' => (defined($stickyTag)?$stickyTag:undef) }; + + # TODO: Convert -D value into the form 2011.04.10.04.46.57, + # similar to an entry line's sticky date, without the D prefix. + # It sometimes (always?) arrives as something more like + # '10 Apr 2011 04:46:57 -0000'... + # $result={ 'date' => (defined($stickyDate)?$stickyDate:undef) }; + } + elsif( defined($state->{entries}{$filename}) && + defined($state->{entries}{$filename}{tag_or_date}) && + $state->{entries}{$filename}{tag_or_date} ne "" ) + { + my($tagOrDate)=$state->{entries}{$filename}{tag_or_date}; + if($tagOrDate=~/^T([^ ]+)\s*$/) + { + $result = { 'tag' => $1 }; + } + elsif($tagOrDate=~/^D([0-9.]+)\s*$/) + { + $result= { 'date' => $1 }; + } + else + { + die "Unknown tag_or_date format\n"; + } + } + else + { + $result=getDirStickyInfo($filename); + } + + return $result; +} + +# Convert a stickyInfo (ref to a hash) as returned by resolveStickyInfo into +# a form appropriate for the sticky tag field of an Entries +# line (field index 5, 0-based). +sub getStickyTagOrDate +{ + my($stickyInfo)=@_; + + my $result; + if(defined($stickyInfo) && defined($stickyInfo->{tag})) + { + $result="T$stickyInfo->{tag}"; + } + # TODO: When/if we actually pick versions by {date} properly, + # also handle it here: + # "D$stickyInfo->{date}" (example: "D2011.04.13.20.37.07"). + else + { + $result=""; + } + + return $result; +} + # This method cleans up the $state variable after a command that uses arguments has run sub statecleanup { @@ -2220,6 +2386,7 @@ sub statecleanup $state->{args} = []; $state->{arguments} = []; $state->{entries} = {}; + $state->{dirMap} = {}; } # Return working directory CVS revision "1.X" out From bfdafa099eb9fd8c3b99f5debdddfa251dcdd855 Mon Sep 17 00:00:00 2001 From: Matthew Ogilvie Date: Sat, 13 Oct 2012 23:42:29 -0600 Subject: [PATCH 06/18] cvsserver: generalize getmeta() to recognize commit refs This allows getmeta() to recognize any commitish (sha1, tag/branch name, etc). Signed-off-by: Matthew Ogilvie Signed-off-by: Junio C Hamano --- git-cvsserver.perl | 156 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 145 insertions(+), 11 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 53d8de7f8..7bb6f83b9 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -4041,6 +4041,19 @@ =head2 getmeta This function takes a filename (with path) argument and returns a hashref of metadata for that file. +There are several ways $revision can be specified: + + - A reference to hash that contains a "tag" that is the + actual revision (one of the below). TODO: Also allow it to + specify a "date" in the hash. + - undef, to refer to the latest version on the main branch. + - Full CVS client revision number (mapped to integer in DB, without the + "1." prefix), + - Complex CVS-compatible "special" revision number for + non-linear history (see comment below) + - git commit sha1 hash + - branch or tag name + =cut sub getmeta @@ -4051,23 +4064,144 @@ sub getmeta my $tablename_rev = $self->tablename("revision"); my $tablename_head = $self->tablename("head"); - my $db_query; - if ( defined($revision) and $revision =~ /^1\.(\d+)$/ ) + if ( ref($revision) eq "HASH" ) { - my ($intRev) = $1; - $db_query = $self->{dbh}->prepare_cached("SELECT * FROM $tablename_rev WHERE name=? AND revision=?",{},1); - $db_query->execute($filename, $intRev); + $revision = $revision->{tag}; + } + + # Overview of CVS revision numbers: + # + # General CVS numbering scheme: + # - Basic mainline branch numbers: "1.1", "1.2", "1.3", etc. + # - Result of "cvs checkin -r" (possible, but not really + # recommended): "2.1", "2.2", etc + # - Branch tag: "1.2.0.n", where "1.2" is revision it was branched + # from, "0" is a magic placeholder that identifies it as a + # branch tag instead of a version tag, and n is 2 times the + # branch number off of "1.2", starting with "2". + # - Version on a branch: "1.2.n.x", where "1.2" is branch-from, "n" + # is branch number off of "1.2" (like n above), and "x" is + # the version number on the branch. + # - Branches can branch off of branches: "1.3.2.7.4.1" (even number + # of components). + # - Odd "n"s are used by "vendor branches" that result + # from "cvs import". Vendor branches have additional + # strangeness in the sense that the main rcs "head" of the main + # branch will (temporarily until first normal commit) point + # to the version on the vendor branch, rather than the actual + # main branch. (FUTURE: This may provide an opportunity + # to use "strange" revision numbers for fast-forward-merged + # branch tip when CVS client is asking for the main branch.) + # + # git-cvsserver CVS-compatible special numbering schemes: + # - Currently git-cvsserver only tries to be identical to CVS for + # simple "1.x" numbers on the "main" branch (as identified + # by the module name that was originally cvs checkout'ed). + # - The database only stores the "x" part, for historical reasons. + # But most of the rest of the cvsserver preserves + # and thinks using the full revision number. + # - To handle non-linear history, it uses a version of the form + # "2.1.1.2000.b.b.b."..., where the 2.1.1.2000 is to help uniquely + # identify this as a special revision number, and there are + # 20 b's that together encode the sha1 git commit from which + # this version of this file originated. Each b is + # the numerical value of the corresponding byte plus + # 100. + # - "plus 100" avoids "0"s, and also reduces the + # likelyhood of a collision in the case that someone someday + # writes an import tool that tries to preserve original + # CVS revision numbers, and the original CVS data had done + # lots of branches off of branches and other strangeness to + # end up with a real version number that just happens to look + # like this special revision number form. Also, if needed + # there are several ways to extend/identify alternative encodings + # within the "2.1.1.2000" part if necessary. + # - Unlike real CVS revisions, you can't really reconstruct what + # relation a revision of this form has to other revisions. + # - FUTURE: TODO: Rework database somehow to make up and remember + # fully-CVS-compatible branches and branch version numbers. + + my $meta; + if ( defined($revision) ) + { + if ( $revision =~ /^1\.(\d+)$/ ) + { + my ($intRev) = $1; + my $db_query; + $db_query = $self->{dbh}->prepare_cached( + "SELECT * FROM $tablename_rev WHERE name=? AND revision=?", + {},1); + $db_query->execute($filename, $intRev); + $meta = $db_query->fetchrow_hashref; + } + elsif ( $revision =~ /^2\.1\.1\.2000(\.[1-3][0-9][0-9]){20}$/ ) + { + my ($commitHash)=($revision=~/^2\.1\.1\.2000(.*)$/); + $commitHash=~s/\.([0-9]+)/sprintf("%02x",$1-100)/eg; + if($commitHash=~/^[0-9a-f]{40}$/) + { + return $self->getMetaFromCommithash($filename,$commitHash); + } + + # error recovery: fall back on head version below + print "E Failed to find $filename version=$revision or commit=$commitHash\n"; + $log->warning("failed get $revision with commithash=$commitHash"); + undef $revision; + } + elsif ( $revision =~ /^[0-9a-f]{40}$/ ) + { + # Try DB first. This is mostly only useful for req_annotate(), + # which only calls this for stuff that should already be in + # the DB. It is fairly likely to be a waste of time + # in most other cases [unless the file happened to be + # modified in $revision specifically], but + # it is probably in the noise compared to how long + # getMetaFromCommithash() will take. + my $db_query; + $db_query = $self->{dbh}->prepare_cached( + "SELECT * FROM $tablename_rev WHERE name=? AND commithash=?", + {},1); + $db_query->execute($filename, $revision); + $meta = $db_query->fetchrow_hashref; + + if(! $meta) + { + my($revCommit)=$self->lookupCommitRef($revision); + if($revCommit=~/^[0-9a-f]{40}$/) + { + return $self->getMetaFromCommithash($filename,$revCommit); + } + + # error recovery: nothing found: + print "E Failed to find $filename version=$revision\n"; + $log->warning("failed get $revision"); + return $meta; + } + } + else + { + my($revCommit)=$self->lookupCommitRef($revision); + if($revCommit=~/^[0-9a-f]{40}$/) + { + return $self->getMetaFromCommithash($filename,$revCommit); + } + + # error recovery: fall back on head version below + print "E Failed to find $filename version=$revision\n"; + $log->warning("failed get $revision"); + undef $revision; # Allow fallback + } } - elsif ( defined($revision) and $revision =~ /^[a-zA-Z0-9]{40}$/ ) + + if(!defined($revision)) { - $db_query = $self->{dbh}->prepare_cached("SELECT * FROM $tablename_rev WHERE name=? AND commithash=?",{},1); - $db_query->execute($filename, $revision); - } else { - $db_query = $self->{dbh}->prepare_cached("SELECT * FROM $tablename_head WHERE name=?",{},1); + my $db_query; + $db_query = $self->{dbh}->prepare_cached( + "SELECT * FROM $tablename_head WHERE name=?",{},1); $db_query->execute($filename); + $meta = $db_query->fetchrow_hashref; } - my $meta = $db_query->fetchrow_hashref; if($meta) { $meta->{revision} = "1.$meta->{revision}"; From d66e8f8cf33909fe43f58ca5d87de8624986425c Mon Sep 17 00:00:00 2001 From: Matthew Ogilvie Date: Sat, 13 Oct 2012 23:42:30 -0600 Subject: [PATCH 07/18] cvsserver: Add version awareness to argsfromdir Signed-off-by: Matthew Ogilvie Signed-off-by: Junio C Hamano --- git-cvsserver.perl | 228 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 198 insertions(+), 30 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 7bb6f83b9..5e558d137 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -2226,55 +2226,222 @@ sub argsplit } } -# This method uses $state->{directory} to populate $state->{args} with a list of filenames -sub argsfromdir +# Used by argsfromdir +sub expandArg { - my $updater = shift; + my ($updater,$outNameMap,$outDirMap,$path,$isDir) = @_; - $state->{args} = [] if ( scalar(@{$state->{args}}) == 1 and $state->{args}[0] eq "." ); + my $fullPath = filecleanup($path); - return if ( scalar ( @{$state->{args}} ) > 1 ); + # Is it a directory? + if( defined($state->{dirMap}{$fullPath}) || + defined($state->{dirMap}{"$fullPath/"}) ) + { + # It is a directory in the user's sandbox. + $isDir=1; - my @gethead = @{$updater->gethead}; + if(defined($state->{entries}{$fullPath})) + { + $log->fatal("Inconsistent file/dir type"); + die "Inconsistent file/dir type"; + } + } + elsif(defined($state->{entries}{$fullPath})) + { + # It is a file in the user's sandbox. + $isDir=0; + } + my($revDirMap,$otherRevDirMap); + if(!defined($isDir) || $isDir) + { + # Resolve version tree for sticky tag: + # (for now we only want list of files for the version, not + # particular versions of those files: assume it is a directory + # for the moment; ignore Entry's stick tag) + + # Order of precedence of sticky tags: + # -A [head] + # -r /tag/ + # [file entry sticky tag, but that is only relevant to files] + # [the tag specified in dir req_Sticky] + # [the tag specified in a parent dir req_Sticky] + # [head] + # Also, -r may appear twice (for diff). + # + # FUTURE: When/if -j (merges) are supported, we also + # need to add relevant files from one or two + # versions specified with -j. + + if(exists($state->{opt}{A})) + { + $revDirMap=$updater->getRevisionDirMap(); + } + elsif( defined($state->{opt}{r}) and + ref $state->{opt}{r} eq "ARRAY" ) + { + $revDirMap=$updater->getRevisionDirMap($state->{opt}{r}[0]); + $otherRevDirMap=$updater->getRevisionDirMap($state->{opt}{r}[1]); + } + elsif(defined($state->{opt}{r})) + { + $revDirMap=$updater->getRevisionDirMap($state->{opt}{r}); + } + else + { + my($sticky)=getDirStickyInfo($fullPath); + $revDirMap=$updater->getRevisionDirMap($sticky->{tag}); + } - # push added files - foreach my $file (keys %{$state->{entries}}) { - if ( exists $state->{entries}{$file}{revision} && - $state->{entries}{$file}{revision} eq '0' ) - { - push @gethead, { name => $file, filehash => 'added' }; - } + # Is it a directory? + if( defined($revDirMap->{$fullPath}) || + defined($otherRevDirMap->{$fullPath}) ) + { + $isDir=1; + } } - if ( scalar(@{$state->{args}}) == 1 ) + # What to do with it? + if(!$isDir) { - my $arg = $state->{args}[0]; - $arg .= $state->{prependdir} if ( defined ( $state->{prependdir} ) ); - - $log->info("Only one arg specified, checking for directory expansion on '$arg'"); + $outNameMap->{$fullPath}=1; + } + else + { + $outDirMap->{$fullPath}=1; - foreach my $file ( @gethead ) + if(defined($revDirMap->{$fullPath})) { - next if ( $file->{filehash} eq "deleted" and not defined ( $state->{entries}{$file->{name}} ) ); - next unless ( $file->{name} =~ /^$arg\// or $file->{name} eq $arg ); - push @{$state->{args}}, $file->{name}; + addDirMapFiles($updater,$outNameMap,$outDirMap, + $revDirMap->{$fullPath}); } + if( defined($otherRevDirMap) && + defined($otherRevDirMap->{$fullPath}) ) + { + addDirMapFiles($updater,$outNameMap,$outDirMap, + $otherRevDirMap->{$fullPath}); + } + } +} - shift @{$state->{args}} if ( scalar(@{$state->{args}}) > 1 ); - } else { - $log->info("Only one arg specified, populating file list automatically"); +# Used by argsfromdir +# Add entries from dirMap to outNameMap. Also recurse into entries +# that are subdirectories. +sub addDirMapFiles +{ + my($updater,$outNameMap,$outDirMap,$dirMap)=@_; - $state->{args} = []; + my($fullName); + foreach $fullName (keys(%$dirMap)) + { + my $cleanName=$fullName; + if(defined($state->{prependdir})) + { + if(!($cleanName=~s/^\Q$state->{prependdir}\E//)) + { + $log->fatal("internal error stripping prependdir"); + die "internal error stripping prependdir"; + } + } - foreach my $file ( @gethead ) + if($dirMap->{$fullName} eq "F") + { + $outNameMap->{$cleanName}=1; + } + elsif($dirMap->{$fullName} eq "D") + { + if(!$state->{opt}{l}) + { + expandArg($updater,$outNameMap,$outDirMap,$cleanName,1); + } + } + else { - next if ( $file->{filehash} eq "deleted" and not defined ( $state->{entries}{$file->{name}} ) ); - next unless ( $file->{name} =~ s/^$state->{prependdir}// ); - push @{$state->{args}}, $file->{name}; + $log->fatal("internal error in addDirMapFiles"); + die "internal error in addDirMapFiles"; } } } +# This method replaces $state->{args} with a directory-expanded +# list of all relevant filenames (recursively unless -d), based +# on $state->{entries}, and the "current" list of files in +# each directory. "Current" files as determined by +# either the requested (-r/-A) or "req_Sticky" version of +# that directory. +# Both the input args and the new output args are relative +# to the cvs-client's CWD, although some of the internal +# computations are relative to the top of the project. +sub argsfromdir +{ + my $updater = shift; + + # Notes about requirements for specific callers: + # update # "standard" case (entries; a single -r/-A/default; -l) + # # Special case: -d for create missing directories. + # diff # 0 or 1 -r's: "standard" case. + # # 2 -r's: We could ignore entries (just use the two -r's), + # # but it doesn't really matter. + # annotate # "standard" case + # log # Punting: log -r has a more complex non-"standard" + # # meaning, and we don't currently try to support log'ing + # # branches at all (need a lot of work to + # # support CVS-consistent branch relative version + # # numbering). +#HERE: But we still want to expand directories. Maybe we should +# essentially force "-A". + # status # "standard", except that -r/-A/default are not possible. + # # Mostly only used to expand entries only) + # + # Don't use argsfromdir at all: + # add # Explicit arguments required. Directory args imply add + # # the directory itself, not the files in it. + # co # Obtain list directly. + # remove # HERE: TEST: MAYBE client does the recursion for us, + # # since it only makes sense to remove stuff already in + # # the sandobx? + # ci # HERE: Similar to remove... + # # Don't try to implement the confusing/weird + # # ci -r bug er.."feature". + + if(scalar(@{$state->{args}})==0) + { + $state->{args} = [ "." ]; + } + my %allArgs; + my %allDirs; + for my $file (@{$state->{args}}) + { + expandArg($updater,\%allArgs,\%allDirs,$file); + } + + # Include any entries from sandbox. Generally client won't + # send entries that shouldn't be used. + foreach my $file (keys %{$state->{entries}}) + { + $allArgs{remove_prependdir($file)} = 1; + } + + $state->{dirArgs} = \%allDirs; + $state->{args} = [ + sort { + # Sort priority: by directory depth, then actual file name: + my @piecesA=split('/',$a); + my @piecesB=split('/',$b); + + my $count=scalar(@piecesA); + my $tmp=scalar(@piecesB); + return $count<=>$tmp if($count!=$tmp); + + for($tmp=0;$tmp<$count;$tmp++) + { + if($piecesA[$tmp] ne $piecesB[$tmp]) + { + return $piecesA[$tmp] cmp $piecesB[$tmp] + } + } + return 0; + } keys(%allArgs) ]; +} ## look up directory sticky tag, of either fullPath or a parent: sub getDirStickyInfo @@ -2383,6 +2550,7 @@ sub getStickyTagOrDate sub statecleanup { $state->{files} = []; + $state->{dirArgs} = {}; $state->{args} = []; $state->{arguments} = []; $state->{entries} = {}; From 61717661e6ffc8aa4d0928d04edafa0961e6aadb Mon Sep 17 00:00:00 2001 From: Matthew Ogilvie Date: Sat, 13 Oct 2012 23:42:31 -0600 Subject: [PATCH 08/18] cvsserver: support -r and sticky tags for most operations - Split off prepDirForOutput for "update" and "commit". Some low level protocol details were changed to more closely resemble CVS even in non-tagged cases. Hopefully it still works with finicky clients like Eclipse. - Substantial changes to "diff". The output is now closer to standard CVS (including exit status), and can be used as a patch, but there are still a number of differences compared to CVS. - Tweaks to "add", "remove", "status", and "commit". - FUTURE: CVS revision numbers for branches simply encode git commit IDs in a way that resembles CVS revision numbers, dropping all normal CVS structural relations between different revision numbers. - FUTURE: "log" doesn't try to work properly at all with branches and tags. - FUTURE: "annotate" probably doesn't work with branches or tags either (untested)? Signed-off-by: Matthew Ogilvie Signed-off-by: Junio C Hamano --- git-cvsserver.perl | 713 ++++++++++++++++++++++++++++++++------------- 1 file changed, 510 insertions(+), 203 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 5e558d137..367907498 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -611,7 +611,10 @@ sub req_add { $filename = filecleanup($filename); - my $meta = $updater->getmeta($filename); + # no -r, -A, or -D with add + my $stickyInfo = resolveStickyInfo($filename); + + my $meta = $updater->getmeta($filename,$stickyInfo); my $wrev = revparse($filename); if ($wrev && $meta && ($wrev=~/^-/)) @@ -634,8 +637,10 @@ sub req_add # this is an "entries" line my $kopts = kopts_from_path($filename,"sha1",$meta->{filehash}); - $log->debug("/$filepart/$meta->{revision}//$kopts/"); - print "/$filepart/$meta->{revision}//$kopts/\n"; + my $entryLine = "/$filepart/$meta->{revision}//$kopts/"; + $entryLine .= getStickyTagOrDate($stickyInfo); + $log->debug($entryLine); + print "$entryLine\n"; # permissions $log->debug("SEND : u=$meta->{mode},g=$meta->{mode},o=$meta->{mode}"); print "u=$meta->{mode},g=$meta->{mode},o=$meta->{mode}\n"; @@ -666,7 +671,8 @@ sub req_add print "$filename\n"; my $kopts = kopts_from_path($filename,"file", $state->{entries}{$filename}{modified_filename}); - print "/$filepart/0//$kopts/\n"; + print "/$filepart/0//$kopts/" . + getStickyTagOrDate($stickyInfo) . "\n"; my $requestedKopts = $state->{opt}{k}; if(defined($requestedKopts)) @@ -734,7 +740,10 @@ sub req_remove next; } - my $meta = $updater->getmeta($filename); + # only from entries + my $stickyInfo = resolveStickyInfo($filename); + + my $meta = $updater->getmeta($filename,$stickyInfo); my $wrev = revparse($filename); unless ( defined ( $wrev ) ) @@ -764,7 +773,7 @@ sub req_remove print "Checked-in $dirpart\n"; print "$filename\n"; my $kopts = kopts_from_path($filename,"sha1",$meta->{filehash}); - print "/$filepart/-$wrev//$kopts/\n"; + print "/$filepart/-$wrev//$kopts/" . getStickyTagOrDate($stickyInfo) . "\n"; $rmcount++; } @@ -944,6 +953,9 @@ sub req_co return 1; } + my $stickyInfo = { 'tag' => $state->{opt}{r}, + 'date' => $state->{opt}{D} }; + my $module = $state->{args}[0]; $state->{module} = $module; my $checkout_path = $module; @@ -961,64 +973,32 @@ sub req_co my $updater = GITCVS::updater->new($state->{CVSROOT}, $module, $log); $updater->update(); - $checkout_path =~ s|/$||; # get rid of trailing slashes + my $headHash; + if( defined($stickyInfo) && defined($stickyInfo->{tag}) ) + { + $headHash = $updater->lookupCommitRef($stickyInfo->{tag}); + if( !defined($headHash) ) + { + print "error 1 no such tag `$stickyInfo->{tag}'\n"; + cleanupWorkTree(); + exit; + } + } - # Eclipse seems to need the Clear-sticky command - # to prepare the 'Entries' file for the new directory. - print "Clear-sticky $checkout_path/\n"; - print $state->{CVSROOT} . "/$module/\n"; - print "Clear-static-directory $checkout_path/\n"; - print $state->{CVSROOT} . "/$module/\n"; - print "Clear-sticky $checkout_path/\n"; # yes, twice - print $state->{CVSROOT} . "/$module/\n"; - print "Template $checkout_path/\n"; - print $state->{CVSROOT} . "/$module/\n"; - print "0\n"; - - # instruct the client that we're checking out to $checkout_path - print "E cvs checkout: Updating $checkout_path\n"; + $checkout_path =~ s|/$||; # get rid of trailing slashes my %seendirs = (); my $lastdir =''; - # recursive - sub prepdir { - my ($dir, $repodir, $remotedir, $seendirs) = @_; - my $parent = dirname($dir); - $dir =~ s|/+$||; - $repodir =~ s|/+$||; - $remotedir =~ s|/+$||; - $parent =~ s|/+$||; - $log->debug("announcedir $dir, $repodir, $remotedir" ); - - if ($parent eq '.' || $parent eq './') { - $parent = ''; - } - # recurse to announce unseen parents first - if (length($parent) && !exists($seendirs->{$parent})) { - prepdir($parent, $repodir, $remotedir, $seendirs); - } - # Announce that we are going to modify at the parent level - if ($parent) { - print "E cvs checkout: Updating $remotedir/$parent\n"; - } else { - print "E cvs checkout: Updating $remotedir\n"; - } - print "Clear-sticky $remotedir/$parent/\n"; - print "$repodir/$parent/\n"; - - print "Clear-static-directory $remotedir/$dir/\n"; - print "$repodir/$dir/\n"; - print "Clear-sticky $remotedir/$parent/\n"; # yes, twice - print "$repodir/$parent/\n"; - print "Template $remotedir/$dir/\n"; - print "$repodir/$dir/\n"; - print "0\n"; - - $seendirs->{$dir} = 1; - } - - foreach my $git ( @{$updater->gethead} ) + prepDirForOutput( + ".", + $state->{CVSROOT} . "/$module", + $checkout_path, + \%seendirs, + 'checkout', + $state->{dirArgs} ); + + foreach my $git ( @{$updater->getAnyHead($headHash)} ) { # Don't want to check out deleted files next if ( $git->{filehash} eq "deleted" ); @@ -1026,16 +1006,13 @@ sub req_co my $fullName = $git->{name}; ( $git->{name}, $git->{dir} ) = filenamesplit($git->{name}); - if (length($git->{dir}) && $git->{dir} ne './' - && $git->{dir} ne $lastdir ) { - unless (exists($seendirs{$git->{dir}})) { - prepdir($git->{dir}, $state->{CVSROOT} . "/$module/", - $checkout_path, \%seendirs); - $lastdir = $git->{dir}; - $seendirs{$git->{dir}} = 1; - } - print "E cvs checkout: Updating /$checkout_path/$git->{dir}\n"; - } + unless (exists($seendirs{$git->{dir}})) { + prepDirForOutput($git->{dir}, $state->{CVSROOT} . "/$module/", + $checkout_path, \%seendirs, 'checkout', + $state->{dirArgs} ); + $lastdir = $git->{dir}; + $seendirs{$git->{dir}} = 1; + } # modification time of this file print "Mod-time $git->{modified}\n"; @@ -1055,7 +1032,8 @@ sub req_co # this is an "entries" line my $kopts = kopts_from_path($fullName,"sha1",$git->{filehash}); - print "/$git->{name}/$git->{revision}//$kopts/\n"; + print "/$git->{name}/$git->{revision}//$kopts/" . + getStickyTagOrDate($stickyInfo) . "\n"; # permissions print "u=$git->{mode},g=$git->{mode},o=$git->{mode}\n"; @@ -1068,6 +1046,119 @@ sub req_co statecleanup(); } +# used by req_co and req_update to set up directories for files +# recursively handles parents +sub prepDirForOutput +{ + my ($dir, $repodir, $remotedir, $seendirs, $request, $dirArgs) = @_; + + my $parent = dirname($dir); + $dir =~ s|/+$||; + $repodir =~ s|/+$||; + $remotedir =~ s|/+$||; + $parent =~ s|/+$||; + + if ($parent eq '.' || $parent eq './') + { + $parent = ''; + } + # recurse to announce unseen parents first + if( length($parent) && + !exists($seendirs->{$parent}) && + ( $request eq "checkout" || + exists($dirArgs->{$parent}) ) ) + { + prepDirForOutput($parent, $repodir, $remotedir, + $seendirs, $request, $dirArgs); + } + # Announce that we are going to modify at the parent level + if ($dir eq '.' || $dir eq './') + { + $dir = ''; + } + if(exists($seendirs->{$dir})) + { + return; + } + $log->debug("announcedir $dir, $repodir, $remotedir" ); + my($thisRemoteDir,$thisRepoDir); + if ($dir ne "") + { + $thisRepoDir="$repodir/$dir"; + if($remotedir eq ".") + { + $thisRemoteDir=$dir; + } + else + { + $thisRemoteDir="$remotedir/$dir"; + } + } + else + { + $thisRepoDir=$repodir; + $thisRemoteDir=$remotedir; + } + unless ( $state->{globaloptions}{-Q} || $state->{globaloptions}{-q} ) + { + print "E cvs $request: Updating $thisRemoteDir\n"; + } + + my ($opt_r)=$state->{opt}{r}; + my $stickyInfo; + if(exists($state->{opt}{A})) + { + # $stickyInfo=undef; + } + elsif( defined($opt_r) && $opt_r ne "" ) + # || ( defined($state->{opt}{D}) && $state->{opt}{D} ne "" ) # TODO + { + $stickyInfo={ 'tag' => (defined($opt_r)?$opt_r:undef) }; + + # TODO: Convert -D value into the form 2011.04.10.04.46.57, + # similar to an entry line's sticky date, without the D prefix. + # It sometimes (always?) arrives as something more like + # '10 Apr 2011 04:46:57 -0000'... + # $stickyInfo={ 'date' => (defined($stickyDate)?$stickyDate:undef) }; + } + else + { + $stickyInfo=getDirStickyInfo($state->{prependdir} . $dir); + } + + my $stickyResponse; + if(defined($stickyInfo)) + { + $stickyResponse = "Set-sticky $thisRemoteDir/\n" . + "$thisRepoDir/\n" . + getStickyTagOrDate($stickyInfo) . "\n"; + } + else + { + $stickyResponse = "Clear-sticky $thisRemoteDir/\n" . + "$thisRepoDir/\n"; + } + + unless ( $state->{globaloptions}{-n} ) + { + print $stickyResponse; + + print "Clear-static-directory $thisRemoteDir/\n"; + print "$thisRepoDir/\n"; + print $stickyResponse; # yes, twice + print "Template $thisRemoteDir/\n"; + print "$thisRepoDir/\n"; + print "0\n"; + } + + $seendirs->{$dir} = 1; + + # FUTURE: This would more accurately emulate CVS by sending + # another copy of sticky after processing the files in that + # directory. Or intermediate: perhaps send all sticky's for + # $seendirs after after processing all files. +} + # update \n # Response expected: yes. Actually do a cvs update command. This uses any # previous Argument, Directory, Entry, or Modified requests, if they have @@ -1111,29 +1202,19 @@ sub req_update #$log->debug("update state : " . Dumper($state)); - my $last_dirname = "///"; + my($repoDir); + $repoDir=$state->{CVSROOT} . "/$state->{module}/$state->{prependdir}"; + + my %seendirs = (); # foreach file specified on the command line ... - foreach my $filename ( @{$state->{args}} ) + foreach my $argsFilename ( @{$state->{args}} ) { - $filename = filecleanup($filename); + my $filename; + $filename = filecleanup($argsFilename); $log->debug("Processing file $filename"); - unless ( $state->{globaloptions}{-Q} || $state->{globaloptions}{-q} ) - { - my $cur_dirname = dirname($filename); - if ( $cur_dirname ne $last_dirname ) - { - $last_dirname = $cur_dirname; - if ( $cur_dirname eq "" ) - { - $cur_dirname = "."; - } - print "E cvs update: Updating $cur_dirname\n"; - } - } - # if we have a -C we should pretend we never saw modified stuff if ( exists ( $state->{opt}{C} ) ) { @@ -1142,13 +1223,11 @@ sub req_update $state->{entries}{$filename}{unchanged} = 1; } - my $meta; - if ( defined($state->{opt}{r}) and $state->{opt}{r} =~ /^(1\.\d+)$/ ) - { - $meta = $updater->getmeta($filename, $1); - } else { - $meta = $updater->getmeta($filename); - } + my $stickyInfo = resolveStickyInfo($filename, + $state->{opt}{r}, + $state->{opt}{D}, + exists($state->{opt}{A})); + my $meta = $updater->getmeta($filename, $stickyInfo); # If -p was given, "print" the contents of the requested revision. if ( exists ( $state->{opt}{p} ) ) { @@ -1161,6 +1240,17 @@ sub req_update next; } + # Directories: + prepDirForOutput( + dirname($argsFilename), + $repoDir, + ".", + \%seendirs, + "update", + $state->{dirArgs} ); + + my $wrev = revparse($filename); + if ( ! defined $meta ) { $meta = { @@ -1168,16 +1258,23 @@ sub req_update revision => '0', filehash => 'added' }; + if($wrev ne "0") + { + $meta->{filehash}='deleted'; + } } my $oldmeta = $meta; - my $wrev = revparse($filename); - # If the working copy is an old revision, lets get that version too for comparison. - if ( defined($wrev) and $wrev ne $meta->{revision} ) + my $oldWrev=$wrev; + if(defined($oldWrev)) { - $oldmeta = $updater->getmeta($filename, $wrev); + $oldWrev=~s/^-//; + if($oldWrev ne $meta->{revision}) + { + $oldmeta = $updater->getmeta($filename, $oldWrev); + } } #$log->debug("Target revision is $meta->{revision}, current working revision is $wrev"); @@ -1195,6 +1292,7 @@ sub req_update if ( defined ( $wrev ) and defined($meta->{revision}) and $wrev eq $meta->{revision} + and $wrev ne "0" and defined($state->{entries}{$filename}{modified_hash}) and not exists ( $state->{opt}{C} ) ) { @@ -1205,7 +1303,7 @@ sub req_update next; } - if ( $meta->{filehash} eq "deleted" ) + if ( $meta->{filehash} eq "deleted" && $wrev ne "0" ) { # TODO: If it has been modified in the sandbox, error out # with the appropriate message, rather than deleting a modified @@ -1267,10 +1365,6 @@ sub req_update $log->debug("Updating existing file 'Update-existing $dirpart'"); } else { # instruct client we're sending a file to put in this path as a new file - print "Clear-static-directory $dirpart\n"; - print $state->{CVSROOT} . "/$state->{module}/$dirpart\n"; - print "Clear-sticky $dirpart\n"; - print $state->{CVSROOT} . "/$state->{module}/$dirpart\n"; $log->debug("Creating new file 'Created $dirpart'"); print "Created $dirpart\n"; @@ -1279,8 +1373,10 @@ sub req_update # this is an "entries" line my $kopts = kopts_from_path($filename,"sha1",$meta->{filehash}); - $log->debug("/$filepart/$meta->{revision}//$kopts/"); - print "/$filepart/$meta->{revision}//$kopts/\n"; + my $entriesLine = "/$filepart/$meta->{revision}//$kopts/"; + $entriesLine .= getStickyTagOrDate($stickyInfo); + $log->debug($entriesLine); + print "$entriesLine\n"; # permissions $log->debug("SEND : u=$meta->{mode},g=$meta->{mode},o=$meta->{mode}"); @@ -1328,7 +1424,9 @@ sub req_update my $kopts = kopts_from_path("$dirpart/$filepart", "file",$mergedFile); $log->debug("/$filepart/$meta->{revision}//$kopts/"); - print "/$filepart/$meta->{revision}//$kopts/\n"; + my $entriesLine="/$filepart/$meta->{revision}//$kopts/"; + $entriesLine .= getStickyTagOrDate($stickyInfo); + print "$entriesLine\n"; } } elsif ( $return == 1 ) @@ -1344,7 +1442,9 @@ sub req_update print $state->{CVSROOT} . "/$state->{module}/$filename\n"; my $kopts = kopts_from_path("$dirpart/$filepart", "file",$mergedFile); - print "/$filepart/$meta->{revision}/+/$kopts/\n"; + my $entriesLine = "/$filepart/$meta->{revision}/+/$kopts/"; + $entriesLine .= getStickyTagOrDate($stickyInfo); + print "$entriesLine\n"; } } else @@ -1372,6 +1472,43 @@ sub req_update } + # prepDirForOutput() any other existing directories unless they already + # have the right sticky tag: + unless ( $state->{globaloptions}{n} ) + { + my $dir; + foreach $dir (keys(%{$state->{dirMap}})) + { + if( ! $seendirs{$dir} && + exists($state->{dirArgs}{$dir}) ) + { + my($oldTag); + $oldTag=$state->{dirMap}{$dir}{tagspec}; + + unless( ( exists($state->{opt}{A}) && + defined($oldTag) ) || + ( defined($state->{opt}{r}) && + ( !defined($oldTag) || + $state->{opt}{r} ne $oldTag ) ) ) + # TODO?: OR sticky dir is different... + { + next; + } + + prepDirForOutput( + $dir, + $repoDir, + ".", + \%seendirs, + 'update', + $state->{dirArgs} ); + } + + # TODO?: Consider sending a final duplicate Sticky response + # to more closely mimic real CVS. + } + } + print "ok\n"; } @@ -1404,23 +1541,11 @@ sub req_ci my $updater = GITCVS::updater->new($state->{CVSROOT}, $state->{module}, $log); $updater->update(); - # Remember where the head was at the beginning. - my $parenthash = `git show-ref -s refs/heads/$state->{module}`; - chomp $parenthash; - if ($parenthash !~ /^[0-9a-f]{40}$/) { - print "error 1 pserver cannot find the current HEAD of module"; - cleanupWorkTree(); - exit; - } - - setupWorkTree($parenthash); - - $log->info("Lockless commit start, basing commit on '$work->{workDir}', index file is '$work->{index}'"); - - $log->info("Created index '$work->{index}' for head $state->{module} - exit status $?"); - my @committedfiles = (); my %oldmeta; + my $stickyInfo; + my $branchRef; + my $parenthash; # foreach file specified on the command line ... foreach my $filename ( @{$state->{args}} ) @@ -1430,7 +1555,67 @@ sub req_ci next unless ( exists $state->{entries}{$filename}{modified_filename} or not $state->{entries}{$filename}{unchanged} ); - my $meta = $updater->getmeta($filename); + ##### + # Figure out which branch and parenthash we are committing + # to, and setup worktree: + + # should always come from entries: + my $fileStickyInfo = resolveStickyInfo($filename); + if( !defined($branchRef) ) + { + $stickyInfo = $fileStickyInfo; + if( defined($stickyInfo) && + ( defined($stickyInfo->{date}) || + !defined($stickyInfo->{tag}) ) ) + { + print "error 1 cannot commit with sticky date for file `$filename'\n"; + cleanupWorkTree(); + exit; + } + + $branchRef = "refs/heads/$state->{module}"; + if ( defined($stickyInfo) && defined($stickyInfo->{tag}) ) + { + $branchRef = "refs/heads/$stickyInfo->{tag}"; + } + + $parenthash = `git show-ref -s $branchRef`; + chomp $parenthash; + if ($parenthash !~ /^[0-9a-f]{40}$/) + { + if ( defined($stickyInfo) && defined($stickyInfo->{tag}) ) + { + print "error 1 sticky tag `$stickyInfo->{tag}' for file `$filename' is not a branch\n"; + } + else + { + print "error 1 pserver cannot find the current HEAD of module"; + } + cleanupWorkTree(); + exit; + } + + setupWorkTree($parenthash); + + $log->info("Lockless commit start, basing commit on '$work->{workDir}', index file is '$work->{index}'"); + + $log->info("Created index '$work->{index}' for head $state->{module} - exit status $?"); + } + elsif( !refHashEqual($stickyInfo,$fileStickyInfo) ) + { + #TODO: We could split the cvs commit into multiple + # git commits by distinct stickyTag values, but that + # is lowish priority. + print "error 1 Committing different files to different" + . " branches is not currently supported\n"; + cleanupWorkTree(); + exit; + } + + ##### + # Process this file: + + my $meta = $updater->getmeta($filename,$stickyInfo); $oldmeta{$filename} = $meta; my $wrev = revparse($filename); @@ -1532,7 +1717,7 @@ sub req_ci } ### Emulate git-receive-pack by running hooks/update - my @hook = ( $ENV{GIT_DIR}.'hooks/update', "refs/heads/$state->{module}", + my @hook = ( $ENV{GIT_DIR}.'hooks/update', $branchRef, $parenthash, $commithash ); if( -x $hook[0] ) { unless( system( @hook ) == 0 ) @@ -1546,7 +1731,7 @@ sub req_ci ### Update the ref if (system(qw(git update-ref -m), "cvsserver ci", - "refs/heads/$state->{module}", $commithash, $parenthash)) { + $branchRef, $commithash, $parenthash)) { $log->warn("update-ref for $state->{module} failed."); print "error 1 Cannot commit -- update first\n"; cleanupWorkTree(); @@ -1560,7 +1745,7 @@ sub req_ci local $SIG{PIPE} = sub { die 'pipe broke' }; - print $pipe "$parenthash $commithash refs/heads/$state->{module}\n"; + print $pipe "$parenthash $commithash $branchRef\n"; close $pipe || die "bad pipe: $! $?"; } @@ -1570,7 +1755,7 @@ sub req_ci ### Then hooks/post-update $hook = $ENV{GIT_DIR}.'hooks/post-update'; if (-x $hook) { - system($hook, "refs/heads/$state->{module}"); + system($hook, $branchRef); } # foreach file specified on the command line ... @@ -1578,7 +1763,7 @@ sub req_ci { $filename = filecleanup($filename); - my $meta = $updater->getmeta($filename); + my $meta = $updater->getmeta($filename,$stickyInfo); unless (defined $meta->{revision}) { $meta->{revision} = "1.1"; } @@ -1602,7 +1787,8 @@ sub req_ci print "Checked-in $dirpart\n"; print "$filename\n"; my $kopts = kopts_from_path($filename,"sha1",$meta->{filehash}); - print "/$filepart/$meta->{revision}//$kopts/\n"; + print "/$filepart/$meta->{revision}//$kopts/" . + getStickyTagOrDate($stickyInfo) . "\n"; } } @@ -1639,16 +1825,19 @@ sub req_status next; } - my $meta = $updater->getmeta($filename); - my $oldmeta = $meta; - my $wrev = revparse($filename); + my $stickyInfo = resolveStickyInfo($filename); + my $meta = $updater->getmeta($filename,$stickyInfo); + my $oldmeta = $meta; + # If the working copy is an old revision, lets get that # version too for comparison. if ( defined($wrev) and $wrev ne $meta->{revision} ) { - $oldmeta = $updater->getmeta($filename, $wrev); + my($rmRev)=$wrev; + $rmRev=~s/^-//; + $oldmeta = $updater->getmeta($filename, $rmRev); } # TODO : All possible statuses aren't yet implemented @@ -1691,6 +1880,7 @@ sub req_status # same revision but there are local changes if ( defined ( $wrev ) and defined($meta->{revision}) and $wrev eq $meta->{revision} and + $wrev ne "0" and $state->{entries}{$filename}{modified_filename} ) { $status ||= "Locally Modified"; @@ -1706,7 +1896,8 @@ sub req_status } if ( defined ( $state->{entries}{$filename}{revision} ) and - not defined ( $meta->{revision} ) ) + ( !defined($meta->{revision}) || + $meta->{revision} eq "0" ) ) { $status ||= "Locally Added"; } @@ -1802,98 +1993,133 @@ sub req_diff # be providing status on ... argsfromdir($updater); + my($foundDiff); + # foreach file specified on the command line ... - foreach my $filename ( @{$state->{args}} ) + foreach my $argFilename ( @{$state->{args}} ) { - $filename = filecleanup($filename); + my($filename) = filecleanup($argFilename); my ( $fh, $file1, $file2, $meta1, $meta2, $filediff ); my $wrev = revparse($filename); - # We need _something_ to diff against - next unless ( defined ( $wrev ) ); + # Priority for revision1: + # 1. First -r (missing file: check -N) + # 2. wrev from client's Entry line + # - missing line/file: check -N + # - "0": added file not committed (empty contents for rev1) + # - Prefixed with dash (to be removed): check -N - # if we have a -r switch, use it if ( defined ( $revision1 ) ) { - ( undef, $file1 ) = tempfile( DIR => $TEMP_DIR, OPEN => 0 ); $meta1 = $updater->getmeta($filename, $revision1); - unless ( defined ( $meta1 ) and $meta1->{filehash} ne "deleted" ) + } + elsif( defined($wrev) && $wrev ne "0" ) + { + my($rmRev)=$wrev; + $rmRev=~s/^-//; + $meta1 = $updater->getmeta($filename, $rmRev); + } + if ( !defined($meta1) || + $meta1->{filehash} eq "deleted" ) + { + if( !exists($state->{opt}{N}) ) { - print "E File $filename at revision $revision1 doesn't exist\n"; + if(!defined($revision1)) + { + print "E File $filename at revision $revision1 doesn't exist\n"; + } next; } - 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}, { targetfile => $file1 }); + elsif( !defined($meta1) ) + { + $meta1 = { + name => $filename, + revision => '0', + filehash => 'deleted' + }; + } } + # Priority for revision2: + # 1. Second -r (missing file: check -N) + # 2. Modified file contents from client + # 3. wrev from client's Entry line + # - missing line/file: check -N + # - Prefixed with dash (to be removed): check -N + # if we have a second -r switch, use it too if ( defined ( $revision2 ) ) { - ( undef, $file2 ) = tempfile( DIR => $TEMP_DIR, OPEN => 0 ); $meta2 = $updater->getmeta($filename, $revision2); - - unless ( defined ( $meta2 ) and $meta2->{filehash} ne "deleted" ) - { - print "E File $filename at revision $revision2 doesn't exist\n"; - next; - } - - transmitfile($meta2->{filehash}, { targetfile => $file2 }); } - # otherwise we just use the working copy - else + elsif(defined($state->{entries}{$filename}{modified_filename})) { $file2 = $state->{entries}{$filename}{modified_filename}; + $meta2 = { + name => $filename, + revision => '0', + filehash => 'modified' + }; } - - # if we have been given -r, and we don't have a $file2 yet, lets - # get one - if ( defined ( $revision1 ) and not defined ( $file2 ) ) + elsif( defined($wrev) && ($wrev!~/^-/) ) { - ( undef, $file2 ) = tempfile( DIR => $TEMP_DIR, OPEN => 0 ); + if(!defined($revision1)) # no revision and no modifications: + { + next; + } $meta2 = $updater->getmeta($filename, $wrev); - transmitfile($meta2->{filehash}, { targetfile => $file2 }); + } + if(!defined($file2)) + { + if ( !defined($meta2) || + $meta2->{filehash} eq "deleted" ) + { + if( !exists($state->{opt}{N}) ) + { + if(!defined($revision2)) + { + print "E File $filename at revision $revision2 doesn't exist\n"; + } + next; + } + elsif( !defined($meta2) ) + { + $meta2 = { + name => $filename, + revision => '0', + filehash => 'deleted' + }; + } + } } - # We need to have retrieved something useful - next unless ( defined ( $meta1 ) ); - - # Files to date if the working copy and repo copy have the same - # revision, and the working copy is unmodified - if ( not defined ( $meta2 ) and $wrev eq $meta1->{revision} and - ( ( $state->{entries}{$filename}{unchanged} and - ( not defined ( $state->{entries}{$filename}{conflict} ) or - $state->{entries}{$filename}{conflict} !~ /^\+=/ ) ) or - ( defined($state->{entries}{$filename}{modified_hash}) and - $state->{entries}{$filename}{modified_hash} eq - $meta1->{filehash} ) ) ) + if( $meta1->{filehash} eq $meta2->{filehash} ) { + $log->info("unchanged $filename"); next; } - # Apparently we only show diffs for locally modified files - unless ( defined($meta2) or - defined ( $state->{entries}{$filename}{modified_filename} ) ) + # Retrieve revision contents: + ( undef, $file1 ) = tempfile( DIR => $TEMP_DIR, OPEN => 0 ); + transmitfile($meta1->{filehash}, { targetfile => $file1 }); + + if(!defined($file2)) { - next; + ( undef, $file2 ) = tempfile( DIR => $TEMP_DIR, OPEN => 0 ); + transmitfile($meta2->{filehash}, { targetfile => $file2 }); } - print "M Index: $filename\n"; + # Generate the actual diff: + print "M Index: $argFilename\n"; print "M =======" . ( "=" x 60 ) . "\n"; print "M RCS file: $state->{CVSROOT}/$state->{module}/$filename,v\n"; - if ( defined ( $meta1 ) ) + if ( defined ( $meta1 ) && $meta1->{revision} ne "0" ) { print "M retrieving revision $meta1->{revision}\n" } - if ( defined ( $meta2 ) ) + if ( defined ( $meta2 ) && $meta2->{revision} ne "0" ) { print "M retrieving revision $meta2->{revision}\n" } @@ -1914,33 +2140,73 @@ sub req_diff } } } - print "$filename\n"; + print "$argFilename\n"; $log->info("Diffing $filename -r $meta1->{revision} -r " . ( $meta2->{revision} or "workingcopy" )); - ( $fh, $filediff ) = tempfile ( DIR => $TEMP_DIR ); - - if ( exists $state->{opt}{u} ) + # TODO: Use --label instead of -L because -L is no longer + # documented and may go away someday. Not sure if there there are + # versions that only support -L, which would make this change risky? + # http://osdir.com/ml/bug-gnu-utils-gnu/2010-12/msg00060.html + # ("man diff" should actually document the best migration strategy, + # [current behavior, future changes, old compatibility issues + # or lack thereof, etc], not just stop mentioning the option...) + # TODO: Real CVS seems to include a date in the label, before + # the revision part, without the keyword "revision". The following + # has minimal changes compared to original versions of + # git-cvsserver.perl. (Mostly tab vs space after filename.) + + my (@diffCmd) = ( 'diff' ); + if ( exists($state->{opt}{N}) ) { - system("diff -u -L '$filename revision $meta1->{revision}'" . - " -L '$filename " . - ( defined($meta2->{revision}) ? - "revision $meta2->{revision}" : - "working copy" ) . - "' $file1 $file2 > $filediff" ); - } else { - system("diff $file1 $file2 > $filediff"); + push @diffCmd,"-N"; } + if ( exists $state->{opt}{u} ) + { + push @diffCmd,("-u","-L"); + if( $meta1->{filehash} eq "deleted" ) + { + push @diffCmd,"/dev/null"; + } else { + push @diffCmd,("$argFilename\trevision $meta1->{revision}"); + } - while ( <$fh> ) + if( defined($meta2->{filehash}) ) + { + if( $meta2->{filehash} eq "deleted" ) + { + push @diffCmd,("-L","/dev/null"); + } else { + push @diffCmd,("-L", + "$argFilename\trevision $meta2->{revision}"); + } + } else { + push @diffCmd,("-L","$argFilename\tworking copy"); + } + } + push @diffCmd,($file1,$file2); + if(!open(DIFF,"-|",@diffCmd)) { - print "M $_"; + $log->warn("Unable to run diff: $!"); } - close $fh; + my($diffLine); + while(defined($diffLine=)) + { + print "M $diffLine"; + $foundDiff=1; + } + close(DIFF); } - print "ok\n"; + if($foundDiff) + { + print "error \n"; + } + else + { + print "ok\n"; + } } sub req_log @@ -2160,7 +2426,7 @@ sub argsplit $opt = { A => 0, N => 0, P => 0, R => 0, c => 0, f => 0, l => 0, n => 0, p => 0, s => 0, r => 1, D => 1, d => 1, k => 1, j => 1, } if ( $type eq "co" ); $opt = { v => 0, l => 0, R => 0 } if ( $type eq "status" ); $opt = { A => 0, P => 0, C => 0, d => 0, f => 0, l => 0, R => 0, p => 0, k => 1, r => 1, D => 1, j => 1, I => 1, W => 1 } if ( $type eq "update" ); - $opt = { l => 0, R => 0, k => 1, D => 1, D => 1, r => 2 } if ( $type eq "diff" ); + $opt = { l => 0, R => 0, k => 1, D => 1, D => 1, r => 2, N => 0 } if ( $type eq "diff" ); $opt = { c => 0, R => 0, l => 0, f => 0, F => 1, m => 1, r => 1 } if ( $type eq "ci" ); $opt = { k => 1, m => 1 } if ( $type eq "add" ); $opt = { f => 0, l => 0, R => 0 } if ( $type eq "remove" ); @@ -3101,6 +3367,45 @@ sub descramble return $ret; } +# Test if the (deep) values of two references to a hash are the same. +sub refHashEqual +{ + my($v1,$v2) = @_; + + my $out; + if(!defined($v1)) + { + if(!defined($v2)) + { + $out=1; + } + } + elsif( !defined($v2) || + scalar(keys(%{$v1})) != scalar(keys(%{$v2})) ) + { + # $out=undef; + } + else + { + $out=1; + + my $key; + foreach $key (keys(%{$v1})) + { + if( !exists($v2->{$key}) || + defined($v1->{$key}) ne defined($v2->{$key}) || + ( defined($v1->{$key}) && + $v1->{$key} ne $v2->{$key} ) ) + { + $out=undef; + last; + } + } + } + + return $out; +} + package GITCVS::log; @@ -3506,6 +3811,8 @@ sub update my $lastcommit = $self->_get_prop("last_commit"); if (defined $lastcommit && $lastcommit eq $commitsha1) { # up-to-date + # invalidate the gethead cache + $self->clearCommitRefCaches(); return 1; } From aa7aab3b0b4e001f10c5c7d8f721aaff8753cd88 Mon Sep 17 00:00:00 2001 From: Matthew Ogilvie Date: Sat, 13 Oct 2012 23:42:32 -0600 Subject: [PATCH 09/18] cvsserver: add t9402 to test branch and tag refs Signed-off-by: Matthew Ogilvie Signed-off-by: Junio C Hamano --- t/t9402-git-cvsserver-refs.sh | 558 ++++++++++++++++++++++++++++++++++ 1 file changed, 558 insertions(+) create mode 100755 t/t9402-git-cvsserver-refs.sh diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh new file mode 100755 index 000000000..858ef0fc4 --- /dev/null +++ b/t/t9402-git-cvsserver-refs.sh @@ -0,0 +1,558 @@ +#!/bin/sh + +test_description='git-cvsserver and git refspecs + +tests ability for git-cvsserver to switch between and compare +tags, branches and other git refspecs' + +. ./test-lib.sh + +######### + +check_start_tree() { + rm -f "$WORKDIR/check.list" + echo "start $1" >> "${WORKDIR}/check.log" +} + +check_file() { + sandbox="$1" + file="$2" + ver="$3" + GIT_DIR=$SERVERDIR git show "${ver}:${file}" \ + > "$WORKDIR/check.got" 2> "$WORKDIR/check.stderr" + test_cmp "$WORKDIR/check.got" "$sandbox/$file" + stat=$? + echo "check_file $sandbox $file $ver : $stat" >> "$WORKDIR/check.log" + echo "$file" >> "$WORKDIR/check.list" + return $stat +} + +check_end_tree() { + sandbox="$1" + expectCount=$(wc -l < "$WORKDIR/check.list") + cvsCount=$(find "$sandbox" -name CVS -prune -o -type f -print | wc -l) + test x"$cvsCount" = x"$expectCount" + stat=$? + echo "check_end $sandbox : $stat cvs=$cvsCount expect=$expectCount" \ + >> "$WORKDIR/check.log" + return $stat +} + +check_end_full_tree() { + sandbox="$1" + ver="$2" + expectCount=$(wc -l < "$WORKDIR/check.list") + cvsCount=$(find "$sandbox" -name CVS -prune -o -type f -print | wc -l) + gitCount=$(git ls-tree -r "$2" | wc -l) + test x"$cvsCount" = x"$expectCount" -a x"$gitCount" = x"$expectCount" + stat=$? + echo "check_end $sandbox : $stat cvs=$cvsCount git=$gitCount expect=$expectCount" \ + >> "$WORKDIR/check.log" + return $stat +} + +######### + +check_diff() { + diffFile="$1" + vOld="$2" + vNew="$3" + rm -rf diffSandbox + git clone -q -n . diffSandbox && + ( cd diffSandbox && + git checkout "$vOld" && + git apply -p0 --index <"../$diffFile" && + git diff --exit-code "$vNew" ) > check_diff_apply.out 2>&1 +} + +######### + +cvs >/dev/null 2>&1 +if test $? -ne 1 +then + skip_all='skipping git-cvsserver tests, cvs not found' + test_done +fi +if ! test_have_prereq PERL +then + skip_all='skipping git-cvsserver tests, perl not available' + test_done +fi +"$PERL_PATH" -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || { + skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable' + test_done +} + +unset GIT_DIR GIT_CONFIG +WORKDIR=$(pwd) +SERVERDIR=$(pwd)/gitcvs.git +git_config="$SERVERDIR/config" +CVSROOT=":fork:$SERVERDIR" +CVSWORK="$(pwd)/cvswork" +CVS_SERVER=git-cvsserver +export CVSROOT CVS_SERVER + +rm -rf "$CVSWORK" "$SERVERDIR" +test_expect_success 'setup v1, b1' ' + echo "Simple text file" > textfile.c && + echo "t2" > t2 && + mkdir adir && + echo "adir/afile line1" > adir/afile && + echo "adir/afile line2" >> adir/afile && + echo "adir/afile line3" >> adir/afile && + echo "adir/afile line4" >> adir/afile && + echo "adir/a2file" >> adir/a2file && + mkdir adir/bdir && + echo "adir/bdir/bfile line 1" > adir/bdir/bfile && + echo "adir/bdir/bfile line 2" >> adir/bdir/bfile && + echo "adir/bdir/b2file" > adir/bdir/b2file && + git add textfile.c t2 adir && + git commit -q -m "First Commit (v1)" && + git tag v1 && + git branch b1 && + git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 && + GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true && + GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" +' + +rm -rf cvswork +test_expect_success 'cvs co v1' ' + cvs -f -Q co -r v1 -d cvswork master >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_tree cvswork +' + +rm -rf cvswork +test_expect_success 'cvs co b1' ' + cvs -f co -r b1 -d cvswork master >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_tree cvswork +' + +test_expect_success 'cvs co b1 [cvswork3]' ' + cvs -f co -r b1 -d cvswork3 master >cvs.log 2>&1 && + check_start_tree cvswork3 && + check_file cvswork3 textfile.c v1 && + check_file cvswork3 t2 v1 && + check_file cvswork3 adir/afile v1 && + check_file cvswork3 adir/a2file v1 && + check_file cvswork3 adir/bdir/bfile v1 && + check_file cvswork3 adir/bdir/b2file v1 && + check_end_full_tree cvswork3 v1 +' + +test_expect_success 'edit cvswork3 and save diff' ' + ( cd cvswork3 && + sed -i -e "s/line1/line1 - data/" adir/afile && + echo "afile5" > adir/afile5 && + rm t2 && + cvs -f add adir/afile5 && + cvs -f rm t2 && + test_must_fail cvs -f diff -N -u >"$WORKDIR/cvswork3edit.diff" + ) +' + +test_expect_success 'setup v1.2 on b1' ' + git checkout b1 && + echo "new v1.2" > t3 && + rm t2 && + sed -i -e "s/line3/line3 - more data/" adir/afile && + rm adir/a2file && + echo "a3file" >> adir/a3file && + echo "bfile line 3" >> adir/bdir/bfile && + rm adir/bdir/b2file && + echo "b3file" > adir/bdir/b3file && + mkdir cdir && + echo "cdir/cfile" > cdir/cfile && + git add -A cdir adir t3 t2 && + git commit -q -m 'v1.2' && + git tag v1.2 && + git push --tags gitcvs.git b1:b1 +' + +test_expect_success 'cvs -f up (on b1 adir)' ' + ( cd cvswork/adir && + cvs -f up -d ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1.2 && + check_file cvswork adir/a3file v1.2 && + check_file cvswork adir/bdir/bfile v1.2 && + check_file cvswork adir/bdir/b3file v1.2 && + check_end_tree cvswork +' + +test_expect_success 'cvs up (on b1 /)' ' + ( cd cvswork && + cvs -f up -d ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1.2 && + check_file cvswork t3 v1.2 && + check_file cvswork adir/afile v1.2 && + check_file cvswork adir/a3file v1.2 && + check_file cvswork adir/bdir/bfile v1.2 && + check_file cvswork adir/bdir/b3file v1.2 && + check_file cvswork cdir/cfile v1.2 && + check_end_tree cvswork +' + +# Make sure "CVS/Tag" files didn't get messed up: +test_expect_success 'cvs up (on b1 /) (again; check CVS/Tag files)' ' + ( cd cvswork && + cvs -f up -d ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1.2 && + check_file cvswork t3 v1.2 && + check_file cvswork adir/afile v1.2 && + check_file cvswork adir/a3file v1.2 && + check_file cvswork adir/bdir/bfile v1.2 && + check_file cvswork adir/bdir/b3file v1.2 && + check_file cvswork cdir/cfile v1.2 && + check_end_tree cvswork +' + +# update to another version: +test_expect_success 'cvs up -r v1' ' + ( cd cvswork && + cvs -f up -r v1 ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_tree cvswork +' + +test_expect_success 'cvs up' ' + ( cd cvswork && + cvs -f up ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_tree cvswork +' + +test_expect_success 'cvs up (again; check CVS/Tag files)' ' + ( cd cvswork && + cvs -f up -d ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_tree cvswork +' + +test_expect_success 'setup simple b2' ' + git branch b2 v1 && + git push --tags gitcvs.git b2:b2 +' + +test_expect_success 'cvs co b2 [into cvswork2]' ' + cvs -f co -r b2 -d cvswork2 master >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_tree cvswork +' + +test_expect_success 'root dir edit [cvswork2]' ' + ( cd cvswork2 && + echo "Line 2" >> textfile.c && + test_must_fail cvs -f diff -u >"$WORKDIR/cvsEdit1.diff" && + cvs -f commit -m "edit textfile.c" textfile.c + ) >cvsEdit1.log 2>&1 +' + +test_expect_success 'root dir rm file [cvswork2]' ' + ( cd cvswork2 && + cvs -f rm -f t2 && + cvs -f diff -u > ../cvsEdit2-empty.diff && + test_must_fail cvs -f diff -N -u >"$WORKDIR/cvsEdit2-N.diff" && + cvs -f commit -m "rm t2" + ) > cvsEdit2.log 2>&1 +' + +test_expect_success 'subdir edit/add/rm files [cvswork2' ' + ( cd cvswork2 && + sed -i -e "s/line 1/line 1 (v2)/" adir/bdir/bfile && + rm adir/bdir/b2file && + cd adir && + cvs -f rm bdir/b2file && + echo "4th file" > bdir/b4file && + cvs -f add bdir/b4file && + test_must_fail cvs -f diff -N -u >"$WORKDIR/cvsEdit3.diff" && + git fetch gitcvs.git b2:b2 && + ( cd .. && + test_must_fail cvs -f diff -u -N -r v1.2 >"$WORKDIR/cvsEdit3-v1.2.diff" && + test_must_fail cvs -f diff -u -N -r v1.2 -r v1 >"$WORKDIR/cvsEdit3-v1.2-v1.diff" + ) && + cvs -f commit -m "various add/rm/edit" + ) >cvs.log 2>&1 +' + +test_expect_success 'validate result of edits [cvswork2]' ' + git fetch gitcvs.git b2:b2 && + git tag v2 b2 && + git push --tags gitcvs.git b2:b2 && + check_start_tree cvswork2 && + check_file cvswork2 textfile.c v2 && + check_file cvswork2 adir/afile v2 && + check_file cvswork2 adir/a2file v2 && + check_file cvswork2 adir/bdir/bfile v2 && + check_file cvswork2 adir/bdir/b4file v2 && + check_end_full_tree cvswork2 v2 +' + +test_expect_success 'validate basic diffs saved during above cvswork2 edits' ' + test $(grep Index: cvsEdit1.diff | wc -l) = 1 && + test ! -s cvsEdit2-empty.diff && + test $(grep Index: cvsEdit2-N.diff | wc -l) = 1 && + test $(grep Index: cvsEdit3.diff | wc -l) = 3 && + rm -rf diffSandbox && + git clone -q -n . diffSandbox && + ( cd diffSandbox && + git checkout v1 && + git apply -p0 --index <"$WORKDIR/cvsEdit1.diff" && + git apply -p0 --index <"$WORKDIR/cvsEdit2-N.diff" && + git apply -p0 --directory=adir --index <"$WORKDIR/cvsEdit3.diff" && + git diff --exit-code v2 ) >"check_diff_apply.out" 2>&1 +' + +test_expect_success 'validate v1.2 diff saved during last cvswork2 edit' ' + test $(grep Index: cvsEdit3-v1.2.diff | wc -l) = 9 && + check_diff cvsEdit3-v1.2.diff v1.2 v2 +' + +test_expect_success 'validate v1.2 v1 diff saved during last cvswork2 edit' ' + test $(grep Index: cvsEdit3-v1.2-v1.diff | wc -l) = 9 && + check_diff cvsEdit3-v1.2-v1.diff v1.2 v1 +' + +test_expect_success 'cvs up [cvswork2]' ' + ( cd cvswork2 && + cvs -f up ) >cvs.log 2>&1 && + check_start_tree cvswork2 && + check_file cvswork2 textfile.c v2 && + check_file cvswork2 adir/afile v2 && + check_file cvswork2 adir/a2file v2 && + check_file cvswork2 adir/bdir/bfile v2 && + check_file cvswork2 adir/bdir/b4file v2 && + check_end_full_tree cvswork2 v2 +' + +test_expect_success 'cvs up -r b2 [back to cvswork]' ' + ( cd cvswork && + cvs -f up -r b2 ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v2 && + check_file cvswork adir/afile v2 && + check_file cvswork adir/a2file v2 && + check_file cvswork adir/bdir/bfile v2 && + check_file cvswork adir/bdir/b4file v2 && + check_end_full_tree cvswork v2 +' + +test_expect_success 'cvs up -r b1' ' + ( cd cvswork && + cvs -f up -r b1 ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1.2 && + check_file cvswork t3 v1.2 && + check_file cvswork adir/afile v1.2 && + check_file cvswork adir/a3file v1.2 && + check_file cvswork adir/bdir/bfile v1.2 && + check_file cvswork adir/bdir/b3file v1.2 && + check_file cvswork cdir/cfile v1.2 && + check_end_full_tree cvswork v1.2 +' + +test_expect_success 'cvs up -A' ' + ( cd cvswork && + cvs -f up -A ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_full_tree cvswork v1 +' + +test_expect_success 'cvs up (check CVS/Tag files)' ' + ( cd cvswork && + cvs -f up ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_full_tree cvswork v1 +' + +# This is not really legal CVS, but it seems to work anyway: +test_expect_success 'cvs up -r heads/b1' ' + ( cd cvswork && + cvs -f up -r heads/b1 ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1.2 && + check_file cvswork t3 v1.2 && + check_file cvswork adir/afile v1.2 && + check_file cvswork adir/a3file v1.2 && + check_file cvswork adir/bdir/bfile v1.2 && + check_file cvswork adir/bdir/b3file v1.2 && + check_file cvswork cdir/cfile v1.2 && + check_end_full_tree cvswork v1.2 +' + +# But this should work even if CVS client checks -r more carefully: +test_expect_success 'cvs up -r heads_-s-b2 (cvsserver escape mechanism)' ' + ( cd cvswork && + cvs -f up -r heads_-s-b2 ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v2 && + check_file cvswork adir/afile v2 && + check_file cvswork adir/a2file v2 && + check_file cvswork adir/bdir/bfile v2 && + check_file cvswork adir/bdir/b4file v2 && + check_end_full_tree cvswork v2 +' + +v1hash=$(git rev-parse v1) +test_expect_success 'cvs up -r $(git rev-parse v1)' ' + test -n "$v1hash" && + ( cd cvswork && + cvs -f up -r "$v1hash" ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_full_tree cvswork v1 +' + +test_expect_success 'cvs diff -r v1 -u' ' + ( cd cvswork && + cvs -f diff -r v1 -u ) >cvsDiff.out 2>cvs.log && + test ! -s cvsDiff.out && + test ! -s cvs.log +' + +test_expect_success 'cvs diff -N -r v2 -u' ' + ( cd cvswork && + test_must_fail cvs -f diff -N -r v2 -u ) >cvsDiff.out 2>cvs.log && + test ! -s cvs.log && + test -s cvsDiff.out && + check_diff cvsDiff.out v2 v1 > check_diff.out 2>&1 +' + +test_expect_success 'cvs diff -N -r v2 -r v1.2' ' + ( cd cvswork && + test_must_fail cvs -f diff -N -r v2 -r v1.2 -u ) >cvsDiff.out 2>cvs.log && + test ! -s cvs.log && + test -s cvsDiff.out && + check_diff cvsDiff.out v2 v1.2 > check_diff.out 2>&1 +' + +test_expect_success 'apply early [cvswork3] diff to b3' ' + git clone -q . gitwork3 && + ( cd gitwork3 && + git checkout -b b3 v1 && + git apply -p0 --index <"$WORKDIR/cvswork3edit.diff" && + git commit -m "cvswork3 edits applied" ) && + git fetch gitwork3 b3:b3 && + git tag v3 b3 +' + +test_expect_success 'check [cvswork3] diff' ' + ( cd cvswork3 && + test_must_fail cvs -f diff -N -u ) >"$WORKDIR/cvsDiff.out" 2>cvs.log && + test ! -s cvs.log && + test -s cvsDiff.out && + test $(grep Index: cvsDiff.out | wc -l) = 3 && + test_cmp cvsDiff.out cvswork3edit.diff && + check_diff cvsDiff.out v1 v3 > check_diff.out 2>&1 +' + +test_expect_success 'merge early [cvswork3] b3 with b1' ' + ( cd gitwork3 && + git merge "message" HEAD b1 ) + git fetch gitwork3 b3:b3 && + git tag v3merged b3 && + git push --tags gitcvs.git b3:b3 +' + +# This test would fail if cvsserver properly created a ".#afile"* file +# for the merge. +# TODO: Validate that the .# file was saved properly, and then +# delete/ignore it when checking the tree. +test_expect_success 'cvs up dirty [cvswork3]' ' + ( cd cvswork3 && + cvs -f up && + test_must_fail cvs -f diff -N -u >"$WORKDIR/cvsDiff.out" ) >cvs.log 2>&1 && + test -s cvsDiff.out && + test $(grep Index: cvsDiff.out | wc -l) = 2 + check_start_tree cvswork3 && + check_file cvswork3 textfile.c v3merged && + check_file cvswork3 t3 v3merged && + check_file cvswork3 adir/afile v3merged && + check_file cvswork3 adir/a3file v3merged && + check_file cvswork3 adir/afile5 v3merged && + check_file cvswork3 adir/bdir/bfile v3merged && + check_file cvswork3 adir/bdir/b3file v3merged && + check_file cvswork3 cdir/cfile v3merged && + check_end_full_tree cvswork3 v3merged +' + +# TODO: test cvs status + +test_expect_success 'cvs commit [cvswork3' ' + ( cd cvswork3 && + cvs -f commit -m "dirty sandbox after auto-merge" + ) > cvs.log 2>&1 && + check_start_tree cvswork3 && + check_file cvswork3 textfile.c v3merged && + check_file cvswork3 t3 v3merged && + check_file cvswork3 adir/afile v3merged && + check_file cvswork3 adir/a3file v3merged && + check_file cvswork3 adir/afile5 v3merged && + check_file cvswork3 adir/bdir/bfile v3merged && + check_file cvswork3 adir/bdir/b3file v3merged && + check_file cvswork3 cdir/cfile v3merged && + check_end_full_tree cvswork3 v3merged && + git fetch gitcvs.git b3:b4 && + git tag v4.1 b4 && + git diff --exit-code v4.1 v3merged > check_diff_apply.out 2>&1 +' + +test_done From 96bc8f66f9f3a56c89c9440171049c95f162a3fa Mon Sep 17 00:00:00 2001 From: Matthew Ogilvie Date: Sat, 13 Oct 2012 23:42:33 -0600 Subject: [PATCH 10/18] cvsserver Documentation: new cvs ... -r support Signed-off-by: Matthew Ogilvie Signed-off-by: Junio C Hamano --- Documentation/git-cvsserver.txt | 37 +++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt index 88d814af0..940c2ba66 100644 --- a/Documentation/git-cvsserver.txt +++ b/Documentation/git-cvsserver.txt @@ -359,6 +359,43 @@ Operations supported All the operations required for normal use are supported, including checkout, diff, status, update, log, add, remove, commit. + +Most CVS command arguments that read CVS tags or revision numbers +(typically -r) work, and also support any git refspec +(tag, branch, commit ID, etc). +However, CVS revision numbers for non-default branches are not well +emulated, and cvs log does not show tags or branches at +all. (Non-main-branch CVS revision numbers superficially resemble CVS +revision numbers, but they actually encode a git commit ID directly, +rather than represent the number of revisions since the branch point.) + +Note that there are two ways to checkout a particular branch. +As described elsewhere on this page, the "module" parameter +of cvs checkout is interpreted as a branch name, and it becomes +the main branch. It remains the main branch for a given sandbox +even if you temporarily make another branch sticky with +cvs update -r. Alternatively, the -r argument can indicate +some other branch to actually checkout, even though the module +is still the "main" branch. Tradeoffs (as currently +implemented): Each new "module" creates a new database on disk with +a history for the given module, and after the database is created, +operations against that main branch are fast. Or alternatively, +-r doesn't take any extra disk space, but may be significantly slower for +many operations, like cvs update. + +If you want to refer to a git refspec that has characters that are +not allowed by CVS, you have two options. First, it may just work +to supply the git refspec directly to the appropriate CVS -r argument; +some CVS clients don't seem to do much sanity checking of the argument. +Second, if that fails, you can use a special character escape mechanism +that only uses characters that are valid in CVS tags. A sequence +of 4 or 5 characters of the form (underscore (`"_"`), dash (`"-"`), +one or two characters, and dash (`"-"`)) can encode various characters based +on the one or two letters: `"s"` for slash (`"/"`), `"p"` for +period (`"."`), `"u"` for underscore (`"_"`), or two hexadecimal digits +for any byte value at all (typically an ASCII number, or perhaps a part +of a UTF-8 encoded character). + Legacy monitoring operations are not supported (edit, watch and related). Exports and tagging (tags and branches) are not supported at this stage. From 76095f6d609a465840f62dd601a1ad9f90c75330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20B=C3=B6gershausen?= Date: Sat, 8 Dec 2012 22:33:38 +0100 Subject: [PATCH 11/18] t9402: sed -i is not portable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On some systems sed allows the usage of e.g. sed -i -e "s/line1/line2/" afile to edit the file "in place". Other systems don't allow that: one observed behaviour is that sed -i -e "s/line1/line2/" afile creates a backup file called afile-e, which breaks the test. As sed -i is not part of POSIX, avoid it. Signed-off-by: Torsten Bögershausen Signed-off-by: Junio C Hamano --- t/t9402-git-cvsserver-refs.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh index 858ef0fc4..c01748ae0 100755 --- a/t/t9402-git-cvsserver-refs.sh +++ b/t/t9402-git-cvsserver-refs.sh @@ -155,7 +155,8 @@ test_expect_success 'cvs co b1 [cvswork3]' ' test_expect_success 'edit cvswork3 and save diff' ' ( cd cvswork3 && - sed -i -e "s/line1/line1 - data/" adir/afile && + sed -e "s/line1/line1 - data/" adir/afile >adir/afileNEW && + mv -f adir/afileNEW adir/afile && echo "afile5" > adir/afile5 && rm t2 && cvs -f add adir/afile5 && @@ -168,7 +169,8 @@ test_expect_success 'setup v1.2 on b1' ' git checkout b1 && echo "new v1.2" > t3 && rm t2 && - sed -i -e "s/line3/line3 - more data/" adir/afile && + sed -e "s/line3/line3 - more data/" adir/afile >adir/afileNEW && + mv -f adir/afileNEW adir/afile && rm adir/a2file && echo "a3file" >> adir/a3file && echo "bfile line 3" >> adir/bdir/bfile && @@ -300,7 +302,8 @@ test_expect_success 'root dir rm file [cvswork2]' ' test_expect_success 'subdir edit/add/rm files [cvswork2' ' ( cd cvswork2 && - sed -i -e "s/line 1/line 1 (v2)/" adir/bdir/bfile && + sed -e "s/line 1/line 1 (v2)/" adir/bdir/bfile >adir/bdir/bfileNEW && + mv -f adir/bdir/bfileNEW adir/bdir/bfile && rm adir/bdir/b2file && cd adir && cvs -f rm bdir/b2file && From 173581412819d5be9035e641bc895d5d4a468d6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20B=C3=B6gershausen?= Date: Sat, 8 Dec 2012 22:33:55 +0100 Subject: [PATCH 12/18] t9402: improve check_end_tree() and check_end_full_tree() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit check_end_tree(): - Instead of counting lines using wc in expectCount and cvsCount: Sort and compare the files byte by byte with test_cmp, which is more exact and easier to debug - Chain all shell comands together using && check_end_full_tree() - Instead of counting lines using wc in expectCount, cvsCount and gitCount: Sort and compare the files byte by byte with test_cmp, which is more exact and easier to debug - Break the test using two conditions anded together with -a into to call to test_cmp - Chain all shell comands together using && Signed-off-by: Torsten Bögershausen Signed-off-by: Junio C Hamano --- t/t9402-git-cvsserver-refs.sh | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh index c01748ae0..f1bea0fed 100755 --- a/t/t9402-git-cvsserver-refs.sh +++ b/t/t9402-git-cvsserver-refs.sh @@ -28,27 +28,22 @@ check_file() { } check_end_tree() { - sandbox="$1" - expectCount=$(wc -l < "$WORKDIR/check.list") - cvsCount=$(find "$sandbox" -name CVS -prune -o -type f -print | wc -l) - test x"$cvsCount" = x"$expectCount" - stat=$? - echo "check_end $sandbox : $stat cvs=$cvsCount expect=$expectCount" \ - >> "$WORKDIR/check.log" - return $stat + sandbox="$1" && + find "$sandbox" -name CVS -prune -o -type f -print >"$WORKDIR/check.cvsCount" && + sort <"$WORKDIR/check.list" >expected && + sort <"$WORKDIR/check.cvsCount" | sed -e "s%cvswork/%%" >actual && + test_cmp expected actual && + rm expected actual } check_end_full_tree() { - sandbox="$1" - ver="$2" - expectCount=$(wc -l < "$WORKDIR/check.list") - cvsCount=$(find "$sandbox" -name CVS -prune -o -type f -print | wc -l) - gitCount=$(git ls-tree -r "$2" | wc -l) - test x"$cvsCount" = x"$expectCount" -a x"$gitCount" = x"$expectCount" - stat=$? - echo "check_end $sandbox : $stat cvs=$cvsCount git=$gitCount expect=$expectCount" \ - >> "$WORKDIR/check.log" - return $stat + sandbox="$1" && + sort <"$WORKDIR/check.list" >expected && + find "$sandbox" -name CVS -prune -o -type f -print | sed -e "s%$sandbox/%%" | sort >act1 && + test_cmp expected act1 && + git ls-tree -r "$2" | sed -e "s/^.*blob [0-9a-fA-F]*[ ]*//" | sort >act2 && + test_cmp expected act2 && + rm expected act1 act2 } ######### From 341bf11245463a59dc20060f3157386b6d4aa7e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20B=C3=B6gershausen?= Date: Sat, 8 Dec 2012 22:34:15 +0100 Subject: [PATCH 13/18] t9402: Dont use test_must_fail cvs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace "test_must_fail cvs" with "! cvs" Signed-off-by: Torsten Bögershausen Signed-off-by: Junio C Hamano --- t/t9402-git-cvsserver-refs.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh index f1bea0fed..21000ec77 100755 --- a/t/t9402-git-cvsserver-refs.sh +++ b/t/t9402-git-cvsserver-refs.sh @@ -156,7 +156,7 @@ test_expect_success 'edit cvswork3 and save diff' ' rm t2 && cvs -f add adir/afile5 && cvs -f rm t2 && - test_must_fail cvs -f diff -N -u >"$WORKDIR/cvswork3edit.diff" + ! cvs -f diff -N -u >"$WORKDIR/cvswork3edit.diff" ) ' @@ -281,7 +281,7 @@ test_expect_success 'cvs co b2 [into cvswork2]' ' test_expect_success 'root dir edit [cvswork2]' ' ( cd cvswork2 && echo "Line 2" >> textfile.c && - test_must_fail cvs -f diff -u >"$WORKDIR/cvsEdit1.diff" && + ! cvs -f diff -u >"$WORKDIR/cvsEdit1.diff" && cvs -f commit -m "edit textfile.c" textfile.c ) >cvsEdit1.log 2>&1 ' @@ -290,7 +290,7 @@ test_expect_success 'root dir rm file [cvswork2]' ' ( cd cvswork2 && cvs -f rm -f t2 && cvs -f diff -u > ../cvsEdit2-empty.diff && - test_must_fail cvs -f diff -N -u >"$WORKDIR/cvsEdit2-N.diff" && + ! cvs -f diff -N -u >"$WORKDIR/cvsEdit2-N.diff" && cvs -f commit -m "rm t2" ) > cvsEdit2.log 2>&1 ' @@ -304,11 +304,11 @@ test_expect_success 'subdir edit/add/rm files [cvswork2' ' cvs -f rm bdir/b2file && echo "4th file" > bdir/b4file && cvs -f add bdir/b4file && - test_must_fail cvs -f diff -N -u >"$WORKDIR/cvsEdit3.diff" && + ! cvs -f diff -N -u >"$WORKDIR/cvsEdit3.diff" && git fetch gitcvs.git b2:b2 && ( cd .. && - test_must_fail cvs -f diff -u -N -r v1.2 >"$WORKDIR/cvsEdit3-v1.2.diff" && - test_must_fail cvs -f diff -u -N -r v1.2 -r v1 >"$WORKDIR/cvsEdit3-v1.2-v1.diff" + ! cvs -f diff -u -N -r v1.2 >"$WORKDIR/cvsEdit3-v1.2.diff" && + ! cvs -f diff -u -N -r v1.2 -r v1 >"$WORKDIR/cvsEdit3-v1.2-v1.diff" ) && cvs -f commit -m "various add/rm/edit" ) >cvs.log 2>&1 @@ -468,7 +468,7 @@ test_expect_success 'cvs diff -r v1 -u' ' test_expect_success 'cvs diff -N -r v2 -u' ' ( cd cvswork && - test_must_fail cvs -f diff -N -r v2 -u ) >cvsDiff.out 2>cvs.log && + ! cvs -f diff -N -r v2 -u ) >cvsDiff.out 2>cvs.log && test ! -s cvs.log && test -s cvsDiff.out && check_diff cvsDiff.out v2 v1 > check_diff.out 2>&1 @@ -476,7 +476,7 @@ test_expect_success 'cvs diff -N -r v2 -u' ' test_expect_success 'cvs diff -N -r v2 -r v1.2' ' ( cd cvswork && - test_must_fail cvs -f diff -N -r v2 -r v1.2 -u ) >cvsDiff.out 2>cvs.log && + ! cvs -f diff -N -r v2 -r v1.2 -u ) >cvsDiff.out 2>cvs.log && test ! -s cvs.log && test -s cvsDiff.out && check_diff cvsDiff.out v2 v1.2 > check_diff.out 2>&1 @@ -494,7 +494,7 @@ test_expect_success 'apply early [cvswork3] diff to b3' ' test_expect_success 'check [cvswork3] diff' ' ( cd cvswork3 && - test_must_fail cvs -f diff -N -u ) >"$WORKDIR/cvsDiff.out" 2>cvs.log && + ! cvs -f diff -N -u ) >"$WORKDIR/cvsDiff.out" 2>cvs.log && test ! -s cvs.log && test -s cvsDiff.out && test $(grep Index: cvsDiff.out | wc -l) = 3 && @@ -517,7 +517,7 @@ test_expect_success 'merge early [cvswork3] b3 with b1' ' test_expect_success 'cvs up dirty [cvswork3]' ' ( cd cvswork3 && cvs -f up && - test_must_fail cvs -f diff -N -u >"$WORKDIR/cvsDiff.out" ) >cvs.log 2>&1 && + ! cvs -f diff -N -u >"$WORKDIR/cvsDiff.out" ) >cvs.log 2>&1 && test -s cvsDiff.out && test $(grep Index: cvsDiff.out | wc -l) = 2 check_start_tree cvswork3 && From 0684371844335bb20979bc308e1b133de30abaee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20B=C3=B6gershausen?= Date: Sat, 8 Dec 2012 22:34:38 +0100 Subject: [PATCH 14/18] t9402: No space after IO-redirection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Redirection should not have SP before the filename (i.e. ">out", not "> out"). Signed-off-by: Torsten Bögershausen Signed-off-by: Junio C Hamano --- t/t9402-git-cvsserver-refs.sh | 60 +++++++++++++++++------------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh index 21000ec77..2f633319f 100755 --- a/t/t9402-git-cvsserver-refs.sh +++ b/t/t9402-git-cvsserver-refs.sh @@ -11,7 +11,7 @@ tags, branches and other git refspecs' check_start_tree() { rm -f "$WORKDIR/check.list" - echo "start $1" >> "${WORKDIR}/check.log" + echo "start $1" >>"${WORKDIR}/check.log" } check_file() { @@ -19,11 +19,11 @@ check_file() { file="$2" ver="$3" GIT_DIR=$SERVERDIR git show "${ver}:${file}" \ - > "$WORKDIR/check.got" 2> "$WORKDIR/check.stderr" + >"$WORKDIR/check.got" 2>"$WORKDIR/check.stderr" test_cmp "$WORKDIR/check.got" "$sandbox/$file" stat=$? - echo "check_file $sandbox $file $ver : $stat" >> "$WORKDIR/check.log" - echo "$file" >> "$WORKDIR/check.list" + echo "check_file $sandbox $file $ver : $stat" >>"$WORKDIR/check.log" + echo "$file" >>"$WORKDIR/check.list" return $stat } @@ -57,7 +57,7 @@ check_diff() { ( cd diffSandbox && git checkout "$vOld" && git apply -p0 --index <"../$diffFile" && - git diff --exit-code "$vNew" ) > check_diff_apply.out 2>&1 + git diff --exit-code "$vNew" ) >check_diff_apply.out 2>&1 } ######### @@ -89,18 +89,18 @@ export CVSROOT CVS_SERVER rm -rf "$CVSWORK" "$SERVERDIR" test_expect_success 'setup v1, b1' ' - echo "Simple text file" > textfile.c && - echo "t2" > t2 && + echo "Simple text file" >textfile.c && + echo "t2" >t2 && mkdir adir && - echo "adir/afile line1" > adir/afile && - echo "adir/afile line2" >> adir/afile && - echo "adir/afile line3" >> adir/afile && - echo "adir/afile line4" >> adir/afile && - echo "adir/a2file" >> adir/a2file && + echo "adir/afile line1" >adir/afile && + echo "adir/afile line2" >>adir/afile && + echo "adir/afile line3" >>adir/afile && + echo "adir/afile line4" >>adir/afile && + echo "adir/a2file" >>adir/a2file && mkdir adir/bdir && - echo "adir/bdir/bfile line 1" > adir/bdir/bfile && - echo "adir/bdir/bfile line 2" >> adir/bdir/bfile && - echo "adir/bdir/b2file" > adir/bdir/b2file && + echo "adir/bdir/bfile line 1" >adir/bdir/bfile && + echo "adir/bdir/bfile line 2" >>adir/bdir/bfile && + echo "adir/bdir/b2file" >adir/bdir/b2file && git add textfile.c t2 adir && git commit -q -m "First Commit (v1)" && git tag v1 && @@ -152,7 +152,7 @@ test_expect_success 'edit cvswork3 and save diff' ' ( cd cvswork3 && sed -e "s/line1/line1 - data/" adir/afile >adir/afileNEW && mv -f adir/afileNEW adir/afile && - echo "afile5" > adir/afile5 && + echo "afile5" >adir/afile5 && rm t2 && cvs -f add adir/afile5 && cvs -f rm t2 && @@ -162,17 +162,17 @@ test_expect_success 'edit cvswork3 and save diff' ' test_expect_success 'setup v1.2 on b1' ' git checkout b1 && - echo "new v1.2" > t3 && + echo "new v1.2" >t3 && rm t2 && sed -e "s/line3/line3 - more data/" adir/afile >adir/afileNEW && mv -f adir/afileNEW adir/afile && rm adir/a2file && - echo "a3file" >> adir/a3file && - echo "bfile line 3" >> adir/bdir/bfile && + echo "a3file" >>adir/a3file && + echo "bfile line 3" >>adir/bdir/bfile && rm adir/bdir/b2file && - echo "b3file" > adir/bdir/b3file && + echo "b3file" >adir/bdir/b3file && mkdir cdir && - echo "cdir/cfile" > cdir/cfile && + echo "cdir/cfile" >cdir/cfile && git add -A cdir adir t3 t2 && git commit -q -m 'v1.2' && git tag v1.2 && @@ -280,7 +280,7 @@ test_expect_success 'cvs co b2 [into cvswork2]' ' test_expect_success 'root dir edit [cvswork2]' ' ( cd cvswork2 && - echo "Line 2" >> textfile.c && + echo "Line 2" >>textfile.c && ! cvs -f diff -u >"$WORKDIR/cvsEdit1.diff" && cvs -f commit -m "edit textfile.c" textfile.c ) >cvsEdit1.log 2>&1 @@ -289,10 +289,10 @@ test_expect_success 'root dir edit [cvswork2]' ' test_expect_success 'root dir rm file [cvswork2]' ' ( cd cvswork2 && cvs -f rm -f t2 && - cvs -f diff -u > ../cvsEdit2-empty.diff && + cvs -f diff -u >../cvsEdit2-empty.diff && ! cvs -f diff -N -u >"$WORKDIR/cvsEdit2-N.diff" && cvs -f commit -m "rm t2" - ) > cvsEdit2.log 2>&1 + ) >cvsEdit2.log 2>&1 ' test_expect_success 'subdir edit/add/rm files [cvswork2' ' @@ -302,7 +302,7 @@ test_expect_success 'subdir edit/add/rm files [cvswork2' ' rm adir/bdir/b2file && cd adir && cvs -f rm bdir/b2file && - echo "4th file" > bdir/b4file && + echo "4th file" >bdir/b4file && cvs -f add bdir/b4file && ! cvs -f diff -N -u >"$WORKDIR/cvsEdit3.diff" && git fetch gitcvs.git b2:b2 && @@ -471,7 +471,7 @@ test_expect_success 'cvs diff -N -r v2 -u' ' ! cvs -f diff -N -r v2 -u ) >cvsDiff.out 2>cvs.log && test ! -s cvs.log && test -s cvsDiff.out && - check_diff cvsDiff.out v2 v1 > check_diff.out 2>&1 + check_diff cvsDiff.out v2 v1 >check_diff.out 2>&1 ' test_expect_success 'cvs diff -N -r v2 -r v1.2' ' @@ -479,7 +479,7 @@ test_expect_success 'cvs diff -N -r v2 -r v1.2' ' ! cvs -f diff -N -r v2 -r v1.2 -u ) >cvsDiff.out 2>cvs.log && test ! -s cvs.log && test -s cvsDiff.out && - check_diff cvsDiff.out v2 v1.2 > check_diff.out 2>&1 + check_diff cvsDiff.out v2 v1.2 >check_diff.out 2>&1 ' test_expect_success 'apply early [cvswork3] diff to b3' ' @@ -499,7 +499,7 @@ test_expect_success 'check [cvswork3] diff' ' test -s cvsDiff.out && test $(grep Index: cvsDiff.out | wc -l) = 3 && test_cmp cvsDiff.out cvswork3edit.diff && - check_diff cvsDiff.out v1 v3 > check_diff.out 2>&1 + check_diff cvsDiff.out v1 v3 >check_diff.out 2>&1 ' test_expect_success 'merge early [cvswork3] b3 with b1' ' @@ -537,7 +537,7 @@ test_expect_success 'cvs up dirty [cvswork3]' ' test_expect_success 'cvs commit [cvswork3' ' ( cd cvswork3 && cvs -f commit -m "dirty sandbox after auto-merge" - ) > cvs.log 2>&1 && + ) >cvs.log 2>&1 && check_start_tree cvswork3 && check_file cvswork3 textfile.c v3merged && check_file cvswork3 t3 v3merged && @@ -550,7 +550,7 @@ test_expect_success 'cvs commit [cvswork3' ' check_end_full_tree cvswork3 v3merged && git fetch gitcvs.git b3:b4 && git tag v4.1 b4 && - git diff --exit-code v4.1 v3merged > check_diff_apply.out 2>&1 + git diff --exit-code v4.1 v3merged >check_diff_apply.out 2>&1 ' test_done From bd6f62c3ff61873dc6a323020162067536283a53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20B=C3=B6gershausen?= Date: Sat, 8 Dec 2012 22:34:55 +0100 Subject: [PATCH 15/18] t9402: Add missing &&; Code style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add missing && at 2 places Re-formated the sub-shell parantheses (coding style) Added missing ] in the test_expect_success header at 2 places Signed-off-by: Torsten Bögershausen Signed-off-by: Junio C Hamano --- t/t9402-git-cvsserver-refs.sh | 106 ++++++++++++++++------------------ 1 file changed, 50 insertions(+), 56 deletions(-) diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh index 2f633319f..c15abde3b 100755 --- a/t/t9402-git-cvsserver-refs.sh +++ b/t/t9402-git-cvsserver-refs.sh @@ -54,10 +54,12 @@ check_diff() { vNew="$3" rm -rf diffSandbox git clone -q -n . diffSandbox && - ( cd diffSandbox && + ( + cd diffSandbox && git checkout "$vOld" && git apply -p0 --index <"../$diffFile" && - git diff --exit-code "$vNew" ) >check_diff_apply.out 2>&1 + git diff --exit-code "$vNew" + ) >check_diff_apply.out 2>&1 } ######### @@ -149,7 +151,8 @@ test_expect_success 'cvs co b1 [cvswork3]' ' ' test_expect_success 'edit cvswork3 and save diff' ' - ( cd cvswork3 && + ( + cd cvswork3 && sed -e "s/line1/line1 - data/" adir/afile >adir/afileNEW && mv -f adir/afileNEW adir/afile && echo "afile5" >adir/afile5 && @@ -180,8 +183,7 @@ test_expect_success 'setup v1.2 on b1' ' ' test_expect_success 'cvs -f up (on b1 adir)' ' - ( cd cvswork/adir && - cvs -f up -d ) >cvs.log 2>&1 && + ( cd cvswork/adir && cvs -f up -d ) >cvs.log 2>&1 && check_start_tree cvswork && check_file cvswork textfile.c v1 && check_file cvswork t2 v1 && @@ -193,8 +195,7 @@ test_expect_success 'cvs -f up (on b1 adir)' ' ' test_expect_success 'cvs up (on b1 /)' ' - ( cd cvswork && - cvs -f up -d ) >cvs.log 2>&1 && + ( cd cvswork && cvs -f up -d ) >cvs.log 2>&1 && check_start_tree cvswork && check_file cvswork textfile.c v1.2 && check_file cvswork t3 v1.2 && @@ -208,8 +209,7 @@ test_expect_success 'cvs up (on b1 /)' ' # Make sure "CVS/Tag" files didn't get messed up: test_expect_success 'cvs up (on b1 /) (again; check CVS/Tag files)' ' - ( cd cvswork && - cvs -f up -d ) >cvs.log 2>&1 && + ( cd cvswork && cvs -f up -d ) >cvs.log 2>&1 && check_start_tree cvswork && check_file cvswork textfile.c v1.2 && check_file cvswork t3 v1.2 && @@ -223,8 +223,7 @@ test_expect_success 'cvs up (on b1 /) (again; check CVS/Tag files)' ' # update to another version: test_expect_success 'cvs up -r v1' ' - ( cd cvswork && - cvs -f up -r v1 ) >cvs.log 2>&1 && + ( cd cvswork && cvs -f up -r v1 ) >cvs.log 2>&1 && check_start_tree cvswork && check_file cvswork textfile.c v1 && check_file cvswork t2 v1 && @@ -236,8 +235,7 @@ test_expect_success 'cvs up -r v1' ' ' test_expect_success 'cvs up' ' - ( cd cvswork && - cvs -f up ) >cvs.log 2>&1 && + ( cd cvswork && cvs -f up ) >cvs.log 2>&1 && check_start_tree cvswork && check_file cvswork textfile.c v1 && check_file cvswork t2 v1 && @@ -249,8 +247,7 @@ test_expect_success 'cvs up' ' ' test_expect_success 'cvs up (again; check CVS/Tag files)' ' - ( cd cvswork && - cvs -f up -d ) >cvs.log 2>&1 && + ( cd cvswork && cvs -f up -d ) >cvs.log 2>&1 && check_start_tree cvswork && check_file cvswork textfile.c v1 && check_file cvswork t2 v1 && @@ -279,15 +276,16 @@ test_expect_success 'cvs co b2 [into cvswork2]' ' ' test_expect_success 'root dir edit [cvswork2]' ' - ( cd cvswork2 && - echo "Line 2" >>textfile.c && + ( + cd cvswork2 && echo "Line 2" >>textfile.c && ! cvs -f diff -u >"$WORKDIR/cvsEdit1.diff" && cvs -f commit -m "edit textfile.c" textfile.c ) >cvsEdit1.log 2>&1 ' test_expect_success 'root dir rm file [cvswork2]' ' - ( cd cvswork2 && + ( + cd cvswork2 && cvs -f rm -f t2 && cvs -f diff -u >../cvsEdit2-empty.diff && ! cvs -f diff -N -u >"$WORKDIR/cvsEdit2-N.diff" && @@ -295,8 +293,9 @@ test_expect_success 'root dir rm file [cvswork2]' ' ) >cvsEdit2.log 2>&1 ' -test_expect_success 'subdir edit/add/rm files [cvswork2' ' - ( cd cvswork2 && +test_expect_success 'subdir edit/add/rm files [cvswork2]' ' + ( + cd cvswork2 && sed -e "s/line 1/line 1 (v2)/" adir/bdir/bfile >adir/bdir/bfileNEW && mv -f adir/bdir/bfileNEW adir/bdir/bfile && rm adir/bdir/b2file && @@ -306,7 +305,8 @@ test_expect_success 'subdir edit/add/rm files [cvswork2' ' cvs -f add bdir/b4file && ! cvs -f diff -N -u >"$WORKDIR/cvsEdit3.diff" && git fetch gitcvs.git b2:b2 && - ( cd .. && + ( + cd .. && ! cvs -f diff -u -N -r v1.2 >"$WORKDIR/cvsEdit3-v1.2.diff" && ! cvs -f diff -u -N -r v1.2 -r v1 >"$WORKDIR/cvsEdit3-v1.2-v1.diff" ) && @@ -334,12 +334,14 @@ test_expect_success 'validate basic diffs saved during above cvswork2 edits' ' test $(grep Index: cvsEdit3.diff | wc -l) = 3 && rm -rf diffSandbox && git clone -q -n . diffSandbox && - ( cd diffSandbox && + ( + cd diffSandbox && git checkout v1 && git apply -p0 --index <"$WORKDIR/cvsEdit1.diff" && git apply -p0 --index <"$WORKDIR/cvsEdit2-N.diff" && git apply -p0 --directory=adir --index <"$WORKDIR/cvsEdit3.diff" && - git diff --exit-code v2 ) >"check_diff_apply.out" 2>&1 + git diff --exit-code v2 + ) >"check_diff_apply.out" 2>&1 ' test_expect_success 'validate v1.2 diff saved during last cvswork2 edit' ' @@ -353,8 +355,7 @@ test_expect_success 'validate v1.2 v1 diff saved during last cvswork2 edit' ' ' test_expect_success 'cvs up [cvswork2]' ' - ( cd cvswork2 && - cvs -f up ) >cvs.log 2>&1 && + ( cd cvswork2 && cvs -f up ) >cvs.log 2>&1 && check_start_tree cvswork2 && check_file cvswork2 textfile.c v2 && check_file cvswork2 adir/afile v2 && @@ -365,8 +366,7 @@ test_expect_success 'cvs up [cvswork2]' ' ' test_expect_success 'cvs up -r b2 [back to cvswork]' ' - ( cd cvswork && - cvs -f up -r b2 ) >cvs.log 2>&1 && + ( cd cvswork && cvs -f up -r b2 ) >cvs.log 2>&1 && check_start_tree cvswork && check_file cvswork textfile.c v2 && check_file cvswork adir/afile v2 && @@ -377,8 +377,7 @@ test_expect_success 'cvs up -r b2 [back to cvswork]' ' ' test_expect_success 'cvs up -r b1' ' - ( cd cvswork && - cvs -f up -r b1 ) >cvs.log 2>&1 && + ( cd cvswork && cvs -f up -r b1 ) >cvs.log 2>&1 && check_start_tree cvswork && check_file cvswork textfile.c v1.2 && check_file cvswork t3 v1.2 && @@ -391,8 +390,7 @@ test_expect_success 'cvs up -r b1' ' ' test_expect_success 'cvs up -A' ' - ( cd cvswork && - cvs -f up -A ) >cvs.log 2>&1 && + ( cd cvswork && cvs -f up -A ) >cvs.log 2>&1 && check_start_tree cvswork && check_file cvswork textfile.c v1 && check_file cvswork t2 v1 && @@ -404,8 +402,7 @@ test_expect_success 'cvs up -A' ' ' test_expect_success 'cvs up (check CVS/Tag files)' ' - ( cd cvswork && - cvs -f up ) >cvs.log 2>&1 && + ( cd cvswork && cvs -f up ) >cvs.log 2>&1 && check_start_tree cvswork && check_file cvswork textfile.c v1 && check_file cvswork t2 v1 && @@ -418,8 +415,7 @@ test_expect_success 'cvs up (check CVS/Tag files)' ' # This is not really legal CVS, but it seems to work anyway: test_expect_success 'cvs up -r heads/b1' ' - ( cd cvswork && - cvs -f up -r heads/b1 ) >cvs.log 2>&1 && + ( cd cvswork && cvs -f up -r heads/b1 ) >cvs.log 2>&1 && check_start_tree cvswork && check_file cvswork textfile.c v1.2 && check_file cvswork t3 v1.2 && @@ -433,8 +429,7 @@ test_expect_success 'cvs up -r heads/b1' ' # But this should work even if CVS client checks -r more carefully: test_expect_success 'cvs up -r heads_-s-b2 (cvsserver escape mechanism)' ' - ( cd cvswork && - cvs -f up -r heads_-s-b2 ) >cvs.log 2>&1 && + ( cd cvswork && cvs -f up -r heads_-s-b2 ) >cvs.log 2>&1 && check_start_tree cvswork && check_file cvswork textfile.c v2 && check_file cvswork adir/afile v2 && @@ -447,8 +442,7 @@ test_expect_success 'cvs up -r heads_-s-b2 (cvsserver escape mechanism)' ' v1hash=$(git rev-parse v1) test_expect_success 'cvs up -r $(git rev-parse v1)' ' test -n "$v1hash" && - ( cd cvswork && - cvs -f up -r "$v1hash" ) >cvs.log 2>&1 && + ( cd cvswork && cvs -f up -r "$v1hash" ) >cvs.log 2>&1 && check_start_tree cvswork && check_file cvswork textfile.c v1 && check_file cvswork t2 v1 && @@ -460,23 +454,20 @@ test_expect_success 'cvs up -r $(git rev-parse v1)' ' ' test_expect_success 'cvs diff -r v1 -u' ' - ( cd cvswork && - cvs -f diff -r v1 -u ) >cvsDiff.out 2>cvs.log && + ( cd cvswork && cvs -f diff -r v1 -u ) >cvsDiff.out 2>cvs.log && test ! -s cvsDiff.out && test ! -s cvs.log ' test_expect_success 'cvs diff -N -r v2 -u' ' - ( cd cvswork && - ! cvs -f diff -N -r v2 -u ) >cvsDiff.out 2>cvs.log && + ( cd cvswork && ! cvs -f diff -N -r v2 -u ) >cvsDiff.out 2>cvs.log && test ! -s cvs.log && test -s cvsDiff.out && check_diff cvsDiff.out v2 v1 >check_diff.out 2>&1 ' test_expect_success 'cvs diff -N -r v2 -r v1.2' ' - ( cd cvswork && - ! cvs -f diff -N -r v2 -r v1.2 -u ) >cvsDiff.out 2>cvs.log && + ( cd cvswork && ! cvs -f diff -N -r v2 -r v1.2 -u ) >cvsDiff.out 2>cvs.log && test ! -s cvs.log && test -s cvsDiff.out && check_diff cvsDiff.out v2 v1.2 >check_diff.out 2>&1 @@ -484,17 +475,18 @@ test_expect_success 'cvs diff -N -r v2 -r v1.2' ' test_expect_success 'apply early [cvswork3] diff to b3' ' git clone -q . gitwork3 && - ( cd gitwork3 && + ( + cd gitwork3 && git checkout -b b3 v1 && git apply -p0 --index <"$WORKDIR/cvswork3edit.diff" && - git commit -m "cvswork3 edits applied" ) && + git commit -m "cvswork3 edits applied" + ) && git fetch gitwork3 b3:b3 && git tag v3 b3 ' test_expect_success 'check [cvswork3] diff' ' - ( cd cvswork3 && - ! cvs -f diff -N -u ) >"$WORKDIR/cvsDiff.out" 2>cvs.log && + ( cd cvswork3 && ! cvs -f diff -N -u ) >"$WORKDIR/cvsDiff.out" 2>cvs.log && test ! -s cvs.log && test -s cvsDiff.out && test $(grep Index: cvsDiff.out | wc -l) = 3 && @@ -503,8 +495,7 @@ test_expect_success 'check [cvswork3] diff' ' ' test_expect_success 'merge early [cvswork3] b3 with b1' ' - ( cd gitwork3 && - git merge "message" HEAD b1 ) + ( cd gitwork3 && git merge "message" HEAD b1 ) && git fetch gitwork3 b3:b3 && git tag v3merged b3 && git push --tags gitcvs.git b3:b3 @@ -515,11 +506,13 @@ test_expect_success 'merge early [cvswork3] b3 with b1' ' # TODO: Validate that the .# file was saved properly, and then # delete/ignore it when checking the tree. test_expect_success 'cvs up dirty [cvswork3]' ' - ( cd cvswork3 && + ( + cd cvswork3 && cvs -f up && - ! cvs -f diff -N -u >"$WORKDIR/cvsDiff.out" ) >cvs.log 2>&1 && + ! cvs -f diff -N -u >"$WORKDIR/cvsDiff.out" + ) >cvs.log 2>&1 && test -s cvsDiff.out && - test $(grep Index: cvsDiff.out | wc -l) = 2 + test $(grep Index: cvsDiff.out | wc -l) = 2 && check_start_tree cvswork3 && check_file cvswork3 textfile.c v3merged && check_file cvswork3 t3 v3merged && @@ -534,8 +527,9 @@ test_expect_success 'cvs up dirty [cvswork3]' ' # TODO: test cvs status -test_expect_success 'cvs commit [cvswork3' ' - ( cd cvswork3 && +test_expect_success 'cvs commit [cvswork3]' ' + ( + cd cvswork3 && cvs -f commit -m "dirty sandbox after auto-merge" ) >cvs.log 2>&1 && check_start_tree cvswork3 && From 941c1e0402442cebf0add6b609a0efc23f520446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20B=C3=B6gershausen?= Date: Sat, 8 Dec 2012 22:35:06 +0100 Subject: [PATCH 16/18] t9402: Simplify git ls-tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use "git ls-tree --name-only" which does not need a sed to filter out the sha Signed-off-by: Torsten Bögershausen Signed-off-by: Junio C Hamano --- t/t9402-git-cvsserver-refs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh index c15abde3b..7953ffccc 100755 --- a/t/t9402-git-cvsserver-refs.sh +++ b/t/t9402-git-cvsserver-refs.sh @@ -41,7 +41,7 @@ check_end_full_tree() { sort <"$WORKDIR/check.list" >expected && find "$sandbox" -name CVS -prune -o -type f -print | sed -e "s%$sandbox/%%" | sort >act1 && test_cmp expected act1 && - git ls-tree -r "$2" | sed -e "s/^.*blob [0-9a-fA-F]*[ ]*//" | sort >act2 && + git ls-tree --name-only -r "$2" | sort >act2 && test_cmp expected act2 && rm expected act1 act2 } From eacdd428bcc32dcb934c0e1e67ba187fb872c290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20B=C3=B6gershausen?= Date: Sat, 8 Dec 2012 22:35:39 +0100 Subject: [PATCH 17/18] t9402: Rename check.cvsCount and check.list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Checking and comparing the number of line in check.list and check.cvsCount had been replaced by comparing both files line by line. Rename the filenames to make clear which is expected and which is actual: check.list -> list.expected check.cvsCount-> list.actual Signed-off-by: Torsten Bögershausen Signed-off-by: Junio C Hamano --- t/t9402-git-cvsserver-refs.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh index 7953ffccc..1f9675859 100755 --- a/t/t9402-git-cvsserver-refs.sh +++ b/t/t9402-git-cvsserver-refs.sh @@ -10,7 +10,7 @@ tags, branches and other git refspecs' ######### check_start_tree() { - rm -f "$WORKDIR/check.list" + rm -f "$WORKDIR/list.expected" echo "start $1" >>"${WORKDIR}/check.log" } @@ -23,22 +23,22 @@ check_file() { test_cmp "$WORKDIR/check.got" "$sandbox/$file" stat=$? echo "check_file $sandbox $file $ver : $stat" >>"$WORKDIR/check.log" - echo "$file" >>"$WORKDIR/check.list" + echo "$file" >>"$WORKDIR/list.expected" return $stat } check_end_tree() { sandbox="$1" && - find "$sandbox" -name CVS -prune -o -type f -print >"$WORKDIR/check.cvsCount" && - sort <"$WORKDIR/check.list" >expected && - sort <"$WORKDIR/check.cvsCount" | sed -e "s%cvswork/%%" >actual && + find "$sandbox" -name CVS -prune -o -type f -print >"$WORKDIR/list.actual" && + sort <"$WORKDIR/list.expected" >expected && + sort <"$WORKDIR/list.actual" | sed -e "s%cvswork/%%" >actual && test_cmp expected actual && rm expected actual } check_end_full_tree() { sandbox="$1" && - sort <"$WORKDIR/check.list" >expected && + sort <"$WORKDIR/list.expected" >expected && find "$sandbox" -name CVS -prune -o -type f -print | sed -e "s%$sandbox/%%" | sort >act1 && test_cmp expected act1 && git ls-tree --name-only -r "$2" | sort >act2 && From 828eff76b058c21912e1c6dac33dde1b5d9f913a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20B=C3=B6gershausen?= Date: Sat, 8 Dec 2012 22:35:53 +0100 Subject: [PATCH 18/18] t9402: Use TABs for indentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use TAB's for indentation, and wrap overlong lines. Put the closing ' at the beginning of the line. Signed-off-by: Torsten Bögershausen Signed-off-by: Junio C Hamano --- t/t9402-git-cvsserver-refs.sh | 751 +++++++++++++++++----------------- 1 file changed, 376 insertions(+), 375 deletions(-) diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh index 1f9675859..735a018ec 100755 --- a/t/t9402-git-cvsserver-refs.sh +++ b/t/t9402-git-cvsserver-refs.sh @@ -10,56 +10,57 @@ tags, branches and other git refspecs' ######### check_start_tree() { - rm -f "$WORKDIR/list.expected" - echo "start $1" >>"${WORKDIR}/check.log" + rm -f "$WORKDIR/list.expected" + echo "start $1" >>"${WORKDIR}/check.log" } check_file() { - sandbox="$1" - file="$2" - ver="$3" - GIT_DIR=$SERVERDIR git show "${ver}:${file}" \ - >"$WORKDIR/check.got" 2>"$WORKDIR/check.stderr" - test_cmp "$WORKDIR/check.got" "$sandbox/$file" - stat=$? - echo "check_file $sandbox $file $ver : $stat" >>"$WORKDIR/check.log" - echo "$file" >>"$WORKDIR/list.expected" - return $stat + sandbox="$1" + file="$2" + ver="$3" + GIT_DIR=$SERVERDIR git show "${ver}:${file}" \ + >"$WORKDIR/check.got" 2>"$WORKDIR/check.stderr" + test_cmp "$WORKDIR/check.got" "$sandbox/$file" + stat=$? + echo "check_file $sandbox $file $ver : $stat" >>"$WORKDIR/check.log" + echo "$file" >>"$WORKDIR/list.expected" + return $stat } check_end_tree() { - sandbox="$1" && - find "$sandbox" -name CVS -prune -o -type f -print >"$WORKDIR/list.actual" && - sort <"$WORKDIR/list.expected" >expected && - sort <"$WORKDIR/list.actual" | sed -e "s%cvswork/%%" >actual && - test_cmp expected actual && - rm expected actual + sandbox="$1" && + find "$sandbox" -name CVS -prune -o -type f -print >"$WORKDIR/list.actual" && + sort <"$WORKDIR/list.expected" >expected && + sort <"$WORKDIR/list.actual" | sed -e "s%cvswork/%%" >actual && + test_cmp expected actual && + rm expected actual } check_end_full_tree() { - sandbox="$1" && - sort <"$WORKDIR/list.expected" >expected && - find "$sandbox" -name CVS -prune -o -type f -print | sed -e "s%$sandbox/%%" | sort >act1 && - test_cmp expected act1 && - git ls-tree --name-only -r "$2" | sort >act2 && - test_cmp expected act2 && - rm expected act1 act2 + sandbox="$1" && + sort <"$WORKDIR/list.expected" >expected && + find "$sandbox" -name CVS -prune -o -type f -print | + sed -e "s%$sandbox/%%" | sort >act1 && + test_cmp expected act1 && + git ls-tree --name-only -r "$2" | sort >act2 && + test_cmp expected act2 && + rm expected act1 act2 } ######### check_diff() { - diffFile="$1" - vOld="$2" - vNew="$3" - rm -rf diffSandbox - git clone -q -n . diffSandbox && - ( - cd diffSandbox && - git checkout "$vOld" && - git apply -p0 --index <"../$diffFile" && - git diff --exit-code "$vNew" - ) >check_diff_apply.out 2>&1 + diffFile="$1" + vOld="$2" + vNew="$3" + rm -rf diffSandbox + git clone -q -n . diffSandbox && + ( + cd diffSandbox && + git checkout "$vOld" && + git apply -p0 --index <"../$diffFile" && + git diff --exit-code "$vNew" + ) >check_diff_apply.out 2>&1 } ######### @@ -67,17 +68,17 @@ check_diff() { cvs >/dev/null 2>&1 if test $? -ne 1 then - skip_all='skipping git-cvsserver tests, cvs not found' - test_done + skip_all='skipping git-cvsserver tests, cvs not found' + test_done fi if ! test_have_prereq PERL then - skip_all='skipping git-cvsserver tests, perl not available' - test_done + skip_all='skipping git-cvsserver tests, perl not available' + test_done fi "$PERL_PATH" -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || { - skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable' - test_done + skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable' + test_done } unset GIT_DIR GIT_CONFIG @@ -91,414 +92,414 @@ export CVSROOT CVS_SERVER rm -rf "$CVSWORK" "$SERVERDIR" test_expect_success 'setup v1, b1' ' - echo "Simple text file" >textfile.c && - echo "t2" >t2 && - mkdir adir && - echo "adir/afile line1" >adir/afile && - echo "adir/afile line2" >>adir/afile && - echo "adir/afile line3" >>adir/afile && - echo "adir/afile line4" >>adir/afile && - echo "adir/a2file" >>adir/a2file && - mkdir adir/bdir && - echo "adir/bdir/bfile line 1" >adir/bdir/bfile && - echo "adir/bdir/bfile line 2" >>adir/bdir/bfile && - echo "adir/bdir/b2file" >adir/bdir/b2file && - git add textfile.c t2 adir && - git commit -q -m "First Commit (v1)" && - git tag v1 && - git branch b1 && - git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 && - GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true && - GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" + echo "Simple text file" >textfile.c && + echo "t2" >t2 && + mkdir adir && + echo "adir/afile line1" >adir/afile && + echo "adir/afile line2" >>adir/afile && + echo "adir/afile line3" >>adir/afile && + echo "adir/afile line4" >>adir/afile && + echo "adir/a2file" >>adir/a2file && + mkdir adir/bdir && + echo "adir/bdir/bfile line 1" >adir/bdir/bfile && + echo "adir/bdir/bfile line 2" >>adir/bdir/bfile && + echo "adir/bdir/b2file" >adir/bdir/b2file && + git add textfile.c t2 adir && + git commit -q -m "First Commit (v1)" && + git tag v1 && + git branch b1 && + git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 && + GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true && + GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" ' rm -rf cvswork test_expect_success 'cvs co v1' ' - cvs -f -Q co -r v1 -d cvswork master >cvs.log 2>&1 && - check_start_tree cvswork && - check_file cvswork textfile.c v1 && - check_file cvswork t2 v1 && - check_file cvswork adir/afile v1 && - check_file cvswork adir/a2file v1 && - check_file cvswork adir/bdir/bfile v1 && - check_file cvswork adir/bdir/b2file v1 && - check_end_tree cvswork + cvs -f -Q co -r v1 -d cvswork master >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_tree cvswork ' rm -rf cvswork test_expect_success 'cvs co b1' ' - cvs -f co -r b1 -d cvswork master >cvs.log 2>&1 && - check_start_tree cvswork && - check_file cvswork textfile.c v1 && - check_file cvswork t2 v1 && - check_file cvswork adir/afile v1 && - check_file cvswork adir/a2file v1 && - check_file cvswork adir/bdir/bfile v1 && - check_file cvswork adir/bdir/b2file v1 && - check_end_tree cvswork + cvs -f co -r b1 -d cvswork master >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_tree cvswork ' test_expect_success 'cvs co b1 [cvswork3]' ' - cvs -f co -r b1 -d cvswork3 master >cvs.log 2>&1 && - check_start_tree cvswork3 && - check_file cvswork3 textfile.c v1 && - check_file cvswork3 t2 v1 && - check_file cvswork3 adir/afile v1 && - check_file cvswork3 adir/a2file v1 && - check_file cvswork3 adir/bdir/bfile v1 && - check_file cvswork3 adir/bdir/b2file v1 && - check_end_full_tree cvswork3 v1 + cvs -f co -r b1 -d cvswork3 master >cvs.log 2>&1 && + check_start_tree cvswork3 && + check_file cvswork3 textfile.c v1 && + check_file cvswork3 t2 v1 && + check_file cvswork3 adir/afile v1 && + check_file cvswork3 adir/a2file v1 && + check_file cvswork3 adir/bdir/bfile v1 && + check_file cvswork3 adir/bdir/b2file v1 && + check_end_full_tree cvswork3 v1 ' test_expect_success 'edit cvswork3 and save diff' ' - ( - cd cvswork3 && - sed -e "s/line1/line1 - data/" adir/afile >adir/afileNEW && - mv -f adir/afileNEW adir/afile && - echo "afile5" >adir/afile5 && - rm t2 && - cvs -f add adir/afile5 && - cvs -f rm t2 && - ! cvs -f diff -N -u >"$WORKDIR/cvswork3edit.diff" - ) + ( + cd cvswork3 && + sed -e "s/line1/line1 - data/" adir/afile >adir/afileNEW && + mv -f adir/afileNEW adir/afile && + echo "afile5" >adir/afile5 && + rm t2 && + cvs -f add adir/afile5 && + cvs -f rm t2 && + ! cvs -f diff -N -u >"$WORKDIR/cvswork3edit.diff" + ) ' test_expect_success 'setup v1.2 on b1' ' - git checkout b1 && - echo "new v1.2" >t3 && - rm t2 && - sed -e "s/line3/line3 - more data/" adir/afile >adir/afileNEW && - mv -f adir/afileNEW adir/afile && - rm adir/a2file && - echo "a3file" >>adir/a3file && - echo "bfile line 3" >>adir/bdir/bfile && - rm adir/bdir/b2file && - echo "b3file" >adir/bdir/b3file && - mkdir cdir && - echo "cdir/cfile" >cdir/cfile && - git add -A cdir adir t3 t2 && - git commit -q -m 'v1.2' && - git tag v1.2 && - git push --tags gitcvs.git b1:b1 + git checkout b1 && + echo "new v1.2" >t3 && + rm t2 && + sed -e "s/line3/line3 - more data/" adir/afile >adir/afileNEW && + mv -f adir/afileNEW adir/afile && + rm adir/a2file && + echo "a3file" >>adir/a3file && + echo "bfile line 3" >>adir/bdir/bfile && + rm adir/bdir/b2file && + echo "b3file" >adir/bdir/b3file && + mkdir cdir && + echo "cdir/cfile" >cdir/cfile && + git add -A cdir adir t3 t2 && + git commit -q -m 'v1.2' && + git tag v1.2 && + git push --tags gitcvs.git b1:b1 ' test_expect_success 'cvs -f up (on b1 adir)' ' - ( cd cvswork/adir && cvs -f up -d ) >cvs.log 2>&1 && - check_start_tree cvswork && - check_file cvswork textfile.c v1 && - check_file cvswork t2 v1 && - check_file cvswork adir/afile v1.2 && - check_file cvswork adir/a3file v1.2 && - check_file cvswork adir/bdir/bfile v1.2 && - check_file cvswork adir/bdir/b3file v1.2 && - check_end_tree cvswork + ( cd cvswork/adir && cvs -f up -d ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1.2 && + check_file cvswork adir/a3file v1.2 && + check_file cvswork adir/bdir/bfile v1.2 && + check_file cvswork adir/bdir/b3file v1.2 && + check_end_tree cvswork ' test_expect_success 'cvs up (on b1 /)' ' - ( cd cvswork && cvs -f up -d ) >cvs.log 2>&1 && - check_start_tree cvswork && - check_file cvswork textfile.c v1.2 && - check_file cvswork t3 v1.2 && - check_file cvswork adir/afile v1.2 && - check_file cvswork adir/a3file v1.2 && - check_file cvswork adir/bdir/bfile v1.2 && - check_file cvswork adir/bdir/b3file v1.2 && - check_file cvswork cdir/cfile v1.2 && - check_end_tree cvswork + ( cd cvswork && cvs -f up -d ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1.2 && + check_file cvswork t3 v1.2 && + check_file cvswork adir/afile v1.2 && + check_file cvswork adir/a3file v1.2 && + check_file cvswork adir/bdir/bfile v1.2 && + check_file cvswork adir/bdir/b3file v1.2 && + check_file cvswork cdir/cfile v1.2 && + check_end_tree cvswork ' # Make sure "CVS/Tag" files didn't get messed up: test_expect_success 'cvs up (on b1 /) (again; check CVS/Tag files)' ' - ( cd cvswork && cvs -f up -d ) >cvs.log 2>&1 && - check_start_tree cvswork && - check_file cvswork textfile.c v1.2 && - check_file cvswork t3 v1.2 && - check_file cvswork adir/afile v1.2 && - check_file cvswork adir/a3file v1.2 && - check_file cvswork adir/bdir/bfile v1.2 && - check_file cvswork adir/bdir/b3file v1.2 && - check_file cvswork cdir/cfile v1.2 && - check_end_tree cvswork + ( cd cvswork && cvs -f up -d ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1.2 && + check_file cvswork t3 v1.2 && + check_file cvswork adir/afile v1.2 && + check_file cvswork adir/a3file v1.2 && + check_file cvswork adir/bdir/bfile v1.2 && + check_file cvswork adir/bdir/b3file v1.2 && + check_file cvswork cdir/cfile v1.2 && + check_end_tree cvswork ' # update to another version: test_expect_success 'cvs up -r v1' ' - ( cd cvswork && cvs -f up -r v1 ) >cvs.log 2>&1 && - check_start_tree cvswork && - check_file cvswork textfile.c v1 && - check_file cvswork t2 v1 && - check_file cvswork adir/afile v1 && - check_file cvswork adir/a2file v1 && - check_file cvswork adir/bdir/bfile v1 && - check_file cvswork adir/bdir/b2file v1 && - check_end_tree cvswork + ( cd cvswork && cvs -f up -r v1 ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_tree cvswork ' test_expect_success 'cvs up' ' - ( cd cvswork && cvs -f up ) >cvs.log 2>&1 && - check_start_tree cvswork && - check_file cvswork textfile.c v1 && - check_file cvswork t2 v1 && - check_file cvswork adir/afile v1 && - check_file cvswork adir/a2file v1 && - check_file cvswork adir/bdir/bfile v1 && - check_file cvswork adir/bdir/b2file v1 && - check_end_tree cvswork + ( cd cvswork && cvs -f up ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_tree cvswork ' test_expect_success 'cvs up (again; check CVS/Tag files)' ' - ( cd cvswork && cvs -f up -d ) >cvs.log 2>&1 && - check_start_tree cvswork && - check_file cvswork textfile.c v1 && - check_file cvswork t2 v1 && - check_file cvswork adir/afile v1 && - check_file cvswork adir/a2file v1 && - check_file cvswork adir/bdir/bfile v1 && - check_file cvswork adir/bdir/b2file v1 && - check_end_tree cvswork + ( cd cvswork && cvs -f up -d ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_tree cvswork ' test_expect_success 'setup simple b2' ' - git branch b2 v1 && - git push --tags gitcvs.git b2:b2 + git branch b2 v1 && + git push --tags gitcvs.git b2:b2 ' test_expect_success 'cvs co b2 [into cvswork2]' ' - cvs -f co -r b2 -d cvswork2 master >cvs.log 2>&1 && - check_start_tree cvswork && - check_file cvswork textfile.c v1 && - check_file cvswork t2 v1 && - check_file cvswork adir/afile v1 && - check_file cvswork adir/a2file v1 && - check_file cvswork adir/bdir/bfile v1 && - check_file cvswork adir/bdir/b2file v1 && - check_end_tree cvswork + cvs -f co -r b2 -d cvswork2 master >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_tree cvswork ' test_expect_success 'root dir edit [cvswork2]' ' - ( - cd cvswork2 && echo "Line 2" >>textfile.c && - ! cvs -f diff -u >"$WORKDIR/cvsEdit1.diff" && - cvs -f commit -m "edit textfile.c" textfile.c - ) >cvsEdit1.log 2>&1 + ( + cd cvswork2 && echo "Line 2" >>textfile.c && + ! cvs -f diff -u >"$WORKDIR/cvsEdit1.diff" && + cvs -f commit -m "edit textfile.c" textfile.c + ) >cvsEdit1.log 2>&1 ' test_expect_success 'root dir rm file [cvswork2]' ' - ( - cd cvswork2 && - cvs -f rm -f t2 && - cvs -f diff -u >../cvsEdit2-empty.diff && - ! cvs -f diff -N -u >"$WORKDIR/cvsEdit2-N.diff" && - cvs -f commit -m "rm t2" - ) >cvsEdit2.log 2>&1 + ( + cd cvswork2 && + cvs -f rm -f t2 && + cvs -f diff -u >../cvsEdit2-empty.diff && + ! cvs -f diff -N -u >"$WORKDIR/cvsEdit2-N.diff" && + cvs -f commit -m "rm t2" + ) >cvsEdit2.log 2>&1 ' test_expect_success 'subdir edit/add/rm files [cvswork2]' ' - ( - cd cvswork2 && - sed -e "s/line 1/line 1 (v2)/" adir/bdir/bfile >adir/bdir/bfileNEW && - mv -f adir/bdir/bfileNEW adir/bdir/bfile && - rm adir/bdir/b2file && - cd adir && - cvs -f rm bdir/b2file && - echo "4th file" >bdir/b4file && - cvs -f add bdir/b4file && - ! cvs -f diff -N -u >"$WORKDIR/cvsEdit3.diff" && - git fetch gitcvs.git b2:b2 && - ( - cd .. && - ! cvs -f diff -u -N -r v1.2 >"$WORKDIR/cvsEdit3-v1.2.diff" && - ! cvs -f diff -u -N -r v1.2 -r v1 >"$WORKDIR/cvsEdit3-v1.2-v1.diff" - ) && - cvs -f commit -m "various add/rm/edit" - ) >cvs.log 2>&1 + ( + cd cvswork2 && + sed -e "s/line 1/line 1 (v2)/" adir/bdir/bfile >adir/bdir/bfileNEW && + mv -f adir/bdir/bfileNEW adir/bdir/bfile && + rm adir/bdir/b2file && + cd adir && + cvs -f rm bdir/b2file && + echo "4th file" >bdir/b4file && + cvs -f add bdir/b4file && + ! cvs -f diff -N -u >"$WORKDIR/cvsEdit3.diff" && + git fetch gitcvs.git b2:b2 && + ( + cd .. && + ! cvs -f diff -u -N -r v1.2 >"$WORKDIR/cvsEdit3-v1.2.diff" && + ! cvs -f diff -u -N -r v1.2 -r v1 >"$WORKDIR/cvsEdit3-v1.2-v1.diff" + ) && + cvs -f commit -m "various add/rm/edit" + ) >cvs.log 2>&1 ' test_expect_success 'validate result of edits [cvswork2]' ' - git fetch gitcvs.git b2:b2 && - git tag v2 b2 && - git push --tags gitcvs.git b2:b2 && - check_start_tree cvswork2 && - check_file cvswork2 textfile.c v2 && - check_file cvswork2 adir/afile v2 && - check_file cvswork2 adir/a2file v2 && - check_file cvswork2 adir/bdir/bfile v2 && - check_file cvswork2 adir/bdir/b4file v2 && - check_end_full_tree cvswork2 v2 + git fetch gitcvs.git b2:b2 && + git tag v2 b2 && + git push --tags gitcvs.git b2:b2 && + check_start_tree cvswork2 && + check_file cvswork2 textfile.c v2 && + check_file cvswork2 adir/afile v2 && + check_file cvswork2 adir/a2file v2 && + check_file cvswork2 adir/bdir/bfile v2 && + check_file cvswork2 adir/bdir/b4file v2 && + check_end_full_tree cvswork2 v2 ' test_expect_success 'validate basic diffs saved during above cvswork2 edits' ' - test $(grep Index: cvsEdit1.diff | wc -l) = 1 && - test ! -s cvsEdit2-empty.diff && - test $(grep Index: cvsEdit2-N.diff | wc -l) = 1 && - test $(grep Index: cvsEdit3.diff | wc -l) = 3 && - rm -rf diffSandbox && - git clone -q -n . diffSandbox && - ( - cd diffSandbox && - git checkout v1 && - git apply -p0 --index <"$WORKDIR/cvsEdit1.diff" && - git apply -p0 --index <"$WORKDIR/cvsEdit2-N.diff" && - git apply -p0 --directory=adir --index <"$WORKDIR/cvsEdit3.diff" && - git diff --exit-code v2 - ) >"check_diff_apply.out" 2>&1 + test $(grep Index: cvsEdit1.diff | wc -l) = 1 && + test ! -s cvsEdit2-empty.diff && + test $(grep Index: cvsEdit2-N.diff | wc -l) = 1 && + test $(grep Index: cvsEdit3.diff | wc -l) = 3 && + rm -rf diffSandbox && + git clone -q -n . diffSandbox && + ( + cd diffSandbox && + git checkout v1 && + git apply -p0 --index <"$WORKDIR/cvsEdit1.diff" && + git apply -p0 --index <"$WORKDIR/cvsEdit2-N.diff" && + git apply -p0 --directory=adir --index <"$WORKDIR/cvsEdit3.diff" && + git diff --exit-code v2 + ) >"check_diff_apply.out" 2>&1 ' test_expect_success 'validate v1.2 diff saved during last cvswork2 edit' ' - test $(grep Index: cvsEdit3-v1.2.diff | wc -l) = 9 && - check_diff cvsEdit3-v1.2.diff v1.2 v2 + test $(grep Index: cvsEdit3-v1.2.diff | wc -l) = 9 && + check_diff cvsEdit3-v1.2.diff v1.2 v2 ' test_expect_success 'validate v1.2 v1 diff saved during last cvswork2 edit' ' - test $(grep Index: cvsEdit3-v1.2-v1.diff | wc -l) = 9 && - check_diff cvsEdit3-v1.2-v1.diff v1.2 v1 + test $(grep Index: cvsEdit3-v1.2-v1.diff | wc -l) = 9 && + check_diff cvsEdit3-v1.2-v1.diff v1.2 v1 ' test_expect_success 'cvs up [cvswork2]' ' - ( cd cvswork2 && cvs -f up ) >cvs.log 2>&1 && - check_start_tree cvswork2 && - check_file cvswork2 textfile.c v2 && - check_file cvswork2 adir/afile v2 && - check_file cvswork2 adir/a2file v2 && - check_file cvswork2 adir/bdir/bfile v2 && - check_file cvswork2 adir/bdir/b4file v2 && - check_end_full_tree cvswork2 v2 + ( cd cvswork2 && cvs -f up ) >cvs.log 2>&1 && + check_start_tree cvswork2 && + check_file cvswork2 textfile.c v2 && + check_file cvswork2 adir/afile v2 && + check_file cvswork2 adir/a2file v2 && + check_file cvswork2 adir/bdir/bfile v2 && + check_file cvswork2 adir/bdir/b4file v2 && + check_end_full_tree cvswork2 v2 ' test_expect_success 'cvs up -r b2 [back to cvswork]' ' - ( cd cvswork && cvs -f up -r b2 ) >cvs.log 2>&1 && - check_start_tree cvswork && - check_file cvswork textfile.c v2 && - check_file cvswork adir/afile v2 && - check_file cvswork adir/a2file v2 && - check_file cvswork adir/bdir/bfile v2 && - check_file cvswork adir/bdir/b4file v2 && - check_end_full_tree cvswork v2 + ( cd cvswork && cvs -f up -r b2 ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v2 && + check_file cvswork adir/afile v2 && + check_file cvswork adir/a2file v2 && + check_file cvswork adir/bdir/bfile v2 && + check_file cvswork adir/bdir/b4file v2 && + check_end_full_tree cvswork v2 ' test_expect_success 'cvs up -r b1' ' - ( cd cvswork && cvs -f up -r b1 ) >cvs.log 2>&1 && - check_start_tree cvswork && - check_file cvswork textfile.c v1.2 && - check_file cvswork t3 v1.2 && - check_file cvswork adir/afile v1.2 && - check_file cvswork adir/a3file v1.2 && - check_file cvswork adir/bdir/bfile v1.2 && - check_file cvswork adir/bdir/b3file v1.2 && - check_file cvswork cdir/cfile v1.2 && - check_end_full_tree cvswork v1.2 + ( cd cvswork && cvs -f up -r b1 ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1.2 && + check_file cvswork t3 v1.2 && + check_file cvswork adir/afile v1.2 && + check_file cvswork adir/a3file v1.2 && + check_file cvswork adir/bdir/bfile v1.2 && + check_file cvswork adir/bdir/b3file v1.2 && + check_file cvswork cdir/cfile v1.2 && + check_end_full_tree cvswork v1.2 ' test_expect_success 'cvs up -A' ' - ( cd cvswork && cvs -f up -A ) >cvs.log 2>&1 && - check_start_tree cvswork && - check_file cvswork textfile.c v1 && - check_file cvswork t2 v1 && - check_file cvswork adir/afile v1 && - check_file cvswork adir/a2file v1 && - check_file cvswork adir/bdir/bfile v1 && - check_file cvswork adir/bdir/b2file v1 && - check_end_full_tree cvswork v1 + ( cd cvswork && cvs -f up -A ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_full_tree cvswork v1 ' test_expect_success 'cvs up (check CVS/Tag files)' ' - ( cd cvswork && cvs -f up ) >cvs.log 2>&1 && - check_start_tree cvswork && - check_file cvswork textfile.c v1 && - check_file cvswork t2 v1 && - check_file cvswork adir/afile v1 && - check_file cvswork adir/a2file v1 && - check_file cvswork adir/bdir/bfile v1 && - check_file cvswork adir/bdir/b2file v1 && - check_end_full_tree cvswork v1 + ( cd cvswork && cvs -f up ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_full_tree cvswork v1 ' # This is not really legal CVS, but it seems to work anyway: test_expect_success 'cvs up -r heads/b1' ' - ( cd cvswork && cvs -f up -r heads/b1 ) >cvs.log 2>&1 && - check_start_tree cvswork && - check_file cvswork textfile.c v1.2 && - check_file cvswork t3 v1.2 && - check_file cvswork adir/afile v1.2 && - check_file cvswork adir/a3file v1.2 && - check_file cvswork adir/bdir/bfile v1.2 && - check_file cvswork adir/bdir/b3file v1.2 && - check_file cvswork cdir/cfile v1.2 && - check_end_full_tree cvswork v1.2 + ( cd cvswork && cvs -f up -r heads/b1 ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1.2 && + check_file cvswork t3 v1.2 && + check_file cvswork adir/afile v1.2 && + check_file cvswork adir/a3file v1.2 && + check_file cvswork adir/bdir/bfile v1.2 && + check_file cvswork adir/bdir/b3file v1.2 && + check_file cvswork cdir/cfile v1.2 && + check_end_full_tree cvswork v1.2 ' # But this should work even if CVS client checks -r more carefully: test_expect_success 'cvs up -r heads_-s-b2 (cvsserver escape mechanism)' ' - ( cd cvswork && cvs -f up -r heads_-s-b2 ) >cvs.log 2>&1 && - check_start_tree cvswork && - check_file cvswork textfile.c v2 && - check_file cvswork adir/afile v2 && - check_file cvswork adir/a2file v2 && - check_file cvswork adir/bdir/bfile v2 && - check_file cvswork adir/bdir/b4file v2 && - check_end_full_tree cvswork v2 + ( cd cvswork && cvs -f up -r heads_-s-b2 ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v2 && + check_file cvswork adir/afile v2 && + check_file cvswork adir/a2file v2 && + check_file cvswork adir/bdir/bfile v2 && + check_file cvswork adir/bdir/b4file v2 && + check_end_full_tree cvswork v2 ' v1hash=$(git rev-parse v1) test_expect_success 'cvs up -r $(git rev-parse v1)' ' - test -n "$v1hash" && - ( cd cvswork && cvs -f up -r "$v1hash" ) >cvs.log 2>&1 && - check_start_tree cvswork && - check_file cvswork textfile.c v1 && - check_file cvswork t2 v1 && - check_file cvswork adir/afile v1 && - check_file cvswork adir/a2file v1 && - check_file cvswork adir/bdir/bfile v1 && - check_file cvswork adir/bdir/b2file v1 && - check_end_full_tree cvswork v1 + test -n "$v1hash" && + ( cd cvswork && cvs -f up -r "$v1hash" ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_full_tree cvswork v1 ' test_expect_success 'cvs diff -r v1 -u' ' - ( cd cvswork && cvs -f diff -r v1 -u ) >cvsDiff.out 2>cvs.log && - test ! -s cvsDiff.out && - test ! -s cvs.log + ( cd cvswork && cvs -f diff -r v1 -u ) >cvsDiff.out 2>cvs.log && + test ! -s cvsDiff.out && + test ! -s cvs.log ' test_expect_success 'cvs diff -N -r v2 -u' ' - ( cd cvswork && ! cvs -f diff -N -r v2 -u ) >cvsDiff.out 2>cvs.log && - test ! -s cvs.log && - test -s cvsDiff.out && - check_diff cvsDiff.out v2 v1 >check_diff.out 2>&1 + ( cd cvswork && ! cvs -f diff -N -r v2 -u ) >cvsDiff.out 2>cvs.log && + test ! -s cvs.log && + test -s cvsDiff.out && + check_diff cvsDiff.out v2 v1 >check_diff.out 2>&1 ' test_expect_success 'cvs diff -N -r v2 -r v1.2' ' - ( cd cvswork && ! cvs -f diff -N -r v2 -r v1.2 -u ) >cvsDiff.out 2>cvs.log && - test ! -s cvs.log && - test -s cvsDiff.out && - check_diff cvsDiff.out v2 v1.2 >check_diff.out 2>&1 + ( cd cvswork && ! cvs -f diff -N -r v2 -r v1.2 -u ) >cvsDiff.out 2>cvs.log && + test ! -s cvs.log && + test -s cvsDiff.out && + check_diff cvsDiff.out v2 v1.2 >check_diff.out 2>&1 ' test_expect_success 'apply early [cvswork3] diff to b3' ' - git clone -q . gitwork3 && - ( - cd gitwork3 && - git checkout -b b3 v1 && - git apply -p0 --index <"$WORKDIR/cvswork3edit.diff" && - git commit -m "cvswork3 edits applied" - ) && - git fetch gitwork3 b3:b3 && - git tag v3 b3 + git clone -q . gitwork3 && + ( + cd gitwork3 && + git checkout -b b3 v1 && + git apply -p0 --index <"$WORKDIR/cvswork3edit.diff" && + git commit -m "cvswork3 edits applied" + ) && + git fetch gitwork3 b3:b3 && + git tag v3 b3 ' test_expect_success 'check [cvswork3] diff' ' - ( cd cvswork3 && ! cvs -f diff -N -u ) >"$WORKDIR/cvsDiff.out" 2>cvs.log && - test ! -s cvs.log && - test -s cvsDiff.out && - test $(grep Index: cvsDiff.out | wc -l) = 3 && - test_cmp cvsDiff.out cvswork3edit.diff && - check_diff cvsDiff.out v1 v3 >check_diff.out 2>&1 + ( cd cvswork3 && ! cvs -f diff -N -u ) >"$WORKDIR/cvsDiff.out" 2>cvs.log && + test ! -s cvs.log && + test -s cvsDiff.out && + test $(grep Index: cvsDiff.out | wc -l) = 3 && + test_cmp cvsDiff.out cvswork3edit.diff && + check_diff cvsDiff.out v1 v3 >check_diff.out 2>&1 ' test_expect_success 'merge early [cvswork3] b3 with b1' ' - ( cd gitwork3 && git merge "message" HEAD b1 ) && - git fetch gitwork3 b3:b3 && - git tag v3merged b3 && - git push --tags gitcvs.git b3:b3 + ( cd gitwork3 && git merge "message" HEAD b1 ) && + git fetch gitwork3 b3:b3 && + git tag v3merged b3 && + git push --tags gitcvs.git b3:b3 ' # This test would fail if cvsserver properly created a ".#afile"* file @@ -506,45 +507,45 @@ test_expect_success 'merge early [cvswork3] b3 with b1' ' # TODO: Validate that the .# file was saved properly, and then # delete/ignore it when checking the tree. test_expect_success 'cvs up dirty [cvswork3]' ' - ( - cd cvswork3 && - cvs -f up && - ! cvs -f diff -N -u >"$WORKDIR/cvsDiff.out" - ) >cvs.log 2>&1 && - test -s cvsDiff.out && - test $(grep Index: cvsDiff.out | wc -l) = 2 && - check_start_tree cvswork3 && - check_file cvswork3 textfile.c v3merged && - check_file cvswork3 t3 v3merged && - check_file cvswork3 adir/afile v3merged && - check_file cvswork3 adir/a3file v3merged && - check_file cvswork3 adir/afile5 v3merged && - check_file cvswork3 adir/bdir/bfile v3merged && - check_file cvswork3 adir/bdir/b3file v3merged && - check_file cvswork3 cdir/cfile v3merged && - check_end_full_tree cvswork3 v3merged + ( + cd cvswork3 && + cvs -f up && + ! cvs -f diff -N -u >"$WORKDIR/cvsDiff.out" + ) >cvs.log 2>&1 && + test -s cvsDiff.out && + test $(grep Index: cvsDiff.out | wc -l) = 2 && + check_start_tree cvswork3 && + check_file cvswork3 textfile.c v3merged && + check_file cvswork3 t3 v3merged && + check_file cvswork3 adir/afile v3merged && + check_file cvswork3 adir/a3file v3merged && + check_file cvswork3 adir/afile5 v3merged && + check_file cvswork3 adir/bdir/bfile v3merged && + check_file cvswork3 adir/bdir/b3file v3merged && + check_file cvswork3 cdir/cfile v3merged && + check_end_full_tree cvswork3 v3merged ' # TODO: test cvs status test_expect_success 'cvs commit [cvswork3]' ' - ( - cd cvswork3 && - cvs -f commit -m "dirty sandbox after auto-merge" - ) >cvs.log 2>&1 && - check_start_tree cvswork3 && - check_file cvswork3 textfile.c v3merged && - check_file cvswork3 t3 v3merged && - check_file cvswork3 adir/afile v3merged && - check_file cvswork3 adir/a3file v3merged && - check_file cvswork3 adir/afile5 v3merged && - check_file cvswork3 adir/bdir/bfile v3merged && - check_file cvswork3 adir/bdir/b3file v3merged && - check_file cvswork3 cdir/cfile v3merged && - check_end_full_tree cvswork3 v3merged && - git fetch gitcvs.git b3:b4 && - git tag v4.1 b4 && - git diff --exit-code v4.1 v3merged >check_diff_apply.out 2>&1 + ( + cd cvswork3 && + cvs -f commit -m "dirty sandbox after auto-merge" + ) >cvs.log 2>&1 && + check_start_tree cvswork3 && + check_file cvswork3 textfile.c v3merged && + check_file cvswork3 t3 v3merged && + check_file cvswork3 adir/afile v3merged && + check_file cvswork3 adir/a3file v3merged && + check_file cvswork3 adir/afile5 v3merged && + check_file cvswork3 adir/bdir/bfile v3merged && + check_file cvswork3 adir/bdir/b3file v3merged && + check_file cvswork3 cdir/cfile v3merged && + check_end_full_tree cvswork3 v3merged && + git fetch gitcvs.git b3:b4 && + git tag v4.1 b4 && + git diff --exit-code v4.1 v3merged >check_diff_apply.out 2>&1 ' test_done