]> git.ipfire.org Git - thirdparty/git.git/commitdiff
checkout: rollback lock on early returns in merge_working_tree
authorHarald Nordgren <haraldnordgren@gmail.com>
Fri, 24 Apr 2026 21:10:11 +0000 (21:10 +0000)
committerJunio C Hamano <gitster@pobox.com>
Sat, 25 Apr 2026 10:28:56 +0000 (19:28 +0900)
merge_working_tree() acquires the index lock via
repo_hold_locked_index() but several early return paths exit
without calling rollback_lock_file(), leaving the lock held.
While this is currently harmless because the process exits soon
after, it becomes a problem if the function is ever called more
than once in the same process.

Add rollback_lock_file() calls to all early return paths.

Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/checkout.c

index e031e6188613a6ceede132f4c3fe5023af36e6e9..c80c62b37b9e8061574acf1ab5af2b36aab920a1 100644 (file)
@@ -783,8 +783,10 @@ static int merge_working_tree(const struct checkout_opts *opts,
        struct tree *new_tree;
 
        repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
-       if (repo_read_index_preload(the_repository, NULL, 0) < 0)
+       if (repo_read_index_preload(the_repository, NULL, 0) < 0) {
+               rollback_lock_file(&lock_file);
                return error(_("index file corrupt"));
+       }
 
        resolve_undo_clear_index(the_repository->index);
        if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
@@ -797,14 +799,18 @@ static int merge_working_tree(const struct checkout_opts *opts,
        } else {
                new_tree = repo_get_commit_tree(the_repository,
                                                new_branch_info->commit);
-               if (!new_tree)
+               if (!new_tree) {
+                       rollback_lock_file(&lock_file);
                        return error(_("unable to read tree (%s)"),
                                     oid_to_hex(&new_branch_info->commit->object.oid));
+               }
        }
        if (opts->discard_changes) {
                ret = reset_tree(new_tree, opts, 1, writeout_error, new_branch_info);
-               if (ret)
+               if (ret) {
+                       rollback_lock_file(&lock_file);
                        return ret;
+               }
        } else {
                struct tree_desc trees[2];
                struct tree *tree;
@@ -814,6 +820,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
                refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
 
                if (unmerged_index(the_repository->index)) {
+                       rollback_lock_file(&lock_file);
                        error(_("you need to resolve your current index first"));
                        return 1;
                }
@@ -857,15 +864,19 @@ static int merge_working_tree(const struct checkout_opts *opts,
                        struct strbuf sb = STRBUF_INIT;
                        struct strbuf old_commit_shortname = STRBUF_INIT;
 
-                       if (!opts->merge)
+                       if (!opts->merge) {
+                               rollback_lock_file(&lock_file);
                                return 1;
+                       }
 
                        /*
                         * Without old_branch_info->commit, the below is the same as
                         * the two-tree unpack we already tried and failed.
                         */
-                       if (!old_branch_info->commit)
+                       if (!old_branch_info->commit) {
+                               rollback_lock_file(&lock_file);
                                return 1;
+                       }
                        old_tree = repo_get_commit_tree(the_repository,
                                                        old_branch_info->commit);
 
@@ -897,8 +908,10 @@ static int merge_working_tree(const struct checkout_opts *opts,
                        ret = reset_tree(new_tree,
                                         opts, 1,
                                         writeout_error, new_branch_info);
-                       if (ret)
+                       if (ret) {
+                               rollback_lock_file(&lock_file);
                                return ret;
+                       }
                        o.ancestor = old_branch_info->name;
                        if (!old_branch_info->name) {
                                strbuf_add_unique_abbrev(&old_commit_shortname,
@@ -920,8 +933,10 @@ static int merge_working_tree(const struct checkout_opts *opts,
                                         writeout_error, new_branch_info);
                        strbuf_release(&o.obuf);
                        strbuf_release(&old_commit_shortname);
-                       if (ret)
+                       if (ret) {
+                               rollback_lock_file(&lock_file);
                                return ret;
+                       }
                }
        }