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