Add "git-clone-pack" program to help with "git clone"
[git/dscho.git] / rev-parse.c
blobd62efdaf2d50684510dc696b2b71858036b0ea0b
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;
17 #define NORMAL 0
18 #define REVERSED 1
19 static int show_type = NORMAL;
21 static int get_extended_sha1(char *name, unsigned char *sha1);
24 * Some arguments are relevant "revision" arguments,
25 * others are about output format or other details.
26 * This sorts it all out.
28 static int is_rev_argument(const char *arg)
30 static const char *rev_args[] = {
31 "--max-count=",
32 "--max-age=",
33 "--min-age=",
34 "--merge-order",
35 NULL
37 const char **p = rev_args;
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;
50 static void show_rev(int type, const unsigned char *sha1)
52 if (no_revs)
53 return;
54 output_revs++;
55 printf("%s%s\n", type == show_type ? "" : "^", sha1_to_hex(sha1));
58 static void show_rev_arg(char *rev)
60 if (no_revs)
61 return;
62 puts(rev);
65 static void show_norev(char *norev)
67 if (revs_only)
68 return;
69 puts(norev);
72 static void show_arg(char *arg)
74 if (do_rev_argument && is_rev_argument(arg))
75 show_rev_arg(arg);
76 else
77 show_norev(arg);
80 static int get_parent(char *name, unsigned char *result, int idx)
82 unsigned char sha1[20];
83 int ret = get_extended_sha1(name, sha1);
84 struct commit *commit;
85 struct commit_list *p;
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;
100 p = p->next;
102 return -1;
105 static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
107 static char dirname[PATH_MAX];
108 char hex[40];
109 DIR *dir;
110 int found;
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;
127 closedir(dir);
129 if (found == 1)
130 return get_sha1_hex(hex, sha1) == 0;
131 return 0;
134 static int match_sha(unsigned len, const unsigned char *a, const unsigned char *b)
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;
149 static int find_short_packed_object(int len, const unsigned char *match, unsigned char *sha1)
151 struct packed_git *p;
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;
162 nth_packed_object_sha1(p, mid, now);
163 cmp = memcmp(match, now, 20);
164 if (!cmp) {
165 first = mid;
166 break;
168 if (cmp > 0) {
169 first = mid+1;
170 continue;
172 last = mid;
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;
185 return 0;
188 static int get_short_sha1(char *name, unsigned char *sha1)
190 int i;
191 char canonical[40];
192 unsigned char res[20];
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';
209 else
210 return -1;
211 canonical[i] = c;
212 if (!(i & 1))
213 val <<= 4;
214 res[i >> 1] |= val;
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;
226 * This is like "get_sha1()", except it allows "sha1 expressions",
227 * notably "xyz^" for "parent of xyz"
229 static int get_extended_sha1(char *name, unsigned char *sha1)
231 int parent, ret;
232 int len = strlen(name);
234 parent = 1;
235 if (len > 2 && name[len-1] >= '1' && name[len-1] <= '9') {
236 parent = name[len-1] - '0';
237 len--;
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;
246 ret = get_sha1(name, sha1);
247 if (!ret)
248 return 0;
249 return get_short_sha1(name, sha1);
252 static void show_default(void)
254 char *s = def;
256 if (s) {
257 unsigned char sha1[20];
259 def = NULL;
260 if (!get_extended_sha1(s, sha1)) {
261 show_rev(NORMAL, sha1);
262 return;
264 show_arg(s);
268 static int show_reference(const char *refname, const unsigned char *sha1)
270 show_rev(NORMAL, sha1);
271 return 0;
274 int main(int argc, char **argv)
276 int i, as_is = 0;
277 unsigned char sha1[20];
279 for (i = 1; i < argc; i++) {
280 char *arg = argv[i];
281 char *dotdot;
283 if (as_is) {
284 show_norev(arg);
285 continue;
287 if (*arg == '-') {
288 if (!strcmp(arg, "--")) {
289 show_default();
290 if (revs_only)
291 break;
292 as_is = 1;
294 if (!strcmp(arg, "--default")) {
295 def = argv[i+1];
296 i++;
297 continue;
299 if (!strcmp(arg, "--revs-only")) {
300 revs_only = 1;
301 continue;
303 if (!strcmp(arg, "--no-revs")) {
304 no_revs = 1;
305 continue;
307 if (!strcmp(arg, "--verify")) {
308 revs_only = 1;
309 do_rev_argument = 0;
310 single_rev = 1;
311 continue;
313 if (!strcmp(arg, "--not")) {
314 show_type ^= REVERSED;
315 continue;
317 if (!strcmp(arg, "--all")) {
318 for_each_ref(show_reference);
319 continue;
321 show_arg(arg);
322 continue;
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;
341 *dotdot = '.';
343 if (!get_extended_sha1(arg, sha1)) {
344 if (no_revs)
345 continue;
346 def = NULL;
347 show_rev(NORMAL, sha1);
348 continue;
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;
357 show_default();
358 show_norev(arg);
360 show_default();
361 if (single_rev && output_revs != 1) {
362 fprintf(stderr, "Needed a single revision\n");
363 exit(1);
365 return 0;