From 334f4831e5a779d42e521b770a26eae1ecb27e86 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 18 Oct 2007 02:19:15 -0400 Subject: send-pack: don't update tracking refs on error Previously, we updated the tracking refs (which match refs we are pushing) while generating the list of refs to send. However, at that point we don't know whether the refs were accepted. Instead, we now wait until we get a response code from the server. If an error was indicated, we don't update any local tracking refs. Technically some refs could have been updated on the remote, but since the local ref update is just an optimization to avoid an extra fetch, we are better off erring on the side of correctness. The user-visible message is now generated much later in the program, and has been tweaked to make more sense. Signed-off-by: Jeff King Signed-off-by: Shawn O. Pearce --- send-pack.c | 50 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/send-pack.c b/send-pack.c index c1807f0794..1a7397f3a6 100644 --- a/send-pack.c +++ b/send-pack.c @@ -178,6 +178,35 @@ static int receive_status(int in) 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) + 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; + + if (!remote_find_tracking(remote, &rs)) { + fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst); + if (is_null_sha1(ref->peer_ref->new_sha1)) { + if (delete_ref(rs.dst, NULL)) + error("Failed to delete"); + } else + update_ref("update by push", rs.dst, + ref->new_sha1, NULL, 0, 0); + free(rs.dst); + } +} + static int send_pack(int in, int out, struct remote *remote, int nr_refspec, char **refspec) { struct ref *ref; @@ -306,22 +335,6 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha fprintf(stderr, "\n from %s\n to %s\n", old_hex, new_hex); } - if (remote && !dry_run) { - struct refspec rs; - rs.src = ref->name; - rs.dst = NULL; - if (!remote_find_tracking(remote, &rs)) { - fprintf(stderr, " Also local %s\n", rs.dst); - if (will_delete_ref) { - if (delete_ref(rs.dst, NULL)) { - error("Failed to delete"); - } - } else - update_ref("update by push", rs.dst, - ref->new_sha1, NULL, 0, 0); - free(rs.dst); - } - } } packet_flush(out); @@ -334,6 +347,11 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha ret = -4; } + if (!dry_run && remote && ret == 0) { + for (ref = remote_refs; ref; ref = ref->next) + update_tracking_ref(remote, ref); + } + if (!new_refs && ret == 0) fprintf(stderr, "Everything up-to-date\n"); return ret; -- cgit v1.2.1 From 09fba7a59d38d1cafaf33eadaf1d409c4113b30c Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 18 Oct 2007 02:17:46 -0400 Subject: t5516: test update of local refs on push The first test (updating local refs) should succeed without the prior commit, but the second one (not updating on error) used to fail before the prior commit was written. Signed-off-by: Jeff King Signed-off-by: Shawn O. Pearce --- t/t5516-fetch-push.sh | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 4fbd5b1f47..86f9b5346a 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -254,4 +254,32 @@ test_expect_success 'push with dry-run' ' check_push_result $old_commit heads/master ' +test_expect_success 'push updates local refs' ' + + rm -rf parent child && + mkdir parent && cd parent && git init && + echo one >foo && git add foo && git commit -m one && + cd .. && + git clone parent child && cd child && + echo two >foo && git commit -a -m two && + git push && + test $(git rev-parse master) = $(git rev-parse remotes/origin/master) + +' + +test_expect_success 'push does not update local refs on failure' ' + + rm -rf parent child && + mkdir parent && cd parent && git init && + echo one >foo && git add foo && git commit -m one && + echo exit 1 >.git/hooks/pre-receive && + chmod +x .git/hooks/pre-receive && + cd .. && + git clone parent child && cd child && + echo two >foo && git commit -a -m two || exit 1 + git push && exit 1 + test $(git rev-parse master) != $(git rev-parse remotes/origin/master) + +' + test_done -- cgit v1.2.1