sub req_ci()

in git-cvsserver.perl [1519:1801]


sub req_ci
{
    my ( $cmd, $data ) = @_;

    argsplit("ci");

    #$log->debug("State : " . Dumper($state));

    $log->info("req_ci : " . ( defined($data) ? $data : "[NULL]" ));

    if ( $state->{method} eq 'pserver' and $state->{user} eq 'anonymous' )
    {
        print "error 1 anonymous user cannot commit via pserver\n";
        cleanupWorkTree();
        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";
        cleanupWorkTree();
        exit;
    }

    # Grab a handle to the SQLite db and do any necessary updates
    my $updater = GITCVS::updater->new($state->{CVSROOT}, $state->{module}, $log);
    $updater->update();

    my @committedfiles = ();
    my %oldmeta;
    my $stickyInfo;
    my $branchRef;
    my $parenthash;

    # foreach file specified on the command line ...
    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} );

        #####
        # 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 = safe_pipe_capture('git', 'show-ref', '-s', $branchRef);
            chomp $parenthash;
            if ($parenthash !~ /^[0-9a-f]{$state->{hexsz}}$/)
            {
                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);

        my ( $filepart, $dirpart ) = filenamesplit($filename);

	# do a checkout of the file if it is part of this tree
        if ($wrev) {
            system('git', 'checkout-index', '-f', '-u', $filename);
            unless ($? == 0) {
                die "Error running git-checkout-index -f -u $filename : $!";
            }
        }

        my $addflag = 0;
        my $rmflag = 0;
        $rmflag = 1 if ( defined($wrev) and ($wrev=~/^-/) );
        $addflag = 1 unless ( -e $filename );

        # Do up to date checking
        unless ( $addflag or $wrev eq $meta->{revision} or
                 ( $rmflag and $wrev eq "-$meta->{revision}" ) )
        {
            # fail everything if an up to date check fails
            print "error 1 Up to date check failed for $filename\n";
            cleanupWorkTree();
            exit;
        }

        push @committedfiles, $committedfile;
        $log->info("Committing $filename");

        system("mkdir","-p",$dirpart) unless ( -d $dirpart );

        unless ( $rmflag )
        {
            $log->debug("rename $state->{entries}{$filename}{modified_filename} $filename");
            rename $state->{entries}{$filename}{modified_filename},$filename;

            # Calculate modes to remove
            my $invmode = "";
            foreach ( qw (r w x) ) { $invmode .= $_ unless ( $state->{entries}{$filename}{modified_mode} =~ /$_/ ); }

            $log->debug("chmod u+" . $state->{entries}{$filename}{modified_mode} . "-" . $invmode . " $filename");
            system("chmod","u+" .  $state->{entries}{$filename}{modified_mode} . "-" . $invmode, $filename);
        }

        if ( $rmflag )
        {
            $log->info("Removing file '$filename'");
            unlink($filename);
            system("git", "update-index", "--remove", $filename);
        }
        elsif ( $addflag )
        {
            $log->info("Adding file '$filename'");
            system("git", "update-index", "--add", $filename);
        } else {
            $log->info("UpdatingX2 file '$filename'");
            system("git", "update-index", $filename);
        }
    }

    unless ( scalar(@committedfiles) > 0 )
    {
        print "E No files to commit\n";
        print "ok\n";
        cleanupWorkTree();
        return;
    }

    my $treehash = safe_pipe_capture(qw(git write-tree));
    chomp $treehash;

    $log->debug("Treehash : $treehash, Parenthash : $parenthash");

    # write our commit message out if we have one ...
    my ( $msg_fh, $msg_filename ) = tempfile( DIR => $TEMP_DIR );
    print $msg_fh $state->{opt}{m};# if ( exists ( $state->{opt}{m} ) );
    if ( defined ( $cfg->{gitcvs}{commitmsgannotation} ) ) {
        if ($cfg->{gitcvs}{commitmsgannotation} !~ /^\s*$/ ) {
            print $msg_fh "\n\n".$cfg->{gitcvs}{commitmsgannotation}."\n"
        }
    } else {
        print $msg_fh "\n\nvia git-CVS emulator\n";
    }
    close $msg_fh;

    my $commithash = safe_pipe_capture('git', 'commit-tree', $treehash, '-p', $parenthash, '-F', $msg_filename);
    chomp($commithash);
    $log->info("Commit hash : $commithash");

    unless ( $commithash =~ /[a-zA-Z0-9]{$state->{hexsz}}/ )
    {
        $log->warn("Commit failed (Invalid commit hash)");
        print "error 1 Commit failed (unknown reason)\n";
        cleanupWorkTree();
        exit;
    }

	### Emulate git-receive-pack by running hooks/update
	my @hook = ( $ENV{GIT_DIR}.'hooks/update', $branchRef,
			$parenthash, $commithash );
	if( -x $hook[0] ) {
		unless( system( @hook ) == 0 )
		{
			$log->warn("Commit failed (update hook declined to update ref)");
			print "error 1 Commit failed (update hook declined)\n";
			cleanupWorkTree();
			exit;
		}
	}

	### Update the ref
	if (system(qw(git update-ref -m), "cvsserver ci",
			$branchRef, $commithash, $parenthash)) {
		$log->warn("update-ref for $state->{module} failed.");
		print "error 1 Cannot commit -- update first\n";
		cleanupWorkTree();
		exit;
	}

	### Emulate git-receive-pack by running hooks/post-receive
	my $hook = $ENV{GIT_DIR}.'hooks/post-receive';
	if( -x $hook ) {
		open(my $pipe, "| $hook") || die "can't fork $!";

		local $SIG{PIPE} = sub { die 'pipe broke' };

		print $pipe "$parenthash $commithash $branchRef\n";

		close $pipe || die "bad pipe: $! $?";
	}

    $updater->update();

	### Then hooks/post-update
	$hook = $ENV{GIT_DIR}.'hooks/post-update';
	if (-x $hook) {
		system($hook, $branchRef);
	}

    # foreach file specified on the command line ...
    foreach my $filename ( @committedfiles )
    {
        $filename = filecleanup($filename);

        my $meta = $updater->getmeta($filename,$stickyInfo);
	unless (defined $meta->{revision}) {
	  $meta->{revision} = "1.1";
	}

        my ( $filepart, $dirpart ) = filenamesplit($filename, 1);

        $log->debug("Checked-in $dirpart : $filename");

	print "M $state->{CVSROOT}/$state->{module}/$filename,v  <--  $dirpart$filepart\n";
        if ( defined $meta->{filehash} && $meta->{filehash} eq "deleted" )
        {
            print "M new revision: delete; previous revision: $oldmeta{$filename}{revision}\n";
            print "Remove-entry $dirpart\n";
            print "$filename\n";
        } else {
            if ($meta->{revision} eq "1.1") {
	        print "M initial revision: 1.1\n";
            } else {
	        print "M new revision: $meta->{revision}; previous revision: $oldmeta{$filename}{revision}\n";
            }
            print "Checked-in $dirpart\n";
            print "$filename\n";
            my $kopts = kopts_from_path($filename,"sha1",$meta->{filehash});
            print "/$filepart/$meta->{revision}//$kopts/" .
                  getStickyTagOrDate($stickyInfo) . "\n";
        }
    }

    cleanupWorkTree();
    print "ok\n";
}