threads / patch / 4391

patchgit: handle aliases defined in $GIT_DIR/config

Subject: [PATCH] git: handle aliases defined in $GIT_DIR/config

## tl;dr

5 messages between Jun 4, 2006 and Jun 5, 2006. Diffs are folded; open one to read it.

replies: 4people: 2as markdown or json

Johannes Schindelin· Jun 4, 2006, 18:47 UTC · lore
If you have a config containing something like this:
	[alias]
		l = "log --stat -M ORIG_HEAD.."
you can call
	git l
and it will do the same as
	git log --stat -M ORIG_HEAD..
This also works with hard links.
Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
---
	For me, short cuts have to be easy to type, so they never
	include digits, and they are never case sensitive, so I do not
	need any fancy config stuff...
 git.c |   58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 58 insertions(+), 0 deletions(-)
Show changes to git.c +58 −0
diff --git a/git.c b/git.c
index bc463c9..846062f 100644
--- a/git.c
+++ b/git.c
@@ -10,6 +10,7 @@ #include <limits.h>
 #include <stdarg.h>
 #include "git-compat-util.h"
 #include "exec_cmd.h"
+#include "cache.h"
 
 #include "builtin.h"
 
@@ -32,6 +33,60 @@ static void prepend_to_path(const char *
 	setenv("PATH", path, 1);
 }
 
+static const char *alias_command;
+static char *alias_string = NULL;
+
+static int git_alias_config(const char *var, const char *value)
+{
+	if (!strncmp(var, "alias.", 6) && !strcmp(var + 6, alias_command)) {
+		alias_string = strdup(value);
+	}
+	return 0;
+}
+
+#define MAX_ALIAS_ARGS 32
+
+static int handle_alias(int *argcp, const char **argv, char **envp)
+{
+	int i, i2, j = 0;
+	char *new_argv[MAX_ALIAS_ARGS];
+
+	alias_command = argv[0];
+	git_config(git_alias_config);
+	if (!alias_string)
+		return 0;
+
+	/* split alias_string */
+	new_argv[j++] = alias_string;
+	for (i = i2 = 0; alias_string[i]; i++, i2++) {
+		if (isspace(alias_string[i])) {
+			alias_string[i2] = 0;
+			while (alias_string[++i] && isspace(alias_string[i]));
+			new_argv[j++] = alias_string + i;
+			i2 = i;
+			if (j >= MAX_ALIAS_ARGS)
+				die("too many args in alias %s",
+						alias_command);
+		} else {
+			if (alias_string[i] == '\\')
+				i++;
+			if (i != i2)
+				alias_string[i2] = alias_string[i];
+		}
+	}
+
+	if (j < 1)
+		die("empty alias: %s", alias_command);
+
+	/* insert after command name */
+	if (j > 1)
+		memmove(argv + j, argv + 1, (*argcp - 1) * sizeof(char*));
+	memcpy(argv, new_argv, j * sizeof(char*));
+	*argcp += j - 1;
+
+	return 1;
+}
+
 const char git_version_string[] = GIT_VERSION;
 
 static void handle_internal_command(int argc, const char **argv, char **envp)
@@ -121,6 +176,7 @@ int main(int argc, const char **argv, ch
 	if (!strncmp(cmd, "git-", 4)) {
 		cmd += 4;
 		argv[0] = cmd;
+		handle_alias(&argc, argv, envp);
 		handle_internal_command(argc, argv, envp);
 		die("cannot handle %s internally", cmd);
 	}
@@ -178,6 +234,8 @@ int main(int argc, const char **argv, ch
 	exec_path = git_exec_path();
 	prepend_to_path(exec_path, strlen(exec_path));
 
+	handle_alias(&argc, argv, envp);
+
 	/* See if it's an internal command */
 	handle_internal_command(argc, argv, envp);
 
-- 
1.3.3.ga182-dirty
Junio C Hamano· Jun 4, 2006, 20:24 UTC · re: Johannes Schindelin · lore

Re: [PATCH] git: handle aliases defined in $GIT_DIR/config

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 	For me, short cuts have to be easy to type, so they never
> 	include digits, and they are never case sensitive, so I do not
> 	need any fancy config stuff...

Fair enough, and the spirit is the same as what Pasky suggested earlier, I think.

However, I am not sure about some parts of the code. I started mucking with it myself, but realized it is far easier for me to just let the original submitter, especially the capable one like you, do a bit more work ;-).

Show 5 quoted lines
> +#define MAX_ALIAS_ARGS 32
> +
> +static int handle_alias(int *argcp, const char **argv, char **envp)
> +{
> +	int i, i2, j = 0;
Please name them src, dst and cnt.
Show 13 quoted lines
> +	char *new_argv[MAX_ALIAS_ARGS];
> +
> +	alias_command = argv[0];
> +	git_config(git_alias_config);
> +	if (!alias_string)
> +		return 0;
> +
> +	/* split alias_string */
> +	new_argv[j++] = alias_string;
> +	for (i = i2 = 0; alias_string[i]; i++, i2++) {
> +		if (isspace(alias_string[i])) {
> +			alias_string[i2] = 0;
> +			while (alias_string[++i] && isspace(alias_string[i]));
Please make empty loops easier to read by saying:
	while (alias_string[++src] && isspace(alias_string[src]))
		; /* skip */
> +			new_argv[j++] = alias_string + i;
> +			i2 = i;

Do we need to reset dst here? I suspect starting the next parsed string immediately after the terminating NUL might be cleaner.

Show 6 quoted lines
> +			if (j >= MAX_ALIAS_ARGS)
> +				die("too many args in alias %s",
> +						alias_command);
> +		} else {
> +			if (alias_string[i] == '\\')
> +				i++;

Barf when the config line ends with a lone backslash, perhaps? Since the configuration file parser uses backslash for quoting itself, the user would need to have double backslashes, I suspect. We might want to support single/double quote pairs in this parser as well. I would further suggest separating this "split single string into a pair of (argc, argv)" into a helper function, so we can reuse it in other parts of the system later.

> +			if (i != i2)
> +				alias_string[i2] = alias_string[i];

Doing this unconditionally is probably more readable (i.e. lose the if condition) -- this is not performance critical part of the system.

Show 10 quoted lines
> +		}
> +	}
> +
> +	if (j < 1)
> +		die("empty alias: %s", alias_command);
> +
> +	/* insert after command name */
> +	if (j > 1)
> +		memmove(argv + j, argv + 1, (*argcp - 1) * sizeof(char*));
> +	memcpy(argv, new_argv, j * sizeof(char*));

Who guarantees the original argv array main() received is big enough to hold (j-1) additional pointers, I wonder? I think you would need to allocate a new array, and muck with both argc and argv of the caller by passing the pointers to them, not just argc.

Show 5 quoted lines
> @@ -121,6 +176,7 @@ int main(int argc, const char **argv, ch
>  	if (!strncmp(cmd, "git-", 4)) {
>  		cmd += 4;
>  		argv[0] = cmd;
> +		handle_alias(&argc, argv, envp);
Hence probably "handle_alias(&argc, &argv, envp)" is needed here.
Show 9 quoted lines
>  		handle_internal_command(argc, argv, envp);
>  		die("cannot handle %s internally", cmd);
>  	}
> @@ -178,6 +234,8 @@ int main(int argc, const char **argv, ch
>  	exec_path = git_exec_path();
>  	prepend_to_path(exec_path, strlen(exec_path));
>  
> +	handle_alias(&argc, argv, envp);
> +
... and here.
Johannes Schindelin· Jun 5, 2006, 16:51 UTC · re: Junio C Hamano · lore

Re: [PATCH] git: handle aliases defined in $GIT_DIR/config

Hi,
On Sun, 4 Jun 2006, Junio C Hamano wrote:
Show 13 quoted lines
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > 	For me, short cuts have to be easy to type, so they never
> > 	include digits, and they are never case sensitive, so I do not
> > 	need any fancy config stuff...
> 
> Fair enough, and the spirit is the same as what Pasky suggested
> earlier, I think.
> 
> However, I am not sure about some parts of the code.  I started
> mucking with it myself, but realized it is far easier for me to
> just let the original submitter, especially the capable one like
> you, do a bit more work ;-).
Are you trying to butter me up? If so, it's working ;-)

Here is a revised patch which addresses all of your comments (and Pasky's implicit ones) except the move of split_cmdline to somewhere central (I am not sure if that function is really needed elsewhere...):

---
 git.c |  113 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 113 insertions(+), 0 deletions(-)
Show changes to git.c +113 −0
diff --git a/git.c b/git.c
index bc463c9..db6ac61 100644
--- a/git.c
+++ b/git.c
@@ -10,6 +10,7 @@ #include <limits.h>
 #include <stdarg.h>
 #include "git-compat-util.h"
 #include "exec_cmd.h"
+#include "cache.h"
 
 #include "builtin.h"
 
@@ -32,6 +33,115 @@ static void prepend_to_path(const char *
 	setenv("PATH", path, 1);
 }
 
+static const char *alias_command;
+static char *alias_string = NULL;
+
+static int git_alias_config(const char *var, const char *value)
+{
+	if (!strncmp(var, "alias.", 6) && !strcmp(var + 6, alias_command)) {
+		alias_string = strdup(value);
+	}
+	return 0;
+}
+
+static int split_cmdline(char *cmdline, const char ***argv)
+{
+	int src, dst, count = 0, size = 16;
+	char quoted = 0;
+
+	*argv = malloc(sizeof(char*) * size);
+
+	/* split alias_string */
+	(*argv)[count++] = cmdline;
+	for (src = dst = 0; cmdline[src];) {
+		char c = cmdline[src];
+		if (!quoted && isspace(c)) {
+			cmdline[dst++] = 0;
+			while (cmdline[++src]
+					&& isspace(cmdline[src]))
+				; /* skip */
+			if (count >= size) {
+				size += 16;
+				*argv = realloc(*argv, sizeof(char*) * size);
+			}
+			(*argv)[count++] = cmdline + dst;
+		} else if(!quoted && (c == '\'' || c == '"')) {
+			quoted = c;
+			src++;
+		} else if (c == quoted) {
+			quoted = 0;
+			src++;
+		} else {
+			if (c == '\\' && quoted != '\'') {
+				src++;
+				c = cmdline[src];
+				if (!c) {
+					free(*argv);
+					*argv = NULL;
+					return error("cmdline ends with \\");
+				}
+			}
+			cmdline[dst++] = c;
+			src++;
+		}
+	}
+
+	cmdline[dst] = 0;
+
+	if (quoted) {
+		free(*argv);
+		*argv = NULL;
+		return error("unclosed quote");
+	}
+
+	return count;
+}
+
+static int handle_alias(int *argcp, const char ***argv)
+{
+	int nongit = 0;
+	const char *subdir;
+
+	subdir = setup_git_directory_gently(&nongit);
+	if (!nongit) {
+		int count;
+		const char** new_argv;
+
+		alias_command = (*argv)[0];
+		git_config(git_alias_config);
+		if (!alias_string)
+			return 0;
+
+		count = split_cmdline(alias_string, &new_argv);
+
+		if (count < 1)
+			die("empty alias for %s", alias_command);
+
+		if (!strcmp(alias_command, new_argv[0]))
+			die("recursive alias: %s", alias_command);
+
+		/* insert after command name */
+		if (*argcp > 1) {
+			new_argv = realloc(new_argv,
+					sizeof(char*) * (count + *argcp - 1));
+			memcpy(new_argv + count, *argv, sizeof(char*) * (*argcp - 1));
+		}
+
+		*argv = new_argv;
+		*argcp += count - 1;
+
+		if (subdir)
+			chdir(subdir);
+
+		return 1;
+	}
+
+	if (subdir)
+		chdir(subdir);
+
+	return 0;
+}
+
 const char git_version_string[] = GIT_VERSION;
 
 static void handle_internal_command(int argc, const char **argv, char **envp)
@@ -121,6 +231,7 @@ int main(int argc, const char **argv, ch
 	if (!strncmp(cmd, "git-", 4)) {
 		cmd += 4;
 		argv[0] = cmd;
+		handle_alias(&argc, &argv);
 		handle_internal_command(argc, argv, envp);
 		die("cannot handle %s internally", cmd);
 	}
@@ -178,6 +289,8 @@ int main(int argc, const char **argv, ch
 	exec_path = git_exec_path();
 	prepend_to_path(exec_path, strlen(exec_path));
 
+	handle_alias(&argc, &argv);
+
 	/* See if it's an internal command */
 	handle_internal_command(argc, argv, envp);
 
Johannes Schindelin· Jun 5, 2006, 17:43 UTC · re: Johannes Schindelin · lore

Re: [PATCH] git: handle aliases defined in $GIT_DIR/config

Hi,
On Mon, 5 Jun 2006, Johannes Schindelin wrote:
> Hi,
> 
> sorry, I did not test with the subdir=... stuff I copied from Pasky's 
> patch. It breaks things for me. Looking into it...

There were actually two bugs: I did not change the subdir back in all cases, and git_setup_directory_gently had a bug (will send patch separately). The updated patch:

---
 git.c |  111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 111 insertions(+), 0 deletions(-)
Show changes to git.c +111 −0
diff --git a/git.c b/git.c
index bc463c9..8854472 100644
--- a/git.c
+++ b/git.c
@@ -10,6 +10,7 @@ #include <limits.h>
 #include <stdarg.h>
 #include "git-compat-util.h"
 #include "exec_cmd.h"
+#include "cache.h"
 
 #include "builtin.h"
 
@@ -32,6 +33,113 @@ static void prepend_to_path(const char *
 	setenv("PATH", path, 1);
 }
 
+static const char *alias_command;
+static char *alias_string = NULL;
+
+static int git_alias_config(const char *var, const char *value)
+{
+	if (!strncmp(var, "alias.", 6) && !strcmp(var + 6, alias_command)) {
+		alias_string = strdup(value);
+	}
+	return 0;
+}
+
+static int split_cmdline(char *cmdline, const char ***argv)
+{
+	int src, dst, count = 0, size = 16;
+	char quoted = 0;
+
+	*argv = malloc(sizeof(char*) * size);
+
+	/* split alias_string */
+	(*argv)[count++] = cmdline;
+	for (src = dst = 0; cmdline[src];) {
+		char c = cmdline[src];
+		if (!quoted && isspace(c)) {
+			cmdline[dst++] = 0;
+			while (cmdline[++src]
+					&& isspace(cmdline[src]))
+				; /* skip */
+			if (count >= size) {
+				size += 16;
+				*argv = realloc(*argv, sizeof(char*) * size);
+			}
+			(*argv)[count++] = cmdline + dst;
+		} else if(!quoted && (c == '\'' || c == '"')) {
+			quoted = c;
+			src++;
+		} else if (c == quoted) {
+			quoted = 0;
+			src++;
+		} else {
+			if (c == '\\' && quoted != '\'') {
+				src++;
+				c = cmdline[src];
+				if (!c) {
+					free(*argv);
+					*argv = NULL;
+					return error("cmdline ends with \\");
+				}
+			}
+			cmdline[dst++] = c;
+			src++;
+		}
+	}
+
+	cmdline[dst] = 0;
+
+	if (quoted) {
+		free(*argv);
+		*argv = NULL;
+		return error("unclosed quote");
+	}
+
+	return count;
+}
+
+static int handle_alias(int *argcp, const char ***argv)
+{
+	int nongit = 0, ret = 0;
+	const char *subdir;
+
+	subdir = setup_git_directory_gently(&nongit);
+	if (!nongit) {
+		int count;
+		const char** new_argv;
+
+		alias_command = (*argv)[0];
+		git_config(git_alias_config);
+		if (alias_string) {
+
+			count = split_cmdline(alias_string, &new_argv);
+
+			if (count < 1)
+				die("empty alias for %s", alias_command);
+
+			if (!strcmp(alias_command, new_argv[0]))
+				die("recursive alias: %s", alias_command);
+
+			/* insert after command name */
+			if (*argcp > 1) {
+				new_argv = realloc(new_argv, sizeof(char*) *
+						(count + *argcp - 1));
+				memcpy(new_argv + count, *argv, sizeof(char*) *
+						(*argcp - 1));
+			}
+
+			*argv = new_argv;
+			*argcp += count - 1;
+
+			ret = 1;
+		}
+	}
+
+	if (subdir)
+		chdir(subdir);
+
+	return ret;
+}
+
 const char git_version_string[] = GIT_VERSION;
 
 static void handle_internal_command(int argc, const char **argv, char **envp)
@@ -121,6 +229,7 @@ int main(int argc, const char **argv, ch
 	if (!strncmp(cmd, "git-", 4)) {
 		cmd += 4;
 		argv[0] = cmd;
+		handle_alias(&argc, &argv);
 		handle_internal_command(argc, argv, envp);
 		die("cannot handle %s internally", cmd);
 	}
@@ -178,6 +287,8 @@ int main(int argc, const char **argv, ch
 	exec_path = git_exec_path();
 	prepend_to_path(exec_path, strlen(exec_path));
 
+	handle_alias(&argc, &argv);
+
 	/* See if it's an internal command */
 	handle_internal_command(argc, argv, envp);
 

← back to recent threads