Merge branch 'lt/merge-tree'
[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 #include "quote.h"
10
11 #define DO_REVS         1
12 #define DO_NOREV        2
13 #define DO_FLAGS        4
14 #define DO_NONFLAGS     8
15 static int filter = ~0;
16
17 static char *def = NULL;
18
19 #define NORMAL 0
20 #define REVERSED 1
21 static int show_type = NORMAL;
22 static int symbolic = 0;
23 static int abbrev = 0;
24 static int output_sq = 0;
25
26 static int revs_count = 0;
27
28 /*
29  * Some arguments are relevant "revision" arguments,
30  * others are about output format or other details.
31  * This sorts it all out.
32  */
33 static int is_rev_argument(const char *arg)
34 {
35         static const char *rev_args[] = {
36                 "--all",
37                 "--bisect",
38                 "--dense",
39                 "--header",
40                 "--max-age=",
41                 "--max-count=",
42                 "--merge-order",
43                 "--min-age=",
44                 "--no-merges",
45                 "--objects",
46                 "--parents",
47                 "--pretty",
48                 "--show-breaks",
49                 "--sparse",
50                 "--topo-order",
51                 "--date-order",
52                 "--unpacked",
53                 NULL
54         };
55         const char **p = rev_args;
56
57         /* accept -<digit>, like traditional "head" */
58         if ((*arg == '-') && isdigit(arg[1]))
59                 return 1;
60
61         for (;;) {
62                 const char *str = *p++;
63                 int len;
64                 if (!str)
65                         return 0;
66                 len = strlen(str);
67                 if (!strcmp(arg, str) ||
68                     (str[len-1] == '=' && !strncmp(arg, str, len)))
69                         return 1;
70         }
71 }
72
73 /* Output argument as a string, either SQ or normal */
74 static void show(const char *arg)
75 {
76         if (output_sq) {
77                 int sq = '\'', ch;
78
79                 putchar(sq);
80                 while ((ch = *arg++)) {
81                         if (ch == sq)
82                                 fputs("'\\'", stdout);
83                         putchar(ch);
84                 }
85                 putchar(sq);
86                 putchar(' ');
87         }
88         else
89                 puts(arg);
90 }
91
92 /* Output a revision, only if filter allows it */
93 static void show_rev(int type, const unsigned char *sha1, const char *name)
94 {
95         if (!(filter & DO_REVS))
96                 return;
97         def = NULL;
98         revs_count++;
99
100         if (type != show_type)
101                 putchar('^');
102         if (symbolic && name)
103                 show(name);
104         else if (abbrev)
105                 show(find_unique_abbrev(sha1, abbrev));
106         else
107                 show(sha1_to_hex(sha1));
108 }
109
110 /* Output a flag, only if filter allows it. */
111 static int show_flag(char *arg)
112 {
113         if (!(filter & DO_FLAGS))
114                 return 0;
115         if (filter & (is_rev_argument(arg) ? DO_REVS : DO_NOREV)) {
116                 show(arg);
117                 return 1;
118         }
119         return 0;
120 }
121
122 static void show_default(void)
123 {
124         char *s = def;
125
126         if (s) {
127                 unsigned char sha1[20];
128
129                 def = NULL;
130                 if (!get_sha1(s, sha1)) {
131                         show_rev(NORMAL, sha1, s);
132                         return;
133                 }
134         }
135 }
136
137 static int show_reference(const char *refname, const unsigned char *sha1)
138 {
139         show_rev(NORMAL, sha1, refname);
140         return 0;
141 }
142
143 static void show_datestring(const char *flag, const char *datestr)
144 {
145         static char buffer[100];
146
147         /* date handling requires both flags and revs */
148         if ((filter & (DO_FLAGS | DO_REVS)) != (DO_FLAGS | DO_REVS))
149                 return;
150         snprintf(buffer, sizeof(buffer), "%s%lu", flag, approxidate(datestr));
151         show(buffer);
152 }
153
154 static int show_file(const char *arg)
155 {
156         show_default();
157         if ((filter & (DO_NONFLAGS|DO_NOREV)) == (DO_NONFLAGS|DO_NOREV)) {
158                 show(arg);
159                 return 1;
160         }
161         return 0;
162 }
163
164 int main(int argc, char **argv)
165 {
166         int i, as_is = 0, verify = 0;
167         unsigned char sha1[20];
168         const char *prefix = setup_git_directory();
169         
170         for (i = 1; i < argc; i++) {
171                 struct stat st;
172                 char *arg = argv[i];
173                 char *dotdot;
174         
175                 if (as_is) {
176                         show_file(arg);
177                         continue;
178                 }
179                 if (!strcmp(arg,"-n")) {
180                         if (++i >= argc)
181                                 die("-n requires an argument");
182                         if ((filter & DO_FLAGS) && (filter & DO_REVS)) {
183                                 show(arg);
184                                 show(argv[i]);
185                         }
186                         continue;
187                 }
188                 if (!strncmp(arg,"-n",2)) {
189                         if ((filter & DO_FLAGS) && (filter & DO_REVS))
190                                 show(arg);
191                         continue;
192                 }
193
194                 if (*arg == '-') {
195                         if (!strcmp(arg, "--")) {
196                                 as_is = 1;
197                                 /* Pass on the "--" if we show anything but files.. */
198                                 if (filter & (DO_FLAGS | DO_REVS))
199                                         show_file(arg);
200                                 continue;
201                         }
202                         if (!strcmp(arg, "--default")) {
203                                 def = argv[i+1];
204                                 i++;
205                                 continue;
206                         }
207                         if (!strcmp(arg, "--revs-only")) {
208                                 filter &= ~DO_NOREV;
209                                 continue;
210                         }
211                         if (!strcmp(arg, "--no-revs")) {
212                                 filter &= ~DO_REVS;
213                                 continue;
214                         }
215                         if (!strcmp(arg, "--flags")) {
216                                 filter &= ~DO_NONFLAGS;
217                                 continue;
218                         }
219                         if (!strcmp(arg, "--no-flags")) {
220                                 filter &= ~DO_FLAGS;
221                                 continue;
222                         }
223                         if (!strcmp(arg, "--verify")) {
224                                 filter &= ~(DO_FLAGS|DO_NOREV);
225                                 verify = 1;
226                                 continue;
227                         }
228                         if (!strcmp(arg, "--short") ||
229                             !strncmp(arg, "--short=", 8)) {
230                                 filter &= ~(DO_FLAGS|DO_NOREV);
231                                 verify = 1;
232                                 abbrev = DEFAULT_ABBREV;
233                                 if (arg[7] == '=')
234                                         abbrev = strtoul(arg + 8, NULL, 10);
235                                 if (abbrev < MINIMUM_ABBREV)
236                                         abbrev = MINIMUM_ABBREV;
237                                 else if (40 <= abbrev)
238                                         abbrev = 40;
239                                 continue;
240                         }
241                         if (!strcmp(arg, "--sq")) {
242                                 output_sq = 1;
243                                 continue;
244                         }
245                         if (!strcmp(arg, "--not")) {
246                                 show_type ^= REVERSED;
247                                 continue;
248                         }
249                         if (!strcmp(arg, "--symbolic")) {
250                                 symbolic = 1;
251                                 continue;
252                         }
253                         if (!strcmp(arg, "--all")) {
254                                 for_each_ref(show_reference);
255                                 continue;
256                         }
257                         if (!strcmp(arg, "--show-prefix")) {
258                                 if (prefix)
259                                         puts(prefix);
260                                 continue;
261                         }
262                         if (!strcmp(arg, "--show-cdup")) {
263                                 const char *pfx = prefix;
264                                 while (pfx) {
265                                         pfx = strchr(pfx, '/');
266                                         if (pfx) {
267                                                 pfx++;
268                                                 printf("../");
269                                         }
270                                 }
271                                 putchar('\n');
272                                 continue;
273                         }
274                         if (!strcmp(arg, "--git-dir")) {
275                                 const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
276                                 static char cwd[PATH_MAX];
277                                 if (gitdir) {
278                                         puts(gitdir);
279                                         continue;
280                                 }
281                                 if (!prefix) {
282                                         puts(".git");
283                                         continue;
284                                 }
285                                 if (!getcwd(cwd, PATH_MAX))
286                                         die("unable to get current working directory");
287                                 printf("%s/.git\n", cwd);
288                                 continue;
289                         }
290                         if (!strncmp(arg, "--since=", 8)) {
291                                 show_datestring("--max-age=", arg+8);
292                                 continue;
293                         }
294                         if (!strncmp(arg, "--after=", 8)) {
295                                 show_datestring("--max-age=", arg+8);
296                                 continue;
297                         }
298                         if (!strncmp(arg, "--before=", 9)) {
299                                 show_datestring("--min-age=", arg+9);
300                                 continue;
301                         }
302                         if (!strncmp(arg, "--until=", 8)) {
303                                 show_datestring("--min-age=", arg+8);
304                                 continue;
305                         }
306                         if (show_flag(arg) && verify)
307                                 die("Needed a single revision");
308                         continue;
309                 }
310
311                 /* Not a flag argument */
312                 dotdot = strstr(arg, "..");
313                 if (dotdot) {
314                         unsigned char end[20];
315                         char *n = dotdot+2;
316                         *dotdot = 0;
317                         if (!get_sha1(arg, sha1)) {
318                                 if (!*n)
319                                         n = "HEAD";
320                                 if (!get_sha1(n, end)) {
321                                         show_rev(NORMAL, end, n);
322                                         show_rev(REVERSED, sha1, arg);
323                                         continue;
324                                 }
325                         }
326                         *dotdot = '.';
327                 }
328                 if (!get_sha1(arg, sha1)) {
329                         show_rev(NORMAL, sha1, arg);
330                         continue;
331                 }
332                 if (*arg == '^' && !get_sha1(arg+1, sha1)) {
333                         show_rev(REVERSED, sha1, arg+1);
334                         continue;
335                 }
336                 as_is = 1;
337                 if (!show_file(arg))
338                         continue;
339                 if (verify)
340                         die("Needed a single revision");
341                 if (lstat(arg, &st) < 0)
342                         die("'%s': %s", arg, strerror(errno));
343         }
344         show_default();
345         if (verify && revs_count != 1)
346                 die("Needed a single revision");
347         return 0;
348 }