Skip to content

Commit 2eac2a4

Browse files
committed
ls-files -k: a directory only can be killed if the index has a non-directory
"ls-files -o" and "ls-files -k" both traverse the working tree down to find either all untracked paths or those that will be "killed" (removed from the working tree to make room) when the paths recorded in the index are checked out. It is necessary to traverse the working tree fully when enumerating all the "other" paths, but when we are only interested in "killed" paths, we can take advantage of the fact that paths that do not overlap with entries in the index can never be killed. The treat_one_path() helper function, which is called during the recursive traversal, is the ideal place to implement an optimization. When we are looking at a directory P in the working tree, there are three cases: (1) P exists in the index. Everything inside the directory P in the working tree needs to go when P is checked out from the index. (2) P does not exist in the index, but there is P/Q in the index. We know P will stay a directory when we check out the contents of the index, but we do not know yet if there is a directory P/Q in the working tree to be killed, so we need to recurse. (3) P does not exist in the index, and there is no P/Q in the index to require P to be a directory, either. Only in this case, we know that everything inside P will not be killed without recursing. Note that this helper is called by treat_leading_path() that decides if we need to traverse only subdirectories of a single common leading directory, which is essential for this optimization to be correct. This caller checks each level of the leading path component from shallower directory to deeper ones, and that is what allows us to only check if the path appears in the index. If the call to treat_one_path() weren't there, given a path P/Q/R, the real traversal may start from directory P/Q/R, even when the index records P as a regular file, and we would end up having to check if any leading subpath in P/Q/R, e.g. P, appears in the index. Signed-off-by: Junio C Hamano <[email protected]>
1 parent 7126102 commit 2eac2a4

File tree

3 files changed

+31
-3
lines changed

3 files changed

+31
-3
lines changed

builtin/ls-files.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ static void show_files(struct dir_struct *dir)
213213

214214
/* For cached/deleted files we don't need to even do the readdir */
215215
if (show_others || show_killed) {
216+
if (!show_others)
217+
dir->flags |= DIR_COLLECT_KILLED_ONLY;
216218
fill_directory(dir, pathspec);
217219
if (show_others)
218220
show_other_files(dir);

dir.c

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,12 +1173,37 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
11731173
int dtype, struct dirent *de)
11741174
{
11751175
int exclude;
1176+
int has_path_in_index = !!cache_name_exists(path->buf, path->len, ignore_case);
1177+
11761178
if (dtype == DT_UNKNOWN)
11771179
dtype = get_dtype(de, path->buf, path->len);
11781180

11791181
/* Always exclude indexed files */
1180-
if (dtype != DT_DIR &&
1181-
cache_name_exists(path->buf, path->len, ignore_case))
1182+
if (dtype != DT_DIR && has_path_in_index)
1183+
return path_none;
1184+
1185+
/*
1186+
* When we are looking at a directory P in the working tree,
1187+
* there are three cases:
1188+
*
1189+
* (1) P exists in the index. Everything inside the directory P in
1190+
* the working tree needs to go when P is checked out from the
1191+
* index.
1192+
*
1193+
* (2) P does not exist in the index, but there is P/Q in the index.
1194+
* We know P will stay a directory when we check out the contents
1195+
* of the index, but we do not know yet if there is a directory
1196+
* P/Q in the working tree to be killed, so we need to recurse.
1197+
*
1198+
* (3) P does not exist in the index, and there is no P/Q in the index
1199+
* to require P to be a directory, either. Only in this case, we
1200+
* know that everything inside P will not be killed without
1201+
* recursing.
1202+
*/
1203+
if ((dir->flags & DIR_COLLECT_KILLED_ONLY) &&
1204+
(dtype == DT_DIR) &&
1205+
!has_path_in_index &&
1206+
(directory_exists_in_index(path->buf, path->len) == index_nonexistent))
11821207
return path_none;
11831208

11841209
exclude = is_excluded(dir, path->buf, &dtype);

dir.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ struct dir_struct {
8080
DIR_HIDE_EMPTY_DIRECTORIES = 1<<2,
8181
DIR_NO_GITLINKS = 1<<3,
8282
DIR_COLLECT_IGNORED = 1<<4,
83-
DIR_SHOW_IGNORED_TOO = 1<<5
83+
DIR_SHOW_IGNORED_TOO = 1<<5,
84+
DIR_COLLECT_KILLED_ONLY = 1<<6
8485
} flags;
8586
struct dir_entry **entries;
8687
struct dir_entry **ignored;

0 commit comments

Comments
 (0)