t0410: make test description clearer
[git/gitster.git] / builtin / ls-tree.c
blob8542b5d53e435d0bec9878410a57cf013fceb25b
1 /*
2 * GIT - The information manager from hell
4 * Copyright (C) Linus Torvalds, 2005
5 */
6 #define USE_THE_REPOSITORY_VARIABLE
7 #include "builtin.h"
9 #include "config.h"
10 #include "gettext.h"
11 #include "hex.h"
12 #include "object-name.h"
13 #include "object-store-ll.h"
14 #include "tree.h"
15 #include "path.h"
16 #include "quote.h"
17 #include "parse-options.h"
18 #include "pathspec.h"
20 static const char * const ls_tree_usage[] = {
21 N_("git ls-tree [<options>] <tree-ish> [<path>...]"),
22 NULL
25 static void expand_objectsize(struct strbuf *line, const struct object_id *oid,
26 const enum object_type type, unsigned int padded)
28 if (type == OBJ_BLOB) {
29 unsigned long size;
30 if (oid_object_info(the_repository, oid, &size) < 0)
31 die(_("could not get object info about '%s'"),
32 oid_to_hex(oid));
33 if (padded)
34 strbuf_addf(line, "%7"PRIuMAX, (uintmax_t)size);
35 else
36 strbuf_addf(line, "%"PRIuMAX, (uintmax_t)size);
37 } else if (padded) {
38 strbuf_addf(line, "%7s", "-");
39 } else {
40 strbuf_addstr(line, "-");
44 struct ls_tree_options {
45 unsigned null_termination:1;
46 int abbrev;
47 enum ls_tree_path_options {
48 LS_RECURSIVE = 1 << 0,
49 LS_TREE_ONLY = 1 << 1,
50 LS_SHOW_TREES = 1 << 2,
51 } ls_options;
52 struct pathspec pathspec;
53 const char *prefix;
54 const char *format;
57 static int show_recursive(struct ls_tree_options *options, const char *base,
58 size_t baselen, const char *pathname)
60 int i;
62 if (options->ls_options & LS_RECURSIVE)
63 return 1;
65 if (!options->pathspec.nr)
66 return 0;
68 for (i = 0; i < options->pathspec.nr; i++) {
69 const char *spec = options->pathspec.items[i].match;
70 size_t len, speclen;
72 if (strncmp(base, spec, baselen))
73 continue;
74 len = strlen(pathname);
75 spec += baselen;
76 speclen = strlen(spec);
77 if (speclen <= len)
78 continue;
79 if (spec[len] && spec[len] != '/')
80 continue;
81 if (memcmp(pathname, spec, len))
82 continue;
83 return 1;
85 return 0;
88 static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,
89 const char *pathname, unsigned mode, void *context)
91 struct ls_tree_options *options = context;
92 int recurse = 0;
93 struct strbuf sb = STRBUF_INIT;
94 enum object_type type = object_type(mode);
95 const char *format = options->format;
97 if (type == OBJ_TREE && show_recursive(options, base->buf, base->len, pathname))
98 recurse = READ_TREE_RECURSIVE;
99 if (type == OBJ_TREE && recurse && !(options->ls_options & LS_SHOW_TREES))
100 return recurse;
101 if (type == OBJ_BLOB && (options->ls_options & LS_TREE_ONLY))
102 return 0;
104 while (strbuf_expand_step(&sb, &format)) {
105 size_t len;
107 if (skip_prefix(format, "%", &format))
108 strbuf_addch(&sb, '%');
109 else if ((len = strbuf_expand_literal(&sb, format)))
110 format += len;
111 else if (skip_prefix(format, "(objectmode)", &format))
112 strbuf_addf(&sb, "%06o", mode);
113 else if (skip_prefix(format, "(objecttype)", &format))
114 strbuf_addstr(&sb, type_name(type));
115 else if (skip_prefix(format, "(objectsize:padded)", &format))
116 expand_objectsize(&sb, oid, type, 1);
117 else if (skip_prefix(format, "(objectsize)", &format))
118 expand_objectsize(&sb, oid, type, 0);
119 else if (skip_prefix(format, "(objectname)", &format))
120 strbuf_add_unique_abbrev(&sb, oid, options->abbrev);
121 else if (skip_prefix(format, "(path)", &format)) {
122 const char *name;
123 const char *prefix = options->prefix;
124 struct strbuf sbuf = STRBUF_INIT;
125 size_t baselen = base->len;
127 strbuf_addstr(base, pathname);
128 name = relative_path(base->buf, prefix, &sbuf);
129 quote_c_style(name, &sb, NULL, 0);
130 strbuf_setlen(base, baselen);
131 strbuf_release(&sbuf);
132 } else
133 strbuf_expand_bad_format(format, "ls-tree");
135 strbuf_addch(&sb, options->null_termination ? '\0' : '\n');
136 fwrite(sb.buf, sb.len, 1, stdout);
137 strbuf_release(&sb);
138 return recurse;
141 static int show_tree_common(struct ls_tree_options *options, int *recurse,
142 struct strbuf *base, const char *pathname,
143 enum object_type type)
145 int ret = -1;
146 *recurse = 0;
148 if (type == OBJ_BLOB) {
149 if (options->ls_options & LS_TREE_ONLY)
150 ret = 0;
151 } else if (type == OBJ_TREE &&
152 show_recursive(options, base->buf, base->len, pathname)) {
153 *recurse = READ_TREE_RECURSIVE;
154 if (!(options->ls_options & LS_SHOW_TREES))
155 ret = *recurse;
158 return ret;
161 static void show_tree_common_default_long(struct ls_tree_options *options,
162 struct strbuf *base,
163 const char *pathname,
164 const size_t baselen)
166 const char *prefix = options->prefix;
168 strbuf_addstr(base, pathname);
170 if (options->null_termination) {
171 struct strbuf sb = STRBUF_INIT;
172 const char *name = relative_path(base->buf, prefix, &sb);
174 fputs(name, stdout);
175 fputc('\0', stdout);
177 strbuf_release(&sb);
178 } else {
179 write_name_quoted_relative(base->buf, prefix, stdout, '\n');
182 strbuf_setlen(base, baselen);
185 static int show_tree_default(const struct object_id *oid, struct strbuf *base,
186 const char *pathname, unsigned mode,
187 void *context)
189 struct ls_tree_options *options = context;
190 int early;
191 int recurse;
192 enum object_type type = object_type(mode);
194 early = show_tree_common(options, &recurse, base, pathname, type);
195 if (early >= 0)
196 return early;
198 printf("%06o %s %s\t", mode, type_name(object_type(mode)),
199 repo_find_unique_abbrev(the_repository, oid, options->abbrev));
200 show_tree_common_default_long(options, base, pathname, base->len);
201 return recurse;
204 static int show_tree_long(const struct object_id *oid, struct strbuf *base,
205 const char *pathname, unsigned mode,
206 void *context)
208 struct ls_tree_options *options = context;
209 int early;
210 int recurse;
211 char size_text[24];
212 enum object_type type = object_type(mode);
214 early = show_tree_common(options, &recurse, base, pathname, type);
215 if (early >= 0)
216 return early;
218 if (type == OBJ_BLOB) {
219 unsigned long size;
220 if (oid_object_info(the_repository, oid, &size) == OBJ_BAD)
221 xsnprintf(size_text, sizeof(size_text), "BAD");
222 else
223 xsnprintf(size_text, sizeof(size_text),
224 "%" PRIuMAX, (uintmax_t)size);
225 } else {
226 xsnprintf(size_text, sizeof(size_text), "-");
229 printf("%06o %s %s %7s\t", mode, type_name(type),
230 repo_find_unique_abbrev(the_repository, oid, options->abbrev),
231 size_text);
232 show_tree_common_default_long(options, base, pathname, base->len);
233 return recurse;
236 static int show_tree_name_only(const struct object_id *oid UNUSED,
237 struct strbuf *base,
238 const char *pathname, unsigned mode,
239 void *context)
241 struct ls_tree_options *options = context;
242 int early;
243 int recurse;
244 const size_t baselen = base->len;
245 enum object_type type = object_type(mode);
246 const char *prefix;
248 early = show_tree_common(options, &recurse, base, pathname, type);
249 if (early >= 0)
250 return early;
252 prefix = options->prefix;
253 strbuf_addstr(base, pathname);
254 if (options->null_termination) {
255 struct strbuf sb = STRBUF_INIT;
256 const char *name = relative_path(base->buf, prefix, &sb);
258 fputs(name, stdout);
259 fputc('\0', stdout);
261 strbuf_release(&sb);
262 } else {
263 write_name_quoted_relative(base->buf, prefix, stdout, '\n');
265 strbuf_setlen(base, baselen);
266 return recurse;
269 static int show_tree_object(const struct object_id *oid, struct strbuf *base,
270 const char *pathname, unsigned mode,
271 void *context)
273 struct ls_tree_options *options = context;
274 int early;
275 int recurse;
276 enum object_type type = object_type(mode);
277 const char *str;
279 early = show_tree_common(options, &recurse, base, pathname, type);
280 if (early >= 0)
281 return early;
283 str = repo_find_unique_abbrev(the_repository, oid, options->abbrev);
284 if (options->null_termination) {
285 fputs(str, stdout);
286 fputc('\0', stdout);
287 } else {
288 puts(str);
290 return recurse;
293 enum ls_tree_cmdmode {
294 MODE_DEFAULT = 0,
295 MODE_LONG,
296 MODE_NAME_ONLY,
297 MODE_NAME_STATUS,
298 MODE_OBJECT_ONLY,
301 struct ls_tree_cmdmode_to_fmt {
302 enum ls_tree_cmdmode mode;
303 const char *const fmt;
304 read_tree_fn_t fn;
307 static struct ls_tree_cmdmode_to_fmt ls_tree_cmdmode_format[] = {
309 .mode = MODE_DEFAULT,
310 .fmt = "%(objectmode) %(objecttype) %(objectname)%x09%(path)",
311 .fn = show_tree_default,
314 .mode = MODE_LONG,
315 .fmt = "%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)",
316 .fn = show_tree_long,
319 .mode = MODE_NAME_ONLY, /* And MODE_NAME_STATUS */
320 .fmt = "%(path)",
321 .fn = show_tree_name_only,
324 .mode = MODE_OBJECT_ONLY,
325 .fmt = "%(objectname)",
326 .fn = show_tree_object
329 /* fallback */
330 .fn = show_tree_default,
334 int cmd_ls_tree(int argc,
335 const char **argv,
336 const char *prefix,
337 struct repository *repo UNUSED)
339 struct object_id oid;
340 struct tree *tree;
341 int i, full_tree = 0;
342 int full_name = !prefix || !*prefix;
343 read_tree_fn_t fn = NULL;
344 enum ls_tree_cmdmode cmdmode = MODE_DEFAULT;
345 int null_termination = 0;
346 struct ls_tree_options options = { 0 };
347 const struct option ls_tree_options[] = {
348 OPT_BIT('d', NULL, &options.ls_options, N_("only show trees"),
349 LS_TREE_ONLY),
350 OPT_BIT('r', NULL, &options.ls_options, N_("recurse into subtrees"),
351 LS_RECURSIVE),
352 OPT_BIT('t', NULL, &options.ls_options, N_("show trees when recursing"),
353 LS_SHOW_TREES),
354 OPT_BOOL('z', NULL, &null_termination,
355 N_("terminate entries with NUL byte")),
356 OPT_CMDMODE('l', "long", &cmdmode, N_("include object size"),
357 MODE_LONG),
358 OPT_CMDMODE(0, "name-only", &cmdmode, N_("list only filenames"),
359 MODE_NAME_ONLY),
360 OPT_CMDMODE(0, "name-status", &cmdmode, N_("list only filenames"),
361 MODE_NAME_STATUS),
362 OPT_CMDMODE(0, "object-only", &cmdmode, N_("list only objects"),
363 MODE_OBJECT_ONLY),
364 OPT_BOOL(0, "full-name", &full_name, N_("use full path names")),
365 OPT_BOOL(0, "full-tree", &full_tree,
366 N_("list entire tree; not just current directory "
367 "(implies --full-name)")),
368 OPT_STRING_F(0, "format", &options.format, N_("format"),
369 N_("format to use for the output"),
370 PARSE_OPT_NONEG),
371 OPT__ABBREV(&options.abbrev),
372 OPT_END()
374 struct ls_tree_cmdmode_to_fmt *m2f = ls_tree_cmdmode_format;
375 struct object_context obj_context = {0};
376 int ret;
378 git_config(git_default_config, NULL);
380 argc = parse_options(argc, argv, prefix, ls_tree_options,
381 ls_tree_usage, 0);
382 options.null_termination = null_termination;
384 if (full_tree)
385 prefix = NULL;
386 options.prefix = full_name ? NULL : prefix;
389 * We wanted to detect conflicts between --name-only and
390 * --name-status, but once we're done with that subsequent
391 * code should only need to check the primary name.
393 if (cmdmode == MODE_NAME_STATUS)
394 cmdmode = MODE_NAME_ONLY;
396 /* -d -r should imply -t, but -d by itself should not have to. */
397 if ( (LS_TREE_ONLY|LS_RECURSIVE) ==
398 ((LS_TREE_ONLY|LS_RECURSIVE) & options.ls_options))
399 options.ls_options |= LS_SHOW_TREES;
401 if (options.format && cmdmode)
402 usage_msg_opt(
403 _("--format can't be combined with other format-altering options"),
404 ls_tree_usage, ls_tree_options);
405 if (argc < 1)
406 usage_with_options(ls_tree_usage, ls_tree_options);
407 if (get_oid_with_context(the_repository, argv[0],
408 GET_OID_HASH_ANY, &oid,
409 &obj_context))
410 die("Not a valid object name %s", argv[0]);
413 * show_recursive() rolls its own matching code and is
414 * generally ignorant of 'struct pathspec'. The magic mask
415 * cannot be lifted until it is converted to use
416 * match_pathspec() or tree_entry_interesting()
418 parse_pathspec(&options.pathspec, PATHSPEC_ALL_MAGIC &
419 ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL),
420 PATHSPEC_PREFER_CWD,
421 prefix, argv + 1);
422 for (i = 0; i < options.pathspec.nr; i++)
423 options.pathspec.items[i].nowildcard_len = options.pathspec.items[i].len;
424 options.pathspec.has_wildcard = 0;
425 tree = parse_tree_indirect(&oid);
426 if (!tree)
427 die("not a tree object");
429 * The generic show_tree_fmt() is slower than show_tree(), so
430 * take the fast path if possible.
432 while (m2f) {
433 if (!m2f->fmt) {
434 fn = options.format ? show_tree_fmt : show_tree_default;
435 } else if (options.format && !strcmp(options.format, m2f->fmt)) {
436 cmdmode = m2f->mode;
437 fn = m2f->fn;
438 } else if (!options.format && cmdmode == m2f->mode) {
439 fn = m2f->fn;
440 } else {
441 m2f++;
442 continue;
444 break;
447 ret = !!read_tree(the_repository, tree, &options.pathspec, fn, &options);
448 clear_pathspec(&options.pathspec);
449 object_context_release(&obj_context);
450 return ret;