refs: handle zero oid for pseudorefs

According to the documentation, it is possible to "specify 40 '0' or an empty string as <oldvalue> to make sure that the ref you are creating does not exist." But in the code for pseudorefs, we do not implement this, as demonstrated by the failing tests added in the previous commit. If we fail to read the old ref, we immediately die. But a failure to read would actually be a good thing if we have been given the zero oid. With the zero oid, allow -- and even require -- the ref-reading to fail. This implements the "make sure that the ref ... does not exist" part of the documentation and fixes both failing tests from the previous commit. Since we have a `strbuf err` for collecting errors, let's use it and signal an error to the caller instead of dying hard. Reported-by: Rafael Ascensão <rafa.almas@gmail.com> Helped-by: Rafael Ascensão <rafa.almas@gmail.com> Signed-off-by: Martin Ågren <martin.agren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Martin Ågren committed May 10, 2018 at 21:29 UTC db0210d445963e5d85f98e48d6a93b971779d449
2 files changed +15 -5
refs.c
+13 -3
@@ -681,9 +681,19 @@ static int write_pseudoref(const char *pseudoref, const struct object_id *oid,
681 if (old_oid) {
682 struct object_id actual_old_oid;
683
684 - if (read_ref(pseudoref, &actual_old_oid))
685 - die("could not read ref '%s'", pseudoref);
686 - if (oidcmp(&actual_old_oid, old_oid)) {
684 + if (read_ref(pseudoref, &actual_old_oid)) {
685 + if (!is_null_oid(old_oid)) {
686 + strbuf_addf(err, "could not read ref '%s'",
687 + pseudoref);
688 + rollback_lock_file(&lock);
689 + goto done;
690 + }
691 + } else if (is_null_oid(old_oid)) {
692 + strbuf_addf(err, "ref '%s' already exists",
693 + pseudoref);
694 + rollback_lock_file(&lock);
695 + goto done;
696 + } else if (oidcmp(&actual_old_oid, old_oid)) {
697 strbuf_addf(err, "unexpected object ID when writing '%s'",
698 pseudoref);
699 rollback_lock_file(&lock);
t/t1400-update-ref.sh
+2 -2
@@ -503,12 +503,12 @@ test_expect_success 'delete pseudoref with correct old value' '
503 test_path_is_missing .git/PSEUDOREF
504 '
505
506 -test_expect_failure 'create pseudoref with old OID zero' '
506 +test_expect_success 'create pseudoref with old OID zero' '
507 git update-ref PSEUDOREF $A $Z &&
508 test $A = $(cat .git/PSEUDOREF)
509 '
510
511 -test_expect_failure 'do not overwrite pseudoref with old OID zero' '
511 +test_expect_success 'do not overwrite pseudoref with old OID zero' '
512 test_when_finished git update-ref -d PSEUDOREF &&
513 test_must_fail git update-ref PSEUDOREF $B $Z 2>err &&
514 test $A = $(cat .git/PSEUDOREF) &&