X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=builtin-send-pack.c;h=8afb1d0bca0635dc22f658455477d9fada231290;hb=5c66d0d4580196094e80c552f141525759a8e249;hp=d42164ec085a4843fb52ee8ed9d04c47e3c09db0;hpb=28b9d6e548322755bbdb24c28a493862f61b1eba;p=git.git diff --git a/builtin-send-pack.c b/builtin-send-pack.c index d42164ec0..8afb1d0bc 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -146,33 +146,67 @@ static void get_local_heads(void) for_each_ref(one_local_ref, NULL); } -static int receive_status(int in) +static int receive_status(int in, struct ref *refs) { + struct ref *hint; char line[1000]; int ret = 0; int len = packet_read_line(in, line, sizeof(line)); - if (len < 10 || memcmp(line, "unpack ", 7)) { - fprintf(stderr, "did not receive status back\n"); - return -1; - } + if (len < 10 || memcmp(line, "unpack ", 7)) + return error("did not receive remote status"); if (memcmp(line, "unpack ok\n", 10)) { - fputs(line, stderr); + char *p = line + strlen(line) - 1; + if (*p == '\n') + *p = '\0'; + error("unpack failed: %s", line + 7); ret = -1; } + hint = NULL; while (1) { + char *refname; + char *msg; len = packet_read_line(in, line, sizeof(line)); if (!len) break; if (len < 3 || - (memcmp(line, "ok", 2) && memcmp(line, "ng", 2))) { + (memcmp(line, "ok ", 3) && memcmp(line, "ng ", 3))) { fprintf(stderr, "protocol error: %s\n", line); ret = -1; break; } - if (!memcmp(line, "ok", 2)) + + line[strlen(line)-1] = '\0'; + refname = line + 3; + msg = strchr(refname, ' '); + if (msg) + *msg++ = '\0'; + + /* first try searching at our hint, falling back to all refs */ + if (hint) + hint = find_ref_by_name(hint, refname); + if (!hint) + hint = find_ref_by_name(refs, refname); + if (!hint) { + warning("remote reported status on unknown ref: %s", + refname); + continue; + } + if (hint->status != REF_STATUS_EXPECTING_REPORT) { + warning("remote reported status on unexpected ref: %s", + refname); continue; - fputs(line, stderr); - ret = -1; + } + + if (line[0] == 'o' && line[1] == 'k') + hint->status = REF_STATUS_OK; + else { + hint->status = REF_STATUS_REMOTE_REJECT; + ret = -1; + } + if (msg) + hint->remote_status = xstrdup(msg); + /* start our next search from the next ref */ + hint = hint->next; } return ret; } @@ -180,24 +214,17 @@ static int receive_status(int in) static void update_tracking_ref(struct remote *remote, struct ref *ref) { struct refspec rs; - int will_delete_ref; - rs.src = ref->name; - rs.dst = NULL; - - if (!ref->peer_ref) + if (ref->status != REF_STATUS_OK) return; - will_delete_ref = is_null_sha1(ref->peer_ref->new_sha1); - - if (!will_delete_ref && - !hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) - return; + rs.src = ref->name; + rs.dst = NULL; if (!remote_find_tracking(remote, &rs)) { if (args.verbose) fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst); - if (is_null_sha1(ref->peer_ref->new_sha1)) { + if (ref->deletion) { if (delete_ref(rs.dst, NULL)) error("Failed to delete"); } else @@ -207,8 +234,9 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref) } } -static const char *prettify_ref(const char *name) +static const char *prettify_ref(const struct ref *ref) { + const char *name = ref->name; return name + ( !prefixcmp(name, "refs/heads/") ? 11 : !prefixcmp(name, "refs/tags/") ? 10 : @@ -218,16 +246,143 @@ static const char *prettify_ref(const char *name) #define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3) +static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg) +{ + fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary); + if (from) + fprintf(stderr, "%s -> %s", prettify_ref(from), prettify_ref(to)); + else + fputs(prettify_ref(to), stderr); + if (msg) { + fputs(" (", stderr); + fputs(msg, stderr); + fputc(')', stderr); + } + fputc('\n', stderr); +} + +static const char *status_abbrev(unsigned char sha1[20]) +{ + const char *abbrev; + abbrev = find_unique_abbrev(sha1, DEFAULT_ABBREV); + return abbrev ? abbrev : sha1_to_hex(sha1); +} + +static void print_ok_ref_status(struct ref *ref) +{ + if (ref->deletion) + print_ref_status('-', "[deleted]", ref, NULL, NULL); + else if (is_null_sha1(ref->old_sha1)) + print_ref_status('*', + (!prefixcmp(ref->name, "refs/tags/") ? "[new tag]" : + "[new branch]"), + ref, ref->peer_ref, NULL); + else { + char quickref[84]; + char type; + const char *msg; + + strcpy(quickref, status_abbrev(ref->old_sha1)); + if (ref->nonfastforward) { + strcat(quickref, "..."); + type = '+'; + msg = "forced update"; + } else { + strcat(quickref, ".."); + type = ' '; + msg = NULL; + } + strcat(quickref, status_abbrev(ref->new_sha1)); + + print_ref_status(type, quickref, ref, ref->peer_ref, msg); + } +} + +static int print_one_push_status(struct ref *ref, const char *dest, int count) +{ + if (!count) + fprintf(stderr, "To %s\n", dest); + + switch(ref->status) { + case REF_STATUS_NONE: + print_ref_status('X', "[no match]", ref, NULL, NULL); + break; + case REF_STATUS_REJECT_NODELETE: + print_ref_status('!', "[rejected]", ref, NULL, + "remote does not support deleting refs"); + break; + case REF_STATUS_UPTODATE: + print_ref_status('=', "[up to date]", ref, + ref->peer_ref, NULL); + break; + case REF_STATUS_REJECT_NONFASTFORWARD: + print_ref_status('!', "[rejected]", ref, ref->peer_ref, + "non-fast forward"); + break; + case REF_STATUS_REMOTE_REJECT: + print_ref_status('!', "[remote rejected]", ref, + ref->deletion ? NULL : ref->peer_ref, + ref->remote_status); + break; + case REF_STATUS_EXPECTING_REPORT: + print_ref_status('!', "[remote failure]", ref, + ref->deletion ? NULL : ref->peer_ref, + "remote failed to report status"); + break; + case REF_STATUS_OK: + print_ok_ref_status(ref); + break; + } + + return 1; +} + +static void print_push_status(const char *dest, struct ref *refs) +{ + struct ref *ref; + int n = 0; + + if (args.verbose) { + for (ref = refs; ref; ref = ref->next) + if (ref->status == REF_STATUS_UPTODATE) + n += print_one_push_status(ref, dest, n); + } + + for (ref = refs; ref; ref = ref->next) + if (ref->status == REF_STATUS_OK) + n += print_one_push_status(ref, dest, n); + + for (ref = refs; ref; ref = ref->next) { + if (ref->status != REF_STATUS_NONE && + ref->status != REF_STATUS_UPTODATE && + ref->status != REF_STATUS_OK) + n += print_one_push_status(ref, dest, n); + } +} + +static int refs_pushed(struct ref *ref) +{ + for (; ref; ref = ref->next) { + switch(ref->status) { + case REF_STATUS_NONE: + case REF_STATUS_UPTODATE: + break; + default: + return 1; + } + } + return 0; +} + static int do_send_pack(int in, int out, struct remote *remote, const char *dest, int nr_refspec, const char **refspec) { struct ref *ref; int new_refs; - int ret = 0; int ask_for_status_report = 0; int allow_deleting_refs = 0; int expect_status_report = 0; - int shown_dest = 0; int flags = MATCH_REFS_NONE; + int ret; if (args.send_all) flags |= MATCH_REFS_ALL; @@ -262,10 +417,6 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest */ new_refs = 0; for (ref = remote_refs; ref; ref = ref->next) { - char old_hex[60], *new_hex; - int will_delete_ref; - const char *pretty_ref; - const char *pretty_peer = NULL; /* only used when not deleting */ const unsigned char *new_sha1; if (!ref->peer_ref) { @@ -276,29 +427,15 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest else new_sha1 = ref->peer_ref->new_sha1; - if (!shown_dest) { - fprintf(stderr, "To %s\n", dest); - shown_dest = 1; - } - - will_delete_ref = is_null_sha1(new_sha1); - pretty_ref = prettify_ref(ref->name); - if (!will_delete_ref) - pretty_peer = prettify_ref(ref->peer_ref->name); - - if (will_delete_ref && !allow_deleting_refs) { - fprintf(stderr, " ! %-*s %s (remote does not support deleting refs)\n", - SUMMARY_WIDTH, "[rejected]", pretty_ref); - ret = -2; + ref->deletion = is_null_sha1(new_sha1); + if (ref->deletion && !allow_deleting_refs) { + ref->status = REF_STATUS_REJECT_NODELETE; continue; } - if (!will_delete_ref && + if (!ref->deletion && !hashcmp(ref->old_sha1, new_sha1)) { - if (args.verbose) - fprintf(stderr, " = %-*s %s -> %s\n", - SUMMARY_WIDTH, "[up to date]", - pretty_peer, pretty_ref); + ref->status = REF_STATUS_UPTODATE; continue; } @@ -321,33 +458,25 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest * always allowed. */ - if (!args.force_update && - !will_delete_ref && + ref->nonfastforward = + !ref->deletion && !is_null_sha1(ref->old_sha1) && - !ref->force) { - if (!has_sha1_file(ref->old_sha1) || - !ref_newer(new_sha1, ref->old_sha1)) { - /* We do not have the remote ref, or - * we know that the remote ref is not - * an ancestor of what we are trying to - * push. Either way this can be losing - * commits at the remote end and likely - * we were not up to date to begin with. - */ - fprintf(stderr, " ! %-*s %s -> %s (non-fast forward)\n", - SUMMARY_WIDTH, "[rejected]", - pretty_peer, pretty_ref); - ret = -2; - continue; - } + (!has_sha1_file(ref->old_sha1) + || !ref_newer(new_sha1, ref->old_sha1)); + + if (ref->nonfastforward && !ref->force && !args.force_update) { + ref->status = REF_STATUS_REJECT_NONFASTFORWARD; + continue; } + hashcpy(ref->new_sha1, new_sha1); - if (!will_delete_ref) + if (!ref->deletion) new_refs++; - strcpy(old_hex, sha1_to_hex(ref->old_sha1)); - new_hex = sha1_to_hex(ref->new_sha1); if (!args.dry_run) { + char *old_hex = sha1_to_hex(ref->old_sha1); + char *new_hex = sha1_to_hex(ref->new_sha1); + if (ask_for_status_report) { packet_write(out, "%s %s %s%c%s", old_hex, new_hex, ref->name, 0, @@ -359,63 +488,47 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest packet_write(out, "%s %s %s", old_hex, new_hex, ref->name); } - if (will_delete_ref) - fprintf(stderr, " - %-*s %s\n", - SUMMARY_WIDTH, "[deleting]", - pretty_ref); - else if (is_null_sha1(ref->old_sha1)) { - const char *msg; - - if (!prefixcmp(ref->name, "refs/tags/")) - msg = "[new tag]"; - else - msg = "[new branch]"; - fprintf(stderr, " * %-*s %s -> %s\n", - SUMMARY_WIDTH, msg, - pretty_peer, pretty_ref); - } - else { - char quickref[83]; - char type = ' '; - const char *msg = ""; - const char *old_abb; - old_abb = find_unique_abbrev(ref->old_sha1, DEFAULT_ABBREV); - strcpy(quickref, old_abb ? old_abb : old_hex); - if (ref_newer(ref->peer_ref->new_sha1, ref->old_sha1)) - strcat(quickref, ".."); - else { - strcat(quickref, "..."); - type = '+'; - msg = " (forced update)"; - } - strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); - - fprintf(stderr, " %c %-*s %s -> %s%s\n", - type, - SUMMARY_WIDTH, quickref, - pretty_peer, pretty_ref, - msg); - } + ref->status = expect_status_report ? + REF_STATUS_EXPECTING_REPORT : + REF_STATUS_OK; } packet_flush(out); - if (new_refs && !args.dry_run) - ret = pack_objects(out, remote_refs); + if (new_refs && !args.dry_run) { + if (pack_objects(out, remote_refs) < 0) { + close(out); + return -1; + } + } close(out); - if (expect_status_report) { - if (receive_status(in)) - ret = -4; - } + if (expect_status_report) + ret = receive_status(in, remote_refs); + else + ret = 0; - if (!args.dry_run && remote && ret == 0) { + print_push_status(dest, remote_refs); + + if (!args.dry_run && remote) { for (ref = remote_refs; ref; ref = ref->next) update_tracking_ref(remote, ref); } - if (!new_refs && ret == 0) + if (!refs_pushed(remote_refs)) fprintf(stderr, "Everything up-to-date\n"); - return ret; + if (ret < 0) + return ret; + for (ref = remote_refs; ref; ref = ref->next) { + switch (ref->status) { + case REF_STATUS_NONE: + case REF_STATUS_UPTODATE: + case REF_STATUS_OK: + break; + default: + return -1; + } + } + return 0; } static void verify_remote_names(int nr_heads, const char **heads) @@ -428,10 +541,12 @@ static void verify_remote_names(int nr_heads, const char **heads) remote = remote ? (remote + 1) : heads[i]; switch (check_ref_format(remote)) { case 0: /* ok */ - case -2: /* ok but a single level -- that is fine for - * a match pattern. - */ - case -3: /* ok but ends with a pattern-match character */ + case CHECK_REF_FORMAT_ONELEVEL: + /* ok but a single level -- that is fine for + * a match pattern. + */ + case CHECK_REF_FORMAT_WILDCARD: + /* ok but ends with a pattern-match character */ continue; } die("remote part of refspec is not a valid name in %s",