Skip to content

Commit d62e44c

Browse files
Segev Finerethomson
authored andcommitted
checkout: Fix removing untracked files by path in subdirectories
The checkout code didn't iterate into a subdir if it didn't match the pathspec, but since the pathspec might match files in the subdir we should recurse into it (In contrast to gitignore handling). Fixes libgit2#5089
1 parent 2a1d97e commit d62e44c

File tree

2 files changed

+33
-2
lines changed

2 files changed

+33
-2
lines changed

src/checkout.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -371,8 +371,13 @@ static int checkout_action_wd_only(
371371
if (!git_pathspec__match(
372372
pathspec, wd->path,
373373
(data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
374-
git_iterator_ignore_case(workdir), NULL, NULL))
375-
return git_iterator_advance(wditem, workdir);
374+
git_iterator_ignore_case(workdir), NULL, NULL)) {
375+
376+
if (wd->mode == GIT_FILEMODE_TREE)
377+
return git_iterator_advance_into(wditem, workdir);
378+
else
379+
return git_iterator_advance(wditem, workdir);
380+
}
376381

377382
/* check if item is tracked in the index but not in the checkout diff */
378383
if (data->index != NULL) {

tests/checkout/head.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,32 @@ void test_checkout_head__do_not_remove_untracked_file_in_subdir(void)
109109
cl_assert(git_path_isfile("testrepo/tracked/subdir/untracked"));
110110
}
111111

112+
void test_checkout_head__do_remove_untracked_paths(void)
113+
{
114+
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
115+
git_index *index;
116+
char *paths[] = {"tracked/untracked"};
117+
118+
cl_git_pass(p_mkdir("testrepo/tracked", 0755));
119+
cl_git_pass(p_mkdir("testrepo/tracked/subdir", 0755));
120+
cl_git_mkfile("testrepo/tracked/tracked", "tracked\n");
121+
cl_git_mkfile("testrepo/tracked/untracked", "untracked\n");
122+
123+
cl_git_pass(git_repository_index(&index, g_repo));
124+
cl_git_pass(git_index_add_bypath(index, "tracked/tracked"));
125+
cl_git_pass(git_index_write(index));
126+
127+
git_index_free(index);
128+
129+
opts.checkout_strategy = GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
130+
opts.paths.strings = paths;
131+
opts.paths.count = 1;
132+
cl_git_pass(git_checkout_head(g_repo, &opts));
133+
134+
cl_assert(git_path_isfile("testrepo/tracked/tracked"));
135+
cl_assert(!git_path_isfile("testrepo/tracked/untracked"));
136+
}
137+
112138
void test_checkout_head__do_remove_tracked_subdir(void)
113139
{
114140
git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;

0 commit comments

Comments
 (0)