{"thread":{"id":"4391","subject":"[PATCH] git: handle aliases defined in $GIT_DIR/config","startedAt":"2006-06-04T18:47:48Z","lastAt":"2006-06-05T17:43:52Z","messageCount":5,"participants":["Johannes Schindelin","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"21218","messageId":"Pine.LNX.4.63.0606042047160.1598@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"4391","inReplyTo":null,"subject":"[PATCH] git: handle aliases defined in $GIT_DIR/config","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-06-04T18:47:48Z","receivedAt":"2006-06-04T18:47:48Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nIf you have a config containing something like this:\n\n\t[alias]\n\t\tl = \"log --stat -M ORIG_HEAD..\"\n\nyou can call\n\n\tgit l\n\nand it will do the same as\n\n\tgit log --stat -M ORIG_HEAD..\n\nThis also works with hard links.\n\nSigned-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n\n---\n\n\tFor me, short cuts have to be easy to type, so they never\n\tinclude digits, and they are never case sensitive, so I do not\n\tneed any fancy config stuff...\n\n git.c |   58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 58 insertions(+), 0 deletions(-)\n\ndiff --git a/git.c b/git.c\nindex bc463c9..846062f 100644\n--- a/git.c\n+++ b/git.c\n@@ -10,6 +10,7 @@ #include <limits.h>\n #include <stdarg.h>\n #include \"git-compat-util.h\"\n #include \"exec_cmd.h\"\n+#include \"cache.h\"\n \n #include \"builtin.h\"\n \n@@ -32,6 +33,60 @@ static void prepend_to_path(const char *\n \tsetenv(\"PATH\", path, 1);\n }\n \n+static const char *alias_command;\n+static char *alias_string = NULL;\n+\n+static int git_alias_config(const char *var, const char *value)\n+{\n+\tif (!strncmp(var, \"alias.\", 6) && !strcmp(var + 6, alias_command)) {\n+\t\talias_string = strdup(value);\n+\t}\n+\treturn 0;\n+}\n+\n+#define MAX_ALIAS_ARGS 32\n+\n+static int handle_alias(int *argcp, const char **argv, char **envp)\n+{\n+\tint i, i2, j = 0;\n+\tchar *new_argv[MAX_ALIAS_ARGS];\n+\n+\talias_command = argv[0];\n+\tgit_config(git_alias_config);\n+\tif (!alias_string)\n+\t\treturn 0;\n+\n+\t/* split alias_string */\n+\tnew_argv[j++] = alias_string;\n+\tfor (i = i2 = 0; alias_string[i]; i++, i2++) {\n+\t\tif (isspace(alias_string[i])) {\n+\t\t\talias_string[i2] = 0;\n+\t\t\twhile (alias_string[++i] && isspace(alias_string[i]));\n+\t\t\tnew_argv[j++] = alias_string + i;\n+\t\t\ti2 = i;\n+\t\t\tif (j >= MAX_ALIAS_ARGS)\n+\t\t\t\tdie(\"too many args in alias %s\",\n+\t\t\t\t\t\talias_command);\n+\t\t} else {\n+\t\t\tif (alias_string[i] == '\\\\')\n+\t\t\t\ti++;\n+\t\t\tif (i != i2)\n+\t\t\t\talias_string[i2] = alias_string[i];\n+\t\t}\n+\t}\n+\n+\tif (j < 1)\n+\t\tdie(\"empty alias: %s\", alias_command);\n+\n+\t/* insert after command name */\n+\tif (j > 1)\n+\t\tmemmove(argv + j, argv + 1, (*argcp - 1) * sizeof(char*));\n+\tmemcpy(argv, new_argv, j * sizeof(char*));\n+\t*argcp += j - 1;\n+\n+\treturn 1;\n+}\n+\n const char git_version_string[] = GIT_VERSION;\n \n static void handle_internal_command(int argc, const char **argv, char **envp)\n@@ -121,6 +176,7 @@ int main(int argc, const char **argv, ch\n \tif (!strncmp(cmd, \"git-\", 4)) {\n \t\tcmd += 4;\n \t\targv[0] = cmd;\n+\t\thandle_alias(&argc, argv, envp);\n \t\thandle_internal_command(argc, argv, envp);\n \t\tdie(\"cannot handle %s internally\", cmd);\n \t}\n@@ -178,6 +234,8 @@ int main(int argc, const char **argv, ch\n \texec_path = git_exec_path();\n \tprepend_to_path(exec_path, strlen(exec_path));\n \n+\thandle_alias(&argc, argv, envp);\n+\n \t/* See if it's an internal command */\n \thandle_internal_command(argc, argv, envp);\n \n-- \n1.3.3.ga182-dirty\n"},{"id":"21222","messageId":"7v3bekacts.fsf@assigned-by-dhcp.cox.net","threadId":"4391","inReplyTo":"Pine.LNX.4.63.0606042047160.1598@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] git: handle aliases defined in $GIT_DIR/config","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-06-04T20:24:47Z","receivedAt":"2006-06-04T20:24:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> \tFor me, short cuts have to be easy to type, so they never\n> \tinclude digits, and they are never case sensitive, so I do not\n> \tneed any fancy config stuff...\n\nFair enough, and the spirit is the same as what Pasky suggested\nearlier, I think.\n\nHowever, I am not sure about some parts of the code.  I started\nmucking with it myself, but realized it is far easier for me to\njust let the original submitter, especially the capable one like\nyou, do a bit more work ;-).\n\n> +#define MAX_ALIAS_ARGS 32\n> +\n> +static int handle_alias(int *argcp, const char **argv, char **envp)\n> +{\n> +\tint i, i2, j = 0;\n\nPlease name them src, dst and cnt.\n\n> +\tchar *new_argv[MAX_ALIAS_ARGS];\n> +\n> +\talias_command = argv[0];\n> +\tgit_config(git_alias_config);\n> +\tif (!alias_string)\n> +\t\treturn 0;\n> +\n> +\t/* split alias_string */\n> +\tnew_argv[j++] = alias_string;\n> +\tfor (i = i2 = 0; alias_string[i]; i++, i2++) {\n> +\t\tif (isspace(alias_string[i])) {\n> +\t\t\talias_string[i2] = 0;\n> +\t\t\twhile (alias_string[++i] && isspace(alias_string[i]));\n\nPlease make empty loops easier to read by saying:\n\n\twhile (alias_string[++src] && isspace(alias_string[src]))\n\t\t; /* skip */\n\n> +\t\t\tnew_argv[j++] = alias_string + i;\n> +\t\t\ti2 = i;\n\nDo we need to reset dst here?  I suspect starting the next\nparsed string immediately after the terminating NUL might be\ncleaner.\n\n> +\t\t\tif (j >= MAX_ALIAS_ARGS)\n> +\t\t\t\tdie(\"too many args in alias %s\",\n> +\t\t\t\t\t\talias_command);\n> +\t\t} else {\n> +\t\t\tif (alias_string[i] == '\\\\')\n> +\t\t\t\ti++;\n\nBarf when the config line ends with a lone backslash, perhaps?\nSince the configuration file parser uses backslash for quoting\nitself, the user would need to have double backslashes, I\nsuspect.  We might want to support single/double quote pairs in\nthis parser as well.  I would further suggest separating this\n\"split single string into a pair of (argc, argv)\" into a helper\nfunction, so we can reuse it in other parts of the system later.\n\n> +\t\t\tif (i != i2)\n> +\t\t\t\talias_string[i2] = alias_string[i];\n\nDoing this unconditionally is probably more readable (i.e. lose\nthe if condition) -- this is not performance critical part of\nthe system.\n\n> +\t\t}\n> +\t}\n> +\n> +\tif (j < 1)\n> +\t\tdie(\"empty alias: %s\", alias_command);\n> +\n> +\t/* insert after command name */\n> +\tif (j > 1)\n> +\t\tmemmove(argv + j, argv + 1, (*argcp - 1) * sizeof(char*));\n> +\tmemcpy(argv, new_argv, j * sizeof(char*));\n\nWho guarantees the original argv array main() received is big\nenough to hold (j-1) additional pointers, I wonder?  I think you\nwould need to allocate a new array, and muck with both argc and\nargv of the caller by passing the pointers to them, not just\nargc.\n\n> @@ -121,6 +176,7 @@ int main(int argc, const char **argv, ch\n>  \tif (!strncmp(cmd, \"git-\", 4)) {\n>  \t\tcmd += 4;\n>  \t\targv[0] = cmd;\n> +\t\thandle_alias(&argc, argv, envp);\n\nHence probably \"handle_alias(&argc, &argv, envp)\" is needed here.\n\n>  \t\thandle_internal_command(argc, argv, envp);\n>  \t\tdie(\"cannot handle %s internally\", cmd);\n>  \t}\n> @@ -178,6 +234,8 @@ int main(int argc, const char **argv, ch\n>  \texec_path = git_exec_path();\n>  \tprepend_to_path(exec_path, strlen(exec_path));\n>  \n> +\thandle_alias(&argc, argv, envp);\n> +\n\n... and here.\n"},{"id":"21262","messageId":"Pine.LNX.4.63.0606051847480.18604@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"4391","inReplyTo":"7v3bekacts.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] git: handle aliases defined in $GIT_DIR/config","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-06-05T16:51:35Z","receivedAt":"2006-06-05T16:51:35Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 4 Jun 2006, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > \tFor me, short cuts have to be easy to type, so they never\n> > \tinclude digits, and they are never case sensitive, so I do not\n> > \tneed any fancy config stuff...\n> \n> Fair enough, and the spirit is the same as what Pasky suggested\n> earlier, I think.\n> \n> However, I am not sure about some parts of the code.  I started\n> mucking with it myself, but realized it is far easier for me to\n> just let the original submitter, especially the capable one like\n> you, do a bit more work ;-).\n\nAre you trying to butter me up? If so, it's working ;-)\n\nHere is a revised patch which addresses all of your comments (and Pasky's \nimplicit ones) except the move of split_cmdline to somewhere central (I \nam not sure if that function is really needed elsewhere...):\n\n---\n\n git.c |  113 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 113 insertions(+), 0 deletions(-)\n\ndiff --git a/git.c b/git.c\nindex bc463c9..db6ac61 100644\n--- a/git.c\n+++ b/git.c\n@@ -10,6 +10,7 @@ #include <limits.h>\n #include <stdarg.h>\n #include \"git-compat-util.h\"\n #include \"exec_cmd.h\"\n+#include \"cache.h\"\n \n #include \"builtin.h\"\n \n@@ -32,6 +33,115 @@ static void prepend_to_path(const char *\n \tsetenv(\"PATH\", path, 1);\n }\n \n+static const char *alias_command;\n+static char *alias_string = NULL;\n+\n+static int git_alias_config(const char *var, const char *value)\n+{\n+\tif (!strncmp(var, \"alias.\", 6) && !strcmp(var + 6, alias_command)) {\n+\t\talias_string = strdup(value);\n+\t}\n+\treturn 0;\n+}\n+\n+static int split_cmdline(char *cmdline, const char ***argv)\n+{\n+\tint src, dst, count = 0, size = 16;\n+\tchar quoted = 0;\n+\n+\t*argv = malloc(sizeof(char*) * size);\n+\n+\t/* split alias_string */\n+\t(*argv)[count++] = cmdline;\n+\tfor (src = dst = 0; cmdline[src];) {\n+\t\tchar c = cmdline[src];\n+\t\tif (!quoted && isspace(c)) {\n+\t\t\tcmdline[dst++] = 0;\n+\t\t\twhile (cmdline[++src]\n+\t\t\t\t\t&& isspace(cmdline[src]))\n+\t\t\t\t; /* skip */\n+\t\t\tif (count >= size) {\n+\t\t\t\tsize += 16;\n+\t\t\t\t*argv = realloc(*argv, sizeof(char*) * size);\n+\t\t\t}\n+\t\t\t(*argv)[count++] = cmdline + dst;\n+\t\t} else if(!quoted && (c == '\\'' || c == '\"')) {\n+\t\t\tquoted = c;\n+\t\t\tsrc++;\n+\t\t} else if (c == quoted) {\n+\t\t\tquoted = 0;\n+\t\t\tsrc++;\n+\t\t} else {\n+\t\t\tif (c == '\\\\' && quoted != '\\'') {\n+\t\t\t\tsrc++;\n+\t\t\t\tc = cmdline[src];\n+\t\t\t\tif (!c) {\n+\t\t\t\t\tfree(*argv);\n+\t\t\t\t\t*argv = NULL;\n+\t\t\t\t\treturn error(\"cmdline ends with \\\\\");\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tcmdline[dst++] = c;\n+\t\t\tsrc++;\n+\t\t}\n+\t}\n+\n+\tcmdline[dst] = 0;\n+\n+\tif (quoted) {\n+\t\tfree(*argv);\n+\t\t*argv = NULL;\n+\t\treturn error(\"unclosed quote\");\n+\t}\n+\n+\treturn count;\n+}\n+\n+static int handle_alias(int *argcp, const char ***argv)\n+{\n+\tint nongit = 0;\n+\tconst char *subdir;\n+\n+\tsubdir = setup_git_directory_gently(&nongit);\n+\tif (!nongit) {\n+\t\tint count;\n+\t\tconst char** new_argv;\n+\n+\t\talias_command = (*argv)[0];\n+\t\tgit_config(git_alias_config);\n+\t\tif (!alias_string)\n+\t\t\treturn 0;\n+\n+\t\tcount = split_cmdline(alias_string, &new_argv);\n+\n+\t\tif (count < 1)\n+\t\t\tdie(\"empty alias for %s\", alias_command);\n+\n+\t\tif (!strcmp(alias_command, new_argv[0]))\n+\t\t\tdie(\"recursive alias: %s\", alias_command);\n+\n+\t\t/* insert after command name */\n+\t\tif (*argcp > 1) {\n+\t\t\tnew_argv = realloc(new_argv,\n+\t\t\t\t\tsizeof(char*) * (count + *argcp - 1));\n+\t\t\tmemcpy(new_argv + count, *argv, sizeof(char*) * (*argcp - 1));\n+\t\t}\n+\n+\t\t*argv = new_argv;\n+\t\t*argcp += count - 1;\n+\n+\t\tif (subdir)\n+\t\t\tchdir(subdir);\n+\n+\t\treturn 1;\n+\t}\n+\n+\tif (subdir)\n+\t\tchdir(subdir);\n+\n+\treturn 0;\n+}\n+\n const char git_version_string[] = GIT_VERSION;\n \n static void handle_internal_command(int argc, const char **argv, char **envp)\n@@ -121,6 +231,7 @@ int main(int argc, const char **argv, ch\n \tif (!strncmp(cmd, \"git-\", 4)) {\n \t\tcmd += 4;\n \t\targv[0] = cmd;\n+\t\thandle_alias(&argc, &argv);\n \t\thandle_internal_command(argc, argv, envp);\n \t\tdie(\"cannot handle %s internally\", cmd);\n \t}\n@@ -178,6 +289,8 @@ int main(int argc, const char **argv, ch\n \texec_path = git_exec_path();\n \tprepend_to_path(exec_path, strlen(exec_path));\n \n+\thandle_alias(&argc, &argv);\n+\n \t/* See if it's an internal command */\n \thandle_internal_command(argc, argv, envp);\n \n"},{"id":"21263","messageId":"Pine.LNX.4.63.0606051902210.20820@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"4391","inReplyTo":"Pine.LNX.4.63.0606051847480.18604@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] git: handle aliases defined in $GIT_DIR/config","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-06-05T17:02:56Z","receivedAt":"2006-06-05T17:02:56Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nsorry, I did not test with the subdir=... stuff I copied from Pasky's \npatch. It breaks things for me. Looking into it...\n\nCiao,\nDscho\n"},{"id":"21265","messageId":"Pine.LNX.4.63.0606051941450.29608@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"4391","inReplyTo":"Pine.LNX.4.63.0606051902210.20820@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] git: handle aliases defined in $GIT_DIR/config","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-06-05T17:43:52Z","receivedAt":"2006-06-05T17:43:52Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 5 Jun 2006, Johannes Schindelin wrote:\n\n> Hi,\n> \n> sorry, I did not test with the subdir=... stuff I copied from Pasky's \n> patch. It breaks things for me. Looking into it...\n\nThere were actually two bugs: I did not change the subdir back in all \ncases, and git_setup_directory_gently had a bug (will send patch \nseparately). The updated patch:\n\n---\n\n git.c |  111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 111 insertions(+), 0 deletions(-)\n\ndiff --git a/git.c b/git.c\nindex bc463c9..8854472 100644\n--- a/git.c\n+++ b/git.c\n@@ -10,6 +10,7 @@ #include <limits.h>\n #include <stdarg.h>\n #include \"git-compat-util.h\"\n #include \"exec_cmd.h\"\n+#include \"cache.h\"\n \n #include \"builtin.h\"\n \n@@ -32,6 +33,113 @@ static void prepend_to_path(const char *\n \tsetenv(\"PATH\", path, 1);\n }\n \n+static const char *alias_command;\n+static char *alias_string = NULL;\n+\n+static int git_alias_config(const char *var, const char *value)\n+{\n+\tif (!strncmp(var, \"alias.\", 6) && !strcmp(var + 6, alias_command)) {\n+\t\talias_string = strdup(value);\n+\t}\n+\treturn 0;\n+}\n+\n+static int split_cmdline(char *cmdline, const char ***argv)\n+{\n+\tint src, dst, count = 0, size = 16;\n+\tchar quoted = 0;\n+\n+\t*argv = malloc(sizeof(char*) * size);\n+\n+\t/* split alias_string */\n+\t(*argv)[count++] = cmdline;\n+\tfor (src = dst = 0; cmdline[src];) {\n+\t\tchar c = cmdline[src];\n+\t\tif (!quoted && isspace(c)) {\n+\t\t\tcmdline[dst++] = 0;\n+\t\t\twhile (cmdline[++src]\n+\t\t\t\t\t&& isspace(cmdline[src]))\n+\t\t\t\t; /* skip */\n+\t\t\tif (count >= size) {\n+\t\t\t\tsize += 16;\n+\t\t\t\t*argv = realloc(*argv, sizeof(char*) * size);\n+\t\t\t}\n+\t\t\t(*argv)[count++] = cmdline + dst;\n+\t\t} else if(!quoted && (c == '\\'' || c == '\"')) {\n+\t\t\tquoted = c;\n+\t\t\tsrc++;\n+\t\t} else if (c == quoted) {\n+\t\t\tquoted = 0;\n+\t\t\tsrc++;\n+\t\t} else {\n+\t\t\tif (c == '\\\\' && quoted != '\\'') {\n+\t\t\t\tsrc++;\n+\t\t\t\tc = cmdline[src];\n+\t\t\t\tif (!c) {\n+\t\t\t\t\tfree(*argv);\n+\t\t\t\t\t*argv = NULL;\n+\t\t\t\t\treturn error(\"cmdline ends with \\\\\");\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tcmdline[dst++] = c;\n+\t\t\tsrc++;\n+\t\t}\n+\t}\n+\n+\tcmdline[dst] = 0;\n+\n+\tif (quoted) {\n+\t\tfree(*argv);\n+\t\t*argv = NULL;\n+\t\treturn error(\"unclosed quote\");\n+\t}\n+\n+\treturn count;\n+}\n+\n+static int handle_alias(int *argcp, const char ***argv)\n+{\n+\tint nongit = 0, ret = 0;\n+\tconst char *subdir;\n+\n+\tsubdir = setup_git_directory_gently(&nongit);\n+\tif (!nongit) {\n+\t\tint count;\n+\t\tconst char** new_argv;\n+\n+\t\talias_command = (*argv)[0];\n+\t\tgit_config(git_alias_config);\n+\t\tif (alias_string) {\n+\n+\t\t\tcount = split_cmdline(alias_string, &new_argv);\n+\n+\t\t\tif (count < 1)\n+\t\t\t\tdie(\"empty alias for %s\", alias_command);\n+\n+\t\t\tif (!strcmp(alias_command, new_argv[0]))\n+\t\t\t\tdie(\"recursive alias: %s\", alias_command);\n+\n+\t\t\t/* insert after command name */\n+\t\t\tif (*argcp > 1) {\n+\t\t\t\tnew_argv = realloc(new_argv, sizeof(char*) *\n+\t\t\t\t\t\t(count + *argcp - 1));\n+\t\t\t\tmemcpy(new_argv + count, *argv, sizeof(char*) *\n+\t\t\t\t\t\t(*argcp - 1));\n+\t\t\t}\n+\n+\t\t\t*argv = new_argv;\n+\t\t\t*argcp += count - 1;\n+\n+\t\t\tret = 1;\n+\t\t}\n+\t}\n+\n+\tif (subdir)\n+\t\tchdir(subdir);\n+\n+\treturn ret;\n+}\n+\n const char git_version_string[] = GIT_VERSION;\n \n static void handle_internal_command(int argc, const char **argv, char **envp)\n@@ -121,6 +229,7 @@ int main(int argc, const char **argv, ch\n \tif (!strncmp(cmd, \"git-\", 4)) {\n \t\tcmd += 4;\n \t\targv[0] = cmd;\n+\t\thandle_alias(&argc, &argv);\n \t\thandle_internal_command(argc, argv, envp);\n \t\tdie(\"cannot handle %s internally\", cmd);\n \t}\n@@ -178,6 +287,8 @@ int main(int argc, const char **argv, ch\n \texec_path = git_exec_path();\n \tprepend_to_path(exec_path, strlen(exec_path));\n \n+\thandle_alias(&argc, &argv);\n+\n \t/* See if it's an internal command */\n \thandle_internal_command(argc, argv, envp);\n \n"}]}