about summary refs log tree commit diff
path: root/third_party/git/builtin/ls-tree.c
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/git/builtin/ls-tree.c')
-rw-r--r--third_party/git/builtin/ls-tree.c190
1 files changed, 190 insertions, 0 deletions
diff --git a/third_party/git/builtin/ls-tree.c b/third_party/git/builtin/ls-tree.c
new file mode 100644
index 0000000000..7cad3f24eb
--- /dev/null
+++ b/third_party/git/builtin/ls-tree.c
@@ -0,0 +1,190 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#include "cache.h"
+#include "config.h"
+#include "object-store.h"
+#include "blob.h"
+#include "tree.h"
+#include "commit.h"
+#include "quote.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "pathspec.h"
+
+static int line_termination = '\n';
+#define LS_RECURSIVE 1
+#define LS_TREE_ONLY 2
+#define LS_SHOW_TREES 4
+#define LS_NAME_ONLY 8
+#define LS_SHOW_SIZE 16
+static int abbrev;
+static int ls_options;
+static struct pathspec pathspec;
+static int chomp_prefix;
+static const char *ls_tree_prefix;
+
+static const  char * const ls_tree_usage[] = {
+	N_("git ls-tree [<options>] <tree-ish> [<path>...]"),
+	NULL
+};
+
+static int show_recursive(const char *base, int baselen, const char *pathname)
+{
+	int i;
+
+	if (ls_options & LS_RECURSIVE)
+		return 1;
+
+	if (!pathspec.nr)
+		return 0;
+
+	for (i = 0; i < pathspec.nr; i++) {
+		const char *spec = pathspec.items[i].match;
+		int len, speclen;
+
+		if (strncmp(base, spec, baselen))
+			continue;
+		len = strlen(pathname);
+		spec += baselen;
+		speclen = strlen(spec);
+		if (speclen <= len)
+			continue;
+		if (spec[len] && spec[len] != '/')
+			continue;
+		if (memcmp(pathname, spec, len))
+			continue;
+		return 1;
+	}
+	return 0;
+}
+
+static int show_tree(const struct object_id *oid, struct strbuf *base,
+		const char *pathname, unsigned mode, int stage, void *context)
+{
+	int retval = 0;
+	int baselen;
+	const char *type = blob_type;
+
+	if (S_ISGITLINK(mode)) {
+		/*
+		 * Maybe we want to have some recursive version here?
+		 *
+		 * Something similar to this incomplete example:
+		 *
+		if (show_subprojects(base, baselen, pathname))
+			retval = READ_TREE_RECURSIVE;
+		 *
+		 */
+		type = commit_type;
+	} else if (S_ISDIR(mode)) {
+		if (show_recursive(base->buf, base->len, pathname)) {
+			retval = READ_TREE_RECURSIVE;
+			if (!(ls_options & LS_SHOW_TREES))
+				return retval;
+		}
+		type = tree_type;
+	}
+	else if (ls_options & LS_TREE_ONLY)
+		return 0;
+
+	if (!(ls_options & LS_NAME_ONLY)) {
+		if (ls_options & LS_SHOW_SIZE) {
+			char size_text[24];
+			if (!strcmp(type, blob_type)) {
+				unsigned long size;
+				if (oid_object_info(the_repository, oid, &size) == OBJ_BAD)
+					xsnprintf(size_text, sizeof(size_text),
+						  "BAD");
+				else
+					xsnprintf(size_text, sizeof(size_text),
+						  "%"PRIuMAX, (uintmax_t)size);
+			} else
+				xsnprintf(size_text, sizeof(size_text), "-");
+			printf("%06o %s %s %7s\t", mode, type,
+			       find_unique_abbrev(oid, abbrev),
+			       size_text);
+		} else
+			printf("%06o %s %s\t", mode, type,
+			       find_unique_abbrev(oid, abbrev));
+	}
+	baselen = base->len;
+	strbuf_addstr(base, pathname);
+	write_name_quoted_relative(base->buf,
+				   chomp_prefix ? ls_tree_prefix : NULL,
+				   stdout, line_termination);
+	strbuf_setlen(base, baselen);
+	return retval;
+}
+
+int cmd_ls_tree(int argc, const char **argv, const char *prefix)
+{
+	struct object_id oid;
+	struct tree *tree;
+	int i, full_tree = 0;
+	const struct option ls_tree_options[] = {
+		OPT_BIT('d', NULL, &ls_options, N_("only show trees"),
+			LS_TREE_ONLY),
+		OPT_BIT('r', NULL, &ls_options, N_("recurse into subtrees"),
+			LS_RECURSIVE),
+		OPT_BIT('t', NULL, &ls_options, N_("show trees when recursing"),
+			LS_SHOW_TREES),
+		OPT_SET_INT('z', NULL, &line_termination,
+			    N_("terminate entries with NUL byte"), 0),
+		OPT_BIT('l', "long", &ls_options, N_("include object size"),
+			LS_SHOW_SIZE),
+		OPT_BIT(0, "name-only", &ls_options, N_("list only filenames"),
+			LS_NAME_ONLY),
+		OPT_BIT(0, "name-status", &ls_options, N_("list only filenames"),
+			LS_NAME_ONLY),
+		OPT_SET_INT(0, "full-name", &chomp_prefix,
+			    N_("use full path names"), 0),
+		OPT_BOOL(0, "full-tree", &full_tree,
+			 N_("list entire tree; not just current directory "
+			    "(implies --full-name)")),
+		OPT__ABBREV(&abbrev),
+		OPT_END()
+	};
+
+	git_config(git_default_config, NULL);
+	ls_tree_prefix = prefix;
+	if (prefix && *prefix)
+		chomp_prefix = strlen(prefix);
+
+	argc = parse_options(argc, argv, prefix, ls_tree_options,
+			     ls_tree_usage, 0);
+	if (full_tree) {
+		ls_tree_prefix = prefix = NULL;
+		chomp_prefix = 0;
+	}
+	/* -d -r should imply -t, but -d by itself should not have to. */
+	if ( (LS_TREE_ONLY|LS_RECURSIVE) ==
+	    ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))
+		ls_options |= LS_SHOW_TREES;
+
+	if (argc < 1)
+		usage_with_options(ls_tree_usage, ls_tree_options);
+	if (get_oid(argv[0], &oid))
+		die("Not a valid object name %s", argv[0]);
+
+	/*
+	 * show_recursive() rolls its own matching code and is
+	 * generally ignorant of 'struct pathspec'. The magic mask
+	 * cannot be lifted until it is converted to use
+	 * match_pathspec() or tree_entry_interesting()
+	 */
+	parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC &
+				  ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL),
+		       PATHSPEC_PREFER_CWD,
+		       prefix, argv + 1);
+	for (i = 0; i < pathspec.nr; i++)
+		pathspec.items[i].nowildcard_len = pathspec.items[i].len;
+	pathspec.has_wildcard = 0;
+	tree = parse_tree_indirect(&oid);
+	if (!tree)
+		die("not a tree object");
+	return !!read_tree_recursive(the_repository, tree, "", 0, 0,
+				     &pathspec, show_tree, NULL);
+}