Make git-fetch-pack actually do all the unpacking etc.
authorLinus Torvalds <torvalds@g5.osdl.org>
Mon, 4 Jul 2005 23:35:13 +0000 (16:35 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Mon, 4 Jul 2005 23:35:13 +0000 (16:35 -0700)
It returns the result SHA1 on stdout, so you can do

remote=$(git-fetch-pack host:dir branchname)

and it will unpack the objects and "remote" will be the SHA1 name of the
branch on the other side.  You can then save that off, or merge it, or
whatever.

fetch-pack.c
upload-pack.c

index 151c1ff..b8367a4 100644 (file)
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "refs.h"
 #include "pkt-line.h"
+#include <sys/wait.h>
 
 static const char fetch_pack_usage[] = "git-fetch-pack [host:]directory [heads]* < mycommitlist";
 static const char *exec = "git-upload-pack";
@@ -26,11 +27,17 @@ static int get_ack(int fd, unsigned char *result_sha1)
 static int find_common(int fd[2], unsigned char *result_sha1, unsigned char *remote)
 {
        static char line[1000];
-       int count = 0, flushes = 0;
+       int count = 0, flushes = 0, retval;
+       FILE *revs;
 
+       revs = popen("git-rev-list $(git-rev-parse --all)", "r");
+       if (!revs)
+               die("unable to run 'git-rev-list'");
        packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
        packet_flush(fd[1]);
-       while (fgets(line, sizeof(line), stdin) != NULL) {
+       flushes = 1;
+       retval = -1;
+       while (fgets(line, sizeof(line), revs) != NULL) {
                unsigned char sha1[20];
                if (get_sha1_hex(line, sha1))
                        die("git-fetch-pack: expected object name, got crud");
@@ -45,19 +52,22 @@ static int find_common(int fd[2], unsigned char *result_sha1, unsigned char *rem
                         */
                        if (count == 32)
                                continue;
-                       if (get_ack(fd[0], result_sha1))
-                               return 0;
+                       if (get_ack(fd[0], result_sha1)) {
+                               flushes = 0;
+                               retval = 0;
+                               break;
+                       }
                        flushes--;
                }
        }
-       flushes++;
-       packet_flush(fd[1]);
+       pclose(revs);
+       packet_write(fd[1], "done\n");
        while (flushes) {
                flushes--;
                if (get_ack(fd[0], result_sha1))
                        return 0;
        }
-       return -1;
+       return retval;
 }
 
 static int get_old_sha1(const char *refname, unsigned char *sha1)
@@ -88,12 +98,12 @@ static int check_ref(const char *refname, const unsigned char *sha1)
                memset(mysha1, 0, 20);
 
        if (!memcmp(sha1, mysha1, 20)) {
-               printf("%s: unchanged\n", refname);
+               fprintf(stderr, "%s: unchanged\n", refname);
                return 0;
        }
        
        memcpy(oldhex, sha1_to_hex(mysha1), 41);
-       printf("%s: %s (%s)\n", refname, sha1_to_hex(sha1), oldhex);
+       fprintf(stderr, "%s: %s (%s)\n", refname, sha1_to_hex(sha1), oldhex);
        return 1;
 }
 
@@ -128,7 +138,8 @@ static int get_remote_heads(int fd, int nr_match, char **match, unsigned char *r
 static int fetch_pack(int fd[2], int nr_match, char **match)
 {
        unsigned char sha1[20], remote[20];
-       int heads;
+       int heads, status;
+       pid_t pid;
 
        heads = get_remote_heads(fd[0], nr_match, match, remote);
        if (heads != 1) {
@@ -137,11 +148,34 @@ static int fetch_pack(int fd[2], int nr_match, char **match)
        }
        if (find_common(fd, sha1, remote) < 0)
                die("git-fetch-pack: no common commits");
-       close(fd[1]);
-       dup2(fd[0], 0);
+       pid = fork();
+       if (pid < 0)
+               die("git-fetch-pack: unable to fork off git-unpack-objects");
+       if (!pid) {
+               close(fd[1]);
+               dup2(fd[0], 0);
+               close(fd[0]);
+               execlp("git-unpack-objects", "git-unpack-objects", NULL);
+               die("git-unpack-objects exec failed");
+       }
        close(fd[0]);
-       execlp("git-unpack-objects", "git-unpack-objects", NULL);
-       die("git-unpack-objects exec failed");
+       close(fd[1]);
+       while (waitpid(pid, &status, 0) < 0) {
+               if (errno != EINTR)
+                       die("waiting for git-unpack-objects: %s", strerror(errno));
+       }
+       if (WIFEXITED(status)) {
+               int code = WEXITSTATUS(status);
+               if (code)
+                       die("git-unpack-objects died with error code %d", code);
+               puts(sha1_to_hex(remote));
+               return 0;
+       }
+       if (WIFSIGNALED(status)) {
+               int sig = WTERMSIG(status);
+               die("git-unpack-objects died of signal %d", sig);
+       }
+       die("Sherlock Holmes! git-unpack-objects died of unnatural causes %d!", status);
 }
 
 int main(int argc, char **argv)
index b454c9e..d35c068 100644 (file)
@@ -10,16 +10,57 @@ static int nr_has = 0, nr_needs = 0;
 static unsigned char has_sha1[MAX_HAS][20];
 static unsigned char needs_sha1[MAX_NEEDS][20];
 
+static int strip(char *line, int len)
+{
+       if (len && line[len-1] == '\n')
+               line[--len] = 0;
+       return len;
+}
+
 static void create_pack_file(void)
 {
-       /*
-        * Here, we should do
-        *
-        *      git-rev-list --objects needs_sha1 --not has_sha1 |
-        *              git-pack-objects --stdout
-        *
-        * but we don't.
-        */
+       int fd[2];
+       pid_t pid;
+
+       if (pipe(fd) < 0)
+               die("git-upload-pack: unable to create pipe");
+       pid = fork();
+       if (pid < 0)
+               die("git-upload-pack: unable to fork git-rev-list");
+
+       if (!pid) {
+               int i;
+               int args = nr_has + nr_needs + 5;
+               char **argv = xmalloc(args * sizeof(char *));
+               char *buf = xmalloc(args * 45);
+               char **p = argv;
+
+               dup2(fd[1], 1);
+               close(0);
+               close(fd[0]);
+               close(fd[1]);
+               *p++ = "git-rev-list";
+               *p++ = "--objects";
+               for (i = 0; i < nr_needs; i++) {
+                       *p++ = buf;
+                       memcpy(buf, sha1_to_hex(needs_sha1[i]), 41);
+                       buf += 41;
+               }
+               for (i = 0; i < nr_has; i++) {
+                       *p++ = buf;
+                       *buf++ = '^';
+                       memcpy(buf, sha1_to_hex(has_sha1[i]), 41);
+                       buf += 41;
+               }
+               *p++ = NULL;
+               execvp("git-rev-list", argv);
+               die("git-upload-pack: unable to exec git-rev-list");
+       }
+       dup2(fd[0], 0);
+       close(fd[0]);
+       close(fd[1]);
+       execlp("git-pack-objects", "git-pack-objects", "--stdout", NULL);
+       die("git-upload-pack: unable to exec git-pack-objects");
 }
 
 static int got_sha1(char *hex, unsigned char *sha1)
@@ -50,8 +91,7 @@ static int get_common_commits(void)
                        packet_write(1, "NAK\n");
                        continue;
                }
-               if (line[len-1] == '\n')
-                       line[--len] = 0;
+               len = strip(line, len);
                if (!strncmp(line, "have ", 5)) {
                        if (got_sha1(line+5, sha1)) {
                                packet_write(1, "ACK %s\n", sha1_to_hex(sha1));
@@ -69,7 +109,8 @@ static int get_common_commits(void)
        for (;;) {
                len = packet_read_line(0, line, sizeof(line));
                if (!len)
-                       break;
+                       continue;
+               len = strip(line, len);
                if (!strncmp(line, "have ", 5)) {
                        got_sha1(line+5, sha1);
                        continue;