Code

Merge branch 'jc/skip-count'
[git.git] / fetch-pack.c
index 1b2d6ee20d16d03a59d2cd2aafb46e9a03c492b0..92322cf4da39434d44e4aa52fa0c3f735b22d752 100644 (file)
@@ -3,6 +3,8 @@
 #include "pkt-line.h"
 #include "commit.h"
 #include "tag.h"
+#include "exec_cmd.h"
+#include "sideband.h"
 
 static int keep_pack;
 static int quiet;
@@ -42,7 +44,7 @@ static void rev_list_push(struct commit *commit, int mark)
        }
 }
 
-static int rev_list_insert_ref(const char *path, const unsigned char *sha1)
+static int rev_list_insert_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
        struct object *o = deref_tag(parse_object(sha1), path, 0);
 
@@ -143,7 +145,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
        unsigned in_vain = 0;
        int got_continue = 0;
 
-       for_each_ref(rev_list_insert_ref);
+       for_each_ref(rev_list_insert_ref, NULL);
 
        fetching = 0;
        for ( ; refs ; refs = refs->next) {
@@ -166,12 +168,13 @@ static int find_common(int fd[2], unsigned char *result_sha1,
                }
 
                if (!fetching)
-                       packet_write(fd[1], "want %s%s%s%s%s\n",
+                       packet_write(fd[1], "want %s%s%s%s%s%s\n",
                                     sha1_to_hex(remote),
                                     (multi_ack ? " multi_ack" : ""),
                                     (use_sideband == 2 ? " side-band-64k" : ""),
                                     (use_sideband == 1 ? " side-band" : ""),
-                                    (use_thin_pack ? " thin-pack" : ""));
+                                    (use_thin_pack ? " thin-pack" : ""),
+                                    " ofs-delta");
                else
                        packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
                fetching++;
@@ -253,7 +256,7 @@ done:
 
 static struct commit_list *complete;
 
-static int mark_complete(const char *path, const unsigned char *sha1)
+static int mark_complete(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
        struct object *o = parse_object(sha1);
 
@@ -365,7 +368,7 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
                }
        }
 
-       for_each_ref(mark_complete);
+       for_each_ref(mark_complete, NULL);
        if (cutoff)
                mark_recent_complete_commits(cutoff);
 
@@ -415,6 +418,103 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
        return retval;
 }
 
+static pid_t setup_sideband(int fd[2], int xd[2])
+{
+       pid_t side_pid;
+
+       if (!use_sideband) {
+               fd[0] = xd[0];
+               fd[1] = xd[1];
+               return 0;
+       }
+       /* xd[] is talking with upload-pack; subprocess reads from
+        * xd[0], spits out band#2 to stderr, and feeds us band#1
+        * through our fd[0].
+        */
+       if (pipe(fd) < 0)
+               die("fetch-pack: unable to set up pipe");
+       side_pid = fork();
+       if (side_pid < 0)
+               die("fetch-pack: unable to fork off sideband demultiplexer");
+       if (!side_pid) {
+               /* subprocess */
+               close(fd[0]);
+               if (xd[0] != xd[1])
+                       close(xd[1]);
+               if (recv_sideband("fetch-pack", xd[0], fd[1], 2))
+                       exit(1);
+               exit(0);
+       }
+       close(xd[0]);
+       close(fd[1]);
+       fd[1] = xd[1];
+       return side_pid;
+}
+
+static int get_pack(int xd[2], const char **argv)
+{
+       int status;
+       pid_t pid, side_pid;
+       int fd[2];
+
+       side_pid = setup_sideband(fd, xd);
+       pid = fork();
+       if (pid < 0)
+               die("fetch-pack: unable to fork off %s", argv[0]);
+       if (!pid) {
+               dup2(fd[0], 0);
+               close(fd[0]);
+               close(fd[1]);
+               execv_git_cmd(argv);
+               die("%s exec failed", argv[0]);
+       }
+       close(fd[0]);
+       close(fd[1]);
+       while (waitpid(pid, &status, 0) < 0) {
+               if (errno != EINTR)
+                       die("waiting for %s: %s", argv[0], strerror(errno));
+       }
+       if (WIFEXITED(status)) {
+               int code = WEXITSTATUS(status);
+               if (code)
+                       die("%s died with error code %d", argv[0], code);
+               return 0;
+       }
+       if (WIFSIGNALED(status)) {
+               int sig = WTERMSIG(status);
+               die("%s died of signal %d", argv[0], sig);
+       }
+       die("%s died of unnatural causes %d", argv[0], status);
+}
+
+static int explode_rx_pack(int xd[2])
+{
+       const char *argv[3] = { "unpack-objects", quiet ? "-q" : NULL, NULL };
+       return get_pack(xd, argv);
+}
+
+static int keep_rx_pack(int xd[2])
+{
+       const char *argv[6];
+       char keep_arg[256];
+       int n = 0;
+
+       argv[n++] = "index-pack";
+       argv[n++] = "--stdin";
+       if (!quiet)
+               argv[n++] = "-v";
+       if (use_thin_pack)
+               argv[n++] = "--fix-thin";
+       if (keep_pack > 1) {
+               int s = sprintf(keep_arg, "--keep=fetch-pack %i on ", getpid());
+               if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
+                       strcpy(keep_arg + s, "localhost");
+               argv[n++] = keep_arg;
+       }
+       argv[n] = NULL;
+       return get_pack(xd, argv);
+}
+
 static int fetch_pack(int fd[2], int nr_match, char **match)
 {
        struct ref *ref;
@@ -446,17 +546,13 @@ static int fetch_pack(int fd[2], int nr_match, char **match)
                goto all_done;
        }
        if (find_common(fd, sha1, ref) < 0)
-               if (!keep_pack)
+               if (keep_pack != 1)
                        /* When cloning, it is not unusual to have
                         * no common commit.
                         */
                        fprintf(stderr, "warning: no common commits\n");
 
-       if (keep_pack)
-               status = receive_keep_pack(fd, "git-fetch-pack", quiet, use_sideband);
-       else
-               status = receive_unpack_pack(fd, "git-fetch-pack", quiet, use_sideband);
-
+       status = (keep_pack) ? keep_rx_pack(fd) : explode_rx_pack(fd);
        if (status)
                die("git-fetch-pack: fetch failed.");
 
@@ -469,6 +565,29 @@ static int fetch_pack(int fd[2], int nr_match, char **match)
        return 0;
 }
 
+static int remove_duplicates(int nr_heads, char **heads)
+{
+       int src, dst;
+
+       for (src = dst = 0; src < nr_heads; src++) {
+               /* If heads[src] is different from any of
+                * heads[0..dst], push it in.
+                */
+               int i;
+               for (i = 0; i < dst; i++) {
+                       if (!strcmp(heads[i], heads[src]))
+                               break;
+               }
+               if (i < dst)
+                       continue;
+               if (src != dst)
+                       heads[dst] = heads[src];
+               dst++;
+       }
+       heads[dst] = 0;
+       return dst;
+}
+
 int main(int argc, char **argv)
 {
        int i, ret, nr_heads;
@@ -493,7 +612,7 @@ int main(int argc, char **argv)
                                continue;
                        }
                        if (!strcmp("--keep", arg) || !strcmp("-k", arg)) {
-                               keep_pack = 1;
+                               keep_pack++;
                                continue;
                        }
                        if (!strcmp("--thin", arg)) {
@@ -517,15 +636,15 @@ int main(int argc, char **argv)
        }
        if (!dest)
                usage(fetch_pack_usage);
-       if (keep_pack)
-               use_thin_pack = 0;
        pid = git_connect(fd, dest, exec);
        if (pid < 0)
                return 1;
+       if (heads && nr_heads)
+               nr_heads = remove_duplicates(nr_heads, heads);
        ret = fetch_pack(fd, nr_heads, heads);
        close(fd[0]);
        close(fd[1]);
-       finish_connect(pid);
+       ret |= finish_connect(pid);
 
        if (!ret && nr_heads) {
                /* If the heads to pull were given, we should have
@@ -540,5 +659,5 @@ int main(int argc, char **argv)
                        }
        }
 
-       return ret;
+       return !!ret;
 }