Subject: [PATCH 13/16] ls: add --recursive and turn default to non-recursive mode

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Wed, 9 Feb 2011 19:24:41 +0700

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 This patch should be after 14/16. Otherwise the requirement in path_too_deep()
 is just wrong. Too late to reorder the series for sending.

 builtin/ls-files.c |   54 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 52 insertions(+), 2 deletions(-)

diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 3195f75..87ee728 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -27,6 +27,7 @@ static int show_killed;
 static int show_valid_bit;
 static int line_terminator = '\n';
 static int debug_mode;
+static int depth_limit;
 
 static const char *prefix;
 static int max_prefix_len;
@@ -63,18 +64,54 @@ static void write_name(const char* name, size_t len)
 			line_terminator);
 }
 
+static const char *path_too_deep(const char *name)
+{
+	int common = 0;
+	if (!depth_limit)
+		return NULL;
+
+	/* When depth_limit is set, pathspec contains exactly 1 item */
+	while (name[common] &&
+	       name[common] == pathspec[0][common])
+		common++;
+	while (common && name[common] != '/')
+		common--;
+	if (name[common] == '/')
+		common++;
+	return strchr(name + common, '/');
+}
+
+static int already_shown(const char *name)
+{
+	const char *last_item;
+	int len;
+
+	if (!output.nr)
+		return 0;
+
+	last_item = output.items[output.nr-1].string;
+	len = strlen(last_item);
+	return !strncmp(last_item, name, len) && name[len] == '/';
+}
+
 static void show_dir_entry(const char *tag, struct dir_entry *ent)
 {
 	int len = max_prefix_len;
+	const char *too_deep;
 
 	if (len >= ent->len)
 		die("git ls-files: internal error - directory entry not superset of prefix");
 
+	if (already_shown(ent->name))
+		return;
+
+	too_deep = path_too_deep(ent->name);
 	if (!match_pathspec(pathspec, ent->name, ent->len, len, ps_matched))
 		return;
 
+	len = too_deep ? too_deep - ent->name : ent->len;
 	fputs(tag, stdout);
-	write_name(ent->name, ent->len);
+	write_name(ent->name, len);
 }
 
 static void show_other_files(struct dir_struct *dir)
@@ -141,12 +178,19 @@ static void show_killed_files(struct dir_struct *dir)
 static void show_ce_entry(const char *tag, struct cache_entry *ce)
 {
 	int len = max_prefix_len;
+	const char *too_deep;
+	int namelen;
 
 	if (len >= ce_namelen(ce))
 		die("git ls-files: internal error - cache entry not superset of prefix");
 
+	if (already_shown(ce->name))
+		return;
+
+	too_deep = path_too_deep(ce->name);
 	if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), len, ps_matched))
 		return;
+	namelen = too_deep ? too_deep - ce->name : ce_namelen(ce);
 
 	if (tag && *tag && show_valid_bit &&
 	    (ce->ce_flags & CE_VALID)) {
@@ -174,7 +218,7 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
 		       find_unique_abbrev(ce->sha1,abbrev),
 		       ce_stage(ce));
 	}
-	write_name(ce->name, ce_namelen(ce));
+	write_name(ce->name, namelen);
 	if (debug_mode) {
 		printf("  ctime: %d:%d\n", ce->ce_ctime.sec, ce->ce_ctime.nsec);
 		printf("  mtime: %d:%d\n", ce->ce_mtime.sec, ce->ce_mtime.nsec);
@@ -635,6 +679,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
 int cmd_ls(int argc, const char **argv, const char *cmd_prefix)
 {
 	struct dir_struct dir;
+	int recursive = 0;
 	struct option builtin_ls_files_options[] = {
 		OPT_BOOLEAN('c', "cached", &show_cached,
 			"show cached files in the output (default)"),
@@ -647,6 +692,8 @@ int cmd_ls(int argc, const char **argv, const char *cmd_prefix)
 		OPT_BIT('i', "ignored", &dir.flags,
 			"show ignored files in the output",
 			DIR_SHOW_IGNORED),
+		OPT_BOOLEAN('r', "recursive", &recursive,
+			    "list recursively"),
 		OPT_BOOLEAN('k', "killed", &show_killed,
 			"show files on the filesystem that need to be removed"),
 		OPT_COLUMN(0, "column", &column_mode, "show files in columns" ),
@@ -683,6 +730,9 @@ int cmd_ls(int argc, const char **argv, const char *cmd_prefix)
 	      show_killed | show_modified | show_resolve_undo))
 		show_cached = 1;
 
+	if (!recursive)
+		depth_limit = 1;
+
 	show_files(&dir);
 	display_columns(&output, column_mode, term_columns(), 2, NULL);
 	return 0;
-- 
1.7.2.2