X-Git-Url: https://git.octo.it/?p=git.git;a=blobdiff_plain;f=git-cvsserver.perl;h=5ccca4f99f31b9fa370baaf7e85795ed2bf49f13;hp=7b3ba1b9de8f55df135c3d27b622aa5357b7e2b8;hb=HEAD;hpb=539d84fe0a661f4e273d8a22e74d55d2fafc7598 diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 7b3ba1b9..5ccca4f9 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -87,12 +87,37 @@ $log->info("--------------- STARTING -----------------"); my $TEMP_DIR = tempdir( CLEANUP => 1 ); $log->debug("Temporary directory is '$TEMP_DIR'"); +# if we are called with a pserver argument, +# deal with the authentication cat before entering the +# main loop +if (@ARGV && $ARGV[0] eq 'pserver') { + my $line = ; chomp $line; + unless( $line eq 'BEGIN AUTH REQUEST') { + die "E Do not understand $line - expecting BEGIN AUTH REQUEST\n"; + } + $line = ; chomp $line; + req_Root('root', $line) # reuse Root + or die "E Invalid root $line \n"; + $line = ; chomp $line; + unless ($line eq 'anonymous') { + print "E Only anonymous user allowed via pserver\n"; + print "I HATE YOU\n"; + } + $line = ; chomp $line; # validate the password? + $line = ; chomp $line; + unless ($line eq 'END AUTH REQUEST') { + die "E Do not understand $line -- expecting END AUTH REQUEST\n"; + } + print "I LOVE YOU\n"; + # and now back to our regular programme... +} + # Keep going until the client closes the connection while () { chomp; - # Check to see if we've seen this method, and call appropiate function. + # Check to see if we've seen this method, and call appropriate function. if ( /^([\w-]+)(?:\s+(.*))?$/ and defined($methods->{$1}) ) { # use the $methods hash to call the appropriate sub for this command @@ -139,8 +164,21 @@ sub req_Root $state->{CVSROOT} = $data; $ENV{GIT_DIR} = $state->{CVSROOT} . "/"; + unless (-d $ENV{GIT_DIR} && -e $ENV{GIT_DIR}.'HEAD') { + print "E $ENV{GIT_DIR} does not seem to be a valid GIT repository\n"; + print "E \n"; + print "error 1 $ENV{GIT_DIR} is not a valid repository\n"; + return 0; + } - foreach my $line ( `git-var -l` ) + my @gitvars = `git-repo-config -l`; + if ($?) { + print "E problems executing git-repo-config on the server -- this is not a git repository or the PATH is not set correctly.\n"; + print "E \n"; + print "error 1 - problem executing git-repo-config\n"; + return 0; + } + foreach my $line ( @gitvars ) { next unless ( $line =~ /^(.*?)\.(.*?)=(.*)$/ ); $cfg->{$1}{$2} = $3; @@ -152,6 +190,7 @@ sub req_Root print "E the repo config file needs a [gitcvs] section added, and the parameter 'enabled' set to 1\n"; print "E \n"; print "error 1 GITCVS emulation disabled\n"; + return 0; } if ( defined ( $cfg->{gitcvs}{logfile} ) ) @@ -160,6 +199,8 @@ sub req_Root } else { $log->nofile(); } + + return 1; } # Global_option option \n @@ -173,8 +214,7 @@ sub req_Globaloption { my ( $cmd, $data ) = @_; $log->debug("req_Globaloption : $data"); - - # TODO : is this data useful ??? + $state->{globaloptions}{$data} = 1; } # Valid-responses request-list \n @@ -183,7 +223,7 @@ sub req_Globaloption sub req_Validresponses { my ( $cmd, $data ) = @_; - $log->debug("req_Validrepsonses : $data"); + $log->debug("req_Validresponses : $data"); # TODO : re-enable this, currently it's not particularly useful #$state->{validresponses} = [ split /\s+/, $data ]; @@ -226,12 +266,32 @@ sub req_Directory $state->{localdir} = $data; $state->{repository} = $repository; - $state->{directory} = $repository; - $state->{directory} =~ s/^$state->{CVSROOT}\///; - $state->{module} = $1 if ($state->{directory} =~ s/^(.*?)(\/|$)//); + $state->{path} = $repository; + $state->{path} =~ s/^$state->{CVSROOT}\///; + $state->{module} = $1 if ($state->{path} =~ s/^(.*?)(\/|$)//); + $state->{path} .= "/" if ( $state->{path} =~ /\S/ ); + + $state->{directory} = $state->{localdir}; + $state->{directory} = "" if ( $state->{directory} eq "." ); $state->{directory} .= "/" if ( $state->{directory} =~ /\S/ ); - $log->debug("req_Directory : localdir=$data repository=$repository directory=$state->{directory} module=$state->{module}"); + if ( not defined($state->{prependdir}) and $state->{localdir} eq "." and $state->{path} =~ /\S/ ) + { + $log->info("Setting prepend to '$state->{path}'"); + $state->{prependdir} = $state->{path}; + foreach my $entry ( keys %{$state->{entries}} ) + { + $state->{entries}{$state->{prependdir} . $entry} = $state->{entries}{$entry}; + delete $state->{entries}{$entry}; + } + } + + if ( defined ( $state->{prependdir} ) ) + { + $log->debug("Prepending '$state->{prependdir}' to state|directory"); + $state->{directory} = $state->{prependdir} . $state->{directory} + } + $log->debug("req_Directory : localdir=$data repository=$repository path=$state->{path} directory=$state->{directory} module=$state->{module}"); } # Entry entry-line \n @@ -249,7 +309,7 @@ sub req_Entry { my ( $cmd, $data ) = @_; - $log->debug("req_Entry : $data"); + #$log->debug("req_Entry : $data"); my @data = split(/\//, $data); @@ -259,6 +319,22 @@ sub req_Entry options => $data[4], tag_or_date => $data[5], }; + + $log->info("Received entry line '$data' => '" . $state->{directory} . $data[1] . "'"); +} + +# Questionable filename \n +# Response expected: no. Additional data: no. Tell the server to check +# whether filename should be ignored, and if not, next time the server +# sends responses, send (in a M response) `?' followed by the directory and +# filename. filename must not contain `/'; it needs to be a file in the +# directory named by the most recent Directory request. +sub req_Questionable +{ + my ( $cmd, $data ) = @_; + + $log->debug("req_Questionable : $data"); + $state->{entries}{$state->{directory}.$data}{questionable} = 1; } # add \n @@ -291,8 +367,7 @@ sub req_add next; } - - my ( $filepart, $dirpart ) = filenamesplit($filename); + my ( $filepart, $dirpart ) = filenamesplit($filename, 1); print "E cvs add: scheduling file `$filename' for addition\n"; @@ -373,7 +448,7 @@ sub req_remove } - my ( $filepart, $dirpart ) = filenamesplit($filename); + my ( $filepart, $dirpart ) = filenamesplit($filename, 1); print "E cvs remove: scheduling `$filename' for removal\n"; @@ -461,22 +536,6 @@ sub req_Unchanged #$log->debug("req_Unchanged : $data"); } -# Questionable filename \n -# Response expected: no. Additional data: no. -# Tell the server to check whether filename should be ignored, -# and if not, next time the server sends responses, send (in -# a M response) `?' followed by the directory and filename. -# filename must not contain `/'; it needs to be a file in the -# directory named by the most recent Directory request. -sub req_Questionable -{ - my ( $cmd, $data ) = @_; - - $state->{entries}{$state->{directory}.$data}{questionable} = 1; - - #$log->debug("req_Questionable : $data"); -} - # Argument text \n # Response expected: no. Save argument for use in a subsequent command. # Arguments accumulate until an argument-using command is given, at which @@ -576,14 +635,57 @@ sub req_co # 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} . "/$checkout_path/\n"; + print $state->{CVSROOT} . "/$module/\n"; print "Clear-static-directory $checkout_path/\n"; - print $state->{CVSROOT} . "/$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"; 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} ) { @@ -592,6 +694,17 @@ sub req_co ( $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"; + } + # modification time of this file print "Mod-time $git->{modified}\n"; @@ -603,22 +716,10 @@ sub req_co print "M U $checkout_path/$git->{name}\n"; } - if (length($git->{dir}) && $git->{dir} ne './' && !exists($seendirs{$git->{dir}})) { + # instruct client we're sending a file to put in this path + print "Created $checkout_path/" . ( defined ( $git->{dir} ) and $git->{dir} ne "./" ? $git->{dir} . "/" : "" ) . "\n"; - # Eclipse seems to need the Clear-sticky command - # to prepare the 'Entries' file for the new directory. - print "Clear-sticky $module/$git->{dir}\n"; - print $state->{CVSROOT} . "/$module/$git->{dir}\n"; - print "Clear-static-directory $module/$git->{dir}\n"; - print $state->{CVSROOT} . "/$module/$git->{dir}\n"; - print "E cvs checkout: Updating /$module/$git->{dir}\n"; - $seendirs{$git->{dir}} = 1; - } - - # instruct client we're sending a file to put in this path - print "Created $checkout_path/" . ( defined ( $git->{dir} ) and $git->{dir} ne "./" ? $git->{dir} . "/" : "" ) . "\n"; - - print $state->{CVSROOT} . "/$module/" . ( defined ( $git->{dir} ) and $git->{dir} ne "./" ? $git->{dir} . "/" : "" ) . "$git->{name}\n"; + print $state->{CVSROOT} . "/$module/" . ( defined ( $git->{dir} ) and $git->{dir} ne "./" ? $git->{dir} . "/" : "" ) . "$git->{name}\n"; # this is an "entries" line print "/$git->{name}/1.$git->{revision}///\n"; @@ -650,7 +751,7 @@ sub req_update argsplit("update"); # - # It may just be a client exploring the available heads/modukles + # It may just be a client exploring the available heads/modules # in that case, list them as top level directories and leave it # at that. Eclipse uses this technique to offer you a list of # projects (heads in this case) to checkout. @@ -674,8 +775,7 @@ sub req_update $updater->update(); - # if no files were specified, we need to work out what files we should be providing status on ... - argsfromdir($updater) if ( scalar ( @{$state->{args}} ) == 0 ); + argsfromdir($updater); #$log->debug("update state : " . Dumper($state)); @@ -684,6 +784,8 @@ sub req_update { $filename = filecleanup($filename); + $log->debug("Processing file $filename"); + # if we have a -C we should pretend we never saw modified stuff if ( exists ( $state->{opt}{C} ) ) { @@ -738,13 +840,16 @@ sub req_update if ( $meta->{filehash} eq "deleted" ) { - my ( $filepart, $dirpart ) = filenamesplit($filename); + my ( $filepart, $dirpart ) = filenamesplit($filename,1); $log->info("Removing '$filename' from working copy (no longer in the repo)"); print "E cvs update: `$filename' is no longer in the repository\n"; - print "Removed $dirpart\n"; - print "$filepart\n"; + # Don't want to actually _DO_ the update if -n specified + unless ( $state->{globaloptions}{-n} ) { + print "Removed $dirpart\n"; + print "$filepart\n"; + } } elsif ( not defined ( $state->{entries}{$filename}{modified_hash} ) or $state->{entries}{$filename}{modified_hash} eq $oldmeta->{filehash} ) @@ -757,34 +862,42 @@ sub req_update print "MT newline\n"; print "MT -updated\n"; - my ( $filepart, $dirpart ) = filenamesplit($filename); - $dirpart =~ s/^$state->{directory}//; - - if ( defined ( $wrev ) ) - { - # instruct client we're sending a file to put in this path as a replacement - print "Update-existing $dirpart\n"; - $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 "Created $dirpart\n"; - $log->debug("Creating new file 'Created $dirpart'"); - } - print $state->{CVSROOT} . "/$state->{module}/$filename\n"; - - # this is an "entries" line - $log->debug("/$filepart/1.$meta->{revision}///"); - print "/$filepart/1.$meta->{revision}///\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"; - - # transmit file - transmitfile($meta->{filehash}); + my ( $filepart, $dirpart ) = filenamesplit($filename,1); + + # Don't want to actually _DO_ the update if -n specified + unless ( $state->{globaloptions}{-n} ) + { + if ( defined ( $wrev ) ) + { + # instruct client we're sending a file to put in this path as a replacement + print "Update-existing $dirpart\n"; + $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"; + } + print $state->{CVSROOT} . "/$state->{module}/$filename\n"; + + # this is an "entries" line + $log->debug("/$filepart/1.$meta->{revision}///"); + print "/$filepart/1.$meta->{revision}///\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"; + + # transmit file + transmitfile($meta->{filehash}); + } } else { $log->info("Updating '$filename'"); - my ( $filepart, $dirpart ) = filenamesplit($meta->{name}); + my ( $filepart, $dirpart ) = filenamesplit($meta->{name},1); my $dir = tempdir( DIR => $TEMP_DIR, CLEANUP => 1 ) . "/"; @@ -809,19 +922,29 @@ sub req_update $log->info("Merged successfully"); print "M M $filename\n"; $log->debug("Update-existing $dirpart"); - print "Update-existing $dirpart\n"; - $log->debug($state->{CVSROOT} . "/$state->{module}/$filename"); - print $state->{CVSROOT} . "/$state->{module}/$filename\n"; - $log->debug("/$filepart/1.$meta->{revision}///"); - print "/$filepart/1.$meta->{revision}///\n"; + + # Don't want to actually _DO_ the update if -n specified + unless ( $state->{globaloptions}{-n} ) + { + print "Update-existing $dirpart\n"; + $log->debug($state->{CVSROOT} . "/$state->{module}/$filename"); + print $state->{CVSROOT} . "/$state->{module}/$filename\n"; + $log->debug("/$filepart/1.$meta->{revision}///"); + print "/$filepart/1.$meta->{revision}///\n"; + } } elsif ( $return == 1 ) { $log->info("Merged with conflicts"); print "M C $filename\n"; - print "Update-existing $dirpart\n"; - print $state->{CVSROOT} . "/$state->{module}/$filename\n"; - print "/$filepart/1.$meta->{revision}/+//\n"; + + # Don't want to actually _DO_ the update if -n specified + unless ( $state->{globaloptions}{-n} ) + { + print "Update-existing $dirpart\n"; + print $state->{CVSROOT} . "/$state->{module}/$filename\n"; + print "/$filepart/1.$meta->{revision}/+//\n"; + } } else { @@ -829,17 +952,21 @@ sub req_update next; } - # permissions - $log->debug("SEND : u=$meta->{mode},g=$meta->{mode},o=$meta->{mode}"); - print "u=$meta->{mode},g=$meta->{mode},o=$meta->{mode}\n"; - - # transmit file, format is single integer on a line by itself (file - # size) followed by the file contents - # TODO : we should copy files in blocks - my $data = `cat $file_local`; - $log->debug("File size : " . length($data)); - print length($data) . "\n"; - print $data; + # Don't want to actually _DO_ the update if -n specified + unless ( $state->{globaloptions}{-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"; + + # transmit file, format is single integer on a line by itself (file + # size) followed by the file contents + # TODO : we should copy files in blocks + my $data = `cat $file_local`; + $log->debug("File size : " . length($data)); + print length($data) . "\n"; + print $data; + } chdir "/"; } @@ -859,8 +986,15 @@ sub req_ci $log->info("req_ci : " . ( defined($data) ? $data : "[NULL]" )); + if ( @ARGV && $ARGV[0] eq 'pserver') + { + print "error 1 pserver access cannot commit\n"; + exit; + } + if ( -e $state->{CVSROOT} . "/index" ) { + $log->warn("file 'index' already exists in the git repository"); print "error 1 Index already exists in git repo\n"; exit; } @@ -868,6 +1002,7 @@ sub req_ci my $lockfile = "$state->{CVSROOT}/refs/heads/$state->{module}.lock"; unless ( sysopen(LOCKFILE,$lockfile,O_EXCL|O_CREAT|O_WRONLY) ) { + $log->warn("lockfile '$lockfile' already exists, please try again"); print "error 1 Lock file '$lockfile' already exists, please try again\n"; exit; } @@ -899,6 +1034,7 @@ sub req_ci # foreach file specified on the commandline ... foreach my $filename ( @{$state->{args}} ) { + my $committedfile = $filename; $filename = filecleanup($filename); next unless ( exists $state->{entries}{$filename}{modified_filename} or not $state->{entries}{$filename}{unchanged} ); @@ -933,7 +1069,7 @@ sub req_ci exit; } - push @committedfiles, $filename; + push @committedfiles, $committedfile; $log->info("Committing $filename"); system("mkdir","-p",$dirpart) unless ( -d $dirpart ); @@ -1016,7 +1152,7 @@ sub req_ci my $meta = $updater->getmeta($filename); - my ( $filepart, $dirpart ) = filenamesplit($filename); + my ( $filepart, $dirpart ) = filenamesplit($filename, 1); $log->debug("Checked-in $dirpart : $filename"); @@ -1052,7 +1188,7 @@ sub req_status $updater->update(); # if no files were specified, we need to work out what files we should be providing status on ... - argsfromdir($updater) if ( scalar ( @{$state->{args}} ) == 0 ); + argsfromdir($updater); # foreach file specified on the commandline ... foreach my $filename ( @{$state->{args}} ) @@ -1153,7 +1289,7 @@ sub req_diff $updater->update(); # if no files were specified, we need to work out what files we should be providing status on ... - argsfromdir($updater) if ( scalar ( @{$state->{args}} ) == 0 ); + argsfromdir($updater); # foreach file specified on the commandline ... foreach my $filename ( @{$state->{args}} ) @@ -1295,7 +1431,7 @@ sub req_log $updater->update(); # if no files were specified, we need to work out what files we should be providing status on ... - argsfromdir($updater) if ( scalar ( @{$state->{args}} ) == 0 ); + argsfromdir($updater); # foreach file specified on the commandline ... foreach my $filename ( @{$state->{args}} ) @@ -1371,7 +1507,7 @@ sub req_annotate $updater->update(); # if no files were specified, we need to work out what files we should be providing annotate on ... - argsfromdir($updater) if ( scalar ( @{$state->{args}} ) == 0 ); + argsfromdir($updater); # we'll need a temporary checkout dir my $tmpdir = tempdir ( DIR => $TEMP_DIR ); @@ -1566,13 +1702,36 @@ sub argsfromdir { my $updater = shift; - $state->{args} = []; + $state->{args} = [] if ( scalar(@{$state->{args}}) == 1 and $state->{args}[0] eq "." ); + + return if ( scalar ( @{$state->{args}} ) > 1 ); - foreach my $file ( @{$updater->gethead} ) + if ( scalar(@{$state->{args}}) == 1 ) { - next if ( $file->{filehash} eq "deleted" and not defined ( $state->{entries}{$file->{name}} ) ); - next unless ( $file->{name} =~ s/^$state->{directory}// ); - push @{$state->{args}}, $file->{name}; + my $arg = $state->{args}[0]; + $arg .= $state->{prependdir} if ( defined ( $state->{prependdir} ) ); + + $log->info("Only one arg specified, checking for directory expansion on '$arg'"); + + foreach my $file ( @{$updater->gethead} ) + { + 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}; + } + + shift @{$state->{args}} if ( scalar(@{$state->{args}}) > 1 ); + } else { + $log->info("Only one arg specified, populating file list automatically"); + + $state->{args} = []; + + foreach my $file ( @{$updater->gethead} ) + { + 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}; + } } } @@ -1642,16 +1801,22 @@ sub transmitfile } # This method takes a file name, and returns ( $dirpart, $filepart ) which -# refers to the directory porition and the file portion of the filename +# refers to the directory portion and the file portion of the filename # respectively sub filenamesplit { my $filename = shift; + my $fixforlocaldir = shift; my ( $filepart, $dirpart ) = ( $filename, "." ); ( $filepart, $dirpart ) = ( $2, $1 ) if ( $filename =~ /(.*)\/(.*)/ ); $dirpart .= "/"; + if ( $fixforlocaldir ) + { + $dirpart =~ s/^$state->{prependdir}//; + } + return ( $filepart, $dirpart ); } @@ -1667,8 +1832,7 @@ sub filecleanup } $filename =~ s/^\.\///g; - $filename = $state->{directory} . $filename; - + $filename = $state->{prependdir} . $filename; return $filename; } @@ -1701,7 +1865,7 @@ Log::Log4perl =head2 new Creates a new log object, optionally you can specify a filename here to -indicate the file to log to. If no log file is specified, you can specifiy one +indicate the file to log to. If no log file is specified, you can specify one later with method setfile, or indicate you no longer want logging with method nofile. @@ -1987,14 +2151,15 @@ sub update # TODO: log processing is memory bound # if we can parse into a 2nd file that is in reverse order # we can probably do something really efficient - my @git_log_params = ('--parents', '--topo-order'); + my @git_log_params = ('--pretty', '--parents', '--topo-order'); if (defined $lastcommit) { push @git_log_params, "$lastcommit..$self->{module}"; } else { push @git_log_params, $self->{module}; } - open(GITLOG, '-|', 'git-log', @git_log_params) or die "Cannot call git-log: $!"; + # 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; @@ -2349,7 +2514,7 @@ sub gethead return $self->{gethead_cache} if ( defined ( $self->{gethead_cache} ) ); - my $db_query = $self->{dbh}->prepare_cached("SELECT name, filehash, mode, revision, modified, commithash, author FROM head",{},1); + my $db_query = $self->{dbh}->prepare_cached("SELECT name, filehash, mode, revision, modified, commithash, author FROM head ORDER BY name ASC",{},1); $db_query->execute(); my $tree = []; @@ -2506,7 +2671,7 @@ sub in_array =head2 safe_pipe_capture -an alterative to `command` that allows input to be passed as an array +an alternative to `command` that allows input to be passed as an array to work around shell problems with weird characters in arguments =cut