checkout: rollback lock on early returns in merge_working_tree
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>
Harald Nordgren committed
Apr 28, 2026 at 18:39 UTC
26e4e50d463e1427c6288b33054e5d9a4a99a8f0
1 file changed
+22
-7
builtin/checkout.c
+22
-7
@@ -783,8 +783,10 @@ static int merge_working_tree(const struct checkout_opts *opts,
783
struct tree *new_tree;
784
785
repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
786
- if (repo_read_index_preload(the_repository, NULL, 0) < 0)
786
+ if (repo_read_index_preload(the_repository, NULL, 0) < 0) {
787
+ rollback_lock_file(&lock_file);
788
return error(_("index file corrupt"));
789
+ }
790
791
resolve_undo_clear_index(the_repository->index);
792
if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
@@ -797,14 +799,18 @@ static int merge_working_tree(const struct checkout_opts *opts,
799
} else {
800
new_tree = repo_get_commit_tree(the_repository,
801
new_branch_info->commit);
800
- if (!new_tree)
802
+ if (!new_tree) {
803
+ rollback_lock_file(&lock_file);
804
return error(_("unable to read tree (%s)"),
805
oid_to_hex(&new_branch_info->commit->object.oid));
806
+ }
807
}
808
if (opts->discard_changes) {
809
ret = reset_tree(new_tree, opts, 1, writeout_error, new_branch_info);
806
- if (ret)
810
+ if (ret) {
811
+ rollback_lock_file(&lock_file);
812
return ret;
813
+ }
814
} else {
815
struct tree_desc trees[2];
816
struct tree *tree;
@@ -814,6 +820,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
820
refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
821
822
if (unmerged_index(the_repository->index)) {
823
+ rollback_lock_file(&lock_file);
824
error(_("you need to resolve your current index first"));
825
return 1;
826
}
@@ -857,15 +864,19 @@ static int merge_working_tree(const struct checkout_opts *opts,
864
struct strbuf sb = STRBUF_INIT;
865
struct strbuf old_commit_shortname = STRBUF_INIT;
866
860
- if (!opts->merge)
867
+ if (!opts->merge) {
868
+ rollback_lock_file(&lock_file);
869
return 1;
870
+ }
871
872
/*
873
* Without old_branch_info->commit, the below is the same as
874
* the two-tree unpack we already tried and failed.
875
*/
867
- if (!old_branch_info->commit)
876
+ if (!old_branch_info->commit) {
877
+ rollback_lock_file(&lock_file);
878
return 1;
879
+ }
880
old_tree = repo_get_commit_tree(the_repository,
881
old_branch_info->commit);
882
@@ -897,8 +908,10 @@ static int merge_working_tree(const struct checkout_opts *opts,
908
ret = reset_tree(new_tree,
909
opts, 1,
910
writeout_error, new_branch_info);
900
- if (ret)
911
+ if (ret) {
912
+ rollback_lock_file(&lock_file);
913
return ret;
914
+ }
915
o.ancestor = old_branch_info->name;
916
if (!old_branch_info->name) {
917
strbuf_add_unique_abbrev(&old_commit_shortname,
@@ -920,8 +933,10 @@ static int merge_working_tree(const struct checkout_opts *opts,
933
writeout_error, new_branch_info);
934
strbuf_release(&o.obuf);
935
strbuf_release(&old_commit_shortname);
923
- if (ret)
936
+ if (ret) {
937
+ rollback_lock_file(&lock_file);
938
return ret;
939
+ }
940
}
941
}
942