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

Re: [PATCH 6/8] ls-tree: work from subdirectory.

From
Linus Torvalds <torvalds@osdl.org>
Date
Nov 26, 2005, 17:38 UTC
Message-ID
<Pine.LNX.4.64.0511260855560.13959@g5.osdl.org>
In-Reply-To
<7vpson4tqi.fsf@assigned-by-dhcp.cox.net>
On Sat, 26 Nov 2005, Junio C Hamano wrote:
Show 9 quoted lines
> 
> --- a/ls-tree.c
> +++ b/ls-tree.c
> @@ -8,6 +8,8 @@
>  #include "tree.h"
>  #include "quote.h"
>  
> +static const char *prefix;
> +
No need to have this visible outside of "main()".
> +	prefix = setup_git_directory_gently(&nongit);
> +	if (prefix)
> +		path0[0] = prefix;
...
Show 5 quoted lines
> +	if (argc == 2)
> +		path = path0;
> +	else
> +		path = get_pathspec(prefix, argv + 2);
> +
And this is just ugly.

git-ls-tree should be rewritten to use a pathspec the same way everybody else does. Right now it's the odd man out: if you do

	git-ls-tree HEAD divers/char drivers/

it will show the same files _twice_, which is not how pathspecs in general work.

How about this patch? It breaks some of the git-ls-tree tests, but it makes git-ls-tree work a lot more like other git pathspec commands, and it removes more than 150 lines by re-using the recursive tree traversal (but the "-d" flag is gone for good, so I'm not pushing this too hard).

		Linus
---
diff --git a/ls-tree.c b/ls-tree.c
index d9f15e3..598b729 100644
--- a/ls-tree.c
+++ b/ls-tree.c
@@ -13,215 +13,32 @@ static int line_termination = '\n';
 #define LS_TREE_ONLY 2
 static int ls_options = 0;
 
-static struct tree_entry_list root_entry;
-
-static void prepare_root(unsigned char *sha1)
-{
-	unsigned char rsha[20];
-	unsigned long size;
-	void *buf;
-	struct tree *root_tree;
-
-	buf = read_object_with_reference(sha1, "tree", &size, rsha);
-	free(buf);
-	if (!buf)
-		die("Could not read %s", sha1_to_hex(sha1));
-
-	root_tree = lookup_tree(rsha);
-	if (!root_tree)
-		die("Could not read %s", sha1_to_hex(sha1));
-
-	/* Prepare a fake entry */
-	root_entry.directory = 1;
-	root_entry.executable = root_entry.symlink = 0;
-	root_entry.mode = S_IFDIR;
-	root_entry.name = "";
-	root_entry.item.tree = root_tree;
-	root_entry.parent = NULL;
-}
-
-static int prepare_children(struct tree_entry_list *elem)
-{
-	if (!elem->directory)
-		return -1;
-	if (!elem->item.tree->object.parsed) {
-		struct tree_entry_list *e;
-		if (parse_tree(elem->item.tree))
-			return -1;
-		/* Set up the parent link */
-		for (e = elem->item.tree->entries; e; e = e->next)
-			e->parent = elem;
-	}
-	return 0;
-}
-
-static struct tree_entry_list *find_entry(const char *path, char *pathbuf)
-{
-	const char *next, *slash;
-	int len;
-	struct tree_entry_list *elem = &root_entry, *oldelem = NULL;
-
-	*(pathbuf) = '\0';
-
-	/* Find tree element, descending from root, that
-	 * corresponds to the named path, lazily expanding
-	 * the tree if possible.
-	 */
-
-	while (path) {
-		/* The fact we still have path means that the caller
-		 * wants us to make sure that elem at this point is a
-		 * directory, and possibly descend into it.  Even what
-		 * is left is just trailing slashes, we loop back to
-		 * here, and this call to prepare_children() will
-		 * catch elem not being a tree.  Nice.
-		 */
-		if (prepare_children(elem))
-			return NULL;
-
-		slash = strchr(path, '/');
-		if (!slash) {
-			len = strlen(path);
-			next = NULL;
-		}
-		else {
-			next = slash + 1;
-			len = slash - path;
-		}
-		if (len) {
-			if (oldelem) {
-				pathbuf += sprintf(pathbuf, "%s/", oldelem->name);
-			}
-
-			/* (len == 0) if the original path was "drivers/char/"
-			 * and we have run already two rounds, having elem
-			 * pointing at the drivers/char directory.
-			 */
-			elem = elem->item.tree->entries;
-			while (elem) {
-				if ((strlen(elem->name) == len) &&
-				    !strncmp(elem->name, path, len)) {
-					/* found */
-					break;
-				}
-				elem = elem->next;
-			}
-			if (!elem)
-				return NULL;
-
-			oldelem = elem;
-		}
-		path = next;
-	}
-
-	return elem;
-}
-
-static const char *entry_type(struct tree_entry_list *e)
-{
-	return (e->directory ? "tree" : "blob");
-}
-
-static const char *entry_hex(struct tree_entry_list *e)
-{
-	return sha1_to_hex(e->directory
-			   ? e->item.tree->object.sha1
-			   : e->item.blob->object.sha1);
-}
-
-/* forward declaration for mutually recursive routines */
-static int show_entry(struct tree_entry_list *, int, char *pathbuf);
-
-static int show_children(struct tree_entry_list *e, int level, char *pathbuf)
-{
-	int oldlen = strlen(pathbuf);
-
-	if (e != &root_entry)
-		sprintf(pathbuf + oldlen, "%s/", e->name);
-
-	if (prepare_children(e))
-		die("internal error: ls-tree show_children called with non tree");
-	e = e->item.tree->entries;
-	while (e) {
-		show_entry(e, level, pathbuf);
-		e = e->next;
-	}
-
-	pathbuf[oldlen] = '\0';
-
-	return 0;
-}
+static const char ls_tree_usage[] =
+	"git-ls-tree [-d] [-r] [-z] <tree-ish> [path...]";
 
-static int show_entry(struct tree_entry_list *e, int level, char *pathbuf)
+static int show_tree(unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage)
 {
-	int err = 0; 
-
-	if (e != &root_entry) {
-		printf("%06o %s %s	",
-		       e->mode, entry_type(e), entry_hex(e));
-		write_name_quoted(pathbuf, e->name, line_termination, stdout);
-		putchar(line_termination);
-	}
+	const char *type = "blob";
+	int retval = 0;
 
-	if (e->directory) {
-		/* If this is a directory, we have the following cases:
-		 * (1) This is the top-level request (explicit path from the
-		 *     command line, or "root" if there is no command line).
-		 *  a. Without any flag.  We show direct children.  We do not 
-		 *     recurse into them.
-		 *  b. With -r.  We do recurse into children.
-		 *  c. With -d.  We do not recurse into children.
-		 * (2) We came here because our caller is either (1-a) or
-		 *     (1-b).
-		 *  a. Without any flag.  We do not show our children (which
-		 *     are grandchildren for the original request).
-		 *  b. With -r.  We continue to recurse into our children.
-		 *  c. With -d.  We should not have come here to begin with.
-		 */
-		if (level == 0 && !(ls_options & LS_TREE_ONLY))
-			/* case (1)-a and (1)-b */
-			err = err | show_children(e, level+1, pathbuf);
-		else if (level && ls_options & LS_RECURSIVE)
-			/* case (2)-b */
-			err = err | show_children(e, level+1, pathbuf);
+	if (S_ISDIR(mode)) {
+		type = "tree";
+		if (ls_options & LS_RECURSIVE)
+			retval = READ_TREE_RECURSIVE;
 	}
-	return err;
-}
 
-static int list_one(const char *path)
-{
-	int err = 0;
-	char pathbuf[MAXPATHLEN + 1];
-	struct tree_entry_list *e = find_entry(path, pathbuf);
-	if (!e) {
-		/* traditionally ls-tree does not complain about
-		 * missing path.  We may change this later to match
-		 * what "/bin/ls -a" does, which is to complain.
-		 */
-		return err;
-	}
-	err = err | show_entry(e, 0, pathbuf);
-	return err;
+	printf("%06o %s %s\t%.*s%s%c", mode, type, sha1_to_hex(sha1), baselen, base, pathname, line_termination);
+	return retval;
 }
 
-static int list(char **path)
+int main(int argc, const char **argv)
 {
-	int i;
-	int err = 0;
-	for (i = 0; path[i]; i++)
-		err = err | list_one(path[i]);
-	return err;
-}
-
-static const char ls_tree_usage[] =
-	"git-ls-tree [-d] [-r] [-z] <tree-ish> [path...]";
-
-int main(int argc, char **argv)
-{
-	static char *path0[] = { "", NULL };
-	char **path;
+	const char **path, *prefix;
 	unsigned char sha1[20];
+	char *buf;
+	unsigned long size;
 
+	prefix = setup_git_directory();
 	while (1 < argc && argv[1][0] == '-') {
 		switch (argv[1][1]) {
 		case 'z':
@@ -244,9 +61,11 @@ int main(int argc, char **argv)
 	if (get_sha1(argv[1], sha1) < 0)
 		usage(ls_tree_usage);
 
-	path = (argc == 2) ? path0 : (argv + 2);
-	prepare_root(sha1);
-	if (list(path) < 0)
-		die("list failed");
+	path = get_pathspec(prefix, argv + 2);
+	buf = read_object_with_reference(sha1, "tree", &size, NULL);
+	if (!buf)
+		die("not a tree object");
+	read_tree_recursive(buf, size, "", 0, 0, path, show_tree);
+
 	return 0;
 }
diff --git a/tree.c b/tree.c
index 8b42a07..043f032 100644
--- a/tree.c
+++ b/tree.c
@@ -9,9 +9,16 @@ const char *tree_type = "tree";
 
 static int read_one_entry(unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage)
 {
-	int len = strlen(pathname);
-	unsigned int size = cache_entry_size(baselen + len);
-	struct cache_entry *ce = xmalloc(size);
+	int len;
+	unsigned int size;
+	struct cache_entry *ce;
+
+	if (S_ISDIR(mode))
+		return READ_TREE_RECURSIVE;
+
+	len = strlen(pathname);
+	size = cache_entry_size(baselen + len);
+	ce = xmalloc(size);
 
 	memset(ce, 0, size);
 
@@ -67,9 +74,10 @@ static int match_tree_entry(const char *
 	return 0;
 }
 
-static int read_tree_recursive(void *buffer, unsigned long size,
-			       const char *base, int baselen,
-			       int stage, const char **match)
+int read_tree_recursive(void *buffer, unsigned long size,
+			const char *base, int baselen,
+			int stage, const char **match,
+			read_tree_fn_t fn)
 {
 	while (size) {
 		int len = strlen(buffer)+1;
@@ -86,6 +94,14 @@ static int read_tree_recursive(void *buf
 		if (!match_tree_entry(base, baselen, path, mode, match))
 			continue;
 
+		switch (fn(sha1, base, baselen, path, mode, stage)) {
+		case 0:
+			continue;
+		case READ_TREE_RECURSIVE:
+			break;;
+		default:
+			return -1;
+		}
 		if (S_ISDIR(mode)) {
 			int retval;
 			int pathlen = strlen(path);
@@ -106,22 +122,20 @@ static int read_tree_recursive(void *buf
 			retval = read_tree_recursive(eltbuf, eltsize,
 						     newbase,
 						     baselen + pathlen + 1,
-						     stage, match);
+						     stage, match, fn);
 			free(eltbuf);
 			free(newbase);
 			if (retval)
 				return -1;
 			continue;
 		}
-		if (read_one_entry(sha1, base, baselen, path, mode, stage) < 0)
-			return -1;
 	}
 	return 0;
 }
 
 int read_tree(void *buffer, unsigned long size, int stage, const char **match)
 {
-	return read_tree_recursive(buffer, size, "", 0, stage, match);
+	return read_tree_recursive(buffer, size, "", 0, stage, match, read_one_entry);
 }
 
 struct tree *lookup_tree(const unsigned char *sha1)
diff --git a/tree.h b/tree.h
index 9975e88..768e5e9 100644
--- a/tree.h
+++ b/tree.h
@@ -35,4 +35,13 @@ int parse_tree(struct tree *tree);
 /* Parses and returns the tree in the given ent, chasing tags and commits. */
 struct tree *parse_tree_indirect(const unsigned char *sha1);
 
+#define READ_TREE_RECURSIVE 1
+typedef int (*read_tree_fn_t)(unsigned char *, const char *, int, const char *, unsigned int, int);
+
+extern int read_tree_recursive(void *buffer, unsigned long size,
+			const char *base, int baselen,
+			int stage, const char **match,
+			read_tree_fn_t fn);
+
+
 #endif /* TREE_H */
Previous: Junio C HamanoNext: Junio C Hamano
Message 54 of 62 in “Get rid of .git/branches/ and .git/remotes/?”
  1. Johannes SchindelinNov 20, 2005
  2. Linus TorvaldsNov 20, 2005
  3. Sven VerdoolaegeNov 20, 2005
  4. Linus TorvaldsNov 20, 2005
  5. Andreas EricssonNov 20, 2005
  6. Johannes SchindelinNov 20, 2005
  7. Petr BaudisNov 26, 2005
  8. Junio C HamanoNov 27, 2005
  9. Josef WeidendorferNov 20, 2005
  10. Johannes SchindelinNov 20, 2005
  11. Josef WeidendorferNov 22, 2005
  12. Johannes SchindelinNov 22, 2005
  13. Andreas EricssonNov 22, 2005
  14. Johannes SchindelinNov 23, 2005
  15. Junio C HamanoNov 23, 2005
  16. Andreas EricssonNov 23, 2005
  17. Johannes SchindelinNov 23, 2005
  18. Andreas EricssonNov 24, 2005
  19. Junio C HamanoNov 24, 2005
  20. Rename git-config-set to git-repo-configJohannes Schindelin, Nov 24, 2005
  21. Junio C HamanoNov 24, 2005
  22. Johannes SchindelinNov 24, 2005
  23. Junio C HamanoNov 24, 2005
  24. Johannes SchindelinNov 24, 2005
  25. Junio C HamanoNov 26, 2005
  26. Linus TorvaldsNov 26, 2005
  27. Linus TorvaldsNov 26, 2005
  28. 0/8 Make C-level operable from subdirectoriesJunio C Hamano, Nov 26, 2005
  29. Junio C HamanoNov 26, 2005
  30. Ryan AndersonNov 26, 2005
  31. Junio C HamanoNov 27, 2005
  32. Petr BaudisNov 27, 2005
  33. Linus TorvaldsNov 27, 2005
  34. Petr BaudisNov 27, 2005
  35. Linus TorvaldsNov 27, 2005
  36. Junio C HamanoNov 28, 2005
  37. Linus TorvaldsNov 28, 2005
  38. Junio C HamanoNov 28, 2005
  39. Linus TorvaldsNov 28, 2005
  40. Junio C HamanoNov 28, 2005
  41. Junio C HamanoNov 28, 2005
  42. ls-tree: Resurrect funny name quoting lost during rewrite.Junio C Hamano, Nov 28, 2005
  43. Junio C HamanoNov 26, 2005
  44. 1/8 git-apply: work from subdirectory.Junio C Hamano, Nov 26, 2005
  45. Linus TorvaldsNov 26, 2005
  46. Junio C HamanoNov 26, 2005
  47. Junio C HamanoNov 27, 2005
  48. Lars Magne IngebrigtsenNov 27, 2005
  49. 2/8 peek-remote: honor proxy config even from subdirectory.Junio C Hamano, Nov 26, 2005
  50. 3/8 fsck-objects: work from subdirectory.Junio C Hamano, Nov 26, 2005
  51. 4/8 checkout-index: work from subdirectory.Junio C Hamano, Nov 26, 2005
  52. 5/8 hash-object: work within subdirectory.Junio C Hamano, Nov 26, 2005
  53. 6/8 ls-tree: work from subdirectory.Junio C Hamano, Nov 26, 2005
  54. Linus TorvaldsNov 26, 2005
  55. 7/8 Make networking commands to work from a subdirectory.Junio C Hamano, Nov 26, 2005
  56. 8/8 Make the rest of commands work from a subdirectory.Junio C Hamano, Nov 26, 2005
  57. Josef WeidendorferNov 22, 2005
  58. Johannes SchindelinNov 23, 2005
  59. Josef WeidendorferNov 23, 2005
  60. Johannes SchindelinNov 23, 2005
  61. Lars Magne IngebrigtsenNov 27, 2005
  62. Junio C HamanoNov 27, 2005

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.