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 <skariel@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
next
Ariel Keselman 2026-09-11 18:46:09 -07:00 committed by Junio C Hamano
parent e9019fcafe
commit d8e4585822
2 changed files with 45 additions and 3 deletions

View File

@ -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(

View File

@ -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