git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] ls-tree path restriction semantics fixes
@ 2005-05-27 12:08 Jason McMullan
  2005-05-27 18:16 ` Junio C Hamano
  0 siblings, 1 reply; 7+ messages in thread
From: Jason McMullan @ 2005-05-27 12:08 UTC (permalink / raw)
  To: git

This patch fixes the git-ls-tree semantics to be less stupid, namely:
	
	* ls of a 'tree' path should just return the SHA1 of the tree
	* ls of a 'tree' path with a trailing '/' should work properly
	* ls of two identical paths should have the same output as ls of
	  a single path. (I consider ls-tree's output to be a hash dictionary)

Also, I added test cases to verify that these issues are fixed.

Old Results:

	$ git-ls-tree t
	040000 tree 4eeb3990955b8badc4c14712b89d8cd9fff02f15    t
	100644 blob 6882e23be568ccf14f3adb0c766139086f2ee952    t/Makefile
	100644 blob 2a94fdb0b83ab5fcbf1a2c6edaf36c2dbe765ec6    t/README
	100644 blob d920c6b3a3bfbb5994244a78d1ad99ce02748122    t/lib-read-tree-m-3way.sh
	...

	$ git-ls-tree t/
	(no output)

	$ git-ls-tree t t
	040000 tree 4eeb3990955b8badc4c14712b89d8cd9fff02f15    t

New Results:

	$ git-ls-tree f
	040000 tree 4eeb3990955b8badc4c14712b89d8cd9fff02f15    t

	$ git-ls-tree t/
	040000 tree 4eeb3990955b8badc4c14712b89d8cd9fff02f15    t

	$ git-ls-tree t t
	040000 tree 4eeb3990955b8badc4c14712b89d8cd9fff02f15    t

Signed-Off-By: Jason McMullan <jason.mcmullan@timesys.com>

diff --git a/ls-tree.c b/ls-tree.c
--- a/ls-tree.c
+++ b/ls-tree.c
@@ -13,8 +13,6 @@ struct path_prefix {
 	const char *name;
 };
 
-#define DEBUG(fmt, ...)	
-
 static int string_path_prefix(char *buff, size_t blen, struct path_prefix *prefix)
 {
 	int len = 0;
@@ -118,6 +116,8 @@ static void list_recursive(void *buffer,
 			mtype = pathcmp(match[mindex],&this_prefix);
 			if (mtype >= 0) {
 				matched = match[mindex];
+				/* Skip over any duplicates */
+				for (; mindex+1 < matches && strcmp(match[mindex+1],matched)==0; mindex++);
 				break;
 			}
 		}
@@ -140,19 +140,22 @@ static void list_recursive(void *buffer,
 		if (matches && ! matched)
 			continue;
 
-		if (! (eltbuf = read_sha1_file(sha1, elttype, &eltsize)) ) {
-			error("cannot read %s", sha1_to_hex(sha1));
-			continue;
-		}
-
 		/* If this is an exact directory match, we may have
 		 * directory files following this path. Match on them.
-		 * Otherwise, we're at a pach subcomponent, and we need
+		 * Otherwise, we're at a path subcomponent, and we need
 		 * to try to match again.
 		 */
 		if (mtype == 0)
 			mindex++;
 
+		if (matched && matches-mindex==0)
+			continue;
+
+		if (! (eltbuf = read_sha1_file(sha1, elttype, &eltsize)) ) {
+			error("cannot read %s", sha1_to_hex(sha1));
+			continue;
+		}
+
 		list_recursive(eltbuf, elttype, eltsize, &this_prefix, &match[mindex], matches-mindex);
 		free(eltbuf);
 	}
@@ -169,9 +172,14 @@ static int list(unsigned char *sha1,char
 	unsigned long size;
 	int npaths;
 
-	for (npaths = 0; path[npaths] != NULL; npaths++)
-		;
+	/* Count the paths, and any trailling '/' */
+	for (npaths = 0; path[npaths] != NULL; npaths++) {
+		char *cp = strrchr(path[npaths],'/');
+		if (cp != NULL && *(cp+1) == 0)
+			*cp=0;
+	}	
 
+	/* Sort the paths */
 	qsort(path,npaths,sizeof(char *),qcmp);
 
 	buffer = read_object_with_reference(sha1, "tree", &size, NULL);
diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh
--- a/t/t3100-ls-tree-restrict.sh
+++ b/t/t3100-ls-tree-restrict.sh
@@ -84,10 +84,22 @@ test_expect_success \
     'git-ls-tree $tree path2 >current &&
      cat >expected <<\EOF &&
 040000 tree X	path2
-040000 tree X	path2/baz
-100644 blob X	path2/baz/b
-120000 blob X	path2/bazbo
-100644 blob X	path2/foo
+EOF
+     test_output'
+
+test_expect_success \
+    'ls-tree filtered' \
+    'git-ls-tree $tree path2/ >current &&
+     cat >expected <<\EOF &&
+040000 tree X	path2
+EOF
+     test_output'
+
+test_expect_success \
+    'ls-tree filtered' \
+    'git-ls-tree $tree path2 path2 >current &&
+     cat >expected <<\EOF &&
+040000 tree X	path2
 EOF
      test_output'
 
@@ -96,7 +108,16 @@ test_expect_success \
     'git-ls-tree $tree path2/baz >current &&
      cat >expected <<\EOF &&
 040000 tree X	path2/baz
-100644 blob X	path2/baz/b
+EOF
+     test_output'
+
+test_expect_success \
+    'ls-tree filtered' \
+    'git-ls-tree $tree path2 path2/bazbo path2/baz >current &&
+     cat >expected <<\EOF &&
+040000 tree X	path2
+040000 tree X	path2/baz
+120000 blob X	path2/bazbo
 EOF
      test_output'
 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] ls-tree path restriction semantics fixes
  2005-05-27 12:08 [PATCH] ls-tree path restriction semantics fixes Jason McMullan
@ 2005-05-27 18:16 ` Junio C Hamano
  2005-05-27 19:26   ` Jason McMullan
  0 siblings, 1 reply; 7+ messages in thread
From: Junio C Hamano @ 2005-05-27 18:16 UTC (permalink / raw)
  To: Jason McMullan; +Cc: git

>>>>> "JM" == Jason McMullan <jason.mcmullan@timesys.com> writes:

JM> This patch fixes the git-ls-tree semantics to be less stupid, namely:
JM> 	* ls of a 'tree' path should just return the SHA1 of the tree
JM> 	* ls of a 'tree' path with a trailing '/' should work properly
JM> 	* ls of two identical paths should have the same output as ls of
JM> 	  a single path. (I consider ls-tree's output to be a hash dictionary)

I haven't read your code yet, but...

JM> Old Results:

JM> 	$ git-ls-tree t
JM> 	040000 tree 4eeb3990955b8badc4c14712b89d8cd9fff02f15    t
JM> 	100644 blob 6882e23be568ccf14f3adb0c766139086f2ee952    t/Makefile
JM> 	100644 blob 2a94fdb0b83ab5fcbf1a2c6edaf36c2dbe765ec6    t/README
JM> 	100644 blob d920c6b3a3bfbb5994244a78d1ad99ce02748122    t/lib-read-tree-m-3way.sh
JM> 	...

I presume the counterpart to this one in your "New Results"
example, which is spelled "git-ls-tree f" is a typo of
"git-ls-tree t", but if that is the case I strongly disagree.

What you really want is something similar to '-d' flag to
/bin/ls.  You are interested in the directory itself not its
contents and I think your gripe is that giving a path that
matches a tree always descends into it (i.e. there is no way to
do the equivalent of "/bin/ls -d t").  I agree that it is a
problem, but changing "/bin/ls t" not to show the directory
contents of "t" is not a solution.

JM> 	$ git-ls-tree t/
JM> 	(no output)

I agree with you that this is not what we want and we should
behave the same way as "git-ls-tree t" would in this case.

JM> 	$ git-ls-tree t t
JM> 	040000 tree 4eeb3990955b8badc4c14712b89d8cd9fff02f15    t

I do not know what you wanted to say in this example.  Your
"Old" and "New" look the same to me.






^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] ls-tree path restriction semantics fixes
  2005-05-27 18:16 ` Junio C Hamano
@ 2005-05-27 19:26   ` Jason McMullan
  2005-05-28  3:31     ` Junio C Hamano
  0 siblings, 1 reply; 7+ messages in thread
From: Jason McMullan @ 2005-05-27 19:26 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

[-- Attachment #1: Type: text/plain, Size: 1147 bytes --]

On Fri, 2005-05-27 at 11:16 -0700, Junio C Hamano wrote:
> What you really want is something similar to '-d' flag to
> /bin/ls.  You are interested in the directory itself not its
> contents and I think your gripe is that giving a path that
> matches a tree always descends into it (i.e. there is no way to
> do the equivalent of "/bin/ls -d t").  I agree that it is a
> problem, but changing "/bin/ls t" not to show the directory
> contents of "t" is not a solution.

  git-ls-tree reporting just the tree's hash is valid, because if
you want everything in that tree, you can just do:

git-ls-tree `git-ls-tree HEAD path/dir | (read m t h n; echo $h)`

  I don't see the problem there.


> JM> 	$ git-ls-tree t t
> JM> 	040000 tree 4eeb3990955b8badc4c14712b89d8cd9fff02f15    t
> 
> I do not know what you wanted to say in this example.  Your
> "Old" and "New" look the same to me.

The problem was that 't' and 't t' produced *vastly* different output
in the old code. 't' would emit everything in the tree, and 't t' would
only emit t's hash.

-- 
Jason McMullan <jason.mcmullan@timesys.com>
TimeSys Corporation


[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] ls-tree path restriction semantics fixes
  2005-05-27 19:26   ` Jason McMullan
@ 2005-05-28  3:31     ` Junio C Hamano
  2005-05-28  7:05       ` [PATCH-RFC] Rewrite ls-tree to behave more like "/bin/ls -a" Junio C Hamano
  0 siblings, 1 reply; 7+ messages in thread
From: Junio C Hamano @ 2005-05-28  3:31 UTC (permalink / raw)
  To: Jason McMullan; +Cc: git

>>>>> "JM" == Jason McMullan <jason.mcmullan@timesys.com> writes:

JM>   git-ls-tree reporting just the tree's hash is valid, because if
JM> you want everything in that tree, you can just do:

JM> git-ls-tree `git-ls-tree HEAD path/dir | (read m t h n; echo $h)`

JM>   I don't see the problem there.

I do not see the problem either in Turing sense, but that is
like saying you could code anything given an assembler.  There
is a difference between being possible and being practical.

I do think the current behaviour is broken, so I think we are in
half agreement.  What I think is the cleanest would be to make
"git-ls-tree $tree" behave similarly to what "/bin/ls -a" does.
Then we have various combination of options, and also path
arguments, to think about.  How about doing something like this?

 - Running without any paths.

   "git-ls-tree $tree" shows everything first level, just like
   "/bin/ls -a" shows everything in cwd.  There is nothing to
   fix here.

 - Running with paths.

   "git-ls-tree $tree path1 path2..." should show path$n if
   path$n is not a tree and everything under path$n including
   path$n itself if path$n is a tree, just like the way "/bin/ls
   -a path1 path2..." works.  There is major breakage here as
   you pointed out with your "git-ls-tree $tree t" vs
   "git-ls-tree $tree t t" example.

 - Recursive behaviour without paths.

   "git-ls-tree -r $tree" should show everything recursively,
   just like what "/bin/ls -a -R" does.  There is nothing to
   fix.

 - Recursive behaviour with paths.

   "git-ls-tree -r $tree path1 path2..." should show everything
   recursively under path$n, just like what "/bin/ls -a -R path1
   path2..." does.  Again this is not how it currently works as
   you pointed out.

 - With paths but not descending into them.

   "git-ls-tree -d $tree path1 path2..." should show only the
   named path$n even when path$n is a tree, just like what
   "/bin/ls -a -R -d path1 path2..." does.  This is what is
   missing from today's git-ls-tree.


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH-RFC] Rewrite ls-tree to behave more like "/bin/ls -a"
  2005-05-28  3:31     ` Junio C Hamano
@ 2005-05-28  7:05       ` Junio C Hamano
  2005-05-28 22:02         ` Jason McMullan
  0 siblings, 1 reply; 7+ messages in thread
From: Junio C Hamano @ 2005-05-28  7:05 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Jason McMullan, git

This is a complete rewrite of ls-tree to make it behave more
like what "/bin/ls -a" does in the current working directory.

Namely, the changes are:

 - Unlike the old ls-tree behaviour that used paths arguments to
   restrict output (not that it worked as intended---as pointed
   out in the mailing list discussion, it was quite incoherent),
   this rewrite uses paths arguments to specify what to show.

 - Without arguments, it implicitly uses the root level as its
   sole argument ("/bin/ls -a" behaves as if "." is given
   without argument).

 - Without -r (recursive) flag, it shows the named blob (either
   file or symlink), or the named tree and its immediate
   children.

 - With -r flag, it shows the named path, and recursively
   descends into it if it is a tree.

 - With -d flag, it shows the named path and does not show its
   children even if the path is a tree, nor descends into it
   recursively.

This is still request-for-comments patch.  There is no mailing
list consensus that this proposed new behaviour is a good one.

The patch to t/t3100-ls-tree-restrict.sh illustrates
user-visible behaviour changes.  Namely:

 * "git-ls-tree $tree path1 path0" lists path1 first and then
   path0.  It used to use paths as an output restrictor and
   showed output in cache entry order (i.e. path0 first and then
   path1) regardless of the order of paths arguments.

 * "git-ls-tree $tree path2" lists path2 and its immediate
   children but having explicit paths argument does not imply
   recursive behaviour anymore, hence paths/baz is shown but not
   paths/baz/b.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---

Documentation/git-ls-tree.txt |   20 +-
ls-tree.c                     |  333 +++++++++++++++++++++++-------------------
t/t3100-ls-tree-restrict.sh   |    3 
tree.c                        |    2 
tree.h                        |    1 
5 files changed, 199 insertions(+), 160 deletions(-)

diff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt
--- a/Documentation/git-ls-tree.txt
+++ b/Documentation/git-ls-tree.txt
@@ -4,23 +4,26 @@ v0.1, May 2005
 
 NAME
 ----
-git-ls-tree - Displays a tree object in human readable form
+git-ls-tree - Lists the contents of a tree object.
 
 
 SYNOPSIS
 --------
-'git-ls-tree' [-r] [-z] <tree-ish> [paths...]
+'git-ls-tree' [-d] [-r] [-z] <tree-ish> [paths...]
 
 DESCRIPTION
 -----------
-Converts the tree object to a human readable (and script processable)
-form.
+Lists the contents of a tree object, like what "/bin/ls -a" does
+in the current working directory.
 
 OPTIONS
 -------
 <tree-ish>::
 	Id of a tree.
 
+-d::
+	show only the named tree entry itself, not its children
+
 -r::
 	recurse into sub-trees
 
@@ -28,18 +31,19 @@ OPTIONS
 	\0 line termination on output
 
 paths::
-	Optionally, restrict the output of git-ls-tree to specific
-	paths. Directories will only list their tree blob ids.
-	Implies -r.
+	When paths are given, shows them.  Otherwise implicitly
+	uses the root level of the tree as the sole path argument.
+
 
 Output Format
 -------------
-        <mode>\t	<type>\t	<object>\t	<file>
+        <mode> SP <type> SP <object> TAB <file>
 
 
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>
+Completely rewritten from scratch by Junio C Hamano <junkio@cox.net>
 
 Documentation
 --------------
diff --git a/ls-tree.c b/ls-tree.c
--- a/ls-tree.c
+++ b/ls-tree.c
@@ -4,188 +4,217 @@
  * Copyright (C) Linus Torvalds, 2005
  */
 #include "cache.h"
+#include "blob.h"
+#include "tree.h"
 
 static int line_termination = '\n';
-static int recursive = 0;
+#define LS_RECURSIVE 1
+#define LS_TREE_ONLY 2
+static int ls_options = 0;
 
-struct path_prefix {
-	struct path_prefix *prev;
-	const char *name;
-};
-
-#define DEBUG(fmt, ...)	
-
-static int string_path_prefix(char *buff, size_t blen, struct path_prefix *prefix)
-{
-	int len = 0;
-	if (prefix) {
-		if (prefix->prev) {
-			len = string_path_prefix(buff,blen,prefix->prev);
-			buff += len;
-			blen -= len;
-			if (blen > 0) {
-				*buff = '/';
-				len++;
-				buff++;
-				blen--;
-			}
-		}
-		strncpy(buff,prefix->name,blen);
-		return len + strlen(prefix->name);
-	}
+static struct tree_entry_list root_entry;
 
-	return 0;
+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 void print_path_prefix(struct path_prefix *prefix)
+static int prepare_children(struct tree_entry_list *elem)
 {
-	if (prefix) {
-		if (prefix->prev) {
-			print_path_prefix(prefix->prev);
-			putchar('/');
-		}
-		fputs(prefix->name, stdout);
+	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;
 }
 
-/*
- * return:
- * 	-1 if prefix is *not* a subset of path
- * 	 0 if prefix == path
- * 	 1 if prefix is a subset of path
- */
-static int pathcmp(const char *path, struct path_prefix *prefix)
-{
-	char buff[PATH_MAX];
-	int len,slen;
+static struct tree_entry_list *find_entry_0(struct tree_entry_list *elem,
+					    const char *path,
+					    const char *path_end)
+{
+	const char *ep;
+	int len;
+
+	while (path < path_end) {
+		if (prepare_children(elem))
+			return NULL;
 
-	if (prefix == NULL)
-		return 1;
+		/* In elem->tree->entries, find the one that has name
+		 * that matches what is between path and ep.
+		 */
+		elem = elem->item.tree->entries;
 
-	len = string_path_prefix(buff, sizeof buff, prefix);
-	slen = strlen(path);
+		ep = strchr(path, '/');
+		if (!ep || path_end <= ep)
+			ep = path_end;
+		len = ep - path;
+
+		while (elem) {
+			if ((strlen(elem->name) == len) &&
+			    !strncmp(elem->name, path, len))
+				break;
+			elem = elem->next;
+		}
+		if (path_end <= ep || !elem)
+			return elem;
+		while (*ep == '/' && ep < path_end)
+			ep++;
+		path = ep;
+	}
+	return NULL;
+}
 
-	if (slen < len)
-		return -1;
+static struct tree_entry_list *find_entry(const char *path,
+					  const char *path_end)
+{
+	/* Find tree element, descending from root, that
+	 * corresponds to the named path, lazily expanding
+	 * the tree if possible.
+	 */
+	if (path == path_end) {
+		/* Special.  This is the root level */
+		return &root_entry;
+	}
+	return find_entry_0(&root_entry, path, path_end);
+}
 
-	if (strncmp(path,buff,len) == 0) {
-		if (slen == len)
-			return 0;
-		else
-			return 1;
+static void show_entry_name(struct tree_entry_list *e)
+{
+	/* This is yucky.  The root level is there for
+	 * our convenience but we really want to do a
+	 * forest.
+	 */
+	if (e->parent && e->parent != &root_entry) {
+		show_entry_name(e->parent);
+		putchar('/');
 	}
+	printf("%s", e->name);
+}
 
-	return -1;
-}	
+static const char *entry_type(struct tree_entry_list *e)
+{
+	return (e->directory ? "tree" : "blob");
+}
 
-/*
- * match may be NULL, or a *sorted* list of paths
- */
-static void list_recursive(void *buffer,
-			   const char *type,
-			   unsigned long size,
-			   struct path_prefix *prefix,
-			   char **match, int matches)
-{
-	struct path_prefix this_prefix;
-	this_prefix.prev = prefix;
-
-	if (strcmp(type, "tree"))
-		die("expected a 'tree' node");
-
-	if (matches)
-		recursive = 1;
-
-	while (size) {
-		int namelen = strlen(buffer)+1;
-		void *eltbuf = NULL;
-		char elttype[20];
-		unsigned long eltsize;
-		unsigned char *sha1 = buffer + namelen;
-		char *path = strchr(buffer, ' ') + 1;
-		unsigned int mode;
-		const char *matched = NULL;
-		int mtype = -1;
-		int mindex;
-
-		if (size < namelen + 20 || sscanf(buffer, "%o", &mode) != 1)
-			die("corrupt 'tree' file");
-		buffer = sha1 + 20;
-		size -= namelen + 20;
-
-		this_prefix.name = path;
-		for ( mindex = 0; mindex < matches; mindex++) {
-			mtype = pathcmp(match[mindex],&this_prefix);
-			if (mtype >= 0) {
-				matched = match[mindex];
-				break;
-			}
-		}
+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);
+}
 
-		/*
-		 * If we're not matching, or if this is an exact match,
-		 * print out the info
-		 */
-		if (!matches || (matched != NULL && mtype == 0)) {
-			printf("%06o %s %s\t", mode,
-			       S_ISDIR(mode) ? "tree" : "blob",
-			       sha1_to_hex(sha1));
-			print_path_prefix(&this_prefix);
-			putchar(line_termination);
-		}
+/* forward declaration for mutually recursive routines */
+static int show_entry(struct tree_entry_list *, int);
 
-		if (! recursive || ! S_ISDIR(mode))
-			continue;
+static int show_children(struct tree_entry_list *e, int level)
+{
+	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);
+		e = e->next;
+	}
+	return 0;
+}
 
-		if (matches && ! matched)
-			continue;
+static int show_entry(struct tree_entry_list *e, int level)
+{
+	int err = 0; 
 
-		if (! (eltbuf = read_sha1_file(sha1, elttype, &eltsize)) ) {
-			error("cannot read %s", sha1_to_hex(sha1));
-			continue;
-		}
+	if (e != &root_entry) {
+		printf("%06o %s %s	", e->mode, entry_type(e),
+		       entry_hex(e));
+		show_entry_name(e);
+		putchar(line_termination);
+	}
 
-		/* If this is an exact directory match, we may have
-		 * directory files following this path. Match on them.
-		 * Otherwise, we're at a pach subcomponent, and we need
-		 * to try to match again.
+	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 (mtype == 0)
-			mindex++;
-
-		list_recursive(eltbuf, elttype, eltsize, &this_prefix, &match[mindex], matches-mindex);
-		free(eltbuf);
+		if (level == 0 && !(ls_options & LS_TREE_ONLY))
+			/* case (1)-a and (1)-b */
+			err = err | show_children(e, level+1);
+		else if (level && ls_options & LS_RECURSIVE)
+			/* case (2)-b */
+			err = err | show_children(e, level+1);
 	}
+	return err;
 }
 
-static int qcmp(const void *a, const void *b)
+static int list_one(const char *path, const char *path_end)
 {
-	return strcmp(*(char **)a, *(char **)b);
+	int err = 0;
+	struct tree_entry_list *e = find_entry(path, path_end);
+	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);
+	return err;
 }
 
-static int list(unsigned char *sha1,char **path)
+static int list(char **path)
 {
-	void *buffer;
-	unsigned long size;
-	int npaths;
-
-	for (npaths = 0; path[npaths] != NULL; npaths++)
-		;
-
-	qsort(path,npaths,sizeof(char *),qcmp);
-
-	buffer = read_object_with_reference(sha1, "tree", &size, NULL);
-	if (!buffer)
-		die("unable to read sha1 file");
-	list_recursive(buffer, "tree", size, NULL, path, npaths);
-	free(buffer);
-	return 0;
+	int i;
+	int err = 0;
+	for (i = 0; path[i]; i++) {
+		int len = strlen(path[i]);
+		while (0 <= len && path[i][len] == '/')
+			len--;
+		err = err | list_one(path[i], path[i] + len);
+	}
+	return err;
 }
 
-static const char *ls_tree_usage = "git-ls-tree [-r] [-z] <key> [paths...]";
+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;
 	unsigned char sha1[20];
 
 	while (1 < argc && argv[1][0] == '-') {
@@ -194,7 +223,10 @@ int main(int argc, char **argv)
 			line_termination = 0;
 			break;
 		case 'r':
-			recursive = 1;
+			ls_options |= LS_RECURSIVE;
+			break;
+		case 'd':
+			ls_options |= LS_TREE_ONLY;
 			break;
 		default:
 			usage(ls_tree_usage);
@@ -206,7 +238,10 @@ int main(int argc, char **argv)
 		usage(ls_tree_usage);
 	if (get_sha1(argv[1], sha1) < 0)
 		usage(ls_tree_usage);
-	if (list(sha1, &argv[2]) < 0)
+
+	path = (argc == 2) ? path0 : (argv + 2);
+	prepare_root(sha1);
+	if (list(path) < 0)
 		die("list failed");
 	return 0;
 }
diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh
--- a/t/t3100-ls-tree-restrict.sh
+++ b/t/t3100-ls-tree-restrict.sh
@@ -74,8 +74,8 @@ test_expect_success \
     'ls-tree filtered' \
     'git-ls-tree $tree path1 path0 >current &&
      cat >expected <<\EOF &&
-100644 blob X	path0
 120000 blob X	path1
+100644 blob X	path0
 EOF
      test_output'
 
@@ -85,7 +85,6 @@ test_expect_success \
      cat >expected <<\EOF &&
 040000 tree X	path2
 040000 tree X	path2/baz
-100644 blob X	path2/baz/b
 120000 blob X	path2/bazbo
 100644 blob X	path2/foo
 EOF
diff --git a/tree.c b/tree.c
--- a/tree.c
+++ b/tree.c
@@ -133,7 +133,7 @@ int parse_tree_buffer(struct tree *item,
 		}
 		if (obj)
 			add_ref(&item->object, obj);
-
+		entry->parent = NULL; /* needs to be filled by the user */
 		*list_p = entry;
 		list_p = &entry->next;
 	}
diff --git a/tree.h b/tree.h
--- a/tree.h
+++ b/tree.h
@@ -16,6 +16,7 @@ struct tree_entry_list {
 		struct tree *tree;
 		struct blob *blob;
 	} item;
+	struct tree_entry_list *parent;
 };
 
 struct tree {
------------------------------------------------


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH-RFC] Rewrite ls-tree to behave more like "/bin/ls -a"
  2005-05-28  7:05       ` [PATCH-RFC] Rewrite ls-tree to behave more like "/bin/ls -a" Junio C Hamano
@ 2005-05-28 22:02         ` Jason McMullan
  2005-05-29 18:44           ` Junio C Hamano
  0 siblings, 1 reply; 7+ messages in thread
From: Jason McMullan @ 2005-05-28 22:02 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Linus Torvalds, git

[-- Attachment #1: Type: text/plain, Size: 1106 bytes --]

On Sat, 2005-05-28 at 00:05 -0700, Junio C Hamano wrote:
>  - Unlike the old ls-tree behaviour that used paths arguments to
>    restrict output (not that it worked as intended---as pointed
>    out in the mailing list discussion, it was quite incoherent),
>    this rewrite uses paths arguments to specify what to show.
> 
>  - Without arguments, it implicitly uses the root level as its
>    sole argument ("/bin/ls -a" behaves as if "." is given
>    without argument).
> 
>  - Without -r (recursive) flag, it shows the named blob (either
>    file or symlink), or the named tree and its immediate
>    children.
> 
>  - With -r flag, it shows the named path, and recursively
>    descends into it if it is a tree.
> 
>  - With -d flag, it shows the named path and does not show its
>    children even if the path is a tree, nor descends into it
>    recursively.

This behavior pattern is very agreeable. I'll take it!

Consider your patch:

Signed-Off-By: Jason McMullan <jason.mcmullan@timesys.com>

-- 
Jason McMullan <jason.mcmullan@timesys.com>
TimeSys Corporation


[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH-RFC] Rewrite ls-tree to behave more like "/bin/ls -a"
  2005-05-28 22:02         ` Jason McMullan
@ 2005-05-29 18:44           ` Junio C Hamano
  0 siblings, 0 replies; 7+ messages in thread
From: Junio C Hamano @ 2005-05-29 18:44 UTC (permalink / raw)
  To: Jason McMullan; +Cc: git

>>>>> "JM" == Jason McMullan <jason.mcmullan@timesys.com> writes:

JM> On Sat, 2005-05-28 at 00:05 -0700, Junio C Hamano wrote:

>> - Unlike the old ls-tree behaviour that used paths arguments to
>> restrict output (not that it worked as intended---as pointed
>> out in the mailing list discussion, it was quite incoherent),
>> this rewrite uses paths arguments to specify what to show.
>> ...

JM> This behavior pattern is very agreeable. I'll take it!

Glad to know we are in agreement.

BTW, long after finishing the rewrite, I realized that all of
the problems you raised did not exist in the very original
version of ls-tree, but were bugs in _your_ patch that added the
paths restriction.  I was merely cleaning up your mess for you
without knowing what I was doing. ;-) Not that I do not like
what the resulting code does, though.

I am not going to re-submit this to Linus right now, since he
seems to be quiet here and spending more time and attention to
the kernel, which is what I want to see.  When Linus starts
pulling in update for GIT, and if you see this one not applied
to his tree, please remind him.


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2005-05-29 18:42 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-05-27 12:08 [PATCH] ls-tree path restriction semantics fixes Jason McMullan
2005-05-27 18:16 ` Junio C Hamano
2005-05-27 19:26   ` Jason McMullan
2005-05-28  3:31     ` Junio C Hamano
2005-05-28  7:05       ` [PATCH-RFC] Rewrite ls-tree to behave more like "/bin/ls -a" Junio C Hamano
2005-05-28 22:02         ` Jason McMullan
2005-05-29 18:44           ` Junio C Hamano

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).