From: Johannes Schindelin Date: Thu, 29 Nov 2007 01:02:53 +0000 (+0000) Subject: receive-pack: allow deletion of corrupt refs X-Git-Tag: v1.5.4-rc0~131 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=28391a80a94d2b59d1d21f8264fe5dab91d77249;p=git.git receive-pack: allow deletion of corrupt refs Occasionally, in some setups (*cough* forks on repo.or.cz *cough*) some refs go stale, e.g. when the forkee rebased and lost some objects needed by the fork. The quick & dirty way to deal with those refs is to delete them and push them again. However, git-push first would first fetch the current commit name for the ref, would receive a null sha1 since the ref does not point to a valid object, then tell receive-pack that it should delete the ref with this commit name. delete_ref() would be subsequently be called, and check that resolve_ref() (which does _not_ check for validity of the object) returns the same commit name. Which would fail. The proper fix is to avoid corrupting repositories, but in the meantime this is a good fix in any case. Incidentally, some instances of "cd .." in the test cases were fixed, so that subsequent test cases run in t/trash/ irrespective of the outcome of the previous test cases. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- diff --git a/receive-pack.c b/receive-pack.c index ed44b897f..fba4cf823 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -200,6 +200,10 @@ static const char *update(struct command *cmd) } if (is_null_sha1(new_sha1)) { + if (!parse_object(old_sha1)) { + warning ("Allowing deletion of corrupt ref."); + old_sha1 = NULL; + } if (delete_ref(name, old_sha1)) { error("failed to delete %s", name); return "failed to delete"; diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 4fbd5b1f4..987c9d21c 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -247,11 +247,50 @@ test_expect_success 'push with colon-less refspec (4)' ' test_expect_success 'push with dry-run' ' mk_test heads/master && - cd testrepo && - old_commit=$(git show-ref -s --verify refs/heads/master) && - cd .. && + (cd testrepo && + old_commit=$(git show-ref -s --verify refs/heads/master)) && git push --dry-run testrepo && 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) && + 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) && + 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 'allow deleting an invalid remote ref' ' + + pwd && + rm -f testrepo/.git/objects/??/* && + git push testrepo :refs/heads/master && + (cd testrepo && ! git rev-parse --verify refs/heads/master) + +' + test_done