]>
Commit | Line | Data |
---|---|---|
7912c070 PB |
1 | /* |
2 | * GIT - The information manager from hell | |
3 | * | |
4 | * Copyright (C) Linus Torvalds, 2005 | |
5 | */ | |
6 | #include "cache.h" | |
b2141fc1 | 7 | #include "config.h" |
cbd53a21 | 8 | #include "object-store.h" |
6af1f019 JH |
9 | #include "blob.h" |
10 | #include "tree.h" | |
f35a6d3b | 11 | #include "commit.h" |
22ddf719 | 12 | #include "quote.h" |
aae01bda | 13 | #include "builtin.h" |
61fdbcf9 | 14 | #include "parse-options.h" |
64acde94 | 15 | #include "pathspec.h" |
7912c070 | 16 | |
e99d59ff | 17 | static int line_termination = '\n'; |
6af1f019 | 18 | #define LS_RECURSIVE 1 |
315f22c8 TL |
19 | #define LS_TREE_ONLY (1 << 1) |
20 | #define LS_SHOW_TREES (1 << 2) | |
96f1e58f DR |
21 | static int abbrev; |
22 | static int ls_options; | |
f0096c06 | 23 | static struct pathspec pathspec; |
96f1e58f | 24 | static int chomp_prefix; |
a633fca0 | 25 | static const char *ls_tree_prefix; |
455923e0 ÆAB |
26 | static const char *format; |
27 | ||
e8151715 ÆAB |
28 | struct show_tree_data { |
29 | unsigned mode; | |
30 | enum object_type type; | |
31 | const struct object_id *oid; | |
32 | const char *pathname; | |
33 | struct strbuf *base; | |
34 | }; | |
aa1c48df | 35 | |
61fdbcf9 | 36 | static const char * const ls_tree_usage[] = { |
373f9221 | 37 | N_("git ls-tree [<options>] <tree-ish> [<path>...]"), |
61fdbcf9 SB |
38 | NULL |
39 | }; | |
0f8f45cb | 40 | |
315f22c8 | 41 | static enum ls_tree_cmdmode { |
455923e0 ÆAB |
42 | MODE_DEFAULT = 0, |
43 | MODE_LONG, | |
315f22c8 TL |
44 | MODE_NAME_ONLY, |
45 | } cmdmode; | |
46 | ||
455923e0 ÆAB |
47 | static void expand_objectsize(struct strbuf *line, const struct object_id *oid, |
48 | const enum object_type type, unsigned int padded) | |
49 | { | |
50 | if (type == OBJ_BLOB) { | |
51 | unsigned long size; | |
52 | if (oid_object_info(the_repository, oid, &size) < 0) | |
53 | die(_("could not get object info about '%s'"), | |
54 | oid_to_hex(oid)); | |
55 | if (padded) | |
56 | strbuf_addf(line, "%7"PRIuMAX, (uintmax_t)size); | |
57 | else | |
58 | strbuf_addf(line, "%"PRIuMAX, (uintmax_t)size); | |
59 | } else if (padded) { | |
60 | strbuf_addf(line, "%7s", "-"); | |
61 | } else { | |
62 | strbuf_addstr(line, "-"); | |
63 | } | |
64 | } | |
65 | ||
66 | static size_t expand_show_tree(struct strbuf *sb, const char *start, | |
67 | void *context) | |
68 | { | |
69 | struct show_tree_data *data = context; | |
70 | const char *end; | |
71 | const char *p; | |
72 | unsigned int errlen; | |
73 | size_t len = strbuf_expand_literal_cb(sb, start, NULL); | |
74 | ||
75 | if (len) | |
76 | return len; | |
77 | if (*start != '(') | |
78 | die(_("bad ls-tree format: element '%s' does not start with '('"), start); | |
79 | ||
80 | end = strchr(start + 1, ')'); | |
81 | if (!end) | |
82 | die(_("bad ls-tree format: element '%s' does not end in ')'"), start); | |
83 | ||
84 | len = end - start + 1; | |
85 | if (skip_prefix(start, "(objectmode)", &p)) { | |
86 | strbuf_addf(sb, "%06o", data->mode); | |
87 | } else if (skip_prefix(start, "(objecttype)", &p)) { | |
88 | strbuf_addstr(sb, type_name(data->type)); | |
89 | } else if (skip_prefix(start, "(objectsize:padded)", &p)) { | |
90 | expand_objectsize(sb, data->oid, data->type, 1); | |
91 | } else if (skip_prefix(start, "(objectsize)", &p)) { | |
92 | expand_objectsize(sb, data->oid, data->type, 0); | |
93 | } else if (skip_prefix(start, "(objectname)", &p)) { | |
94 | strbuf_add_unique_abbrev(sb, data->oid, abbrev); | |
95 | } else if (skip_prefix(start, "(path)", &p)) { | |
96 | const char *name = data->base->buf; | |
97 | const char *prefix = chomp_prefix ? ls_tree_prefix : NULL; | |
98 | struct strbuf quoted = STRBUF_INIT; | |
99 | struct strbuf sbuf = STRBUF_INIT; | |
16fb5c54 RS |
100 | size_t baselen = data->base->len; |
101 | ||
455923e0 ÆAB |
102 | strbuf_addstr(data->base, data->pathname); |
103 | name = relative_path(data->base->buf, prefix, &sbuf); | |
104 | quote_c_style(name, "ed, NULL, 0); | |
16fb5c54 | 105 | strbuf_setlen(data->base, baselen); |
455923e0 ÆAB |
106 | strbuf_addbuf(sb, "ed); |
107 | strbuf_release(&sbuf); | |
108 | strbuf_release("ed); | |
109 | } else { | |
110 | errlen = (unsigned long)len; | |
111 | die(_("bad ls-tree format: %%%.*s"), errlen, start); | |
112 | } | |
113 | return len; | |
114 | } | |
115 | ||
132ceda4 | 116 | static int show_recursive(const char *base, size_t baselen, const char *pathname) |
0f8f45cb | 117 | { |
e1e24edc | 118 | int i; |
0f8f45cb LT |
119 | |
120 | if (ls_options & LS_RECURSIVE) | |
121 | return 1; | |
122 | ||
e1e24edc | 123 | if (!pathspec.nr) |
0f8f45cb LT |
124 | return 0; |
125 | ||
e1e24edc BW |
126 | for (i = 0; i < pathspec.nr; i++) { |
127 | const char *spec = pathspec.items[i].match; | |
132ceda4 | 128 | size_t len, speclen; |
0f8f45cb | 129 | |
0f8f45cb LT |
130 | if (strncmp(base, spec, baselen)) |
131 | continue; | |
132 | len = strlen(pathname); | |
133 | spec += baselen; | |
134 | speclen = strlen(spec); | |
135 | if (speclen <= len) | |
136 | continue; | |
b294ed63 JH |
137 | if (spec[len] && spec[len] != '/') |
138 | continue; | |
0f8f45cb LT |
139 | if (memcmp(pathname, spec, len)) |
140 | continue; | |
141 | return 1; | |
142 | } | |
e1e24edc | 143 | return 0; |
0f8f45cb | 144 | } |
aa1c48df | 145 | |
455923e0 ÆAB |
146 | static int show_tree_fmt(const struct object_id *oid, struct strbuf *base, |
147 | const char *pathname, unsigned mode, void *context) | |
148 | { | |
455923e0 ÆAB |
149 | int recurse = 0; |
150 | struct strbuf sb = STRBUF_INIT; | |
151 | enum object_type type = object_type(mode); | |
152 | ||
153 | struct show_tree_data data = { | |
154 | .mode = mode, | |
155 | .type = type, | |
156 | .oid = oid, | |
157 | .pathname = pathname, | |
158 | .base = base, | |
159 | }; | |
160 | ||
161 | if (type == OBJ_TREE && show_recursive(base->buf, base->len, pathname)) | |
162 | recurse = READ_TREE_RECURSIVE; | |
163 | if (type == OBJ_TREE && recurse && !(ls_options & LS_SHOW_TREES)) | |
164 | return recurse; | |
165 | if (type == OBJ_BLOB && (ls_options & LS_TREE_ONLY)) | |
166 | return 0; | |
167 | ||
455923e0 ÆAB |
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); | |
455923e0 ÆAB |
172 | return recurse; |
173 | } | |
174 | ||
e8151715 | 175 | static int show_default(struct show_tree_data *data) |
315f22c8 | 176 | { |
e8151715 | 177 | size_t baselen = data->base->len; |
315f22c8 TL |
178 | |
179 | if (cmdmode == MODE_LONG) { | |
180 | char size_text[24]; | |
e8151715 | 181 | if (data->type == OBJ_BLOB) { |
315f22c8 | 182 | unsigned long size; |
e8151715 | 183 | if (oid_object_info(the_repository, data->oid, &size) == OBJ_BAD) |
315f22c8 TL |
184 | xsnprintf(size_text, sizeof(size_text), "BAD"); |
185 | else | |
186 | xsnprintf(size_text, sizeof(size_text), | |
187 | "%" PRIuMAX, (uintmax_t)size); | |
188 | } else { | |
189 | xsnprintf(size_text, sizeof(size_text), "-"); | |
190 | } | |
e8151715 ÆAB |
191 | printf("%06o %s %s %7s\t", data->mode, type_name(data->type), |
192 | find_unique_abbrev(data->oid, abbrev), size_text); | |
315f22c8 | 193 | } else { |
e8151715 ÆAB |
194 | printf("%06o %s %s\t", data->mode, type_name(data->type), |
195 | find_unique_abbrev(data->oid, abbrev)); | |
315f22c8 | 196 | } |
e8151715 ÆAB |
197 | baselen = data->base->len; |
198 | strbuf_addstr(data->base, data->pathname); | |
199 | write_name_quoted_relative(data->base->buf, | |
315f22c8 TL |
200 | chomp_prefix ? ls_tree_prefix : NULL, stdout, |
201 | line_termination); | |
e8151715 | 202 | strbuf_setlen(data->base, baselen); |
315f22c8 TL |
203 | return 1; |
204 | } | |
205 | ||
df46d77e | 206 | static int show_tree(const struct object_id *oid, struct strbuf *base, |
47957485 | 207 | const char *pathname, unsigned mode, void *context) |
6af1f019 | 208 | { |
889f7838 | 209 | int recurse = 0; |
132ceda4 | 210 | size_t baselen; |
87af0ddf | 211 | enum object_type type = object_type(mode); |
e8151715 ÆAB |
212 | struct show_tree_data data = { |
213 | .mode = mode, | |
214 | .type = type, | |
215 | .oid = oid, | |
216 | .pathname = pathname, | |
217 | .base = base, | |
218 | }; | |
ab1630a3 | 219 | |
87af0ddf TL |
220 | if (type == OBJ_BLOB) { |
221 | if (ls_options & LS_TREE_ONLY) | |
222 | return 0; | |
223 | } else if (type == OBJ_TREE && | |
224 | show_recursive(base->buf, base->len, pathname)) { | |
225 | recurse = READ_TREE_RECURSIVE; | |
226 | if (!(ls_options & LS_SHOW_TREES)) | |
227 | return recurse; | |
6af1f019 | 228 | } |
ab1630a3 | 229 | |
315f22c8 TL |
230 | if (cmdmode == MODE_NAME_ONLY) { |
231 | baselen = base->len; | |
232 | strbuf_addstr(base, pathname); | |
233 | write_name_quoted_relative(base->buf, | |
234 | chomp_prefix ? ls_tree_prefix : NULL, | |
235 | stdout, line_termination); | |
236 | strbuf_setlen(base, baselen); | |
237 | return recurse; | |
a5bbda8b | 238 | } |
315f22c8 TL |
239 | |
240 | if (cmdmode == MODE_LONG || | |
241 | (!ls_options || (ls_options & LS_RECURSIVE) | |
242 | || (ls_options & LS_SHOW_TREES) | |
243 | || (ls_options & LS_TREE_ONLY))) | |
e8151715 | 244 | show_default(&data); |
315f22c8 | 245 | |
889f7838 | 246 | return recurse; |
6af1f019 | 247 | } |
0f2303f7 | 248 | |
455923e0 ÆAB |
249 | struct ls_tree_cmdmode_to_fmt { |
250 | enum ls_tree_cmdmode mode; | |
251 | const char *const fmt; | |
252 | }; | |
253 | ||
254 | static struct ls_tree_cmdmode_to_fmt ls_tree_cmdmode_format[] = { | |
255 | { | |
256 | .mode = MODE_DEFAULT, | |
257 | .fmt = "%(objectmode) %(objecttype) %(objectname)%x09%(path)", | |
258 | }, | |
259 | { | |
260 | .mode = MODE_LONG, | |
261 | .fmt = "%(objectmode) %(objecttype) %(objectname) %(objectsize:padded)%x09%(path)", | |
262 | }, | |
263 | { | |
264 | .mode = MODE_NAME_ONLY, /* And MODE_NAME_STATUS */ | |
265 | .fmt = "%(path)", | |
266 | }, | |
267 | { 0 }, | |
268 | }; | |
269 | ||
a633fca0 | 270 | int cmd_ls_tree(int argc, const char **argv, const char *prefix) |
6af1f019 | 271 | { |
a9b5f5bf | 272 | struct object_id oid; |
521698b1 | 273 | struct tree *tree; |
f0096c06 | 274 | int i, full_tree = 0; |
455923e0 | 275 | read_tree_fn_t fn = show_tree; |
61fdbcf9 | 276 | const struct option ls_tree_options[] = { |
373f9221 | 277 | OPT_BIT('d', NULL, &ls_options, N_("only show trees"), |
61fdbcf9 | 278 | LS_TREE_ONLY), |
373f9221 | 279 | OPT_BIT('r', NULL, &ls_options, N_("recurse into subtrees"), |
61fdbcf9 | 280 | LS_RECURSIVE), |
373f9221 | 281 | OPT_BIT('t', NULL, &ls_options, N_("show trees when recursing"), |
61fdbcf9 SB |
282 | LS_SHOW_TREES), |
283 | OPT_SET_INT('z', NULL, &line_termination, | |
373f9221 | 284 | N_("terminate entries with NUL byte"), 0), |
315f22c8 TL |
285 | OPT_CMDMODE('l', "long", &cmdmode, N_("include object size"), |
286 | MODE_LONG), | |
287 | OPT_CMDMODE(0, "name-only", &cmdmode, N_("list only filenames"), | |
288 | MODE_NAME_ONLY), | |
289 | OPT_CMDMODE(0, "name-status", &cmdmode, N_("list only filenames"), | |
290 | MODE_NAME_ONLY), | |
61fdbcf9 | 291 | OPT_SET_INT(0, "full-name", &chomp_prefix, |
373f9221 | 292 | N_("use full path names"), 0), |
d5d09d47 SB |
293 | OPT_BOOL(0, "full-tree", &full_tree, |
294 | N_("list entire tree; not just current directory " | |
295 | "(implies --full-name)")), | |
455923e0 ÆAB |
296 | OPT_STRING_F(0, "format", &format, N_("format"), |
297 | N_("format to use for the output"), | |
298 | PARSE_OPT_NONEG), | |
61fdbcf9 SB |
299 | OPT__ABBREV(&abbrev), |
300 | OPT_END() | |
301 | }; | |
7912c070 | 302 | |
ef90d6d4 | 303 | git_config(git_default_config, NULL); |
a633fca0 | 304 | ls_tree_prefix = prefix; |
a69dd585 JH |
305 | if (prefix && *prefix) |
306 | chomp_prefix = strlen(prefix); | |
61fdbcf9 SB |
307 | |
308 | argc = parse_options(argc, argv, prefix, ls_tree_options, | |
309 | ls_tree_usage, 0); | |
310 | if (full_tree) { | |
311 | ls_tree_prefix = prefix = NULL; | |
312 | chomp_prefix = 0; | |
aa1c48df | 313 | } |
f5984671 JH |
314 | /* -d -r should imply -t, but -d by itself should not have to. */ |
315 | if ( (LS_TREE_ONLY|LS_RECURSIVE) == | |
316 | ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options)) | |
317 | ls_options |= LS_SHOW_TREES; | |
aa1c48df | 318 | |
455923e0 ÆAB |
319 | if (format && cmdmode) |
320 | usage_msg_opt( | |
321 | _("--format can't be combined with other format-altering options"), | |
322 | ls_tree_usage, ls_tree_options); | |
61fdbcf9 SB |
323 | if (argc < 1) |
324 | usage_with_options(ls_tree_usage, ls_tree_options); | |
a9b5f5bf | 325 | if (get_oid(argv[0], &oid)) |
61fdbcf9 | 326 | die("Not a valid object name %s", argv[0]); |
6af1f019 | 327 | |
0fdc2ae5 NTND |
328 | /* |
329 | * show_recursive() rolls its own matching code and is | |
330 | * generally ignorant of 'struct pathspec'. The magic mask | |
331 | * cannot be lifted until it is converted to use | |
854b0959 | 332 | * match_pathspec() or tree_entry_interesting() |
0fdc2ae5 | 333 | */ |
e1e24edc BW |
334 | parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC & |
335 | ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL), | |
0fdc2ae5 NTND |
336 | PATHSPEC_PREFER_CWD, |
337 | prefix, argv + 1); | |
f0096c06 | 338 | for (i = 0; i < pathspec.nr; i++) |
170260ae | 339 | pathspec.items[i].nowildcard_len = pathspec.items[i].len; |
33e0f62b | 340 | pathspec.has_wildcard = 0; |
a9dbc179 | 341 | tree = parse_tree_indirect(&oid); |
521698b1 | 342 | if (!tree) |
3c5e8468 | 343 | die("not a tree object"); |
455923e0 ÆAB |
344 | /* |
345 | * The generic show_tree_fmt() is slower than show_tree(), so | |
346 | * take the fast path if possible. | |
347 | */ | |
348 | if (format) { | |
349 | struct ls_tree_cmdmode_to_fmt *m2f; | |
350 | ||
351 | fn = show_tree_fmt; | |
352 | for (m2f = ls_tree_cmdmode_format; m2f->fmt; m2f++) { | |
353 | if (strcmp(format, m2f->fmt)) | |
354 | continue; | |
355 | ||
356 | cmdmode = m2f->mode; | |
357 | fn = show_tree; | |
358 | break; | |
359 | } | |
360 | } | |
361 | ||
362 | return !!read_tree(the_repository, tree, &pathspec, fn, NULL); | |
7912c070 | 363 | } |