ls-tree: simplify nesting if/else logic in "show_tree()"
[git/gitster.git] / builtin / ls-tree.c
blobd4be71bad24820cec75ca1decf763048f319b903
1 /*
2 * GIT - The information manager from hell
4 * Copyright (C) Linus Torvalds, 2005
5 */
6 #include "cache.h"
7 #include "config.h"
8 #include "object-store.h"
9 #include "blob.h"
10 #include "tree.h"
11 #include "commit.h"
12 #include "quote.h"
13 #include "builtin.h"
14 #include "parse-options.h"
15 #include "pathspec.h"
17 static int line_termination = '\n';
18 #define LS_RECURSIVE 1
19 #define LS_TREE_ONLY 2
20 #define LS_SHOW_TREES 4
21 #define LS_NAME_ONLY 8
22 #define LS_SHOW_SIZE 16
23 static int abbrev;
24 static int ls_options;
25 static struct pathspec pathspec;
26 static int chomp_prefix;
27 static const char *ls_tree_prefix;
29 static const char * const ls_tree_usage[] = {
30 N_("git ls-tree [<options>] <tree-ish> [<path>...]"),
31 NULL
34 static int show_recursive(const char *base, size_t baselen, const char *pathname)
36 int i;
38 if (ls_options & LS_RECURSIVE)
39 return 1;
41 if (!pathspec.nr)
42 return 0;
44 for (i = 0; i < pathspec.nr; i++) {
45 const char *spec = pathspec.items[i].match;
46 size_t len, speclen;
48 if (strncmp(base, spec, baselen))
49 continue;
50 len = strlen(pathname);
51 spec += baselen;
52 speclen = strlen(spec);
53 if (speclen <= len)
54 continue;
55 if (spec[len] && spec[len] != '/')
56 continue;
57 if (memcmp(pathname, spec, len))
58 continue;
59 return 1;
61 return 0;
64 static int show_tree(const struct object_id *oid, struct strbuf *base,
65 const char *pathname, unsigned mode, void *context)
67 int recurse = 0;
68 size_t baselen;
69 enum object_type type = object_type(mode);
71 if (type == OBJ_BLOB) {
72 if (ls_options & LS_TREE_ONLY)
73 return 0;
74 } else if (type == OBJ_TREE &&
75 show_recursive(base->buf, base->len, pathname)) {
76 recurse = READ_TREE_RECURSIVE;
77 if (!(ls_options & LS_SHOW_TREES))
78 return recurse;
81 if (!(ls_options & LS_NAME_ONLY)) {
82 if (ls_options & LS_SHOW_SIZE) {
83 char size_text[24];
84 if (type == OBJ_BLOB) {
85 unsigned long size;
86 if (oid_object_info(the_repository, oid, &size) == OBJ_BAD)
87 xsnprintf(size_text, sizeof(size_text),
88 "BAD");
89 else
90 xsnprintf(size_text, sizeof(size_text),
91 "%"PRIuMAX, (uintmax_t)size);
92 } else {
93 xsnprintf(size_text, sizeof(size_text), "-");
95 printf("%06o %s %s %7s\t", mode, type_name(type),
96 find_unique_abbrev(oid, abbrev),
97 size_text);
98 } else {
99 printf("%06o %s %s\t", mode, type_name(type),
100 find_unique_abbrev(oid, abbrev));
103 baselen = base->len;
104 strbuf_addstr(base, pathname);
105 write_name_quoted_relative(base->buf,
106 chomp_prefix ? ls_tree_prefix : NULL,
107 stdout, line_termination);
108 strbuf_setlen(base, baselen);
109 return recurse;
112 int cmd_ls_tree(int argc, const char **argv, const char *prefix)
114 struct object_id oid;
115 struct tree *tree;
116 int i, full_tree = 0;
117 const struct option ls_tree_options[] = {
118 OPT_BIT('d', NULL, &ls_options, N_("only show trees"),
119 LS_TREE_ONLY),
120 OPT_BIT('r', NULL, &ls_options, N_("recurse into subtrees"),
121 LS_RECURSIVE),
122 OPT_BIT('t', NULL, &ls_options, N_("show trees when recursing"),
123 LS_SHOW_TREES),
124 OPT_SET_INT('z', NULL, &line_termination,
125 N_("terminate entries with NUL byte"), 0),
126 OPT_BIT('l', "long", &ls_options, N_("include object size"),
127 LS_SHOW_SIZE),
128 OPT_BIT(0, "name-only", &ls_options, N_("list only filenames"),
129 LS_NAME_ONLY),
130 OPT_BIT(0, "name-status", &ls_options, N_("list only filenames"),
131 LS_NAME_ONLY),
132 OPT_SET_INT(0, "full-name", &chomp_prefix,
133 N_("use full path names"), 0),
134 OPT_BOOL(0, "full-tree", &full_tree,
135 N_("list entire tree; not just current directory "
136 "(implies --full-name)")),
137 OPT__ABBREV(&abbrev),
138 OPT_END()
141 git_config(git_default_config, NULL);
142 ls_tree_prefix = prefix;
143 if (prefix && *prefix)
144 chomp_prefix = strlen(prefix);
146 argc = parse_options(argc, argv, prefix, ls_tree_options,
147 ls_tree_usage, 0);
148 if (full_tree) {
149 ls_tree_prefix = prefix = NULL;
150 chomp_prefix = 0;
152 /* -d -r should imply -t, but -d by itself should not have to. */
153 if ( (LS_TREE_ONLY|LS_RECURSIVE) ==
154 ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))
155 ls_options |= LS_SHOW_TREES;
157 if (argc < 1)
158 usage_with_options(ls_tree_usage, ls_tree_options);
159 if (get_oid(argv[0], &oid))
160 die("Not a valid object name %s", argv[0]);
163 * show_recursive() rolls its own matching code and is
164 * generally ignorant of 'struct pathspec'. The magic mask
165 * cannot be lifted until it is converted to use
166 * match_pathspec() or tree_entry_interesting()
168 parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC &
169 ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL),
170 PATHSPEC_PREFER_CWD,
171 prefix, argv + 1);
172 for (i = 0; i < pathspec.nr; i++)
173 pathspec.items[i].nowildcard_len = pathspec.items[i].len;
174 pathspec.has_wildcard = 0;
175 tree = parse_tree_indirect(&oid);
176 if (!tree)
177 die("not a tree object");
178 return !!read_tree(the_repository, tree,
179 &pathspec, show_tree, NULL);