receive: denyCurrentBranch=updateinstead should not blindly update

The handling of receive.denyCurrentBranch=updateInstead was added to a switch statement that handles other values of the variable, but all the other case arms only checked a condition to reject the attempted push, or let later logic in the same function to still intervene, so that a push that does not fast-forward (which is checked after the switch statement in question) is still rejected. But the handling of updateInstead incorrectly took immediate effect, without giving other checks a chance to intervene. Instead of calling update_worktree() that causes the side effect immediately, just note the fact that we will need to call the function later, and first give other checks a chance to reject the request. After the update-hook gets a chance to reject the push (which happens as the last step in a series of checks), call update_worktree() when we earlier detected the need to. Reported-by: Rajesh Madamanchi Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Oct 19, 2018 at 13:57 UTC b072a25fad21b8aecfa29f6ffb813a0a6194a764
2 files changed +16 -4
builtin/receive-pack.c
+9 -3
@@ -1037,6 +1037,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
1037 const char *ret;
1038 struct object_id *old_oid = &cmd->old_oid;
1039 struct object_id *new_oid = &cmd->new_oid;
1040 + int do_update_worktree = 0;
1041
1042 /* only refs/... are allowed */
1043 if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
@@ -1062,9 +1063,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
1063 refuse_unconfigured_deny();
1064 return "branch is currently checked out";
1065 case DENY_UPDATE_INSTEAD:
1065 - ret = update_worktree(new_oid->hash);
1066 - if (ret)
1067 - return ret;
1066 + /* pass -- let other checks intervene first */
1067 + do_update_worktree = 1;
1068 break;
1069 }
1070 }
@@ -1129,6 +1129,12 @@ static const char *update(struct command *cmd, struct shallow_info *si)
1129 return "hook declined";
1130 }
1131
1132 + if (do_update_worktree) {
1133 + ret = update_worktree(new_oid->hash);
1134 + if (ret)
1135 + return ret;
1136 + }
1137 +
1138 if (is_null_oid(new_oid)) {
1139 struct strbuf err = STRBUF_INIT;
1140 if (!parse_object(old_oid)) {
t/t5516-fetch-push.sh
+7 -1
@@ -1527,7 +1527,13 @@ test_expect_success 'receive.denyCurrentBranch = updateInstead' '
1527 test $(git -C .. rev-parse master) = $(git rev-parse HEAD) &&
1528 git diff --quiet &&
1529 git diff --cached --quiet
1530 - )
1530 + ) &&
1531 +
1532 + # (6) updateInstead intervened by fast-forward check
1533 + test_must_fail git push void master^:master &&
1534 + test $(git -C void rev-parse HEAD) = $(git rev-parse master) &&
1535 + git -C void diff --quiet &&
1536 + git -C void diff --cached --quiet
1537 '
1538
1539 test_expect_success 'updateInstead with push-to-checkout hook' '