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