worktree add: be tolerant of corrupt worktrees
authorNguyễn Thái Ngọc Duy <pclouds@gmail.com>
Mon, 13 May 2019 10:49:44 +0000 (17:49 +0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 15 May 2019 05:17:18 +0000 (14:17 +0900)
find_worktree() can die() unexpectedly because it uses real_path()
instead of the gentler version. When it's used in 'git worktree add' [1]
and there's a bad worktree, this die() could prevent people from adding
new worktrees.

The "bad" condition to trigger this is when a parent of the worktree's
location is deleted. Then real_path() will complain.

Use the other version so that bad worktrees won't affect 'worktree
add'. The bad ones will eventually be pruned, we just have to tolerate
them for a bit.

[1] added in cb56f55c16 (worktree: disallow adding same path multiple
times, 2018-08-28), or since v2.20.0. Though the real bug in
find_worktree() is much older.

Reported-by: Shaheed Haque <shaheedhaque@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
t/t2025-worktree-add.sh
worktree.c
index 286bba35d8ae06f97cbf59263cd472975036deaa..d83a9f0fdc0faaef9756166f7b6223212514a7d6 100755 (executable)
@@ -570,4 +570,16 @@ test_expect_success '"add" an existing locked but missing worktree' '
        git worktree add --force --force --detach gnoo
 '
 
+test_expect_success '"add" should not fail because of another bad worktree' '
+       git init add-fail &&
+       (
+               cd add-fail &&
+               test_commit first &&
+               mkdir sub &&
+               git worktree add sub/to-be-deleted &&
+               rm -rf sub &&
+               git worktree add second
+       )
+'
+
 test_done
index d6a0ee7f730d96a7e8cc04095ddafb43c937105a..c79b3e42bb5b4362660419ce95c844d6248aa3b3 100644 (file)
@@ -222,9 +222,12 @@ struct worktree *find_worktree(struct worktree **list,
                free(to_free);
                return NULL;
        }
-       for (; *list; list++)
-               if (!fspathcmp(path, real_path((*list)->path)))
+       for (; *list; list++) {
+               const char *wt_path = real_path_if_valid((*list)->path);
+
+               if (wt_path && !fspathcmp(path, wt_path))
                        break;
+       }
        free(path);
        free(to_free);
        return *list;