push-to-deploy: allow pushing into an unborn branch and updating it

Setting receive.denycurrentbranch to updateinstead and pushing into the current branch, when the working tree and the index is truly clean, is supposed to reset the working tree and the index to match the tree of the pushed commit. This did not work when pushing into an unborn branch. The code that drives push-to-checkout hook needs no change, as the interface is defined so that hook can decide what to do when the push is coming to an unborn branch and take an appropriate action since the beginning. Acked-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Mar 31, 2015 at 23:15 UTC 1a51b52422e055e433dec9a496621341d70d38ff
2 files changed +74 -2
builtin/receive-pack.c
+20 -1
@@ -733,6 +733,22 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
733 return 0;
734 }
735
736 +/*
737 + * NEEDSWORK: we should consolidate various implementions of "are we
738 + * on an unborn branch?" test into one, and make the unified one more
739 + * robust. !get_sha1() based check used here and elsewhere would not
740 + * allow us to tell an unborn branch from corrupt ref, for example.
741 + * For the purpose of fixing "deploy-to-update does not work when
742 + * pushing into an empty repository" issue, this should suffice for
743 + * now.
744 + */
745 +static int head_has_history(void)
746 +{
747 + unsigned char sha1[20];
748 +
749 + return !get_sha1("HEAD", sha1);
750 +}
751 +
752 static const char *push_to_deploy(unsigned char *sha1,
753 struct argv_array *env,
754 const char *work_tree)
@@ -745,7 +761,7 @@ static const char *push_to_deploy(unsigned char *sha1,
761 };
762 const char *diff_index[] = {
763 "diff-index", "--quiet", "--cached", "--ignore-submodules",
748 - "HEAD", "--", NULL
764 + NULL, "--", NULL
765 };
766 const char *read_tree[] = {
767 "read-tree", "-u", "-m", NULL, NULL
@@ -772,6 +788,9 @@ static const char *push_to_deploy(unsigned char *sha1,
788 if (run_command(&child))
789 return "Working directory has unstaged changes";
790
791 + /* diff-index with either HEAD or an empty tree */
792 + diff_index[4] = head_has_history() ? "HEAD" : EMPTY_TREE_SHA1_HEX;
793 +
794 child_process_init(&child);
795 child.argv = diff_index;
796 child.env = env->argv;
t/t5516-fetch-push.sh
+54 -1
@@ -1430,8 +1430,22 @@ test_expect_success 'receive.denyCurrentBranch = updateInstead' '
1430 test $(git -C .. rev-parse HEAD^^) = $(git rev-parse HEAD) &&
1431 git diff --quiet &&
1432 test fifth = "$(cat path3)"
1433 - )
1433 + ) &&
1434
1435 + # (5) push into void
1436 + rm -fr void &&
1437 + git init void &&
1438 + (
1439 + cd void &&
1440 + git config receive.denyCurrentBranch updateInstead
1441 + ) &&
1442 + git push void master &&
1443 + (
1444 + cd void &&
1445 + test $(git -C .. rev-parse master) = $(git rev-parse HEAD) &&
1446 + git diff --quiet &&
1447 + git diff --cached --quiet
1448 + )
1449 '
1450
1451 test_expect_success 'updateInstead with push-to-checkout hook' '
@@ -1494,6 +1508,45 @@ test_expect_success 'updateInstead with push-to-checkout hook' '
1508 test "$(cat path5)" = irrelevant &&
1509 test "$(git diff --name-only --cached HEAD)" = path5 &&
1510 test $(git -C .. rev-parse HEAD) = $(git rev-parse HEAD)
1511 + ) &&
1512 +
1513 + # push into void
1514 + rm -fr void &&
1515 + git init void &&
1516 + (
1517 + cd void &&
1518 + git config receive.denyCurrentBranch updateInstead &&
1519 + write_script .git/hooks/push-to-checkout <<-\EOF
1520 + if git rev-parse --quiet --verify HEAD
1521 + then
1522 + has_head=yes
1523 + echo >&2 updating from $(git rev-parse HEAD)
1524 + else
1525 + has_head=no
1526 + echo >&2 pushing into void
1527 + fi
1528 + echo >&2 updating to "$1"
1529 +
1530 + git update-index -q --refresh &&
1531 + case "$has_head" in
1532 + yes)
1533 + git read-tree -u -m HEAD "$1" ;;
1534 + no)
1535 + git read-tree -u -m "$1" ;;
1536 + esac || {
1537 + status=$?
1538 + echo >&2 read-tree failed
1539 + exit $status
1540 + }
1541 + EOF
1542 + ) &&
1543 +
1544 + git push void master &&
1545 + (
1546 + cd void &&
1547 + git diff --quiet &&
1548 + git diff --cached --quiet &&
1549 + test $(git -C .. rev-parse HEAD) = $(git rev-parse HEAD)
1550 )
1551 '
1552