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 151c1ff6b35bc62c2a2f010d3125869e5f3b8edc..b8367a4d625fdfe59cf30e0a4aed40443139c40c 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 b454c9e11dbea4d023ec724252e14b4984bfba9b..d35c0685ce2269cec48d19fbf6b0499374a34b7e 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;