git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH] ls-files: fix overeager pathspec optimization

From
Junio C Hamano <gitster@pobox.com>
Date
Jan 8, 2010, 23:01 UTC
Message-ID
<7veim0w68q.fsf_-_@alter.siamese.dyndns.org>
In-Reply-To
<7vvdfcfjxo.fsf@alter.siamese.dyndns.org>

Given pathspecs that share a common prefix, ls-files optimized its call into recursive directory reader by starting at the common prefix directory.

If you have a directory "t" with an untracked file "t/junk" in it, but the top-level .gitignore file told us to ignore "t/", this resulted in an unexpected behaviour:

    $ git ls-files -o --exclude-standard
    $ cd t && git ls-files -o --exclude-standard
    junk
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
Junio C Hamano <gitster@pobox.com> writes:
Show 5 quoted lines
>> I think a proper fix should be in dir.c::read_directory() where it calls
>> read_directory_recursive() without first checking if the directory itself
>> should be ignored.  read_directory_recursive() itself has a logic to see
>> if the dirent it found is worth recursing into and a similar logic should
>> be in the toplevel caller.
So here it is.
 dir.c                              |   37 ++++++++++++++++++++++++++++++++++
 t/t3001-ls-files-others-exclude.sh |   39 ++++++++++++++++++++++++++++++++++++
 2 files changed, 76 insertions(+), 0 deletions(-)
diff --git a/dir.c b/dir.c
index f9ac454..9316eb6 100644
--- a/dir.c
+++ b/dir.c
@@ -787,6 +787,40 @@ static void free_simplify(struct path_simplify *simplify)
 	free(simplify);
 }
 
+static int has_leading_ignored_dir(struct dir_struct *dir,
+				   const char *path_, int len,
+				   const struct path_simplify *simplify)
+{
+	int at, dtype = DT_DIR;
+	char path[PATH_MAX];
+
+	at = 0;
+	memcpy(path, path_, len);
+	while (1) {
+		char *cp;
+		path[at] = '\0';
+		/*
+		 * NOTE! NOTE! NOTE!: we might want to actually lstat(2)
+		 * path[] to make sure it is a directory.
+		 */
+		if (excluded(dir, path, &dtype))
+			return 1;
+		if (at < len) {
+			path[at] = path_[at];
+			if (at < len && path[at] == '/')
+				at++;
+		}
+		if (len <= at)
+			break;
+		cp = memchr(path + at, '/', len - at);
+		if (!cp)
+			at = len;
+		else
+			at = cp - path;
+	}
+	return 0;
+}
+
 int read_directory(struct dir_struct *dir, const char *path, int len, const char **pathspec)
 {
 	struct path_simplify *simplify;
@@ -795,6 +829,9 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const char
 		return dir->nr;
 
 	simplify = create_simplify(pathspec);
+	if (has_leading_ignored_dir(dir, path, len, simplify))
+		return dir->nr;
+
 	read_directory_recursive(dir, path, len, 0, simplify);
 	free_simplify(simplify);
 	qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
index c65bca8..9e71260 100755
--- a/t/t3001-ls-files-others-exclude.sh
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -153,4 +153,43 @@ test_expect_success 'negated exclude matches can override previous ones' '
 	grep "^a.1" output
 '
 
+test_expect_success 'subdirectory ignore (setup)' '
+	mkdir -p top/l1/l2 &&
+	(
+		cd top &&
+		git init &&
+		echo /.gitignore >.gitignore &&
+		echo l1 >>.gitignore &&
+		echo l2 >l1/.gitignore &&
+		>l1/l2/l1
+	)
+'
+
+test_expect_success 'subdirectory ignore (toplevel)' '
+	(
+		cd top &&
+		git ls-files -o --exclude-standard
+	) >actual &&
+	>expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'subdirectory ignore (l1/l2)' '
+	(
+		cd top/l1/l2 &&
+		git ls-files -o --exclude-standard
+	) >actual &&
+	>expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'subdirectory ignore (l1)' '
+	(
+		cd top/l1 &&
+		git ls-files -o --exclude-standard
+	) >actual &&
+	>expect &&
+	test_cmp expect actual
+'
+
 test_done
-- 
1.6.6.209.g52296.dirty
Previous: Junio C HamanoNext: Linus Torvalds
Message 12 of 28 in “Possible bug in git-completion.sh”
  1. Jon ScheweJan 8, 2010
  2. Michael J GruberJan 8, 2010
  3. Jeff KingJan 8, 2010
  4. Junio C HamanoJan 8, 2010
  5. Jeff KingJan 8, 2010
  6. Junio C HamanoJan 8, 2010
  7. Junio C HamanoJan 8, 2010
  8. Jeff KingJan 8, 2010
  9. Jeff KingJan 8, 2010
  10. Junio C HamanoJan 8, 2010
  11. Junio C HamanoJan 8, 2010
  12. ls-files: fix overeager pathspec optimizationJunio C Hamano, Jan 8, 2010
  13. Linus TorvaldsJan 8, 2010
  14. Junio C HamanoJan 8, 2010
  15. Junio C HamanoJan 9, 2010
  16. Linus TorvaldsJan 9, 2010
  17. Junio C HamanoJan 9, 2010
  18. Linus TorvaldsJan 9, 2010
  19. Jeff KingJan 9, 2010
  20. Junio C HamanoJan 9, 2010
  21. 1/4 t3001: test ls-files -o ignored/dirJunio C Hamano, Jan 9, 2010
  22. 2/4 read_directory_recursive(): refactor handling of a single path into a separate functionJunio C Hamano, Jan 9, 2010
  23. 3/4 read_directory(): further split treat_path()Junio C Hamano, Jan 9, 2010
  24. 4/4 ls-files: fix overeager pathspec optimizationJunio C Hamano, Jan 9, 2010
  25. Jeff KingJan 12, 2010
  26. Junio C HamanoJan 9, 2010
  27. Linus TorvaldsJan 9, 2010
  28. Junio C HamanoJan 10, 2010

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.