]>
Commit | Line | Data |
---|---|---|
178cb243 LT |
1 | /* |
2 | * rev-parse.c | |
3 | * | |
4 | * Copyright (C) Linus Torvalds, 2005 | |
5 | */ | |
6 | #include "cache.h" | |
a8be83fe | 7 | #include "commit.h" |
960bba0d | 8 | #include "refs.h" |
a8be83fe | 9 | |
023d66ed LT |
10 | static char *def = NULL; |
11 | static int no_revs = 0; | |
12 | static int single_rev = 0; | |
13 | static int revs_only = 0; | |
14 | static int do_rev_argument = 1; | |
15 | static int output_revs = 0; | |
f79b65aa LT |
16 | static int flags_only = 0; |
17 | static int no_flags = 0; | |
5bb2c65a | 18 | static int output_sq = 0; |
023d66ed | 19 | |
042a4ed7 LT |
20 | #define NORMAL 0 |
21 | #define REVERSED 1 | |
22 | static int show_type = NORMAL; | |
23 | ||
a8be83fe | 24 | static int get_extended_sha1(char *name, unsigned char *sha1); |
178cb243 | 25 | |
921d865e LT |
26 | /* |
27 | * Some arguments are relevant "revision" arguments, | |
28 | * others are about output format or other details. | |
29 | * This sorts it all out. | |
30 | */ | |
31 | static int is_rev_argument(const char *arg) | |
32 | { | |
33 | static const char *rev_args[] = { | |
34 | "--max-count=", | |
35 | "--max-age=", | |
36 | "--min-age=", | |
37 | "--merge-order", | |
38 | NULL | |
39 | }; | |
40 | const char **p = rev_args; | |
41 | ||
42 | for (;;) { | |
43 | const char *str = *p++; | |
44 | int len; | |
45 | if (!str) | |
46 | return 0; | |
47 | len = strlen(str); | |
48 | if (!strncmp(arg, str, len)) | |
49 | return 1; | |
50 | } | |
51 | } | |
52 | ||
5bb2c65a JH |
53 | static void show(const char *arg) |
54 | { | |
55 | if (output_sq) { | |
56 | int sq = '\'', ch; | |
57 | ||
58 | putchar(sq); | |
59 | while ((ch = *arg++)) { | |
60 | if (ch == sq) | |
61 | fputs("'\\'", stdout); | |
62 | putchar(ch); | |
63 | } | |
64 | putchar(sq); | |
65 | putchar(' '); | |
66 | } | |
67 | else | |
68 | puts(arg); | |
69 | } | |
70 | ||
960bba0d | 71 | static void show_rev(int type, const unsigned char *sha1) |
023d66ed LT |
72 | { |
73 | if (no_revs) | |
74 | return; | |
75 | output_revs++; | |
5bb2c65a JH |
76 | |
77 | /* Hexadecimal string plus possibly a carret; | |
78 | * this does not have to be quoted even under output_sq. | |
79 | */ | |
80 | printf("%s%s%c", type == show_type ? "" : "^", sha1_to_hex(sha1), | |
81 | output_sq ? ' ' : '\n'); | |
023d66ed LT |
82 | } |
83 | ||
84 | static void show_rev_arg(char *rev) | |
85 | { | |
86 | if (no_revs) | |
87 | return; | |
5bb2c65a | 88 | show(rev); |
023d66ed LT |
89 | } |
90 | ||
91 | static void show_norev(char *norev) | |
92 | { | |
f79b65aa LT |
93 | if (flags_only) |
94 | return; | |
023d66ed LT |
95 | if (revs_only) |
96 | return; | |
5bb2c65a | 97 | show(norev); |
023d66ed LT |
98 | } |
99 | ||
100 | static void show_arg(char *arg) | |
101 | { | |
f79b65aa LT |
102 | if (no_flags) |
103 | return; | |
023d66ed LT |
104 | if (do_rev_argument && is_rev_argument(arg)) |
105 | show_rev_arg(arg); | |
106 | else | |
107 | show_norev(arg); | |
108 | } | |
109 | ||
a8be83fe LT |
110 | static int get_parent(char *name, unsigned char *result, int idx) |
111 | { | |
112 | unsigned char sha1[20]; | |
113 | int ret = get_extended_sha1(name, sha1); | |
114 | struct commit *commit; | |
115 | struct commit_list *p; | |
116 | ||
117 | if (ret) | |
118 | return ret; | |
119 | commit = lookup_commit_reference(sha1); | |
120 | if (!commit) | |
121 | return -1; | |
122 | if (parse_commit(commit)) | |
123 | return -1; | |
79162bb8 LT |
124 | if (!idx) { |
125 | memcpy(result, commit->object.sha1, 20); | |
126 | return 0; | |
127 | } | |
a8be83fe LT |
128 | p = commit->parents; |
129 | while (p) { | |
130 | if (!--idx) { | |
131 | memcpy(result, p->item->object.sha1, 20); | |
132 | return 0; | |
133 | } | |
134 | p = p->next; | |
135 | } | |
136 | return -1; | |
137 | } | |
138 | ||
5736bef1 LT |
139 | static int find_short_object_filename(int len, const char *name, unsigned char *sha1) |
140 | { | |
141 | static char dirname[PATH_MAX]; | |
142 | char hex[40]; | |
143 | DIR *dir; | |
144 | int found; | |
145 | ||
146 | snprintf(dirname, sizeof(dirname), "%s/%.2s", get_object_directory(), name); | |
147 | dir = opendir(dirname); | |
148 | sprintf(hex, "%.2s", name); | |
149 | found = 0; | |
150 | if (dir) { | |
151 | struct dirent *de; | |
152 | while ((de = readdir(dir)) != NULL) { | |
153 | if (strlen(de->d_name) != 38) | |
154 | continue; | |
155 | if (memcmp(de->d_name, name + 2, len-2)) | |
156 | continue; | |
157 | memcpy(hex + 2, de->d_name, 38); | |
158 | if (++found > 1) | |
159 | break; | |
160 | } | |
161 | closedir(dir); | |
162 | } | |
163 | if (found == 1) | |
164 | return get_sha1_hex(hex, sha1) == 0; | |
165 | return 0; | |
166 | } | |
167 | ||
671fe4bb LT |
168 | static int match_sha(unsigned len, const unsigned char *a, const unsigned char *b) |
169 | { | |
170 | do { | |
171 | if (*a != *b) | |
172 | return 0; | |
173 | a++; | |
174 | b++; | |
175 | len -= 2; | |
176 | } while (len > 1); | |
177 | if (len) | |
178 | if ((*a ^ *b) & 0xf0) | |
179 | return 0; | |
180 | return 1; | |
181 | } | |
182 | ||
5736bef1 LT |
183 | static int find_short_packed_object(int len, const unsigned char *match, unsigned char *sha1) |
184 | { | |
671fe4bb LT |
185 | struct packed_git *p; |
186 | ||
187 | prepare_packed_git(); | |
188 | for (p = packed_git; p; p = p->next) { | |
189 | unsigned num = num_packed_objects(p); | |
190 | unsigned first = 0, last = num; | |
191 | while (first < last) { | |
192 | unsigned mid = (first + last) / 2; | |
193 | unsigned char now[20]; | |
194 | int cmp; | |
195 | ||
196 | nth_packed_object_sha1(p, mid, now); | |
197 | cmp = memcmp(match, now, 20); | |
198 | if (!cmp) { | |
199 | first = mid; | |
200 | break; | |
201 | } | |
202 | if (cmp > 0) { | |
203 | first = mid+1; | |
204 | continue; | |
205 | } | |
206 | last = mid; | |
207 | } | |
208 | if (first < num) { | |
209 | unsigned char now[20], next[20]; | |
210 | nth_packed_object_sha1(p, first, now); | |
211 | if (match_sha(len, match, now)) { | |
212 | if (nth_packed_object_sha1(p, first+1, next) || !match_sha(len, match, next)) { | |
213 | memcpy(sha1, now, 20); | |
214 | return 1; | |
215 | } | |
216 | } | |
217 | } | |
218 | } | |
5736bef1 LT |
219 | return 0; |
220 | } | |
221 | ||
222 | static int get_short_sha1(char *name, unsigned char *sha1) | |
223 | { | |
224 | int i; | |
225 | char canonical[40]; | |
226 | unsigned char res[20]; | |
227 | ||
228 | memset(res, 0, 20); | |
229 | memset(canonical, 'x', 40); | |
230 | for (i = 0;;i++) { | |
231 | unsigned char c = name[i]; | |
232 | unsigned char val; | |
233 | if (!c || i > 40) | |
234 | break; | |
235 | if (c >= '0' && c <= '9') | |
236 | val = c - '0'; | |
237 | else if (c >= 'a' && c <= 'f') | |
238 | val = c - 'a' + 10; | |
239 | else if (c >= 'A' && c <='F') { | |
240 | val = c - 'A' + 10; | |
241 | c -= 'A' - 'a'; | |
242 | } | |
243 | else | |
244 | return -1; | |
245 | canonical[i] = c; | |
246 | if (!(i & 1)) | |
247 | val <<= 4; | |
248 | res[i >> 1] |= val; | |
249 | } | |
250 | if (i < 4) | |
251 | return -1; | |
252 | if (find_short_object_filename(i, canonical, sha1)) | |
253 | return 0; | |
254 | if (find_short_packed_object(i, res, sha1)) | |
255 | return 0; | |
256 | return -1; | |
257 | } | |
258 | ||
a8be83fe LT |
259 | /* |
260 | * This is like "get_sha1()", except it allows "sha1 expressions", | |
218e441d | 261 | * notably "xyz^" for "parent of xyz" |
a8be83fe LT |
262 | */ |
263 | static int get_extended_sha1(char *name, unsigned char *sha1) | |
264 | { | |
5736bef1 | 265 | int parent, ret; |
a8be83fe LT |
266 | int len = strlen(name); |
267 | ||
268 | parent = 1; | |
79162bb8 | 269 | if (len > 2 && name[len-1] >= '0' && name[len-1] <= '9') { |
a8be83fe LT |
270 | parent = name[len-1] - '0'; |
271 | len--; | |
272 | } | |
218e441d | 273 | if (len > 1 && name[len-1] == '^') { |
218e441d | 274 | name[len-1] = 0; |
a8be83fe | 275 | ret = get_parent(name, sha1, parent); |
218e441d | 276 | name[len-1] = '^'; |
a8be83fe LT |
277 | if (!ret) |
278 | return 0; | |
279 | } | |
5736bef1 LT |
280 | ret = get_sha1(name, sha1); |
281 | if (!ret) | |
282 | return 0; | |
283 | return get_short_sha1(name, sha1); | |
a8be83fe LT |
284 | } |
285 | ||
023d66ed LT |
286 | static void show_default(void) |
287 | { | |
288 | char *s = def; | |
289 | ||
290 | if (s) { | |
291 | unsigned char sha1[20]; | |
292 | ||
293 | def = NULL; | |
294 | if (!get_extended_sha1(s, sha1)) { | |
042a4ed7 | 295 | show_rev(NORMAL, sha1); |
023d66ed LT |
296 | return; |
297 | } | |
298 | show_arg(s); | |
299 | } | |
300 | } | |
301 | ||
960bba0d LT |
302 | static int show_reference(const char *refname, const unsigned char *sha1) |
303 | { | |
304 | show_rev(NORMAL, sha1); | |
305 | return 0; | |
306 | } | |
307 | ||
178cb243 LT |
308 | int main(int argc, char **argv) |
309 | { | |
023d66ed | 310 | int i, as_is = 0; |
178cb243 LT |
311 | unsigned char sha1[20]; |
312 | ||
313 | for (i = 1; i < argc; i++) { | |
314 | char *arg = argv[i]; | |
315 | char *dotdot; | |
316 | ||
317 | if (as_is) { | |
023d66ed | 318 | show_norev(arg); |
178cb243 LT |
319 | continue; |
320 | } | |
321 | if (*arg == '-') { | |
322 | if (!strcmp(arg, "--")) { | |
023d66ed | 323 | show_default(); |
8ebb0184 LT |
324 | if (revs_only) |
325 | break; | |
178cb243 LT |
326 | as_is = 1; |
327 | } | |
328 | if (!strcmp(arg, "--default")) { | |
178cb243 LT |
329 | def = argv[i+1]; |
330 | i++; | |
331 | continue; | |
332 | } | |
8ebb0184 LT |
333 | if (!strcmp(arg, "--revs-only")) { |
334 | revs_only = 1; | |
335 | continue; | |
336 | } | |
337 | if (!strcmp(arg, "--no-revs")) { | |
338 | no_revs = 1; | |
339 | continue; | |
340 | } | |
f79b65aa LT |
341 | if (!strcmp(arg, "--flags")) { |
342 | flags_only = 1; | |
343 | continue; | |
344 | } | |
345 | if (!strcmp(arg, "--no-flags")) { | |
346 | no_flags = 1; | |
347 | continue; | |
348 | } | |
023d66ed LT |
349 | if (!strcmp(arg, "--verify")) { |
350 | revs_only = 1; | |
351 | do_rev_argument = 0; | |
352 | single_rev = 1; | |
353 | continue; | |
921d865e | 354 | } |
5bb2c65a JH |
355 | if (!strcmp(arg, "--sq")) { |
356 | output_sq = 1; | |
357 | continue; | |
358 | } | |
042a4ed7 LT |
359 | if (!strcmp(arg, "--not")) { |
360 | show_type ^= REVERSED; | |
361 | continue; | |
362 | } | |
960bba0d LT |
363 | if (!strcmp(arg, "--all")) { |
364 | for_each_ref(show_reference); | |
365 | continue; | |
366 | } | |
023d66ed | 367 | show_arg(arg); |
178cb243 LT |
368 | continue; |
369 | } | |
178cb243 LT |
370 | dotdot = strstr(arg, ".."); |
371 | if (dotdot) { | |
372 | unsigned char end[20]; | |
373 | char *n = dotdot+2; | |
374 | *dotdot = 0; | |
a8be83fe | 375 | if (!get_extended_sha1(arg, sha1)) { |
178cb243 LT |
376 | if (!*n) |
377 | n = "HEAD"; | |
a8be83fe | 378 | if (!get_extended_sha1(n, end)) { |
8ebb0184 LT |
379 | if (no_revs) |
380 | continue; | |
381 | def = NULL; | |
042a4ed7 LT |
382 | show_rev(NORMAL, end); |
383 | show_rev(REVERSED, sha1); | |
178cb243 LT |
384 | continue; |
385 | } | |
386 | } | |
387 | *dotdot = '.'; | |
388 | } | |
a8be83fe | 389 | if (!get_extended_sha1(arg, sha1)) { |
800644c5 LT |
390 | if (no_revs) |
391 | continue; | |
392 | def = NULL; | |
042a4ed7 | 393 | show_rev(NORMAL, sha1); |
800644c5 LT |
394 | continue; |
395 | } | |
a8be83fe | 396 | if (*arg == '^' && !get_extended_sha1(arg+1, sha1)) { |
800644c5 LT |
397 | if (no_revs) |
398 | continue; | |
399 | def = NULL; | |
042a4ed7 | 400 | show_rev(REVERSED, sha1); |
800644c5 LT |
401 | continue; |
402 | } | |
023d66ed LT |
403 | show_default(); |
404 | show_norev(arg); | |
405 | } | |
406 | show_default(); | |
407 | if (single_rev && output_revs != 1) { | |
408 | fprintf(stderr, "Needed a single revision\n"); | |
409 | exit(1); | |
178cb243 | 410 | } |
178cb243 LT |
411 | return 0; |
412 | } |