[PATCH] Help scripts that use git-rev-parse to grok args with SP/TAB/LF
[git/gitweb.git] / rev-parse.c
blob4bd58ebb67358b5640a7c89a24477c80c84ae7f4
1 /*
2 * rev-parse.c
4 * Copyright (C) Linus Torvalds, 2005
5 */
6 #include "cache.h"
7 #include "commit.h"
8 #include "refs.h"
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;
16 static int flags_only = 0;
17 static int no_flags = 0;
18 static int output_sq = 0;
20 #define NORMAL 0
21 #define REVERSED 1
22 static int show_type = NORMAL;
24 static int get_extended_sha1(char *name, unsigned char *sha1);
27 * Some arguments are relevant "revision" arguments,
28 * others are about output format or other details.
29 * This sorts it all out.
31 static int is_rev_argument(const char *arg)
33 static const char *rev_args[] = {
34 "--max-count=",
35 "--max-age=",
36 "--min-age=",
37 "--merge-order",
38 NULL
40 const char **p = rev_args;
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;
53 static void show(const char *arg)
55 if (output_sq) {
56 int sq = '\'', ch;
58 putchar(sq);
59 while ((ch = *arg++)) {
60 if (ch == sq)
61 fputs("'\\'", stdout);
62 putchar(ch);
64 putchar(sq);
65 putchar(' ');
67 else
68 puts(arg);
71 static void show_rev(int type, const unsigned char *sha1)
73 if (no_revs)
74 return;
75 output_revs++;
77 /* Hexadecimal string plus possibly a carret;
78 * this does not have to be quoted even under output_sq.
80 printf("%s%s%c", type == show_type ? "" : "^", sha1_to_hex(sha1),
81 output_sq ? ' ' : '\n');
84 static void show_rev_arg(char *rev)
86 if (no_revs)
87 return;
88 show(rev);
91 static void show_norev(char *norev)
93 if (flags_only)
94 return;
95 if (revs_only)
96 return;
97 show(norev);
100 static void show_arg(char *arg)
102 if (no_flags)
103 return;
104 if (do_rev_argument && is_rev_argument(arg))
105 show_rev_arg(arg);
106 else
107 show_norev(arg);
110 static int get_parent(char *name, unsigned char *result, int idx)
112 unsigned char sha1[20];
113 int ret = get_extended_sha1(name, sha1);
114 struct commit *commit;
115 struct commit_list *p;
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;
124 if (!idx) {
125 memcpy(result, commit->object.sha1, 20);
126 return 0;
128 p = commit->parents;
129 while (p) {
130 if (!--idx) {
131 memcpy(result, p->item->object.sha1, 20);
132 return 0;
134 p = p->next;
136 return -1;
139 static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
141 static char dirname[PATH_MAX];
142 char hex[40];
143 DIR *dir;
144 int found;
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;
161 closedir(dir);
163 if (found == 1)
164 return get_sha1_hex(hex, sha1) == 0;
165 return 0;
168 static int match_sha(unsigned len, const unsigned char *a, const unsigned char *b)
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;
183 static int find_short_packed_object(int len, const unsigned char *match, unsigned char *sha1)
185 struct packed_git *p;
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;
196 nth_packed_object_sha1(p, mid, now);
197 cmp = memcmp(match, now, 20);
198 if (!cmp) {
199 first = mid;
200 break;
202 if (cmp > 0) {
203 first = mid+1;
204 continue;
206 last = mid;
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;
219 return 0;
222 static int get_short_sha1(char *name, unsigned char *sha1)
224 int i;
225 char canonical[40];
226 unsigned char res[20];
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';
243 else
244 return -1;
245 canonical[i] = c;
246 if (!(i & 1))
247 val <<= 4;
248 res[i >> 1] |= val;
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;
260 * This is like "get_sha1()", except it allows "sha1 expressions",
261 * notably "xyz^" for "parent of xyz"
263 static int get_extended_sha1(char *name, unsigned char *sha1)
265 int parent, ret;
266 int len = strlen(name);
268 parent = 1;
269 if (len > 2 && name[len-1] >= '0' && name[len-1] <= '9') {
270 parent = name[len-1] - '0';
271 len--;
273 if (len > 1 && name[len-1] == '^') {
274 name[len-1] = 0;
275 ret = get_parent(name, sha1, parent);
276 name[len-1] = '^';
277 if (!ret)
278 return 0;
280 ret = get_sha1(name, sha1);
281 if (!ret)
282 return 0;
283 return get_short_sha1(name, sha1);
286 static void show_default(void)
288 char *s = def;
290 if (s) {
291 unsigned char sha1[20];
293 def = NULL;
294 if (!get_extended_sha1(s, sha1)) {
295 show_rev(NORMAL, sha1);
296 return;
298 show_arg(s);
302 static int show_reference(const char *refname, const unsigned char *sha1)
304 show_rev(NORMAL, sha1);
305 return 0;
308 int main(int argc, char **argv)
310 int i, as_is = 0;
311 unsigned char sha1[20];
313 for (i = 1; i < argc; i++) {
314 char *arg = argv[i];
315 char *dotdot;
317 if (as_is) {
318 show_norev(arg);
319 continue;
321 if (*arg == '-') {
322 if (!strcmp(arg, "--")) {
323 show_default();
324 if (revs_only)
325 break;
326 as_is = 1;
328 if (!strcmp(arg, "--default")) {
329 def = argv[i+1];
330 i++;
331 continue;
333 if (!strcmp(arg, "--revs-only")) {
334 revs_only = 1;
335 continue;
337 if (!strcmp(arg, "--no-revs")) {
338 no_revs = 1;
339 continue;
341 if (!strcmp(arg, "--flags")) {
342 flags_only = 1;
343 continue;
345 if (!strcmp(arg, "--no-flags")) {
346 no_flags = 1;
347 continue;
349 if (!strcmp(arg, "--verify")) {
350 revs_only = 1;
351 do_rev_argument = 0;
352 single_rev = 1;
353 continue;
355 if (!strcmp(arg, "--sq")) {
356 output_sq = 1;
357 continue;
359 if (!strcmp(arg, "--not")) {
360 show_type ^= REVERSED;
361 continue;
363 if (!strcmp(arg, "--all")) {
364 for_each_ref(show_reference);
365 continue;
367 show_arg(arg);
368 continue;
370 dotdot = strstr(arg, "..");
371 if (dotdot) {
372 unsigned char end[20];
373 char *n = dotdot+2;
374 *dotdot = 0;
375 if (!get_extended_sha1(arg, sha1)) {
376 if (!*n)
377 n = "HEAD";
378 if (!get_extended_sha1(n, end)) {
379 if (no_revs)
380 continue;
381 def = NULL;
382 show_rev(NORMAL, end);
383 show_rev(REVERSED, sha1);
384 continue;
387 *dotdot = '.';
389 if (!get_extended_sha1(arg, sha1)) {
390 if (no_revs)
391 continue;
392 def = NULL;
393 show_rev(NORMAL, sha1);
394 continue;
396 if (*arg == '^' && !get_extended_sha1(arg+1, sha1)) {
397 if (no_revs)
398 continue;
399 def = NULL;
400 show_rev(REVERSED, sha1);
401 continue;
403 show_default();
404 show_norev(arg);
406 show_default();
407 if (single_rev && output_revs != 1) {
408 fprintf(stderr, "Needed a single revision\n");
409 exit(1);
411 return 0;