From d8e458582219ef786545ce7c083595c5ec91d5d3 Mon Sep 17 00:00:00 2001 From: Ariel Keselman Date: Fri, 11 Sep 2026 18:46:09 -0700 Subject: [PATCH] refs/files: avoid packed-refs lock for root ref deletion Deleting a root ref queues a packed-ref transaction in the files backend, even though root refs cannot be packed. For example, holding .git/packed-refs.lock makes "git update-ref --no-deref -d AUTO_MERGE" fail, whether or not AUTO_MERGE exists. This also affects post-commit cleanup, which deletes AUTO_MERGE after updating HEAD. In a linked worktree with read-only shared metadata, commit succeeds but cleanup reports a packed-refs.lock error. Deleting CHERRY_PICK_HEAD and REVERT_HEAD is affected as well. Skip the packed transaction for root-ref deletions. Keep loose-ref locking and packed-ref deletion for other refs unchanged. Signed-off-by: Ariel Keselman Signed-off-by: Junio C Hamano --- refs/files-backend.c | 8 +++++--- t/t0600-reffiles-backend.sh | 40 +++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index a4c7858787..0333d5d355 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2981,10 +2981,12 @@ static int files_transaction_prepare(struct ref_store *ref_store, if (update->flags & REF_DELETING && !(update->flags & REF_LOG_ONLY) && - !(update->flags & REF_IS_PRUNING)) { + !(update->flags & REF_IS_PRUNING) && + !is_root_ref(update->refname)) { /* - * This reference has to be deleted from - * packed-refs if it exists there. + * This reference has to be deleted from packed-refs if it + * exists there. Root refs are never packed, so we do not + * have to delete them from packed-refs. */ if (!packed_transaction) { packed_transaction = ref_store_transaction_begin( diff --git a/t/t0600-reffiles-backend.sh b/t/t0600-reffiles-backend.sh index 74bfa2e9ba..560caf56b0 100755 --- a/t/t0600-reffiles-backend.sh +++ b/t/t0600-reffiles-backend.sh @@ -519,4 +519,44 @@ test_expect_success 'symref transaction supports false symlink config' ' test_cmp expect actual ' +test_expect_success 'deleting a root ref does not lock packed-refs' ' + test_when_finished "rm -rf root-ref" && + git init root-ref && + ( + cd root-ref && + test_commit initial && + git update-ref AUTO_MERGE HEAD && + git pack-refs --all && + test_path_is_file .git/AUTO_MERGE && + cp .git/packed-refs expect && + : >.git/packed-refs.lock && + git update-ref --no-deref -d AUTO_MERGE && + test_path_is_missing .git/AUTO_MERGE && + test_path_is_file .git/packed-refs.lock && + test_cmp expect .git/packed-refs + ) +' + +test_expect_success 'deleting root and packed refs in one transaction requires packed-refs lock' ' + test_when_finished "rm -rf root-ref" && + git init root-ref && + ( + cd root-ref && + test_commit initial && + git update-ref refs/heads/packed-branch HEAD && + git pack-refs --all && + test_path_is_missing .git/refs/heads/packed-branch && + git update-ref AUTO_MERGE HEAD && + git refs list --include-root-refs >expect && + : >.git/packed-refs.lock && + test_must_fail git update-ref --no-deref --stdin 2>err <<-EOF && + delete AUTO_MERGE + delete refs/heads/packed-branch + EOF + test_grep "Unable to create .*packed-refs.lock" err && + git refs list --include-root-refs >actual && + test_cmp expect actual + ) +' + test_done