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