Coverage Report

Created: 2024-09-08 06:23

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