git-send-pack: add "--all" option to send all refs to the other side
[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         if (!idx) {
101                 memcpy(result, commit->object.sha1, 20);
102                 return 0;
103         }
104         p = commit->parents;
105         while (p) {
106                 if (!--idx) {
107                         memcpy(result, p->item->object.sha1, 20);
108                         return 0;
109                 }
110                 p = p->next;
111         }
112         return -1;
113 }
114
115 static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
116 {
117         static char dirname[PATH_MAX];
118         char hex[40];
119         DIR *dir;
120         int found;
121
122         snprintf(dirname, sizeof(dirname), "%s/%.2s", get_object_directory(), name);
123         dir = opendir(dirname);
124         sprintf(hex, "%.2s", name);
125         found = 0;
126         if (dir) {
127                 struct dirent *de;
128                 while ((de = readdir(dir)) != NULL) {
129                         if (strlen(de->d_name) != 38)
130                                 continue;
131                         if (memcmp(de->d_name, name + 2, len-2))
132                                 continue;
133                         memcpy(hex + 2, de->d_name, 38);
134                         if (++found > 1)
135                                 break;
136                 }
137                 closedir(dir);
138         }
139         if (found == 1)
140                 return get_sha1_hex(hex, sha1) == 0;
141         return 0;
142 }
143
144 static int match_sha(unsigned len, const unsigned char *a, const unsigned char *b)
145 {
146         do {
147                 if (*a != *b)
148                         return 0;
149                 a++;
150                 b++;
151                 len -= 2;
152         } while (len > 1);
153         if (len)
154                 if ((*a ^ *b) & 0xf0)
155                         return 0;
156         return 1;
157 }
158
159 static int find_short_packed_object(int len, const unsigned char *match, unsigned char *sha1)
160 {
161         struct packed_git *p;
162
163         prepare_packed_git();
164         for (p = packed_git; p; p = p->next) {
165                 unsigned num = num_packed_objects(p);
166                 unsigned first = 0, last = num;
167                 while (first < last) {
168                         unsigned mid = (first + last) / 2;
169                         unsigned char now[20];
170                         int cmp;
171
172                         nth_packed_object_sha1(p, mid, now);
173                         cmp = memcmp(match, now, 20);
174                         if (!cmp) {
175                                 first = mid;
176                                 break;
177                         }
178                         if (cmp > 0) {
179                                 first = mid+1;
180                                 continue;
181                         }
182                         last = mid;
183                 }
184                 if (first < num) {
185                         unsigned char now[20], next[20];
186                         nth_packed_object_sha1(p, first, now);
187                         if (match_sha(len, match, now)) {
188                                 if (nth_packed_object_sha1(p, first+1, next) || !match_sha(len, match, next)) {
189                                         memcpy(sha1, now, 20);
190                                         return 1;
191                                 }
192                         }
193                 }       
194         }
195         return 0;
196 }
197
198 static int get_short_sha1(char *name, unsigned char *sha1)
199 {
200         int i;
201         char canonical[40];
202         unsigned char res[20];
203
204         memset(res, 0, 20);
205         memset(canonical, 'x', 40);
206         for (i = 0;;i++) {
207                 unsigned char c = name[i];
208                 unsigned char val;
209                 if (!c || i > 40)
210                         break;
211                 if (c >= '0' && c <= '9')
212                         val = c - '0';
213                 else if (c >= 'a' && c <= 'f')
214                         val = c - 'a' + 10;
215                 else if (c >= 'A' && c <='F') {
216                         val = c - 'A' + 10;
217                         c -= 'A' - 'a';
218                 }
219                 else
220                         return -1;
221                 canonical[i] = c;
222                 if (!(i & 1))
223                         val <<= 4;
224                 res[i >> 1] |= val;
225         }
226         if (i < 4)
227                 return -1;
228         if (find_short_object_filename(i, canonical, sha1))
229                 return 0;
230         if (find_short_packed_object(i, res, sha1))
231                 return 0;
232         return -1;
233 }
234
235 /*
236  * This is like "get_sha1()", except it allows "sha1 expressions",
237  * notably "xyz^" for "parent of xyz"
238  */
239 static int get_extended_sha1(char *name, unsigned char *sha1)
240 {
241         int parent, ret;
242         int len = strlen(name);
243
244         parent = 1;
245         if (len > 2 && name[len-1] >= '0' && name[len-1] <= '9') {
246                 parent = name[len-1] - '0';
247                 len--;
248         }
249         if (len > 1 && name[len-1] == '^') {
250                 name[len-1] = 0;
251                 ret = get_parent(name, sha1, parent);
252                 name[len-1] = '^';
253                 if (!ret)
254                         return 0;
255         }
256         ret = get_sha1(name, sha1);
257         if (!ret)
258                 return 0;
259         return get_short_sha1(name, sha1);
260 }
261
262 static void show_default(void)
263 {
264         char *s = def;
265
266         if (s) {
267                 unsigned char sha1[20];
268
269                 def = NULL;
270                 if (!get_extended_sha1(s, sha1)) {
271                         show_rev(NORMAL, sha1);
272                         return;
273                 }
274                 show_arg(s);
275         }
276 }
277
278 static int show_reference(const char *refname, const unsigned char *sha1)
279 {
280         show_rev(NORMAL, sha1);
281         return 0;
282 }
283
284 int main(int argc, char **argv)
285 {
286         int i, as_is = 0;
287         unsigned char sha1[20];
288
289         for (i = 1; i < argc; i++) {
290                 char *arg = argv[i];
291                 char *dotdot;
292         
293                 if (as_is) {
294                         show_norev(arg);
295                         continue;
296                 }
297                 if (*arg == '-') {
298                         if (!strcmp(arg, "--")) {
299                                 show_default();
300                                 if (revs_only)
301                                         break;
302                                 as_is = 1;
303                         }
304                         if (!strcmp(arg, "--default")) {
305                                 def = argv[i+1];
306                                 i++;
307                                 continue;
308                         }
309                         if (!strcmp(arg, "--revs-only")) {
310                                 revs_only = 1;
311                                 continue;
312                         }
313                         if (!strcmp(arg, "--no-revs")) {
314                                 no_revs = 1;
315                                 continue;
316                         }
317                         if (!strcmp(arg, "--flags")) {
318                                 flags_only = 1;
319                                 continue;
320                         }
321                         if (!strcmp(arg, "--no-flags")) {
322                                 no_flags = 1;
323                                 continue;
324                         }
325                         if (!strcmp(arg, "--verify")) {
326                                 revs_only = 1;
327                                 do_rev_argument = 0;
328                                 single_rev = 1;
329                                 continue;
330                         }
331                         if (!strcmp(arg, "--not")) {
332                                 show_type ^= REVERSED;
333                                 continue;
334                         }
335                         if (!strcmp(arg, "--all")) {
336                                 for_each_ref(show_reference);
337                                 continue;
338                         }
339                         show_arg(arg);
340                         continue;
341                 }
342                 dotdot = strstr(arg, "..");
343                 if (dotdot) {
344                         unsigned char end[20];
345                         char *n = dotdot+2;
346                         *dotdot = 0;
347                         if (!get_extended_sha1(arg, sha1)) {
348                                 if (!*n)
349                                         n = "HEAD";
350                                 if (!get_extended_sha1(n, end)) {
351                                         if (no_revs)
352                                                 continue;
353                                         def = NULL;
354                                         show_rev(NORMAL, end);
355                                         show_rev(REVERSED, sha1);
356                                         continue;
357                                 }
358                         }
359                         *dotdot = '.';
360                 }
361                 if (!get_extended_sha1(arg, sha1)) {
362                         if (no_revs)
363                                 continue;
364                         def = NULL;
365                         show_rev(NORMAL, sha1);
366                         continue;
367                 }
368                 if (*arg == '^' && !get_extended_sha1(arg+1, sha1)) {
369                         if (no_revs)
370                                 continue;
371                         def = NULL;
372                         show_rev(REVERSED, sha1);
373                         continue;
374                 }
375                 show_default();
376                 show_norev(arg);
377         }
378         show_default();
379         if (single_rev && output_revs != 1) {
380                 fprintf(stderr, "Needed a single revision\n");
381                 exit(1);
382         }
383         return 0;
384 }