refs = refs->next;
}
+ close(po.in);
if (finish_command(&po))
return error("pack-objects died with strange error");
return 0;
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;
- fputs(line, stderr);
- ret = -1;
+ }
+ if (hint->status != REF_STATUS_EXPECTING_REPORT) {
+ warning("remote reported status on unexpected ref: %s",
+ refname);
+ continue;
+ }
+
+ 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;
}
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
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);
+ return find_unique_abbrev(sha1, DEFAULT_ABBREV);
}
static void print_ok_ref_status(struct ref *ref)
}
}
+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 shown_dest = 0;
+ int n = 0;
- for (ref = refs; ref; ref = ref->next) {
- if (!ref->status)
- continue;
- if (ref->status == REF_STATUS_UPTODATE && !args.verbose)
- continue;
+ if (args.verbose) {
+ for (ref = refs; ref; ref = ref->next)
+ if (ref->status == REF_STATUS_UPTODATE)
+ n += print_one_push_status(ref, dest, n);
+ }
- if (!shown_dest) {
- fprintf(stderr, "To %s\n", dest);
- shown_dest = 1;
- }
+ 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:
- 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_OK:
- print_ok_ref_status(ref);
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)
int allow_deleting_refs = 0;
int expect_status_report = 0;
int flags = MATCH_REFS_NONE;
+ int ret;
if (args.send_all)
flags |= MATCH_REFS_ALL;
if (!remote_tail)
remote_tail = &remote_refs;
if (match_refs(local_refs, remote_refs, &remote_tail,
- nr_refspec, refspec, flags))
+ nr_refspec, refspec, flags)) {
+ close(out);
return -1;
+ }
if (!remote_refs) {
fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
"Perhaps you should specify a branch such as 'master'.\n");
+ close(out);
return 0;
}
hashcpy(ref->new_sha1, new_sha1);
if (!ref->deletion)
new_refs++;
- ref->status = REF_STATUS_OK;
if (!args.dry_run) {
char *old_hex = sha1_to_hex(ref->old_sha1);
packet_write(out, "%s %s %s",
old_hex, new_hex, ref->name);
}
+ ref->status = expect_status_report ?
+ REF_STATUS_EXPECTING_REPORT :
+ REF_STATUS_OK;
}
packet_flush(out);
if (new_refs && !args.dry_run) {
- if (pack_objects(out, remote_refs) < 0) {
- close(out);
+ if (pack_objects(out, remote_refs) < 0)
return -1;
- }
}
- close(out);
+ else
+ close(out);
- print_push_status(dest, remote_refs);
+ if (expect_status_report)
+ ret = receive_status(in, remote_refs);
+ else
+ ret = 0;
- if (expect_status_report) {
- if (receive_status(in))
- return -1;
- }
+ print_push_status(dest, remote_refs);
if (!args.dry_run && remote) {
for (ref = remote_refs; ref; ref = ref->next)
- if (!is_null_sha1(ref->new_sha1))
- update_tracking_ref(remote, ref);
+ update_tracking_ref(remote, ref);
}
- if (!new_refs)
+ if (!refs_pushed(remote_refs))
fprintf(stderr, "Everything up-to-date\n");
+ if (ret < 0)
+ return ret;
for (ref = remote_refs; ref; ref = ref->next) {
switch (ref->status) {
case REF_STATUS_NONE:
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",
conn = git_connect(fd, dest, args.receivepack, args.verbose ? CONNECT_VERBOSE : 0);
ret = do_send_pack(fd[0], fd[1], remote, dest, nr_heads, heads);
close(fd[0]);
- close(fd[1]);
+ /* do_send_pack always closes fd[1] */
ret |= finish_connect(conn);
return !!ret;
}