[PATCH] Reorder Makefile rules
[git.git] / rev-parse.c
1 /*
2  * rev-parse.c
3  *
4  * Copyright (C) Linus Torvalds, 2005
5  */
6 #include "cache.h"
7 #include "commit.h"
8 #include "refs.h"
9
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;
19
20 #define NORMAL 0
21 #define REVERSED 1
22 static int show_type = NORMAL;
23
24 static int get_extended_sha1(char *name, unsigned char *sha1);
25
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
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
71 static void show_rev(int type, const unsigned char *sha1)
72 {
73         if (no_revs)
74                 return;
75         output_revs++;
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');
82 }
83
84 static void show_rev_arg(char *rev)
85 {
86         if (no_revs)
87                 return;
88         show(rev);
89 }
90
91 static void show_norev(char *norev)
92 {
93         if (flags_only)
94                 return;
95         if (revs_only)
96                 return;
97         show(norev);
98 }
99
100 static void show_arg(char *arg)
101 {
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);
108 }
109
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;
124         if (!idx) {
125                 memcpy(result, commit->object.sha1, 20);
126                 return 0;
127         }
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
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
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
183 static int find_short_packed_object(int len, const unsigned char *match, unsigned char *sha1)
184 {
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         }
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
259 /*
260  * This is like "get_sha1()", except it allows "sha1 expressions",
261  * notably "xyz^" for "parent of xyz"
262  */
263 static int get_extended_sha1(char *name, unsigned char *sha1)
264 {
265         int parent, ret;
266         int len = strlen(name);
267
268         parent = 1;
269         if (len > 2 && name[len-1] >= '0' && name[len-1] <= '9') {
270                 parent = name[len-1] - '0';
271                 len--;
272         }
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;
279         }
280         ret = get_sha1(name, sha1);
281         if (!ret)
282                 return 0;
283         return get_short_sha1(name, sha1);
284 }
285
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)) {
295                         show_rev(NORMAL, sha1);
296                         return;
297                 }
298                 show_arg(s);
299         }
300 }
301
302 static int show_reference(const char *refname, const unsigned char *sha1)
303 {
304         show_rev(NORMAL, sha1);
305         return 0;
306 }
307
308 int main(int argc, char **argv)
309 {
310         int i, as_is = 0;
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) {
318                         show_norev(arg);
319                         continue;
320                 }
321                 if (*arg == '-') {
322                         if (!strcmp(arg, "--")) {
323                                 show_default();
324                                 if (revs_only)
325                                         break;
326                                 as_is = 1;
327                         }
328                         if (!strcmp(arg, "--default")) {
329                                 def = argv[i+1];
330                                 i++;
331                                 continue;
332                         }
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                         }
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                         }
349                         if (!strcmp(arg, "--verify")) {
350                                 revs_only = 1;
351                                 do_rev_argument = 0;
352                                 single_rev = 1;
353                                 continue;
354                         }
355                         if (!strcmp(arg, "--sq")) {
356                                 output_sq = 1;
357                                 continue;
358                         }
359                         if (!strcmp(arg, "--not")) {
360                                 show_type ^= REVERSED;
361                                 continue;
362                         }
363                         if (!strcmp(arg, "--all")) {
364                                 for_each_ref(show_reference);
365                                 continue;
366                         }
367                         show_arg(arg);
368                         continue;
369                 }
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;
385                                 }
386                         }
387                         *dotdot = '.';
388                 }
389                 if (!get_extended_sha1(arg, sha1)) {
390                         if (no_revs)
391                                 continue;
392                         def = NULL;
393                         show_rev(NORMAL, sha1);
394                         continue;
395                 }
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;
402                 }
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);
410         }
411         return 0;
412 }