in git-cvsserver.perl [1173:1517]
sub req_update
{
my ( $cmd, $data ) = @_;
$log->debug("req_update : " . ( defined($data) ? $data : "[NULL]" ));
argsplit("update");
#
# 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.
#
if ($state->{module} eq '') {
my $showref = safe_pipe_capture(qw(git show-ref --heads));
print "E cvs update: Updating .\n";
for my $line (split '\n', $showref) {
if ( $line =~ m% refs/heads/(.*)$% ) {
print "E cvs update: New directory `$1'\n";
}
}
print "ok\n";
return 1;
}
# Grab a handle to the SQLite db and do any necessary updates
my $updater = GITCVS::updater->new($state->{CVSROOT}, $state->{module}, $log);
$updater->update();
argsfromdir($updater);
#$log->debug("update state : " . Dumper($state));
my($repoDir);
$repoDir=$state->{CVSROOT} . "/$state->{module}/$state->{prependdir}";
my %seendirs = ();
# foreach file specified on the command line ...
foreach my $argsFilename ( @{$state->{args}} )
{
my $filename;
$filename = filecleanup($argsFilename);
$log->debug("Processing file $filename");
# if we have a -C we should pretend we never saw modified stuff
if ( exists ( $state->{opt}{C} ) )
{
delete $state->{entries}{$filename}{modified_hash};
delete $state->{entries}{$filename}{modified_filename};
$state->{entries}{$filename}{unchanged} = 1;
}
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} ) ) {
if ( defined ( $meta->{revision} ) ) {
$log->info("Printing '$filename' revision " . $meta->{revision});
transmitfile($meta->{filehash}, { print => 1 });
}
next;
}
# Directories:
prepDirForOutput(
dirname($argsFilename),
$repoDir,
".",
\%seendirs,
"update",
$state->{dirArgs} );
my $wrev = revparse($filename);
if ( ! defined $meta )
{
$meta = {
name => $filename,
revision => '0',
filehash => 'added'
};
if($wrev ne "0")
{
$meta->{filehash}='deleted';
}
}
my $oldmeta = $meta;
# If the working copy is an old revision, lets get that version too for comparison.
my $oldWrev=$wrev;
if(defined($oldWrev))
{
$oldWrev=~s/^-//;
if($oldWrev ne $meta->{revision})
{
$oldmeta = $updater->getmeta($filename, $oldWrev);
}
}
#$log->debug("Target revision is $meta->{revision}, current working revision is $wrev");
# Files are up to date if the working copy and repo copy have the same revision,
# and the working copy is unmodified _and_ the user hasn't specified -C
next if ( defined ( $wrev )
and defined($meta->{revision})
and $wrev eq $meta->{revision}
and $state->{entries}{$filename}{unchanged}
and not exists ( $state->{opt}{C} ) );
# If the working copy and repo copy have the same revision,
# but the working copy is modified, tell the client it's modified
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} ) )
{
$log->info("Tell the client the file is modified");
print "MT text M \n";
print "MT fname $filename\n";
print "MT newline\n";
next;
}
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
# file.
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";
# 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}
or $meta->{filehash} eq 'added' )
{
# normal update, just send the new revision (either U=Update,
# or A=Add, or R=Remove)
if ( defined($wrev) && ($wrev=~/^-/) )
{
$log->info("Tell the client the file is scheduled for removal");
print "MT text R \n";
print "MT fname $filename\n";
print "MT newline\n";
next;
}
elsif ( (!defined($wrev) || $wrev eq '0') &&
(!defined($meta->{revision}) || $meta->{revision} eq '0') )
{
$log->info("Tell the client the file is scheduled for addition");
print "MT text A \n";
print "MT fname $filename\n";
print "MT newline\n";
next;
}
else {
$log->info("UpdatingX3 '$filename' to ".$meta->{revision});
print "MT +updated\n";
print "MT text U \n";
print "MT fname $filename\n";
print "MT newline\n";
print "MT -updated\n";
}
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
$log->debug("Creating new file 'Created $dirpart'");
print "Created $dirpart\n";
}
print $state->{CVSROOT} . "/$state->{module}/$filename\n";
# this is an "entries" line
my $kopts = kopts_from_path($filename,"sha1",$meta->{filehash});
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}");
print "u=$meta->{mode},g=$meta->{mode},o=$meta->{mode}\n";
# transmit file
transmitfile($meta->{filehash});
}
} else {
my ( $filepart, $dirpart ) = filenamesplit($meta->{name},1);
my $mergeDir = setupTmpDir();
my $file_local = $filepart . ".mine";
my $mergedFile = "$mergeDir/$file_local";
system("ln","-s",$state->{entries}{$filename}{modified_filename}, $file_local);
my $file_old = $filepart . "." . $oldmeta->{revision};
transmitfile($oldmeta->{filehash}, { targetfile => $file_old });
my $file_new = $filepart . "." . $meta->{revision};
transmitfile($meta->{filehash}, { targetfile => $file_new });
# we need to merge with the local changes ( M=successful merge, C=conflict merge )
$log->info("Merging $file_local, $file_old, $file_new");
print "M Merging differences between $oldmeta->{revision} and $meta->{revision} into $filename\n";
$log->debug("Temporary directory for merge is $mergeDir");
my $return = system("git", "merge-file", $file_local, $file_old, $file_new);
$return >>= 8;
cleanupTmpDir();
if ( $return == 0 )
{
$log->info("Merged successfully");
print "M M $filename\n";
$log->debug("Merged $dirpart");
# Don't want to actually _DO_ the update if -n specified
unless ( $state->{globaloptions}{-n} )
{
print "Merged $dirpart\n";
$log->debug($state->{CVSROOT} . "/$state->{module}/$filename");
print $state->{CVSROOT} . "/$state->{module}/$filename\n";
my $kopts = kopts_from_path("$dirpart/$filepart",
"file",$mergedFile);
$log->debug("/$filepart/$meta->{revision}//$kopts/");
my $entriesLine="/$filepart/$meta->{revision}//$kopts/";
$entriesLine .= getStickyTagOrDate($stickyInfo);
print "$entriesLine\n";
}
}
elsif ( $return == 1 )
{
$log->info("Merged with conflicts");
print "E cvs update: conflicts found in $filename\n";
print "M C $filename\n";
# Don't want to actually _DO_ the update if -n specified
unless ( $state->{globaloptions}{-n} )
{
print "Merged $dirpart\n";
print $state->{CVSROOT} . "/$state->{module}/$filename\n";
my $kopts = kopts_from_path("$dirpart/$filepart",
"file",$mergedFile);
my $entriesLine = "/$filepart/$meta->{revision}/+/$kopts/";
$entriesLine .= getStickyTagOrDate($stickyInfo);
print "$entriesLine\n";
}
}
else
{
$log->warn("Merge failed");
next;
}
# 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 = safe_pipe_capture('cat', $mergedFile);
$log->debug("File size : " . length($data));
print length($data) . "\n";
print $data;
}
}
}
# 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";
}