305 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			305 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * rev-parse.c
 | |
|  *
 | |
|  * Copyright (C) Linus Torvalds, 2005
 | |
|  */
 | |
| #include "cache.h"
 | |
| #include "commit.h"
 | |
| #include "refs.h"
 | |
| #include "quote.h"
 | |
| 
 | |
| #define DO_REVS		1
 | |
| #define DO_NOREV	2
 | |
| #define DO_FLAGS	4
 | |
| #define DO_NONFLAGS	8
 | |
| static int filter = ~0;
 | |
| 
 | |
| static char *def = NULL;
 | |
| 
 | |
| #define NORMAL 0
 | |
| #define REVERSED 1
 | |
| static int show_type = NORMAL;
 | |
| static int symbolic = 0;
 | |
| static int output_sq = 0;
 | |
| 
 | |
| static int revs_count = 0;
 | |
| 
 | |
| /*
 | |
|  * Some arguments are relevant "revision" arguments,
 | |
|  * others are about output format or other details.
 | |
|  * This sorts it all out.
 | |
|  */
 | |
| static int is_rev_argument(const char *arg)
 | |
| {
 | |
| 	static const char *rev_args[] = {
 | |
| 		"--all",
 | |
| 		"--bisect",
 | |
| 		"--dense",
 | |
| 		"--header",
 | |
| 		"--max-age=",
 | |
| 		"--max-count=",
 | |
| 		"--merge-order",
 | |
| 		"--min-age=",
 | |
| 		"--no-merges",
 | |
| 		"--objects",
 | |
| 		"--parents",
 | |
| 		"--pretty",
 | |
| 		"--show-breaks",
 | |
| 		"--sparse",
 | |
| 		"--topo-order",
 | |
| 		"--unpacked",
 | |
| 		NULL
 | |
| 	};
 | |
| 	const char **p = rev_args;
 | |
| 
 | |
| 	for (;;) {
 | |
| 		const char *str = *p++;
 | |
| 		int len;
 | |
| 		if (!str)
 | |
| 			return 0;
 | |
| 		len = strlen(str);
 | |
| 		if (!strcmp(arg, str) ||
 | |
| 		    (str[len-1] == '=' && !strncmp(arg, str, len)))
 | |
| 			return 1;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* Output argument as a string, either SQ or normal */
 | |
| static void show(const char *arg)
 | |
| {
 | |
| 	if (output_sq) {
 | |
| 		int sq = '\'', ch;
 | |
| 
 | |
| 		putchar(sq);
 | |
| 		while ((ch = *arg++)) {
 | |
| 			if (ch == sq)
 | |
| 				fputs("'\\'", stdout);
 | |
| 			putchar(ch);
 | |
| 		}
 | |
| 		putchar(sq);
 | |
| 		putchar(' ');
 | |
| 	}
 | |
| 	else
 | |
| 		puts(arg);
 | |
| }
 | |
| 
 | |
| /* Output a revision, only if filter allows it */
 | |
| static void show_rev(int type, const unsigned char *sha1, const char *name)
 | |
| {
 | |
| 	if (!(filter & DO_REVS))
 | |
| 		return;
 | |
| 	def = NULL;
 | |
| 	revs_count++;
 | |
| 
 | |
| 	if (type != show_type)
 | |
| 		putchar('^');
 | |
| 	if (symbolic && name)
 | |
| 		show(name);
 | |
| 	else
 | |
| 		show(sha1_to_hex(sha1));
 | |
| }
 | |
| 
 | |
| /* Output a flag, only if filter allows it. */
 | |
| static void show_flag(char *arg)
 | |
| {
 | |
| 	if (!(filter & DO_FLAGS))
 | |
| 		return;
 | |
| 	if (filter & (is_rev_argument(arg) ? DO_REVS : DO_NOREV))
 | |
| 		show(arg);
 | |
| }
 | |
| 
 | |
| static void show_default(void)
 | |
| {
 | |
| 	char *s = def;
 | |
| 
 | |
| 	if (s) {
 | |
| 		unsigned char sha1[20];
 | |
| 
 | |
| 		def = NULL;
 | |
| 		if (!get_sha1(s, sha1)) {
 | |
| 			show_rev(NORMAL, sha1, s);
 | |
| 			return;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int show_reference(const char *refname, const unsigned char *sha1)
 | |
| {
 | |
| 	show_rev(NORMAL, sha1, refname);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void show_datestring(const char *flag, const char *datestr)
 | |
| {
 | |
| 	FILE *date;
 | |
| 	static char buffer[100];
 | |
| 	static char cmd[1000];
 | |
| 	int len;
 | |
| 
 | |
| 	/* date handling requires both flags and revs */
 | |
| 	if ((filter & (DO_FLAGS | DO_REVS)) != (DO_FLAGS | DO_REVS))
 | |
| 		return;
 | |
| 	len = strlen(flag);
 | |
| 	memcpy(buffer, flag, len);
 | |
| 
 | |
| 	snprintf(cmd, sizeof(cmd), "date --date=%s +%%s", sq_quote(datestr));
 | |
| 	date = popen(cmd, "r");
 | |
| 	if (!date || !fgets(buffer + len, sizeof(buffer) - len, date))
 | |
| 		die("git-rev-list: bad date string");
 | |
| 	pclose(date);
 | |
| 	len = strlen(buffer);
 | |
| 	if (buffer[len-1] == '\n')
 | |
| 		buffer[--len] = 0;
 | |
| 	show(buffer);
 | |
| }
 | |
| 
 | |
| static void show_file(const char *arg)
 | |
| {
 | |
| 	show_default();
 | |
| 	if ((filter & (DO_NONFLAGS|DO_NOREV)) == (DO_NONFLAGS|DO_NOREV))
 | |
| 		show(arg);
 | |
| }
 | |
| 
 | |
| int main(int argc, char **argv)
 | |
| {
 | |
| 	int i, as_is = 0, verify = 0;
 | |
| 	unsigned char sha1[20];
 | |
| 	const char *prefix = setup_git_directory();
 | |
| 	
 | |
| 	for (i = 1; i < argc; i++) {
 | |
| 		char *arg = argv[i];
 | |
| 		char *dotdot;
 | |
| 	
 | |
| 		if (as_is) {
 | |
| 			show_file(arg);
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (*arg == '-') {
 | |
| 			if (!strcmp(arg, "--")) {
 | |
| 				as_is = 1;
 | |
| 				/* Pass on the "--" if we show anything but files.. */
 | |
| 				if (filter & (DO_FLAGS | DO_REVS))
 | |
| 					show_file(arg);
 | |
| 				continue;
 | |
| 			}
 | |
| 			if (!strcmp(arg, "--default")) {
 | |
| 				def = argv[i+1];
 | |
| 				i++;
 | |
| 				continue;
 | |
| 			}
 | |
| 			if (!strcmp(arg, "--revs-only")) {
 | |
| 				filter &= ~DO_NOREV;
 | |
| 				continue;
 | |
| 			}
 | |
| 			if (!strcmp(arg, "--no-revs")) {
 | |
| 				filter &= ~DO_REVS;
 | |
| 				continue;
 | |
| 			}
 | |
| 			if (!strcmp(arg, "--flags")) {
 | |
| 				filter &= ~DO_NONFLAGS;
 | |
| 				continue;
 | |
| 			}
 | |
| 			if (!strcmp(arg, "--no-flags")) {
 | |
| 				filter &= ~DO_FLAGS;
 | |
| 				continue;
 | |
| 			}
 | |
| 			if (!strcmp(arg, "--verify")) {
 | |
| 				filter &= ~(DO_FLAGS|DO_NOREV);
 | |
| 				verify = 1;
 | |
| 				continue;
 | |
| 			}
 | |
| 			if (!strcmp(arg, "--sq")) {
 | |
| 				output_sq = 1;
 | |
| 				continue;
 | |
| 			}
 | |
| 			if (!strcmp(arg, "--not")) {
 | |
| 				show_type ^= REVERSED;
 | |
| 				continue;
 | |
| 			}
 | |
| 			if (!strcmp(arg, "--symbolic")) {
 | |
| 				symbolic = 1;
 | |
| 				continue;
 | |
| 			}
 | |
| 			if (!strcmp(arg, "--all")) {
 | |
| 				for_each_ref(show_reference);
 | |
| 				continue;
 | |
| 			}
 | |
| 			if (!strcmp(arg, "--show-prefix")) {
 | |
| 				if (prefix)
 | |
| 					puts(prefix);
 | |
| 				continue;
 | |
| 			}
 | |
| 			if (!strcmp(arg, "--git-dir")) {
 | |
| 				const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
 | |
| 				static char cwd[PATH_MAX];
 | |
| 				if (gitdir) {
 | |
| 					puts(gitdir);
 | |
| 					continue;
 | |
| 				}
 | |
| 				if (!prefix) {
 | |
| 					puts(".git");
 | |
| 					continue;
 | |
| 				}
 | |
| 				if (!getcwd(cwd, PATH_MAX))
 | |
| 					die("unable to get current working directory");
 | |
| 				printf("%s/.git\n", cwd);
 | |
| 				continue;
 | |
| 			}
 | |
| 			if (!strncmp(arg, "--since=", 8)) {
 | |
| 				show_datestring("--max-age=", arg+8);
 | |
| 				continue;
 | |
| 			}
 | |
| 			if (!strncmp(arg, "--after=", 8)) {
 | |
| 				show_datestring("--max-age=", arg+8);
 | |
| 				continue;
 | |
| 			}
 | |
| 			if (!strncmp(arg, "--before=", 9)) {
 | |
| 				show_datestring("--min-age=", arg+9);
 | |
| 				continue;
 | |
| 			}
 | |
| 			if (!strncmp(arg, "--until=", 8)) {
 | |
| 				show_datestring("--min-age=", arg+8);
 | |
| 				continue;
 | |
| 			}
 | |
| 			if (verify)
 | |
| 				die("Needed a single revision");
 | |
| 			show_flag(arg);
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		/* Not a flag argument */
 | |
| 		dotdot = strstr(arg, "..");
 | |
| 		if (dotdot) {
 | |
| 			unsigned char end[20];
 | |
| 			char *n = dotdot+2;
 | |
| 			*dotdot = 0;
 | |
| 			if (!get_sha1(arg, sha1)) {
 | |
| 				if (!*n)
 | |
| 					n = "HEAD";
 | |
| 				if (!get_sha1(n, end)) {
 | |
| 					show_rev(NORMAL, end, n);
 | |
| 					show_rev(REVERSED, sha1, arg);
 | |
| 					continue;
 | |
| 				}
 | |
| 			}
 | |
| 			*dotdot = '.';
 | |
| 		}
 | |
| 		if (!get_sha1(arg, sha1)) {
 | |
| 			show_rev(NORMAL, sha1, arg);
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (*arg == '^' && !get_sha1(arg+1, sha1)) {
 | |
| 			show_rev(REVERSED, sha1, arg+1);
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (verify)
 | |
| 			die("Needed a single revision");
 | |
| 		as_is = 1;
 | |
| 		show_file(arg);
 | |
| 	}
 | |
| 	show_default();
 | |
| 	if (verify && revs_count != 1)
 | |
| 		die("Needed a single revision");
 | |
| 	return 0;
 | |
| }
 | 
