]> git.ipfire.org Git - thirdparty/git.git/commitdiff
refs/files: skip updates with errors in batched updates
authorKarthik Nayak <karthik.188@gmail.com>
Fri, 20 Jun 2025 07:15:44 +0000 (09:15 +0200)
committerJunio C Hamano <gitster@pobox.com>
Fri, 20 Jun 2025 16:14:21 +0000 (09:14 -0700)
The commit 23fc8e4f61 (refs: implement batch reference update support,
2025-04-08) introduced support for batched reference updates. This
allows users to batch updates together, while allowing some of the
updates to fail.

Under the hood, batched updates use the reference transaction mechanism.
Each update which fails is marked as such. Any failed updates must be
skipped over in the rest of the code, as they wouldn't apply any more.
In two of the loops within 'files_transaction_finish()' of the files
backend, the failed updates aren't skipped over. This can cause a
SEGFAULT otherwise. Add the missing skips and a test to validate the
same.

Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
refs/files-backend.c
t/t1400-update-ref.sh

index 4d1f65a57a5b55b63bf36451d8f74af267b5e197..c4a0f29072713de9a0bb1a7afe2c7130ab329bdd 100644 (file)
@@ -3208,6 +3208,10 @@ static int files_transaction_finish(struct ref_store *ref_store,
         */
        for (i = 0; i < transaction->nr; i++) {
                struct ref_update *update = transaction->updates[i];
+
+               if (update->rejection_err)
+                       continue;
+
                if (update->flags & REF_DELETING &&
                    !(update->flags & REF_LOG_ONLY) &&
                    !(update->flags & REF_IS_PRUNING)) {
@@ -3239,6 +3243,9 @@ static int files_transaction_finish(struct ref_store *ref_store,
                struct ref_update *update = transaction->updates[i];
                struct ref_lock *lock = update->backend_data;
 
+               if (update->rejection_err)
+                       continue;
+
                if (update->flags & REF_DELETING &&
                    !(update->flags & REF_LOG_ONLY)) {
                        update->flags |= REF_DELETED_RMDIR;
index d29d23cb8905f865e68da0e782c3cbe1948c6c3f..ca7eee7de21fc8db7edc5a756bfde4db84de0dc8 100755 (executable)
@@ -2299,6 +2299,51 @@ do
                        test_grep -q "refname conflict" stdout
                )
        '
+
+       test_expect_success "stdin $type batch-updates delete incorrect symbolic ref" '
+               git init repo &&
+               test_when_finished "rm -fr repo" &&
+               (
+                       cd repo &&
+                       test_commit c1 &&
+                       head=$(git rev-parse HEAD) &&
+                       git symbolic-ref refs/heads/symbolic refs/heads/non-existent &&
+
+                       format_command $type "delete refs/heads/symbolic" "$head" >stdin &&
+                       git update-ref $type --stdin --batch-updates <stdin >stdout &&
+                       test_grep "reference does not exist" stdout
+               )
+       '
+
+       test_expect_success "stdin $type batch-updates delete with incorrect old_oid" '
+               git init repo &&
+               test_when_finished "rm -fr repo" &&
+               (
+                       cd repo &&
+                       test_commit c1 &&
+                       git branch new-branch &&
+                       test_commit c2 &&
+                       head=$(git rev-parse HEAD) &&
+
+                       format_command $type "delete refs/heads/new-branch" "$head" >stdin &&
+                       git update-ref $type --stdin --batch-updates <stdin >stdout &&
+                       test_grep "incorrect old value provided" stdout
+               )
+       '
+
+       test_expect_success "stdin $type batch-updates delete non-existent ref" '
+               git init repo &&
+               test_when_finished "rm -fr repo" &&
+               (
+                       cd repo &&
+                       test_commit commit &&
+                       head=$(git rev-parse HEAD) &&
+
+                       format_command $type "delete refs/heads/non-existent" "$head" >stdin &&
+                       git update-ref $type --stdin --batch-updates <stdin >stdout &&
+                       test_grep "reference does not exist" stdout
+               )
+       '
 done
 
 test_expect_success 'update-ref should also create reflog for HEAD' '