]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * GIT - The information manager from hell | |
3 | * | |
4 | * Copyright (C) Linus Torvalds, 2005 | |
5 | */ | |
6 | #include "cache.h" | |
7 | #include "exec_cmd.h" | |
8 | #include "tag.h" | |
9 | #include "tree.h" | |
10 | #include "builtin.h" | |
11 | #include "parse-options.h" | |
12 | ||
13 | #define BATCH 1 | |
14 | #define BATCH_CHECK 2 | |
15 | ||
16 | static void pprint_tag(const unsigned char *sha1, const char *buf, unsigned long size) | |
17 | { | |
18 | /* the parser in tag.c is useless here. */ | |
19 | const char *endp = buf + size; | |
20 | const char *cp = buf; | |
21 | ||
22 | while (cp < endp) { | |
23 | char c = *cp++; | |
24 | if (c != '\n') | |
25 | continue; | |
26 | if (7 <= endp - cp && !memcmp("tagger ", cp, 7)) { | |
27 | const char *tagger = cp; | |
28 | ||
29 | /* Found the tagger line. Copy out the contents | |
30 | * of the buffer so far. | |
31 | */ | |
32 | write_or_die(1, buf, cp - buf); | |
33 | ||
34 | /* | |
35 | * Do something intelligent, like pretty-printing | |
36 | * the date. | |
37 | */ | |
38 | while (cp < endp) { | |
39 | if (*cp++ == '\n') { | |
40 | /* tagger to cp is a line | |
41 | * that has ident and time. | |
42 | */ | |
43 | const char *sp = tagger; | |
44 | char *ep; | |
45 | unsigned long date; | |
46 | long tz; | |
47 | while (sp < cp && *sp != '>') | |
48 | sp++; | |
49 | if (sp == cp) { | |
50 | /* give up */ | |
51 | write_or_die(1, tagger, | |
52 | cp - tagger); | |
53 | break; | |
54 | } | |
55 | while (sp < cp && | |
56 | !('0' <= *sp && *sp <= '9')) | |
57 | sp++; | |
58 | write_or_die(1, tagger, sp - tagger); | |
59 | date = strtoul(sp, &ep, 10); | |
60 | tz = strtol(ep, NULL, 10); | |
61 | sp = show_date(date, tz, 0); | |
62 | write_or_die(1, sp, strlen(sp)); | |
63 | xwrite(1, "\n", 1); | |
64 | break; | |
65 | } | |
66 | } | |
67 | break; | |
68 | } | |
69 | if (cp < endp && *cp == '\n') | |
70 | /* end of header */ | |
71 | break; | |
72 | } | |
73 | /* At this point, we have copied out the header up to the end of | |
74 | * the tagger line and cp points at one past \n. It could be the | |
75 | * next header line after the tagger line, or it could be another | |
76 | * \n that marks the end of the headers. We need to copy out the | |
77 | * remainder as is. | |
78 | */ | |
79 | if (cp < endp) | |
80 | write_or_die(1, cp, endp - cp); | |
81 | } | |
82 | ||
83 | static int cat_one_file(int opt, const char *exp_type, const char *obj_name) | |
84 | { | |
85 | unsigned char sha1[20]; | |
86 | enum object_type type; | |
87 | void *buf; | |
88 | unsigned long size; | |
89 | ||
90 | if (get_sha1(obj_name, sha1)) | |
91 | die("Not a valid object name %s", obj_name); | |
92 | ||
93 | buf = NULL; | |
94 | switch (opt) { | |
95 | case 't': | |
96 | type = sha1_object_info(sha1, NULL); | |
97 | if (type > 0) { | |
98 | printf("%s\n", typename(type)); | |
99 | return 0; | |
100 | } | |
101 | break; | |
102 | ||
103 | case 's': | |
104 | type = sha1_object_info(sha1, &size); | |
105 | if (type > 0) { | |
106 | printf("%lu\n", size); | |
107 | return 0; | |
108 | } | |
109 | break; | |
110 | ||
111 | case 'e': | |
112 | return !has_sha1_file(sha1); | |
113 | ||
114 | case 'p': | |
115 | type = sha1_object_info(sha1, NULL); | |
116 | if (type < 0) | |
117 | die("Not a valid object name %s", obj_name); | |
118 | ||
119 | /* custom pretty-print here */ | |
120 | if (type == OBJ_TREE) { | |
121 | const char *ls_args[3] = {"ls-tree", obj_name, NULL}; | |
122 | return cmd_ls_tree(2, ls_args, NULL); | |
123 | } | |
124 | ||
125 | buf = read_sha1_file(sha1, &type, &size); | |
126 | if (!buf) | |
127 | die("Cannot read object %s", obj_name); | |
128 | if (type == OBJ_TAG) { | |
129 | pprint_tag(sha1, buf, size); | |
130 | return 0; | |
131 | } | |
132 | ||
133 | /* otherwise just spit out the data */ | |
134 | break; | |
135 | case 0: | |
136 | buf = read_object_with_reference(sha1, exp_type, &size, NULL); | |
137 | break; | |
138 | ||
139 | default: | |
140 | die("git cat-file: unknown option: %s", exp_type); | |
141 | } | |
142 | ||
143 | if (!buf) | |
144 | die("git cat-file %s: bad file", obj_name); | |
145 | ||
146 | write_or_die(1, buf, size); | |
147 | return 0; | |
148 | } | |
149 | ||
150 | static int batch_one_object(const char *obj_name, int print_contents) | |
151 | { | |
152 | unsigned char sha1[20]; | |
153 | enum object_type type = 0; | |
154 | unsigned long size; | |
155 | void *contents = contents; | |
156 | ||
157 | if (!obj_name) | |
158 | return 1; | |
159 | ||
160 | if (get_sha1(obj_name, sha1)) { | |
161 | printf("%s missing\n", obj_name); | |
162 | fflush(stdout); | |
163 | return 0; | |
164 | } | |
165 | ||
166 | if (print_contents == BATCH) | |
167 | contents = read_sha1_file(sha1, &type, &size); | |
168 | else | |
169 | type = sha1_object_info(sha1, &size); | |
170 | ||
171 | if (type <= 0) { | |
172 | printf("%s missing\n", obj_name); | |
173 | fflush(stdout); | |
174 | return 0; | |
175 | } | |
176 | ||
177 | printf("%s %s %lu\n", sha1_to_hex(sha1), typename(type), size); | |
178 | fflush(stdout); | |
179 | ||
180 | if (print_contents == BATCH) { | |
181 | write_or_die(1, contents, size); | |
182 | printf("\n"); | |
183 | fflush(stdout); | |
184 | free(contents); | |
185 | } | |
186 | ||
187 | return 0; | |
188 | } | |
189 | ||
190 | static int batch_objects(int print_contents) | |
191 | { | |
192 | struct strbuf buf = STRBUF_INIT; | |
193 | ||
194 | while (strbuf_getline(&buf, stdin, '\n') != EOF) { | |
195 | int error = batch_one_object(buf.buf, print_contents); | |
196 | if (error) | |
197 | return error; | |
198 | } | |
199 | ||
200 | return 0; | |
201 | } | |
202 | ||
203 | static const char * const cat_file_usage[] = { | |
204 | "git cat-file (-t|-s|-e|-p|<type>) <object>", | |
205 | "git cat-file (--batch|--batch-check) < <list_of_objects>", | |
206 | NULL | |
207 | }; | |
208 | ||
209 | int cmd_cat_file(int argc, const char **argv, const char *prefix) | |
210 | { | |
211 | int opt = 0, batch = 0; | |
212 | const char *exp_type = NULL, *obj_name = NULL; | |
213 | ||
214 | const struct option options[] = { | |
215 | OPT_GROUP("<type> can be one of: blob, tree, commit, tag"), | |
216 | OPT_SET_INT('t', NULL, &opt, "show object type", 't'), | |
217 | OPT_SET_INT('s', NULL, &opt, "show object size", 's'), | |
218 | OPT_SET_INT('e', NULL, &opt, | |
219 | "exit with zero when there's no error", 'e'), | |
220 | OPT_SET_INT('p', NULL, &opt, "pretty-print object's content", 'p'), | |
221 | OPT_SET_INT(0, "batch", &batch, | |
222 | "show info and content of objects feeded on stdin", BATCH), | |
223 | OPT_SET_INT(0, "batch-check", &batch, | |
224 | "show info about objects feeded on stdin", | |
225 | BATCH_CHECK), | |
226 | OPT_END() | |
227 | }; | |
228 | ||
229 | git_config(git_default_config, NULL); | |
230 | ||
231 | if (argc != 3 && argc != 2) | |
232 | usage_with_options(cat_file_usage, options); | |
233 | ||
234 | argc = parse_options(argc, argv, prefix, options, cat_file_usage, 0); | |
235 | ||
236 | if (opt) { | |
237 | if (argc == 1) | |
238 | obj_name = argv[0]; | |
239 | else | |
240 | usage_with_options(cat_file_usage, options); | |
241 | } | |
242 | if (!opt && !batch) { | |
243 | if (argc == 2) { | |
244 | exp_type = argv[0]; | |
245 | obj_name = argv[1]; | |
246 | } else | |
247 | usage_with_options(cat_file_usage, options); | |
248 | } | |
249 | if (batch && (opt || argc)) { | |
250 | usage_with_options(cat_file_usage, options); | |
251 | } | |
252 | ||
253 | if (batch) | |
254 | return batch_objects(batch); | |
255 | ||
256 | return cat_one_file(opt, exp_type, obj_name); | |
257 | } |