gitweb: start to generate PATH_INFO URLs.
[gitweb.git] / git-cvsserver.perl
index ffd9c66f940d6454d077e2b0082f5afd360d901e..2130d57020de2dae570b7be0d9a516389bd1829f 100755 (executable)
@@ -81,7 +81,7 @@
 
 # $state holds all the bits of information the clients sends us that could
 # potentially be useful when it comes to actually _doing_ something.
-my $state = {};
+my $state = { prependdir => '' };
 $log->info("--------------- STARTING -----------------");
 
 my $TEMP_DIR = tempdir( CLEANUP => 1 );
@@ -214,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
@@ -267,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
@@ -290,7 +309,7 @@ sub req_Entry
 {
     my ( $cmd, $data ) = @_;
 
-    $log->debug("req_Entry : $data");
+    #$log->debug("req_Entry : $data");
 
     my @data = split(/\//, $data);
 
@@ -300,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
@@ -332,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";
 
@@ -414,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";
 
@@ -502,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
@@ -529,12 +547,15 @@ sub req_Argument
 {
     my ( $cmd, $data ) = @_;
 
-    # TODO :  Not quite sure how Argument and Argumentx differ, but I assume
-    # it's for multi-line arguments ... somehow ...
+    # Argumentx means: append to last Argument (with a newline in front)
 
     $log->debug("$cmd : $data");
 
-    push @{$state->{arguments}}, $data;
+    if ( $cmd eq 'Argumentx') {
+        ${$state->{arguments}}[$#{$state->{arguments}}] .= "\n" . $data;
+    } else {
+        push @{$state->{arguments}}, $data;
+    }
 }
 
 # expand-modules \n
@@ -757,16 +778,17 @@ 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));
 
-    # foreach file specified on the commandline ...
+    # foreach file specified on the command line ...
     foreach my $filename ( @{$state->{args}} )
     {
         $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} ) )
         {
@@ -821,13 +843,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} )
@@ -840,34 +865,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 ) . "/";
 
@@ -892,19 +925,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
             {
@@ -912,17 +955,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 "/";
         }
@@ -950,6 +997,7 @@ sub req_ci
 
     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;
     }
@@ -957,6 +1005,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;
     }
@@ -985,9 +1034,10 @@ sub req_ci
 
     my @committedfiles = ();
 
-    # foreach file specified on the commandline ...
+    # 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} );
@@ -1022,7 +1072,7 @@ sub req_ci
             exit;
         }
 
-        push @committedfiles, $filename;
+        push @committedfiles, $committedfile;
         $log->info("Committing $filename");
 
         system("mkdir","-p",$dirpart) unless ( -d $dirpart );
@@ -1092,20 +1142,18 @@ sub req_ci
         exit;
     }
 
-    open FILE, ">", "$ENV{GIT_DIR}refs/heads/$state->{module}";
-    print FILE $commithash;
-    close FILE;
+    print LOCKFILE $commithash;
 
     $updater->update();
 
-    # foreach file specified on the commandline ...
+    # foreach file specified on the command line ...
     foreach my $filename ( @committedfiles )
     {
         $filename = filecleanup($filename);
 
         my $meta = $updater->getmeta($filename);
 
-        my ( $filepart, $dirpart ) = filenamesplit($filename);
+        my ( $filepart, $dirpart ) = filenamesplit($filename, 1);
 
         $log->debug("Checked-in $dirpart : $filename");
 
@@ -1121,7 +1169,9 @@ sub req_ci
     }
 
     close LOCKFILE;
-    unlink($lockfile);
+    my $reffile = "$ENV{GIT_DIR}refs/heads/$state->{module}";
+    unlink($reffile);
+    rename($lockfile, $reffile);
     chdir "/";
 
     print "ok\n";
@@ -1141,9 +1191,9 @@ 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 file specified on the command line ...
     foreach my $filename ( @{$state->{args}} )
     {
         $filename = filecleanup($filename);
@@ -1242,9 +1292,9 @@ 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 file specified on the command line ...
     foreach my $filename ( @{$state->{args}} )
     {
         $filename = filecleanup($filename);
@@ -1384,9 +1434,9 @@ 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 file specified on the command line ...
     foreach my $filename ( @{$state->{args}} )
     {
         $filename = filecleanup($filename);
@@ -1460,7 +1510,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 );
@@ -1472,7 +1522,7 @@ sub req_annotate
 
     chdir $tmpdir;
 
-    # foreach file specified on the commandline ...
+    # foreach file specified on the command line ...
     foreach my $filename ( @{$state->{args}} )
     {
         $filename = filecleanup($filename);
@@ -1655,13 +1705,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};
+        }
     }
 }
 
@@ -1736,11 +1809,17 @@ sub transmitfile
 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 );
 }
 
@@ -1756,8 +1835,7 @@ sub filecleanup
     }
 
     $filename =~ s/^\.\///g;
-    $filename = $state->{directory} . $filename;
-
+    $filename = $state->{prependdir} . $filename;
     return $filename;
 }
 
@@ -2054,12 +2132,6 @@ sub update
     # first lets get the commit list
     $ENV{GIT_DIR} = $self->{git_path};
 
-    # prepare database queries
-    my $db_insert_rev = $self->{dbh}->prepare_cached("INSERT INTO revision (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1);
-    my $db_insert_mergelog = $self->{dbh}->prepare_cached("INSERT INTO commitmsgs (key, value) VALUES (?,?)",{},1);
-    my $db_delete_head = $self->{dbh}->prepare_cached("DELETE FROM head",{},1);
-    my $db_insert_head = $self->{dbh}->prepare_cached("INSERT INTO head (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1);
-
     my $commitinfo = `git-cat-file commit $self->{module} 2>&1`;
     unless ( $commitinfo =~ /tree\s+[a-zA-Z0-9]{40}/ )
     {
@@ -2248,7 +2320,7 @@ sub update
                         author => $commit->{author},
                         mode => $git_perms,
                     };
-                    $db_insert_rev->execute($4, $head->{$4}{revision}, $2, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
+                    $self->insert_rev($4, $head->{$4}{revision}, $2, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
                 }
                 elsif ( $3 eq "M" )
                 {
@@ -2262,7 +2334,7 @@ sub update
                         author => $commit->{author},
                         mode => $git_perms,
                     };
-                    $db_insert_rev->execute($4, $head->{$4}{revision}, $2, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
+                    $self->insert_rev($4, $head->{$4}{revision}, $2, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
                 }
                 elsif ( $3 eq "A" )
                 {
@@ -2276,7 +2348,7 @@ sub update
                         author => $commit->{author},
                         mode => $git_perms,
                     };
-                    $db_insert_rev->execute($4, $head->{$4}{revision}, $2, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
+                    $self->insert_rev($4, $head->{$4}{revision}, $2, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
                 }
                 else
                 {
@@ -2333,7 +2405,7 @@ sub update
                     };
 
 
-                    $db_insert_rev->execute($git_filename, $newrevision, $git_hash, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
+                    $self->insert_rev($git_filename, $newrevision, $git_hash, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
                 }
             }
             close FILELIST;
@@ -2349,7 +2421,7 @@ sub update
                     $head->{$file}{modified} = $commit->{date};
                     $head->{$file}{author} = $commit->{author};
 
-                    $db_insert_rev->execute($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}, $commit->{date}, $commit->{author}, $head->{$file}{mode});
                 }
             }
             # END : "Detect deleted files"
@@ -2358,7 +2430,7 @@ sub update
 
         if (exists $commit->{mergemsg})
         {
-            $db_insert_mergelog->execute($commit->{hash}, $commit->{mergemsg});
+            $self->insert_mergelog($commit->{hash}, $commit->{mergemsg});
         }
 
         $lastpicked = $commit->{hash};
@@ -2366,10 +2438,10 @@ sub update
         $self->_set_prop("last_commit", $commit->{hash});
     }
 
-    $db_delete_head->execute();
+    $self->delete_head();
     foreach my $file ( keys %$head )
     {
-        $db_insert_head->execute(
+        $self->insert_head(
             $file,
             $head->{$file}{revision},
             $head->{$file}{filehash},
@@ -2387,6 +2459,54 @@ sub update
     $self->{dbh}->commit() or die "Failed to commit changes to SQLite";
 }
 
+sub insert_rev
+{
+    my $self = shift;
+    my $name = shift;
+    my $revision = shift;
+    my $filehash = shift;
+    my $commithash = shift;
+    my $modified = shift;
+    my $author = shift;
+    my $mode = shift;
+
+    my $insert_rev = $self->{dbh}->prepare_cached("INSERT INTO revision (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1);
+    $insert_rev->execute($name, $revision, $filehash, $commithash, $modified, $author, $mode);
+}
+
+sub insert_mergelog
+{
+    my $self = shift;
+    my $key = shift;
+    my $value = shift;
+
+    my $insert_mergelog = $self->{dbh}->prepare_cached("INSERT INTO commitmsgs (key, value) VALUES (?,?)",{},1);
+    $insert_mergelog->execute($key, $value);
+}
+
+sub delete_head
+{
+    my $self = shift;
+
+    my $delete_head = $self->{dbh}->prepare_cached("DELETE FROM head",{},1);
+    $delete_head->execute();
+}
+
+sub insert_head
+{
+    my $self = shift;
+    my $name = shift;
+    my $revision = shift;
+    my $filehash = shift;
+    my $commithash = shift;
+    my $modified = shift;
+    my $author = shift;
+    my $mode = shift;
+
+    my $insert_head = $self->{dbh}->prepare_cached("INSERT INTO head (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1);
+    $insert_head->execute($name, $revision, $filehash, $commithash, $modified, $author, $mode);
+}
+
 sub _headrev
 {
     my $self = shift;