]>
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 MB |
15 | |
16 | #define BATCH 1 | |
17 | #define BATCH_CHECK 2 | |
a0f15fa5 | 18 | |
eddd1c8c | 19 | static void pprint_tag(const unsigned char *sha1, const char *buf, unsigned long size) |
a0f15fa5 JH |
20 | { |
21 | /* the parser in tag.c is useless here. */ | |
22 | const char *endp = buf + size; | |
23 | const char *cp = buf; | |
24 | ||
25 | while (cp < endp) { | |
26 | char c = *cp++; | |
27 | if (c != '\n') | |
28 | continue; | |
29 | if (7 <= endp - cp && !memcmp("tagger ", cp, 7)) { | |
30 | const char *tagger = cp; | |
31 | ||
32 | /* Found the tagger line. Copy out the contents | |
33 | * of the buffer so far. | |
34 | */ | |
7230e6d0 | 35 | write_or_die(1, buf, cp - buf); |
a0f15fa5 JH |
36 | |
37 | /* | |
38 | * Do something intelligent, like pretty-printing | |
39 | * the date. | |
40 | */ | |
41 | while (cp < endp) { | |
42 | if (*cp++ == '\n') { | |
43 | /* tagger to cp is a line | |
44 | * that has ident and time. | |
45 | */ | |
46 | const char *sp = tagger; | |
47 | char *ep; | |
48 | unsigned long date; | |
49 | long tz; | |
50 | while (sp < cp && *sp != '>') | |
51 | sp++; | |
52 | if (sp == cp) { | |
53 | /* give up */ | |
7230e6d0 | 54 | write_or_die(1, tagger, |
a0f15fa5 JH |
55 | cp - tagger); |
56 | break; | |
57 | } | |
58 | while (sp < cp && | |
59 | !('0' <= *sp && *sp <= '9')) | |
60 | sp++; | |
7230e6d0 | 61 | write_or_die(1, tagger, sp - tagger); |
a0f15fa5 JH |
62 | date = strtoul(sp, &ep, 10); |
63 | tz = strtol(ep, NULL, 10); | |
9a8e35e9 | 64 | sp = show_date(date, tz, 0); |
7230e6d0 | 65 | write_or_die(1, sp, strlen(sp)); |
a0f15fa5 JH |
66 | xwrite(1, "\n", 1); |
67 | break; | |
68 | } | |
69 | } | |
70 | break; | |
71 | } | |
72 | if (cp < endp && *cp == '\n') | |
73 | /* end of header */ | |
74 | break; | |
75 | } | |
76 | /* At this point, we have copied out the header up to the end of | |
77 | * the tagger line and cp points at one past \n. It could be the | |
78 | * next header line after the tagger line, or it could be another | |
79 | * \n that marks the end of the headers. We need to copy out the | |
80 | * remainder as is. | |
81 | */ | |
82 | if (cp < endp) | |
7230e6d0 | 83 | write_or_die(1, cp, endp - cp); |
a0f15fa5 | 84 | } |
e83c5163 | 85 | |
9cf71b17 | 86 | static int cat_one_file(int opt, const char *exp_type, const char *obj_name) |
e83c5163 LT |
87 | { |
88 | unsigned char sha1[20]; | |
21666f1a | 89 | enum object_type type; |
e5fba602 | 90 | char *buf; |
e83c5163 | 91 | unsigned long size; |
e5fba602 | 92 | struct object_context obj_context; |
2b6854c8 | 93 | |
33bd598c | 94 | if (get_sha1_with_context(obj_name, 0, sha1, &obj_context)) |
2b6854c8 | 95 | die("Not a valid object name %s", obj_name); |
7950571a | 96 | |
7950571a PA |
97 | buf = NULL; |
98 | switch (opt) { | |
99 | case 't': | |
21666f1a NP |
100 | type = sha1_object_info(sha1, NULL); |
101 | if (type > 0) { | |
102 | printf("%s\n", typename(type)); | |
f2a06330 | 103 | return 0; |
11e7d5c5 | 104 | } |
7950571a PA |
105 | break; |
106 | ||
107 | case 's': | |
21666f1a NP |
108 | type = sha1_object_info(sha1, &size); |
109 | if (type > 0) { | |
7950571a PA |
110 | printf("%lu\n", size); |
111 | return 0; | |
112 | } | |
113 | break; | |
114 | ||
115 | case 'e': | |
116 | return !has_sha1_file(sha1); | |
117 | ||
a0f15fa5 | 118 | case 'p': |
21666f1a NP |
119 | type = sha1_object_info(sha1, NULL); |
120 | if (type < 0) | |
2b6854c8 | 121 | die("Not a valid object name %s", obj_name); |
a0f15fa5 JH |
122 | |
123 | /* custom pretty-print here */ | |
2b6854c8 | 124 | if (type == OBJ_TREE) { |
66dbfd55 GV |
125 | const char *ls_args[3] = { NULL }; |
126 | ls_args[0] = "ls-tree"; | |
127 | ls_args[1] = obj_name; | |
2b6854c8 SP |
128 | return cmd_ls_tree(2, ls_args, NULL); |
129 | } | |
a0f15fa5 | 130 | |
00c8fd49 NTND |
131 | if (type == OBJ_BLOB) |
132 | return stream_blob_to_fd(1, sha1, NULL, 0); | |
21666f1a | 133 | buf = read_sha1_file(sha1, &type, &size); |
a0f15fa5 | 134 | if (!buf) |
2b6854c8 | 135 | die("Cannot read object %s", obj_name); |
21666f1a | 136 | if (type == OBJ_TAG) { |
eddd1c8c DR |
137 | pprint_tag(sha1, buf, size); |
138 | return 0; | |
139 | } | |
a0f15fa5 JH |
140 | |
141 | /* otherwise just spit out the data */ | |
142 | break; | |
e5fba602 CP |
143 | |
144 | case 'c': | |
145 | if (!obj_context.path[0]) | |
146 | die("git cat-file --textconv %s: <object> must be <sha1:path>", | |
147 | obj_name); | |
148 | ||
e5450100 | 149 | if (!textconv_object(obj_context.path, obj_context.mode, sha1, 1, &buf, &size)) |
e5fba602 CP |
150 | die("git cat-file --textconv: unable to run textconv on %s", |
151 | obj_name); | |
152 | break; | |
153 | ||
7950571a | 154 | case 0: |
00c8fd49 NTND |
155 | if (type_from_string(exp_type) == OBJ_BLOB) { |
156 | unsigned char blob_sha1[20]; | |
157 | if (sha1_object_info(sha1, NULL) == OBJ_TAG) { | |
158 | enum object_type type; | |
159 | unsigned long size; | |
160 | char *buffer = read_sha1_file(sha1, &type, &size); | |
161 | if (memcmp(buffer, "object ", 7) || | |
162 | get_sha1_hex(buffer + 7, blob_sha1)) | |
163 | die("%s not a valid tag", sha1_to_hex(sha1)); | |
164 | free(buffer); | |
165 | } else | |
166 | hashcpy(blob_sha1, sha1); | |
167 | ||
168 | if (sha1_object_info(blob_sha1, NULL) == OBJ_BLOB) | |
169 | return stream_blob_to_fd(1, blob_sha1, NULL, 0); | |
170 | /* | |
171 | * we attempted to dereference a tag to a blob | |
172 | * and failed; there may be new dereference | |
173 | * mechanisms this code is not aware of. | |
174 | * fall-back to the usual case. | |
175 | */ | |
176 | } | |
2b6854c8 | 177 | buf = read_object_with_reference(sha1, exp_type, &size, NULL); |
7950571a PA |
178 | break; |
179 | ||
180 | default: | |
d7530708 | 181 | die("git cat-file: unknown option: %s", exp_type); |
bf0c6e83 LT |
182 | } |
183 | ||
11e7d5c5 | 184 | if (!buf) |
34baebce | 185 | die("git cat-file %s: bad file", obj_name); |
11e7d5c5 | 186 | |
7230e6d0 | 187 | write_or_die(1, buf, size); |
bf0c6e83 | 188 | return 0; |
e83c5163 | 189 | } |
9cf71b17 | 190 | |
a8128ed6 | 191 | static int batch_one_object(const char *obj_name, int print_contents) |
05d5667f AR |
192 | { |
193 | unsigned char sha1[20]; | |
3c076dbe | 194 | enum object_type type = 0; |
05d5667f | 195 | unsigned long size; |
cbfd5e1c | 196 | void *contents; |
05d5667f AR |
197 | |
198 | if (!obj_name) | |
199 | return 1; | |
200 | ||
201 | if (get_sha1(obj_name, sha1)) { | |
202 | printf("%s missing\n", obj_name); | |
422b2063 | 203 | fflush(stdout); |
05d5667f AR |
204 | return 0; |
205 | } | |
206 | ||
15d8e565 | 207 | if (print_contents == BATCH) |
a8128ed6 AR |
208 | contents = read_sha1_file(sha1, &type, &size); |
209 | else | |
210 | type = sha1_object_info(sha1, &size); | |
211 | ||
3c076dbe LW |
212 | if (type <= 0) { |
213 | printf("%s missing\n", obj_name); | |
214 | fflush(stdout); | |
dc4cd767 JM |
215 | if (print_contents == BATCH) |
216 | free(contents); | |
3c076dbe LW |
217 | return 0; |
218 | } | |
05d5667f AR |
219 | |
220 | printf("%s %s %lu\n", sha1_to_hex(sha1), typename(type), size); | |
a8128ed6 AR |
221 | fflush(stdout); |
222 | ||
15d8e565 | 223 | if (print_contents == BATCH) { |
a8128ed6 AR |
224 | write_or_die(1, contents, size); |
225 | printf("\n"); | |
226 | fflush(stdout); | |
5b8a94b1 | 227 | free(contents); |
a8128ed6 | 228 | } |
05d5667f AR |
229 | |
230 | return 0; | |
231 | } | |
232 | ||
a8128ed6 | 233 | static int batch_objects(int print_contents) |
05d5667f | 234 | { |
f285a2d7 | 235 | struct strbuf buf = STRBUF_INIT; |
05d5667f | 236 | |
05d5667f | 237 | while (strbuf_getline(&buf, stdin, '\n') != EOF) { |
a8128ed6 | 238 | int error = batch_one_object(buf.buf, print_contents); |
05d5667f AR |
239 | if (error) |
240 | return error; | |
241 | } | |
242 | ||
243 | return 0; | |
244 | } | |
245 | ||
15d8e565 | 246 | static const char * const cat_file_usage[] = { |
d68faec7 NTND |
247 | N_("git cat-file (-t|-s|-e|-p|<type>|--textconv) <object>"), |
248 | N_("git cat-file (--batch|--batch-check) < <list_of_objects>"), | |
15d8e565 MB |
249 | NULL |
250 | }; | |
4814dbe8 | 251 | |
e5fba602 CP |
252 | static int git_cat_file_config(const char *var, const char *value, void *cb) |
253 | { | |
6680a087 | 254 | if (userdiff_config(var, value) < 0) |
e5fba602 | 255 | return -1; |
e5fba602 CP |
256 | |
257 | return git_default_config(var, value, cb); | |
258 | } | |
259 | ||
9cf71b17 AR |
260 | int cmd_cat_file(int argc, const char **argv, const char *prefix) |
261 | { | |
15d8e565 | 262 | int opt = 0, batch = 0; |
4814dbe8 | 263 | const char *exp_type = NULL, *obj_name = NULL; |
9cf71b17 | 264 | |
15d8e565 | 265 | const struct option options[] = { |
d68faec7 NTND |
266 | OPT_GROUP(N_("<type> can be one of: blob, tree, commit, tag")), |
267 | OPT_SET_INT('t', NULL, &opt, N_("show object type"), 't'), | |
268 | OPT_SET_INT('s', NULL, &opt, N_("show object size"), 's'), | |
15d8e565 | 269 | OPT_SET_INT('e', NULL, &opt, |
d68faec7 NTND |
270 | N_("exit with zero when there's no error"), 'e'), |
271 | OPT_SET_INT('p', NULL, &opt, N_("pretty-print object's content"), 'p'), | |
e5fba602 | 272 | OPT_SET_INT(0, "textconv", &opt, |
d68faec7 | 273 | N_("for blob objects, run textconv on object's content"), 'c'), |
15d8e565 | 274 | OPT_SET_INT(0, "batch", &batch, |
d68faec7 | 275 | N_("show info and content of objects fed from the standard input"), |
9517e6b8 | 276 | BATCH), |
15d8e565 | 277 | OPT_SET_INT(0, "batch-check", &batch, |
d68faec7 | 278 | N_("show info about objects fed from the standard input"), |
15d8e565 MB |
279 | BATCH_CHECK), |
280 | OPT_END() | |
281 | }; | |
a8128ed6 | 282 | |
e5fba602 | 283 | git_config(git_cat_file_config, NULL); |
4814dbe8 | 284 | |
15d8e565 MB |
285 | if (argc != 3 && argc != 2) |
286 | usage_with_options(cat_file_usage, options); | |
4814dbe8 | 287 | |
37782920 | 288 | argc = parse_options(argc, argv, prefix, options, cat_file_usage, 0); |
05d5667f | 289 | |
15d8e565 MB |
290 | if (opt) { |
291 | if (argc == 1) | |
292 | obj_name = argv[0]; | |
293 | else | |
294 | usage_with_options(cat_file_usage, options); | |
295 | } | |
296 | if (!opt && !batch) { | |
297 | if (argc == 2) { | |
298 | exp_type = argv[0]; | |
299 | obj_name = argv[1]; | |
300 | } else | |
301 | usage_with_options(cat_file_usage, options); | |
302 | } | |
303 | if (batch && (opt || argc)) { | |
304 | usage_with_options(cat_file_usage, options); | |
9cf71b17 AR |
305 | } |
306 | ||
15d8e565 | 307 | if (batch) |
a8128ed6 | 308 | return batch_objects(batch); |
05d5667f | 309 | |
9cf71b17 AR |
310 | return cat_one_file(opt, exp_type, obj_name); |
311 | } |