Merge branch 'ab/various-leak-fixes'
[git.git] / builtin / ls-tree.c
blobc3ea09281afebe0c9aefb690f6f10044142a4d1a
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 (1 << 1)
20 #define LS_SHOW_TREES (1 << 2)
21 static int abbrev;
22 static int ls_options;
23 static struct pathspec pathspec;
24 static int chomp_prefix;
25 static const char *ls_tree_prefix;
26 static const char *format;
27 struct show_tree_data {
28 unsigned mode;
29 enum object_type type;
30 const struct object_id *oid;
31 const char *pathname;
32 struct strbuf *base;
35 static const char * const ls_tree_usage[] = {
36 N_("git ls-tree [<options>] <tree-ish> [<path>...]"),
37 NULL
40 static enum ls_tree_cmdmode {
41 MODE_DEFAULT = 0,
42 MODE_LONG,
43 MODE_NAME_ONLY,
44 MODE_NAME_STATUS,
45 MODE_OBJECT_ONLY,
46 } cmdmode;
48 static void expand_objectsize(struct strbuf *line, const struct object_id *oid,
49 const enum object_type type, unsigned int padded)
51 if (type == OBJ_BLOB) {
52 unsigned long size;
53 if (oid_object_info(the_repository, oid, &size) < 0)
54 die(_("could not get object info about '%s'"),
55 oid_to_hex(oid));
56 if (padded)
57 strbuf_addf(line, "%7"PRIuMAX, (uintmax_t)size);
58 else
59 strbuf_addf(line, "%"PRIuMAX, (uintmax_t)size);
60 } else if (padded) {
61 strbuf_addf(line, "%7s", "-");
62 } else {
63 strbuf_addstr(line, "-");
67 static size_t expand_show_tree(struct strbuf *sb, const char *start,
68 void *context)
70 struct show_tree_data *data = context;
71 const char *end;
72 const char *p;
73 unsigned int errlen;
74 size_t len = strbuf_expand_literal_cb(sb, start, NULL);
76 if (len)
77 return len;
78 if (*start != '(')
79 die(_("bad ls-tree format: element '%s' does not start with '('"), start);
81 end = strchr(start + 1, ')');
82 if (!end)
83 die(_("bad ls-tree format: element '%s' does not end in ')'"), start);
85 len = end - start + 1;
86 if (skip_prefix(start, "(objectmode)", &p)) {
87 strbuf_addf(sb, "%06o", data->mode);
88 } else if (skip_prefix(start, "(objecttype)", &p)) {
89 strbuf_addstr(sb, type_name(data->type));
90 } else if (skip_prefix(start, "(objectsize:padded)", &p)) {
91 expand_objectsize(sb, data->oid, data->type, 1);
92 } else if (skip_prefix(start, "(objectsize)", &p)) {
93 expand_objectsize(sb, data->oid, data->type, 0);
94 } else if (skip_prefix(start, "(objectname)", &p)) {
95 strbuf_add_unique_abbrev(sb, data->oid, abbrev);
96 } else if (skip_prefix(start, "(path)", &p)) {
97 const char *name = data->base->buf;
98 const char *prefix = chomp_prefix ? ls_tree_prefix : NULL;
99 struct strbuf quoted = STRBUF_INIT;
100 struct strbuf sbuf = STRBUF_INIT;
101 strbuf_addstr(data->base, data->pathname);
102 name = relative_path(data->base->buf, prefix, &sbuf);
103 quote_c_style(name, &quoted, NULL, 0);
104 strbuf_addbuf(sb, &quoted);
105 strbuf_release(&sbuf);
106 strbuf_release(&quoted);
107 } else {
108 errlen = (unsigned long)len;
109 die(_("bad ls-tree format: %%%.*s"), errlen, start);
111 return len;
114 static int show_recursive(const char *base, size_t baselen, const char *pathname)
116 int i;
118 if (ls_options & LS_RECURSIVE)
119 return 1;
121 if (!pathspec.nr)
122 return 0;
124 for (i = 0; i < pathspec.nr; i++) {
125 const char *spec = pathspec.items[i].match;
126 size_t len, speclen;
128 if (strncmp(base, spec, baselen))
129 continue;
130 len = strlen(pathname);
131 spec += baselen;
132 speclen = strlen(spec);
133 if (speclen <= len)
134 continue;
135 if (spec[len] && spec[len] != '/')
136 continue;
137 if (memcmp(pathname, spec, len))
138 continue;
139 return 1;
141 return 0;
144 static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,
145 const char *pathname, unsigned mode, void *context UNUSED)
147 size_t baselen;
148 int recurse = 0;
149 struct strbuf sb = STRBUF_INIT;
150 enum object_type type = object_type(mode);
152 struct show_tree_data data = {
153 .mode = mode,
154 .type = type,
155 .oid = oid,
156 .pathname = pathname,
157 .base = base,
160 if (type == OBJ_TREE && show_recursive(base->buf, base->len, pathname))
161 recurse = READ_TREE_RECURSIVE;
162 if (type == OBJ_TREE && recurse && !(ls_options & LS_SHOW_TREES))
163 return recurse;
164 if (type == OBJ_BLOB && (ls_options & LS_TREE_ONLY))
165 return 0;
167 baselen = base->len;
168 strbuf_expand(&sb, format, expand_show_tree, &data);
169 strbuf_addch(&sb, line_termination);
170 fwrite(sb.buf, sb.len, 1, stdout);
171 strbuf_release(&sb);
172 strbuf_setlen(base, baselen);
173 return recurse;
176 static int show_tree_common(struct show_tree_data *data, int *recurse,
177 const struct object_id *oid, struct strbuf *base,
178 const char *pathname, unsigned mode)
180 enum object_type type = object_type(mode);
181 int ret = -1;
183 *recurse = 0;
184 data->mode = mode;
185 data->type = type;
186 data->oid = oid;
187 data->pathname = pathname;
188 data->base = base;
190 if (type == OBJ_BLOB) {
191 if (ls_options & LS_TREE_ONLY)
192 ret = 0;
193 } else if (type == OBJ_TREE &&
194 show_recursive(base->buf, base->len, pathname)) {
195 *recurse = READ_TREE_RECURSIVE;
196 if (!(ls_options & LS_SHOW_TREES))
197 ret = *recurse;
200 return ret;
203 static void show_tree_common_default_long(struct strbuf *base,
204 const char *pathname,
205 const size_t baselen)
207 strbuf_addstr(base, pathname);
208 write_name_quoted_relative(base->buf,
209 chomp_prefix ? ls_tree_prefix : NULL, stdout,
210 line_termination);
211 strbuf_setlen(base, baselen);
214 static int show_tree_default(const struct object_id *oid, struct strbuf *base,
215 const char *pathname, unsigned mode,
216 void *context UNUSED)
218 int early;
219 int recurse;
220 struct show_tree_data data = { 0 };
222 early = show_tree_common(&data, &recurse, oid, base, pathname, mode);
223 if (early >= 0)
224 return early;
226 printf("%06o %s %s\t", data.mode, type_name(data.type),
227 find_unique_abbrev(data.oid, abbrev));
228 show_tree_common_default_long(base, pathname, data.base->len);
229 return recurse;
232 static int show_tree_long(const struct object_id *oid, struct strbuf *base,
233 const char *pathname, unsigned mode,
234 void *context UNUSED)
236 int early;
237 int recurse;
238 struct show_tree_data data = { 0 };
239 char size_text[24];
241 early = show_tree_common(&data, &recurse, oid, base, pathname, mode);
242 if (early >= 0)
243 return early;
245 if (data.type == OBJ_BLOB) {
246 unsigned long size;
247 if (oid_object_info(the_repository, data.oid, &size) == OBJ_BAD)
248 xsnprintf(size_text, sizeof(size_text), "BAD");
249 else
250 xsnprintf(size_text, sizeof(size_text),
251 "%" PRIuMAX, (uintmax_t)size);
252 } else {
253 xsnprintf(size_text, sizeof(size_text), "-");
256 printf("%06o %s %s %7s\t", data.mode, type_name(data.type),
257 find_unique_abbrev(data.oid, abbrev), size_text);
258 show_tree_common_default_long(base, pathname, data.base->len);
259 return recurse;
262 static int show_tree_name_only(const struct object_id *oid, struct strbuf *base,
263 const char *pathname, unsigned mode,
264 void *context UNUSED)
266 int early;
267 int recurse;
268 const size_t baselen = base->len;
269 struct show_tree_data data = { 0 };
271 early = show_tree_common(&data, &recurse, oid, base, pathname, mode);
272 if (early >= 0)
273 return early;
275 strbuf_addstr(base, pathname);
276 write_name_quoted_relative(base->buf,
277 chomp_prefix ? ls_tree_prefix : NULL,
278 stdout, line_termination);
279 strbuf_setlen(base, baselen);
280 return recurse;
283 static int show_tree_object(const struct object_id *oid, struct strbuf *base,
284 const char *pathname, unsigned mode,
285 void *context UNUSED)
287 int early;
288 int recurse;
289 struct show_tree_data data = { 0 };
291 early = show_tree_common(&data, &recurse, oid, base, pathname, mode);
292 if (early >= 0)
293 return early;
295 printf("%s%c", find_unique_abbrev(oid, abbrev), line_termination);
296 return recurse;
299 struct ls_tree_cmdmode_to_fmt {
300 enum ls_tree_cmdmode mode;
301 const char *const fmt;
302 read_tree_fn_t fn;
305 static struct ls_tree_cmdmode_to_fmt ls_tree_cmdmode_format[] = {
307 .mode = MODE_DEFAULT,
308 .fmt = "%(objectmode) %(objecttype) %(objectname)%x09%(path)",
309 .fn = show_tree_default,
312 .mode = MODE_LONG,
313 .fmt = "%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)",
314 .fn = show_tree_long,
317 .mode = MODE_NAME_ONLY, /* And MODE_NAME_STATUS */
318 .fmt = "%(path)",
319 .fn = show_tree_name_only,
322 .mode = MODE_OBJECT_ONLY,
323 .fmt = "%(objectname)",
324 .fn = show_tree_object
327 /* fallback */
328 .fn = show_tree_default,
332 int cmd_ls_tree(int argc, const char **argv, const char *prefix)
334 struct object_id oid;
335 struct tree *tree;
336 int i, full_tree = 0;
337 read_tree_fn_t fn = NULL;
338 const struct option ls_tree_options[] = {
339 OPT_BIT('d', NULL, &ls_options, N_("only show trees"),
340 LS_TREE_ONLY),
341 OPT_BIT('r', NULL, &ls_options, N_("recurse into subtrees"),
342 LS_RECURSIVE),
343 OPT_BIT('t', NULL, &ls_options, N_("show trees when recursing"),
344 LS_SHOW_TREES),
345 OPT_SET_INT('z', NULL, &line_termination,
346 N_("terminate entries with NUL byte"), 0),
347 OPT_CMDMODE('l', "long", &cmdmode, N_("include object size"),
348 MODE_LONG),
349 OPT_CMDMODE(0, "name-only", &cmdmode, N_("list only filenames"),
350 MODE_NAME_ONLY),
351 OPT_CMDMODE(0, "name-status", &cmdmode, N_("list only filenames"),
352 MODE_NAME_STATUS),
353 OPT_CMDMODE(0, "object-only", &cmdmode, N_("list only objects"),
354 MODE_OBJECT_ONLY),
355 OPT_SET_INT(0, "full-name", &chomp_prefix,
356 N_("use full path names"), 0),
357 OPT_BOOL(0, "full-tree", &full_tree,
358 N_("list entire tree; not just current directory "
359 "(implies --full-name)")),
360 OPT_STRING_F(0, "format", &format, N_("format"),
361 N_("format to use for the output"),
362 PARSE_OPT_NONEG),
363 OPT__ABBREV(&abbrev),
364 OPT_END()
366 struct ls_tree_cmdmode_to_fmt *m2f = ls_tree_cmdmode_format;
368 git_config(git_default_config, NULL);
369 ls_tree_prefix = prefix;
370 if (prefix)
371 chomp_prefix = strlen(prefix);
373 argc = parse_options(argc, argv, prefix, ls_tree_options,
374 ls_tree_usage, 0);
375 if (full_tree) {
376 ls_tree_prefix = prefix = NULL;
377 chomp_prefix = 0;
380 * We wanted to detect conflicts between --name-only and
381 * --name-status, but once we're done with that subsequent
382 * code should only need to check the primary name.
384 if (cmdmode == MODE_NAME_STATUS)
385 cmdmode = MODE_NAME_ONLY;
387 /* -d -r should imply -t, but -d by itself should not have to. */
388 if ( (LS_TREE_ONLY|LS_RECURSIVE) ==
389 ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))
390 ls_options |= LS_SHOW_TREES;
392 if (format && cmdmode)
393 usage_msg_opt(
394 _("--format can't be combined with other format-altering options"),
395 ls_tree_usage, ls_tree_options);
396 if (argc < 1)
397 usage_with_options(ls_tree_usage, ls_tree_options);
398 if (get_oid(argv[0], &oid))
399 die("Not a valid object name %s", argv[0]);
402 * show_recursive() rolls its own matching code and is
403 * generally ignorant of 'struct pathspec'. The magic mask
404 * cannot be lifted until it is converted to use
405 * match_pathspec() or tree_entry_interesting()
407 parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC &
408 ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL),
409 PATHSPEC_PREFER_CWD,
410 prefix, argv + 1);
411 for (i = 0; i < pathspec.nr; i++)
412 pathspec.items[i].nowildcard_len = pathspec.items[i].len;
413 pathspec.has_wildcard = 0;
414 tree = parse_tree_indirect(&oid);
415 if (!tree)
416 die("not a tree object");
418 * The generic show_tree_fmt() is slower than show_tree(), so
419 * take the fast path if possible.
421 while (m2f) {
422 if (!m2f->fmt) {
423 fn = format ? show_tree_fmt : show_tree_default;
424 } else if (format && !strcmp(format, m2f->fmt)) {
425 cmdmode = m2f->mode;
426 fn = m2f->fn;
427 } else if (!format && cmdmode == m2f->mode) {
428 fn = m2f->fn;
429 } else {
430 m2f++;
431 continue;
433 break;
436 return !!read_tree(the_repository, tree, &pathspec, fn, NULL);