]>
Commit | Line | Data |
---|---|---|
8bc9a0c7 LT |
1 | /* |
2 | * GIT - The information manager from hell | |
3 | * | |
4 | * Copyright (C) Linus Torvalds, 2005 | |
5 | */ | |
e83c5163 | 6 | #include "cache.h" |
a0f15fa5 | 7 | #include "exec_cmd.h" |
8e440259 PE |
8 | #include "tag.h" |
9 | #include "tree.h" | |
f81daefe | 10 | #include "builtin.h" |
15d8e565 | 11 | #include "parse-options.h" |
e5fba602 CP |
12 | #include "diff.h" |
13 | #include "userdiff.h" | |
00c8fd49 | 14 | #include "streaming.h" |
15d8e565 | 15 | |
9cf71b17 | 16 | static int cat_one_file(int opt, const char *exp_type, const char *obj_name) |
e83c5163 LT |
17 | { |
18 | unsigned char sha1[20]; | |
21666f1a | 19 | enum object_type type; |
e5fba602 | 20 | char *buf; |
e83c5163 | 21 | unsigned long size; |
e5fba602 | 22 | struct object_context obj_context; |
2b6854c8 | 23 | |
33bd598c | 24 | if (get_sha1_with_context(obj_name, 0, sha1, &obj_context)) |
2b6854c8 | 25 | die("Not a valid object name %s", obj_name); |
7950571a | 26 | |
7950571a PA |
27 | buf = NULL; |
28 | switch (opt) { | |
29 | case 't': | |
21666f1a NP |
30 | type = sha1_object_info(sha1, NULL); |
31 | if (type > 0) { | |
32 | printf("%s\n", typename(type)); | |
f2a06330 | 33 | return 0; |
11e7d5c5 | 34 | } |
7950571a PA |
35 | break; |
36 | ||
37 | case 's': | |
21666f1a NP |
38 | type = sha1_object_info(sha1, &size); |
39 | if (type > 0) { | |
7950571a PA |
40 | printf("%lu\n", size); |
41 | return 0; | |
42 | } | |
43 | break; | |
44 | ||
45 | case 'e': | |
46 | return !has_sha1_file(sha1); | |
47 | ||
3ac21617 MG |
48 | case 'c': |
49 | if (!obj_context.path[0]) | |
50 | die("git cat-file --textconv %s: <object> must be <sha1:path>", | |
51 | obj_name); | |
52 | ||
53 | if (textconv_object(obj_context.path, obj_context.mode, sha1, 1, &buf, &size)) | |
54 | break; | |
55 | ||
a0f15fa5 | 56 | case 'p': |
21666f1a NP |
57 | type = sha1_object_info(sha1, NULL); |
58 | if (type < 0) | |
2b6854c8 | 59 | die("Not a valid object name %s", obj_name); |
a0f15fa5 JH |
60 | |
61 | /* custom pretty-print here */ | |
2b6854c8 | 62 | if (type == OBJ_TREE) { |
66dbfd55 GV |
63 | const char *ls_args[3] = { NULL }; |
64 | ls_args[0] = "ls-tree"; | |
65 | ls_args[1] = obj_name; | |
2b6854c8 SP |
66 | return cmd_ls_tree(2, ls_args, NULL); |
67 | } | |
a0f15fa5 | 68 | |
00c8fd49 NTND |
69 | if (type == OBJ_BLOB) |
70 | return stream_blob_to_fd(1, sha1, NULL, 0); | |
21666f1a | 71 | buf = read_sha1_file(sha1, &type, &size); |
a0f15fa5 | 72 | if (!buf) |
2b6854c8 | 73 | die("Cannot read object %s", obj_name); |
a0f15fa5 JH |
74 | |
75 | /* otherwise just spit out the data */ | |
76 | break; | |
e5fba602 | 77 | |
7950571a | 78 | case 0: |
00c8fd49 NTND |
79 | if (type_from_string(exp_type) == OBJ_BLOB) { |
80 | unsigned char blob_sha1[20]; | |
81 | if (sha1_object_info(sha1, NULL) == OBJ_TAG) { | |
82 | enum object_type type; | |
83 | unsigned long size; | |
84 | char *buffer = read_sha1_file(sha1, &type, &size); | |
e3f1da98 RS |
85 | const char *target; |
86 | if (!skip_prefix(buffer, "object ", &target) || | |
87 | get_sha1_hex(target, blob_sha1)) | |
00c8fd49 NTND |
88 | die("%s not a valid tag", sha1_to_hex(sha1)); |
89 | free(buffer); | |
90 | } else | |
91 | hashcpy(blob_sha1, sha1); | |
92 | ||
93 | if (sha1_object_info(blob_sha1, NULL) == OBJ_BLOB) | |
94 | return stream_blob_to_fd(1, blob_sha1, NULL, 0); | |
95 | /* | |
96 | * we attempted to dereference a tag to a blob | |
97 | * and failed; there may be new dereference | |
98 | * mechanisms this code is not aware of. | |
99 | * fall-back to the usual case. | |
100 | */ | |
101 | } | |
2b6854c8 | 102 | buf = read_object_with_reference(sha1, exp_type, &size, NULL); |
7950571a PA |
103 | break; |
104 | ||
105 | default: | |
d7530708 | 106 | die("git cat-file: unknown option: %s", exp_type); |
bf0c6e83 LT |
107 | } |
108 | ||
11e7d5c5 | 109 | if (!buf) |
34baebce | 110 | die("git cat-file %s: bad file", obj_name); |
11e7d5c5 | 111 | |
7230e6d0 | 112 | write_or_die(1, buf, size); |
bf0c6e83 | 113 | return 0; |
e83c5163 | 114 | } |
9cf71b17 | 115 | |
93d2a607 JK |
116 | struct expand_data { |
117 | unsigned char sha1[20]; | |
118 | enum object_type type; | |
119 | unsigned long size; | |
a4ac1061 | 120 | unsigned long disk_size; |
97be0407 | 121 | const char *rest; |
65ea9c3c | 122 | unsigned char delta_base_sha1[20]; |
93d2a607 JK |
123 | |
124 | /* | |
125 | * If mark_query is true, we do not expand anything, but rather | |
126 | * just mark the object_info with items we wish to query. | |
127 | */ | |
128 | int mark_query; | |
129 | ||
97be0407 JK |
130 | /* |
131 | * Whether to split the input on whitespace before feeding it to | |
132 | * get_sha1; this is decided during the mark_query phase based on | |
133 | * whether we have a %(rest) token in our format. | |
134 | */ | |
135 | int split_on_whitespace; | |
136 | ||
93d2a607 JK |
137 | /* |
138 | * After a mark_query run, this object_info is set up to be | |
139 | * passed to sha1_object_info_extended. It will point to the data | |
140 | * elements above, so you can retrieve the response from there. | |
141 | */ | |
142 | struct object_info info; | |
143 | }; | |
144 | ||
145 | static int is_atom(const char *atom, const char *s, int slen) | |
146 | { | |
147 | int alen = strlen(atom); | |
148 | return alen == slen && !memcmp(atom, s, alen); | |
149 | } | |
150 | ||
151 | static void expand_atom(struct strbuf *sb, const char *atom, int len, | |
152 | void *vdata) | |
153 | { | |
154 | struct expand_data *data = vdata; | |
155 | ||
156 | if (is_atom("objectname", atom, len)) { | |
157 | if (!data->mark_query) | |
158 | strbuf_addstr(sb, sha1_to_hex(data->sha1)); | |
159 | } else if (is_atom("objecttype", atom, len)) { | |
5b086407 JK |
160 | if (data->mark_query) |
161 | data->info.typep = &data->type; | |
162 | else | |
93d2a607 JK |
163 | strbuf_addstr(sb, typename(data->type)); |
164 | } else if (is_atom("objectsize", atom, len)) { | |
165 | if (data->mark_query) | |
166 | data->info.sizep = &data->size; | |
167 | else | |
168 | strbuf_addf(sb, "%lu", data->size); | |
a4ac1061 JK |
169 | } else if (is_atom("objectsize:disk", atom, len)) { |
170 | if (data->mark_query) | |
171 | data->info.disk_sizep = &data->disk_size; | |
172 | else | |
173 | strbuf_addf(sb, "%lu", data->disk_size); | |
97be0407 JK |
174 | } else if (is_atom("rest", atom, len)) { |
175 | if (data->mark_query) | |
176 | data->split_on_whitespace = 1; | |
177 | else if (data->rest) | |
178 | strbuf_addstr(sb, data->rest); | |
65ea9c3c JK |
179 | } else if (is_atom("deltabase", atom, len)) { |
180 | if (data->mark_query) | |
181 | data->info.delta_base_sha1 = data->delta_base_sha1; | |
182 | else | |
183 | strbuf_addstr(sb, sha1_to_hex(data->delta_base_sha1)); | |
93d2a607 JK |
184 | } else |
185 | die("unknown format element: %.*s", len, atom); | |
186 | } | |
187 | ||
188 | static size_t expand_format(struct strbuf *sb, const char *start, void *data) | |
189 | { | |
190 | const char *end; | |
191 | ||
192 | if (*start != '(') | |
193 | return 0; | |
194 | end = strchr(start + 1, ')'); | |
195 | if (!end) | |
196 | die("format element '%s' does not end in ')'", start); | |
197 | ||
198 | expand_atom(sb, start + 1, end - start - 1, data); | |
199 | ||
200 | return end - start + 1; | |
201 | } | |
202 | ||
370c9268 | 203 | static void print_object_or_die(int fd, struct expand_data *data) |
98e2092b | 204 | { |
370c9268 JK |
205 | const unsigned char *sha1 = data->sha1; |
206 | ||
6554dfa9 JK |
207 | assert(data->info.typep); |
208 | ||
370c9268 | 209 | if (data->type == OBJ_BLOB) { |
98e2092b JK |
210 | if (stream_blob_to_fd(fd, sha1, NULL, 0) < 0) |
211 | die("unable to stream %s to stdout", sha1_to_hex(sha1)); | |
212 | } | |
213 | else { | |
370c9268 JK |
214 | enum object_type type; |
215 | unsigned long size; | |
98e2092b JK |
216 | void *contents; |
217 | ||
370c9268 | 218 | contents = read_sha1_file(sha1, &type, &size); |
98e2092b JK |
219 | if (!contents) |
220 | die("object %s disappeared", sha1_to_hex(sha1)); | |
370c9268 | 221 | if (type != data->type) |
98e2092b | 222 | die("object %s changed type!?", sha1_to_hex(sha1)); |
6554dfa9 | 223 | if (data->info.sizep && size != data->size) |
370c9268 | 224 | die("object %s changed size!?", sha1_to_hex(sha1)); |
98e2092b JK |
225 | |
226 | write_or_die(fd, contents, size); | |
227 | free(contents); | |
228 | } | |
229 | } | |
230 | ||
b71bd480 JK |
231 | struct batch_options { |
232 | int enabled; | |
233 | int print_contents; | |
93d2a607 | 234 | const char *format; |
b71bd480 JK |
235 | }; |
236 | ||
93d2a607 JK |
237 | static int batch_one_object(const char *obj_name, struct batch_options *opt, |
238 | struct expand_data *data) | |
05d5667f | 239 | { |
93d2a607 | 240 | struct strbuf buf = STRBUF_INIT; |
05d5667f AR |
241 | |
242 | if (!obj_name) | |
243 | return 1; | |
244 | ||
93d2a607 | 245 | if (get_sha1(obj_name, data->sha1)) { |
05d5667f | 246 | printf("%s missing\n", obj_name); |
422b2063 | 247 | fflush(stdout); |
05d5667f AR |
248 | return 0; |
249 | } | |
250 | ||
de7b5d62 | 251 | if (sha1_object_info_extended(data->sha1, &data->info, LOOKUP_REPLACE_OBJECT) < 0) { |
3c076dbe LW |
252 | printf("%s missing\n", obj_name); |
253 | fflush(stdout); | |
254 | return 0; | |
255 | } | |
05d5667f | 256 | |
93d2a607 JK |
257 | strbuf_expand(&buf, opt->format, expand_format, data); |
258 | strbuf_addch(&buf, '\n'); | |
259 | write_or_die(1, buf.buf, buf.len); | |
260 | strbuf_release(&buf); | |
a8128ed6 | 261 | |
b71bd480 | 262 | if (opt->print_contents) { |
370c9268 | 263 | print_object_or_die(1, data); |
98e2092b | 264 | write_or_die(1, "\n", 1); |
a8128ed6 | 265 | } |
05d5667f AR |
266 | return 0; |
267 | } | |
268 | ||
b71bd480 | 269 | static int batch_objects(struct batch_options *opt) |
05d5667f | 270 | { |
f285a2d7 | 271 | struct strbuf buf = STRBUF_INIT; |
93d2a607 | 272 | struct expand_data data; |
a42fcd15 | 273 | int save_warning; |
07e23839 | 274 | int retval = 0; |
93d2a607 JK |
275 | |
276 | if (!opt->format) | |
277 | opt->format = "%(objectname) %(objecttype) %(objectsize)"; | |
278 | ||
279 | /* | |
280 | * Expand once with our special mark_query flag, which will prime the | |
281 | * object_info to be handed to sha1_object_info_extended for each | |
282 | * object. | |
283 | */ | |
284 | memset(&data, 0, sizeof(data)); | |
285 | data.mark_query = 1; | |
286 | strbuf_expand(&buf, opt->format, expand_format, &data); | |
287 | data.mark_query = 0; | |
05d5667f | 288 | |
6554dfa9 JK |
289 | /* |
290 | * If we are printing out the object, then always fill in the type, | |
291 | * since we will want to decide whether or not to stream. | |
292 | */ | |
293 | if (opt->print_contents) | |
294 | data.info.typep = &data.type; | |
295 | ||
25fba78d JK |
296 | /* |
297 | * We are going to call get_sha1 on a potentially very large number of | |
298 | * objects. In most large cases, these will be actual object sha1s. The | |
299 | * cost to double-check that each one is not also a ref (just so we can | |
300 | * warn) ends up dwarfing the actual cost of the object lookups | |
301 | * themselves. We can work around it by just turning off the warning. | |
302 | */ | |
a42fcd15 | 303 | save_warning = warn_on_object_refname_ambiguity; |
25fba78d JK |
304 | warn_on_object_refname_ambiguity = 0; |
305 | ||
05d5667f | 306 | while (strbuf_getline(&buf, stdin, '\n') != EOF) { |
97be0407 JK |
307 | if (data.split_on_whitespace) { |
308 | /* | |
309 | * Split at first whitespace, tying off the beginning | |
310 | * of the string and saving the remainder (or NULL) in | |
311 | * data.rest. | |
312 | */ | |
313 | char *p = strpbrk(buf.buf, " \t"); | |
314 | if (p) { | |
315 | while (*p && strchr(" \t", *p)) | |
316 | *p++ = '\0'; | |
317 | } | |
318 | data.rest = p; | |
319 | } | |
320 | ||
07e23839 JK |
321 | retval = batch_one_object(buf.buf, opt, &data); |
322 | if (retval) | |
323 | break; | |
05d5667f AR |
324 | } |
325 | ||
648027c4 | 326 | strbuf_release(&buf); |
a42fcd15 | 327 | warn_on_object_refname_ambiguity = save_warning; |
07e23839 | 328 | return retval; |
05d5667f AR |
329 | } |
330 | ||
15d8e565 | 331 | static const char * const cat_file_usage[] = { |
d68faec7 NTND |
332 | N_("git cat-file (-t|-s|-e|-p|<type>|--textconv) <object>"), |
333 | N_("git cat-file (--batch|--batch-check) < <list_of_objects>"), | |
15d8e565 MB |
334 | NULL |
335 | }; | |
4814dbe8 | 336 | |
e5fba602 CP |
337 | static int git_cat_file_config(const char *var, const char *value, void *cb) |
338 | { | |
6680a087 | 339 | if (userdiff_config(var, value) < 0) |
e5fba602 | 340 | return -1; |
e5fba602 CP |
341 | |
342 | return git_default_config(var, value, cb); | |
343 | } | |
344 | ||
b71bd480 JK |
345 | static int batch_option_callback(const struct option *opt, |
346 | const char *arg, | |
347 | int unset) | |
348 | { | |
349 | struct batch_options *bo = opt->value; | |
350 | ||
351 | if (unset) { | |
352 | memset(bo, 0, sizeof(*bo)); | |
353 | return 0; | |
354 | } | |
355 | ||
356 | bo->enabled = 1; | |
357 | bo->print_contents = !strcmp(opt->long_name, "batch"); | |
93d2a607 | 358 | bo->format = arg; |
b71bd480 JK |
359 | |
360 | return 0; | |
361 | } | |
362 | ||
9cf71b17 AR |
363 | int cmd_cat_file(int argc, const char **argv, const char *prefix) |
364 | { | |
b71bd480 | 365 | int opt = 0; |
4814dbe8 | 366 | const char *exp_type = NULL, *obj_name = NULL; |
b71bd480 | 367 | struct batch_options batch = {0}; |
9cf71b17 | 368 | |
15d8e565 | 369 | const struct option options[] = { |
d68faec7 NTND |
370 | OPT_GROUP(N_("<type> can be one of: blob, tree, commit, tag")), |
371 | OPT_SET_INT('t', NULL, &opt, N_("show object type"), 't'), | |
372 | OPT_SET_INT('s', NULL, &opt, N_("show object size"), 's'), | |
15d8e565 | 373 | OPT_SET_INT('e', NULL, &opt, |
d68faec7 NTND |
374 | N_("exit with zero when there's no error"), 'e'), |
375 | OPT_SET_INT('p', NULL, &opt, N_("pretty-print object's content"), 'p'), | |
e5fba602 | 376 | OPT_SET_INT(0, "textconv", &opt, |
d68faec7 | 377 | N_("for blob objects, run textconv on object's content"), 'c'), |
93d2a607 | 378 | { OPTION_CALLBACK, 0, "batch", &batch, "format", |
b71bd480 | 379 | N_("show info and content of objects fed from the standard input"), |
93d2a607 JK |
380 | PARSE_OPT_OPTARG, batch_option_callback }, |
381 | { OPTION_CALLBACK, 0, "batch-check", &batch, "format", | |
b71bd480 | 382 | N_("show info about objects fed from the standard input"), |
93d2a607 | 383 | PARSE_OPT_OPTARG, batch_option_callback }, |
15d8e565 MB |
384 | OPT_END() |
385 | }; | |
a8128ed6 | 386 | |
e5fba602 | 387 | git_config(git_cat_file_config, NULL); |
4814dbe8 | 388 | |
15d8e565 MB |
389 | if (argc != 3 && argc != 2) |
390 | usage_with_options(cat_file_usage, options); | |
4814dbe8 | 391 | |
37782920 | 392 | argc = parse_options(argc, argv, prefix, options, cat_file_usage, 0); |
05d5667f | 393 | |
15d8e565 MB |
394 | if (opt) { |
395 | if (argc == 1) | |
396 | obj_name = argv[0]; | |
397 | else | |
398 | usage_with_options(cat_file_usage, options); | |
399 | } | |
b71bd480 | 400 | if (!opt && !batch.enabled) { |
15d8e565 MB |
401 | if (argc == 2) { |
402 | exp_type = argv[0]; | |
403 | obj_name = argv[1]; | |
404 | } else | |
405 | usage_with_options(cat_file_usage, options); | |
406 | } | |
b71bd480 | 407 | if (batch.enabled && (opt || argc)) { |
15d8e565 | 408 | usage_with_options(cat_file_usage, options); |
9cf71b17 AR |
409 | } |
410 | ||
b71bd480 JK |
411 | if (batch.enabled) |
412 | return batch_objects(&batch); | |
05d5667f | 413 | |
9cf71b17 AR |
414 | return cat_one_file(opt, exp_type, obj_name); |
415 | } |