X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=upload-pack.c;h=d3a09e78d56642db9b1fb1b1ead34ff60dd8c923;hb=b568a503def81f49704ba94f5a822d523022102a;hp=32b06b2e66099787d621c2557a8a9ee00aec4be6;hpb=e035ce939d7e26afee4936ed32f1db9428ac026e;p=git.git diff --git a/upload-pack.c b/upload-pack.c index 32b06b2e6..d3a09e78d 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -6,6 +6,9 @@ #include "object.h" #include "commit.h" #include "exec_cmd.h" +#include "diff.h" +#include "revision.h" +#include "list-objects.h" static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] "; @@ -16,10 +19,14 @@ static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=n #define COMMON_KNOWN (1u << 14) #define REACHABLE (1u << 15) +#define SHALLOW (1u << 16) +#define NOT_SHALLOW (1u << 17) +#define CLIENT_SHALLOW (1u << 18) + static unsigned long oldest_have; static int multi_ack, nr_our_refs; -static int use_thin_pack, use_ofs_delta; +static int use_thin_pack, use_ofs_delta, no_progress; static struct object_array have_obj; static struct object_array want_obj; static unsigned int timeout; @@ -48,12 +55,47 @@ static ssize_t send_client_data(int fd, const char *data, ssize_t sz) /* emergency quit */ fd = 2; if (fd == 2) { + /* XXX: are we happy to lose stuff here? */ xwrite(fd, data, sz); return sz; } return safe_write(fd, data, sz); } +FILE *pack_pipe = NULL; +static void show_commit(struct commit *commit) +{ + if (commit->object.flags & BOUNDARY) + fputc('-', pack_pipe); + if (fputs(sha1_to_hex(commit->object.sha1), pack_pipe) < 0) + die("broken output pipe"); + fputc('\n', pack_pipe); + fflush(pack_pipe); + free(commit->buffer); + commit->buffer = NULL; +} + +static void show_object(struct object_array_entry *p) +{ + /* An object with name "foo\n0000000..." can be used to + * confuse downstream git-pack-objects very badly. + */ + const char *ep = strchr(p->name, '\n'); + if (ep) { + fprintf(pack_pipe, "%s %.*s\n", sha1_to_hex(p->item->sha1), + (int) (ep - p->name), + p->name); + } + else + fprintf(pack_pipe, "%s %s\n", + sha1_to_hex(p->item->sha1), p->name); +} + +static void show_edge(struct commit *commit) +{ + fprintf(pack_pipe, "-%s\n", sha1_to_hex(commit->object.sha1)); +} + static void create_pack_file(void) { /* Pipes between rev-list to pack-objects, pack-objects to us @@ -75,48 +117,41 @@ static void create_pack_file(void) if (!pid_rev_list) { int i; - int args; - const char **argv; - const char **p; - char *buf; - - if (create_full_pack) { - args = 10; - use_thin_pack = 0; /* no point doing it */ - } - else - args = have_obj.nr + want_obj.nr + 5; - p = xmalloc(args * sizeof(char *)); - argv = (const char **) p; - buf = xmalloc(args * 45); + struct rev_info revs; - dup2(lp_pipe[1], 1); - close(0); close(lp_pipe[0]); - close(lp_pipe[1]); - *p++ = "rev-list"; - *p++ = use_thin_pack ? "--objects-edge" : "--objects"; + pack_pipe = fdopen(lp_pipe[1], "w"); + if (create_full_pack) - *p++ = "--all"; - else { + use_thin_pack = 0; /* no point doing it */ + init_revisions(&revs, NULL); + revs.tag_objects = 1; + revs.tree_objects = 1; + revs.blob_objects = 1; + if (use_thin_pack) + revs.edge_hint = 1; + + if (create_full_pack) { + const char *args[] = {"rev-list", "--all", NULL}; + setup_revisions(2, args, &revs, NULL); + } else { for (i = 0; i < want_obj.nr; i++) { struct object *o = want_obj.objects[i].item; - *p++ = buf; - memcpy(buf, sha1_to_hex(o->sha1), 41); - buf += 41; + /* why??? */ + o->flags &= ~UNINTERESTING; + add_pending_object(&revs, o, NULL); } - } - if (!create_full_pack) for (i = 0; i < have_obj.nr; i++) { struct object *o = have_obj.objects[i].item; - *p++ = buf; - *buf++ = '^'; - memcpy(buf, sha1_to_hex(o->sha1), 41); - buf += 41; + o->flags |= UNINTERESTING; + add_pending_object(&revs, o, NULL); } - *p++ = NULL; - execv_git_cmd(argv); - die("git-upload-pack: unable to exec git-rev-list"); + setup_revisions(0, NULL, &revs, NULL); + } + prepare_revision_walk(&revs); + mark_edges_uninteresting(revs.commits, &revs, show_edge); + traverse_commit_list(&revs, show_commit, show_object); + exit(0); } if (pipe(pu_pipe) < 0) @@ -130,6 +165,9 @@ static void create_pack_file(void) die("git-upload-pack: unable to fork git-pack-objects"); } if (!pid_pack_objects) { + const char *argv[10]; + int i = 0; + dup2(lp_pipe[0], 0); dup2(pu_pipe[1], 1); dup2(pe_pipe[1], 2); @@ -140,9 +178,16 @@ static void create_pack_file(void) close(pu_pipe[1]); close(pe_pipe[0]); close(pe_pipe[1]); - execl_git_cmd("pack-objects", "--stdout", "--progress", - use_ofs_delta ? "--delta-base-offset" : NULL, - NULL); + + argv[i++] = "pack-objects"; + argv[i++] = "--stdout"; + if (!no_progress) + argv[i++] = "--progress"; + if (use_ofs_delta) + argv[i++] = "--delta-base-offset"; + argv[i++] = NULL; + + execv_git_cmd(argv); kill(pid_rev_list, SIGKILL); die("git-upload-pack: unable to exec git-pack-objects"); } @@ -209,7 +254,7 @@ static void create_pack_file(void) *cp++ = buffered; outsz++; } - sz = read(pu_pipe[0], cp, + sz = xread(pu_pipe[0], cp, sizeof(data) - outsz); if (0 < sz) ; @@ -234,7 +279,7 @@ static void create_pack_file(void) /* Status ready; we ship that in the side-band * or dump to the standard error. */ - sz = read(pe_pipe[0], progress, + sz = xread(pe_pipe[0], progress, sizeof(progress)); if (0 < sz) send_client_data(2, progress, sz); @@ -421,7 +466,7 @@ static int get_common_commits(void) continue; } len = strip(line, len); - if (!strncmp(line, "have ", 5)) { + if (!prefixcmp(line, "have ")) { switch (got_sha1(line+5, sha1)) { case -1: /* they have what we do not */ if (multi_ack && ok_to_give_up()) @@ -456,8 +501,9 @@ static int get_common_commits(void) static void receive_needs(void) { + struct object_array shallows = {0, 0, NULL}; static char line[1000]; - int len; + int len, depth = 0; for (;;) { struct object *o; @@ -465,9 +511,30 @@ static void receive_needs(void) len = packet_read_line(0, line, sizeof(line)); reset_timeout(); if (!len) - return; + break; - if (strncmp("want ", line, 5) || + if (!prefixcmp(line, "shallow ")) { + unsigned char sha1[20]; + struct object *object; + use_thin_pack = 0; + if (get_sha1(line + 8, sha1)) + die("invalid shallow line: %s", line); + object = parse_object(sha1); + if (!object) + die("did not find object for %s", line); + object->flags |= CLIENT_SHALLOW; + add_object_array(object, NULL, &shallows); + continue; + } + if (!prefixcmp(line, "deepen ")) { + char *end; + use_thin_pack = 0; + depth = strtol(line + 7, &end, 0); + if (end == line + 7 || depth <= 0) + die("Invalid deepen: %s", line); + continue; + } + if (prefixcmp(line, "want ") || get_sha1_hex(line+5, sha1_buf)) die("git-upload-pack: protocol error, " "expected to get sha, not '%s'", line); @@ -481,6 +548,8 @@ static void receive_needs(void) use_sideband = LARGE_PACKET_MAX; else if (strstr(line+45, "side-band")) use_sideband = DEFAULT_PACKET_MAX; + if (strstr(line+45, "no-progress")) + no_progress = 1; /* We have sent all our refs already, and the other end * should have chosen out of them; otherwise they are @@ -498,11 +567,58 @@ static void receive_needs(void) add_object_array(o, NULL, &want_obj); } } + if (depth == 0 && shallows.nr == 0) + return; + if (depth > 0) { + struct commit_list *result, *backup; + int i; + backup = result = get_shallow_commits(&want_obj, depth, + SHALLOW, NOT_SHALLOW); + while (result) { + struct object *object = &result->item->object; + if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) { + packet_write(1, "shallow %s", + sha1_to_hex(object->sha1)); + register_shallow(object->sha1); + } + result = result->next; + } + free_commit_list(backup); + for (i = 0; i < shallows.nr; i++) { + struct object *object = shallows.objects[i].item; + if (object->flags & NOT_SHALLOW) { + struct commit_list *parents; + packet_write(1, "unshallow %s", + sha1_to_hex(object->sha1)); + object->flags &= ~CLIENT_SHALLOW; + /* make sure the real parents are parsed */ + unregister_shallow(object->sha1); + object->parsed = 0; + parse_commit((struct commit *)object); + parents = ((struct commit *)object)->parents; + while (parents) { + add_object_array(&parents->item->object, + NULL, &want_obj); + parents = parents->next; + } + } + /* make sure commit traversal conforms to client */ + register_shallow(object->sha1); + } + packet_flush(1); + } else + if (shallows.nr > 0) { + int i; + for (i = 0; i < shallows.nr; i++) + register_shallow(shallows.objects[i].item->sha1); + } + free(shallows.objects); } static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { - static const char *capabilities = "multi_ack thin-pack side-band side-band-64k ofs-delta"; + static const char *capabilities = "multi_ack thin-pack side-band" + " side-band-64k ofs-delta shallow no-progress"; struct object *o = parse_object(sha1); if (!o) @@ -553,7 +669,7 @@ int main(int argc, char **argv) strict = 1; continue; } - if (!strncmp(arg, "--timeout=", 10)) { + if (!prefixcmp(arg, "--timeout=")) { timeout = atoi(arg+10); continue; } @@ -569,7 +685,8 @@ int main(int argc, char **argv) if (!enter_repo(dir, strict)) die("'%s': unable to chdir or not a git archive", dir); - + if (is_repository_shallow()) + die("attempt to fetch/clone from a shallow repository"); upload_pack(); return 0; }