Skip to content

Commit

Permalink
send-pack: assign remote errors to each ref
Browse files Browse the repository at this point in the history
This lets us show remote errors (e.g., a denied hook) along
with the usual push output.

There is a slightly clever optimization in receive_status
that bears explanation. We need to correlate the returned
status and our ref objects, which naively could be an O(m*n)
operation. However, since the current implementation of
receive-pack returns the errors to us in the same order that
we sent them, we optimistically look for the next ref to be
looked up to come after the last one we have found. So it
should be an O(m+n) merge if the receive-pack behavior
holds, but we fall back to a correct but slower behavior if
it should change.

Signed-off-by: Jeff King <peff@peff.net>
Acked-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
Jeff King authored and Junio C Hamano committed Nov 17, 2007
1 parent 1f0e2a1 commit ca74c45
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 6 deletions.
51 changes: 45 additions & 6 deletions builtin-send-pack.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,19 +146,43 @@ static void get_local_heads(void)
for_each_ref(one_local_ref, NULL);
}

static int receive_status(int in)
static struct ref *set_ref_error(struct ref *refs, const char *line)
{
struct ref *ref;

for (ref = refs; ref; ref = ref->next) {
const char *msg;
if (prefixcmp(line, ref->name))
continue;
msg = line + strlen(ref->name);
if (*msg++ != ' ')
continue;
ref->status = REF_STATUS_REMOTE_REJECT;
ref->error = xstrdup(msg);
ref->error[strlen(ref->error)-1] = '\0';
return ref;
}
return NULL;
}

/* a return value of -1 indicates that an error occurred,
* but we were able to set individual ref errors. A return
* value of -2 means we couldn't even get that far. */
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;
return -2;
}
if (memcmp(line, "unpack ok\n", 10)) {
fputs(line, stderr);
ret = -1;
}
hint = NULL;
while (1) {
len = packet_read_line(in, line, sizeof(line));
if (!len)
Expand All @@ -171,7 +195,10 @@ static int receive_status(int in)
}
if (!memcmp(line, "ok", 2))
continue;
fputs(line, stderr);
if (hint)
hint = set_ref_error(hint, line + 3);
if (!hint)
hint = set_ref_error(refs, line + 3);
ret = -1;
}
return ret;
Expand Down Expand Up @@ -296,6 +323,12 @@ static void print_push_status(const char *dest, struct ref *refs)
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
"non-fast forward");
break;
case REF_STATUS_REMOTE_REJECT:
if (ref->deletion)
print_ref_status('!', "[remote rejected]", ref, NULL, ref->error);
else
print_ref_status('!', "[remote rejected]", ref, ref->peer_ref, ref->error);
break;
case REF_STATUS_OK:
print_ok_ref_status(ref);
break;
Expand All @@ -311,6 +344,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
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;
Expand Down Expand Up @@ -428,12 +462,15 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
}
close(out);

print_push_status(dest, remote_refs);

if (expect_status_report) {
if (receive_status(in))
ret = receive_status(in, remote_refs);
if (ret == -2)
return -1;
}
else
ret = 0;

print_push_status(dest, remote_refs);

if (!args.dry_run && remote) {
for (ref = remote_refs; ref; ref = ref->next)
Expand All @@ -442,6 +479,8 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest

if (!new_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:
Expand Down
2 changes: 2 additions & 0 deletions cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,9 @@ struct ref {
REF_STATUS_REJECT_NONFASTFORWARD,
REF_STATUS_REJECT_NODELETE,
REF_STATUS_UPTODATE,
REF_STATUS_REMOTE_REJECT,
} status;
char *error;
struct ref *peer_ref; /* when renaming */
char name[FLEX_ARRAY]; /* more */
};
Expand Down
24 changes: 24 additions & 0 deletions t/t5406-remote-rejects.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/sh

test_description='remote push rejects are reported by client'

. ./test-lib.sh

test_expect_success 'setup' '
mkdir .git/hooks &&
(echo "#!/bin/sh" ; echo "exit 1") >.git/hooks/update &&
chmod +x .git/hooks/update &&
echo 1 >file &&
git add file &&
git commit -m 1 &&
git clone . child &&
cd child &&
echo 2 >file &&
git commit -a -m 2
'

test_expect_success 'push reports error' '! git push 2>stderr'

test_expect_success 'individual ref reports error' 'grep rejected stderr'

test_done

0 comments on commit ca74c45

Please sign in to comment.