git-rev-parse: re-organize and be more careful
[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
9 static char *def = NULL;
10 static int no_revs = 0;
11 static int single_rev = 0;
12 static int revs_only = 0;
13 static int do_rev_argument = 1;
14 static int output_revs = 0;
15
16 static int get_extended_sha1(char *name, unsigned char *sha1);
17
18 /*
19  * Some arguments are relevant "revision" arguments,
20  * others are about output format or other details.
21  * This sorts it all out.
22  */
23 static int is_rev_argument(const char *arg)
24 {
25         static const char *rev_args[] = {
26                 "--max-count=",
27                 "--max-age=",
28                 "--min-age=",
29                 "--merge-order",
30                 NULL
31         };
32         const char **p = rev_args;
33
34         for (;;) {
35                 const char *str = *p++;
36                 int len;
37                 if (!str)
38                         return 0;
39                 len = strlen(str);
40                 if (!strncmp(arg, str, len))
41                         return 1;
42         }
43 }
44
45 static void show_rev(unsigned char *sha1)
46 {
47         if (no_revs)
48                 return;
49         output_revs++;
50         puts(sha1_to_hex(sha1));
51 }
52
53 static void show_antirev(unsigned char *sha1)
54 {
55         if (no_revs)
56                 return;
57         output_revs++;
58         printf("^%s\n", sha1_to_hex(sha1));
59 }
60
61 static void show_rev_arg(char *rev)
62 {
63         if (no_revs)
64                 return;
65         puts(rev);
66 }
67
68 static void show_norev(char *norev)
69 {
70         if (revs_only)
71                 return;
72         puts(norev);
73 }
74
75 static void show_arg(char *arg)
76 {
77         if (do_rev_argument && is_rev_argument(arg))
78                 show_rev_arg(arg);
79         else
80                 show_norev(arg);
81 }
82
83 static int get_parent(char *name, unsigned char *result, int idx)
84 {
85         unsigned char sha1[20];
86         int ret = get_extended_sha1(name, sha1);
87         struct commit *commit;
88         struct commit_list *p;
89
90         if (ret)
91                 return ret;
92         commit = lookup_commit_reference(sha1);
93         if (!commit)
94                 return -1;
95         if (parse_commit(commit))
96                 return -1;
97         p = commit->parents;
98         while (p) {
99                 if (!--idx) {
100                         memcpy(result, p->item->object.sha1, 20);
101                         return 0;
102                 }
103                 p = p->next;
104         }
105         return -1;
106 }
107
108 /*
109  * This is like "get_sha1()", except it allows "sha1 expressions",
110  * notably "xyz^" for "parent of xyz"
111  */
112 static int get_extended_sha1(char *name, unsigned char *sha1)
113 {
114         int parent;
115         int len = strlen(name);
116
117         parent = 1;
118         if (len > 2 && name[len-1] >= '1' && name[len-1] <= '9') {
119                 parent = name[len-1] - '0';
120                 len--;
121         }
122         if (len > 1 && name[len-1] == '^') {
123                 int ret;
124                 name[len-1] = 0;
125                 ret = get_parent(name, sha1, parent);
126                 name[len-1] = '^';
127                 if (!ret)
128                         return 0;
129         }
130         return get_sha1(name, sha1);
131 }
132
133 static void show_default(void)
134 {
135         char *s = def;
136
137         if (s) {
138                 unsigned char sha1[20];
139
140                 def = NULL;
141                 if (!get_extended_sha1(s, sha1)) {
142                         show_rev(sha1);
143                         return;
144                 }
145                 show_arg(s);
146         }
147 }
148
149 int main(int argc, char **argv)
150 {
151         int i, as_is = 0;
152         unsigned char sha1[20];
153
154         for (i = 1; i < argc; i++) {
155                 char *arg = argv[i];
156                 char *dotdot;
157         
158                 if (as_is) {
159                         show_norev(arg);
160                         continue;
161                 }
162                 if (*arg == '-') {
163                         if (!strcmp(arg, "--")) {
164                                 show_default();
165                                 if (revs_only)
166                                         break;
167                                 as_is = 1;
168                         }
169                         if (!strcmp(arg, "--default")) {
170                                 def = argv[i+1];
171                                 i++;
172                                 continue;
173                         }
174                         if (!strcmp(arg, "--revs-only")) {
175                                 revs_only = 1;
176                                 continue;
177                         }
178                         if (!strcmp(arg, "--no-revs")) {
179                                 no_revs = 1;
180                                 continue;
181                         }
182                         if (!strcmp(arg, "--verify")) {
183                                 revs_only = 1;
184                                 do_rev_argument = 0;
185                                 single_rev = 1;
186                                 continue;
187                         }
188                         show_arg(arg);
189                         continue;
190                 }
191                 dotdot = strstr(arg, "..");
192                 if (dotdot) {
193                         unsigned char end[20];
194                         char *n = dotdot+2;
195                         *dotdot = 0;
196                         if (!get_extended_sha1(arg, sha1)) {
197                                 if (!*n)
198                                         n = "HEAD";
199                                 if (!get_extended_sha1(n, end)) {
200                                         if (no_revs)
201                                                 continue;
202                                         def = NULL;
203                                         show_rev(end);
204                                         show_antirev(sha1);
205                                         continue;
206                                 }
207                         }
208                         *dotdot = '.';
209                 }
210                 if (!get_extended_sha1(arg, sha1)) {
211                         if (no_revs)
212                                 continue;
213                         def = NULL;
214                         show_rev(sha1);
215                         continue;
216                 }
217                 if (*arg == '^' && !get_extended_sha1(arg+1, sha1)) {
218                         if (no_revs)
219                                 continue;
220                         def = NULL;
221                         show_antirev(sha1);
222                         continue;
223                 }
224                 show_default();
225                 show_norev(arg);
226         }
227         show_default();
228         if (single_rev && output_revs != 1) {
229                 fprintf(stderr, "Needed a single revision\n");
230                 exit(1);
231         }
232         return 0;
233 }