{"thread":{"id":"34547","subject":"[PATCH] builtins: search builtin commands via binary search.","startedAt":"2013-07-26T20:50:50Z","lastAt":"2013-07-29T16:18:31Z","messageCount":8,"participants":["Stefan Beller","Jonathan Nieder","Eric Sunshine","Andreas Schwab","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"224141","messageId":"1374871850-24323-1-git-send-email-stefanbeller@googlemail.com","threadId":"34547","inReplyTo":null,"subject":"[PATCH] builtins: search builtin commands via binary search.","fromName":"Stefan Beller","fromEmail":"stefanbeller@googlemail.com","sentAt":"2013-07-26T20:50:50Z","receivedAt":"2013-07-26T20:50:50Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"There are currently 115 commands built into the git executable.\nBefore this commit, it was iterated over these commands in a linear\norder, i.e. each command was checked.\n\nAs it turns out the commands are already sorted alphabetically, it is easy\nto perform a binary search instead of linear searching.\nThis results in 7 lookups in the worst case.\n\nSigned-off-by: Stefan Beller <stefanbeller@googlemail.com>\n---\n git.c | 19 ++++++++++++++-----\n 1 file changed, 14 insertions(+), 5 deletions(-)\n\ndiff --git a/git.c b/git.c\nindex 2025f77..0d7a9b5 100644\n--- a/git.c\n+++ b/git.c\n@@ -309,9 +309,18 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)\n \treturn 0;\n }\n \n+static int compare_internal_command(const void *a, const void *b) {\n+\t/* The first parameter is of type char* describing the name,\n+\t   the second is a struct cmd_struct */\n+\tconst char *name = (const char*)a;\n+\tconst struct cmd_struct *cmd_struct = (struct cmd_struct*)b;\n+\treturn strcmp(name, cmd_struct->cmd);\n+}\n+\n static void handle_internal_command(int argc, const char **argv)\n {\n \tconst char *cmd = argv[0];\n+\t/* commands must be sorted alphabetically */\n \tstatic struct cmd_struct commands[] = {\n \t\t{ \"add\", cmd_add, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"annotate\", cmd_annotate, RUN_SETUP },\n@@ -447,12 +456,12 @@ static void handle_internal_command(int argc, const char **argv)\n \t\targv[0] = cmd = \"help\";\n \t}\n \n-\tfor (i = 0; i < ARRAY_SIZE(commands); i++) {\n-\t\tstruct cmd_struct *p = commands+i;\n-\t\tif (strcmp(p->cmd, cmd))\n-\t\t\tcontinue;\n+\tstruct cmd_struct *p = (struct cmd_struct *)bsearch(cmd, commands,\n+\t\t\t\tARRAY_SIZE(commands), sizeof(struct cmd_struct),\n+\t\t\t\tcompare_internal_command);\n+\n+\tif (p)\n \t\texit(run_builtin(p, argc, argv));\n-\t}\n }\n \n static void execv_dashed_external(const char **argv)\n-- \n1.8.4.rc0.1.g8f6a3e5\n"},{"id":"224142","messageId":"20130726205737.GI14690@google.com","threadId":"34547","inReplyTo":"1374871850-24323-1-git-send-email-stefanbeller@googlemail.com","subject":"Re: [PATCH] builtins: search builtin commands via binary search.","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2013-07-26T20:57:37Z","receivedAt":"2013-07-26T20:57:37Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi,\n\nStefan Beller wrote:\n\n> --- a/git.c\n> +++ b/git.c\n> @@ -309,9 +309,18 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)\n>  \treturn 0;\n>  }\n>  \n> +static int compare_internal_command(const void *a, const void *b) {\n> +\t/* The first parameter is of type char* describing the name,\n> +\t   the second is a struct cmd_struct */\n\nStyle:\n\n\t/*\n\t * Multi-line comments in git look like this, with an initial\n\t * \"/*\" line, a leading \"*\" on each line with text, and a line\n\t * with '*' '/' at the end.\n\t */\n\n[...]\n> @@ -447,12 +456,12 @@ static void handle_internal_command(int argc, const char **argv)\n>  \t\targv[0] = cmd = \"help\";\n>  \t}\n>  \n> -\tfor (i = 0; i < ARRAY_SIZE(commands); i++) {\n> -\t\tstruct cmd_struct *p = commands+i;\n> -\t\tif (strcmp(p->cmd, cmd))\n> -\t\t\tcontinue;\n> +\tstruct cmd_struct *p = (struct cmd_struct *)bsearch(cmd, commands,\n> +\t\t\t\tARRAY_SIZE(commands), sizeof(struct cmd_struct),\n> +\t\t\t\tcompare_internal_command);\n\nNo need to cast --- this is C.\n\nFun.  Does this result in a measurable speedup, or is it just for more\npleasant reading?\n\nThanks and hope that helps,\nJonathan\n"},{"id":"224161","messageId":"CAPig+cRO4tBi21zPpw3i+9E3xvvJQDmc=CFU5Xyy7zhoJi1Fhg@mail.gmail.com","threadId":"34547","inReplyTo":"1374871850-24323-1-git-send-email-stefanbeller@googlemail.com","subject":"Re: [PATCH] builtins: search builtin commands via binary search.","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2013-07-27T00:26:59Z","receivedAt":"2013-07-27T00:26:59Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Jul 26, 2013 at 4:50 PM, Stefan Beller\n<stefanbeller@googlemail.com> wrote:\n> There are currently 115 commands built into the git executable.\n> Before this commit, it was iterated over these commands in a linear\n> order, i.e. each command was checked.\n>\n> As it turns out the commands are already sorted alphabetically, it is easy\n> to perform a binary search instead of linear searching.\n> This results in 7 lookups in the worst case.\n>\n> Signed-off-by: Stefan Beller <stefanbeller@googlemail.com>\n> ---\n>  git.c | 19 ++++++++++++++-----\n>  1 file changed, 14 insertions(+), 5 deletions(-)\n>\n> diff --git a/git.c b/git.c\n> index 2025f77..0d7a9b5 100644\n> --- a/git.c\n> +++ b/git.c\n> @@ -309,9 +309,18 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)\n>         return 0;\n>  }\n>\n> +static int compare_internal_command(const void *a, const void *b) {\n> +       /* The first parameter is of type char* describing the name,\n> +          the second is a struct cmd_struct */\n> +       const char *name = (const char*)a;\n> +       const struct cmd_struct *cmd_struct = (struct cmd_struct*)b;\n\nComments typically exist to elucidate something non-obvious in the\ncode, however, in this case the code and comment say the same thing,\nmaking the comment redundant. Such redundancy can make code harder to\nread since the reader has to take extra time to figure out if the\ncomment is really explaining something not obvious in the code. Thus,\nthis comment can be removed without loss of clarity.\n\n> +       return strcmp(name, cmd_struct->cmd);\n> +}\n> +\n>  static void handle_internal_command(int argc, const char **argv)\n>  {\n>         const char *cmd = argv[0];\n> +       /* commands must be sorted alphabetically */\n>         static struct cmd_struct commands[] = {\n\nThis new comment, on the other hand does explain something not obvious\nat this point in the code.\n\n>                 { \"add\", cmd_add, RUN_SETUP | NEED_WORK_TREE },\n>                 { \"annotate\", cmd_annotate, RUN_SETUP },\n> @@ -447,12 +456,12 @@ static void handle_internal_command(int argc, const char **argv)\n>                 argv[0] = cmd = \"help\";\n>         }\n>\n> -       for (i = 0; i < ARRAY_SIZE(commands); i++) {\n> -               struct cmd_struct *p = commands+i;\n> -               if (strcmp(p->cmd, cmd))\n> -                       continue;\n> +       struct cmd_struct *p = (struct cmd_struct *)bsearch(cmd, commands,\n> +                               ARRAY_SIZE(commands), sizeof(struct cmd_struct),\n> +                               compare_internal_command);\n\nSince this will break down if the commands[] array becomes unsorted,\nit would make sense to protect against such a failure. For instance,\nyou could add a check in Makefile which triggers when git.c is edited.\nIt might do something like this:\n\n  awk '/cmd_struct commands/,/};/ { if (match($2,/\"/)) print $2 }'\n    <git.c >builtin.actual &&\n  sort <builtin.actual >builtin.expect &&\n  cmp -s builtin.expect builtin.actual &&\n  rm builtin.expect builtin.actual\n\n> +\n> +       if (p)\n>                 exit(run_builtin(p, argc, argv));\n> -       }\n>  }\n>\n>  static void execv_dashed_external(const char **argv)\n> --\n"},{"id":"224170","messageId":"51F38997.9010507@googlemail.com","threadId":"34547","inReplyTo":"20130726205737.GI14690@google.com","subject":"Re: [PATCH] builtins: search builtin commands via binary search.","fromName":"Stefan Beller","fromEmail":"stefanbeller@googlemail.com","sentAt":"2013-07-27T08:49:27Z","receivedAt":"2013-07-27T08:49:27Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On 07/26/2013 10:57 PM, Jonathan Nieder wrote:\n> Hi,\n> \n> Stefan Beller wrote:\n> \n>> --- a/git.c\n>> +++ b/git.c\n>> @@ -309,9 +309,18 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)\n>>  \treturn 0;\n>>  }\n>>  \n>> +static int compare_internal_command(const void *a, const void *b) {\n>> +\t/* The first parameter is of type char* describing the name,\n>> +\t   the second is a struct cmd_struct */\n> \n> Style:\n> \n> \t/*\n> \t * Multi-line comments in git look like this, with an initial\n> \t * \"/*\" line, a leading \"*\" on each line with text, and a line\n> \t * with '*' '/' at the end.\n> \t */\n> \n> [...]\n\nThanks for noting, however as Eric points out, that comment was not enlightening, so I removed it.\n\n\n>> @@ -447,12 +456,12 @@ static void handle_internal_command(int argc, const char **argv)\n>>  \t\targv[0] = cmd = \"help\";\n>>  \t}\n>>  \n>> -\tfor (i = 0; i < ARRAY_SIZE(commands); i++) {\n>> -\t\tstruct cmd_struct *p = commands+i;\n>> -\t\tif (strcmp(p->cmd, cmd))\n>> -\t\t\tcontinue;\n>> +\tstruct cmd_struct *p = (struct cmd_struct *)bsearch(cmd, commands,\n>> +\t\t\t\tARRAY_SIZE(commands), sizeof(struct cmd_struct),\n>> +\t\t\t\tcompare_internal_command);\n> \n> No need to cast --- this is C.\n\nAlso removed.\n\n> \n> Fun.  Does this result in a measurable speedup, or is it just for more\n> pleasant reading?\n> \n> Thanks and hope that helps,\n> Jonathan\n> \n\npremature optimization is the root of all evil....\nI tried hard to come up with a benchmark, but this is lost in the\nnoise. I could not figure out a way to reproducably make sure this\npatch is really faster.\nSo I tried to `time git add COPYING` to show it's not getting slower\nfor the first entries in the list as well as `git fast-external-command`\nwhereas the fast-external-command is just an int main() {return 0; }\nto check if the external commands, which are executed after searching \nthrough all the internals come up faster.\n\nHowever I could not find a speedup.\nSo if the patch is accepted, it would only be for readability.\n\nI was fiddling around with make now to include the suggestion of Eric to\ncheck the arguments for being sorted in make. However I do not \nseem to fully understand the syntax yet.\nMy approach would have been:\n\nsorted_internal_cmds: git.c\n\t{ awk '/cmd_struct commands/,/};/ { if (match($2,/\"/)) print $2 }' <git.c >builtin.actual && \\\n\tsort <builtin.actual >builtin.expect && \\\n\tcmp -s builtin.expect builtin.actual && \\\n\trm builtin.expect builtin.actual \\\n\t}\n\nall:: sorted_internal_cmds\n\nBut then there is \n$ make \n...\n\t}\n/bin/sh: 5: Syntax error: end of file unexpected (expecting \"}\")\n\nSo I suspect the { within the shell code inside the awk parameter is messing up?\n\nThanks,\nStefan\n\n\n"},{"id":"224171","messageId":"1374915038-6717-1-git-send-email-stefanbeller@googlemail.com","threadId":"34547","inReplyTo":"51F38997.9010507@googlemail.com","subject":"[PATCH] builtins: search builtin commands via binary search.","fromName":"Stefan Beller","fromEmail":"stefanbeller@googlemail.com","sentAt":"2013-07-27T08:50:38Z","receivedAt":"2013-07-27T08:50:38Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"There are currently 115 commands built into the git executable.\nBefore this commit, it was iterated over these commands in a linear\norder, i.e. each command was checked.\n\nAs it turns out the commands are already sorted alphabetically, it is easy\nto perform a binary search instead of linear searching.\nThis results in 7 lookups in the worst case.\n\nSigned-off-by: Stefan Beller <stefanbeller@googlemail.com>\n---\n git.c | 15 ++++++++++-----\n 1 file changed, 10 insertions(+), 5 deletions(-)\n\ndiff --git a/git.c b/git.c\nindex 2025f77..6d4de2b 100644\n--- a/git.c\n+++ b/git.c\n@@ -309,9 +309,14 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)\n \treturn 0;\n }\n \n+static int compare_internal_command(const void *name, const void *cmd) {\n+\treturn strcmp((const char*)name, ((const struct cmd_struct*)cmd)->cmd);\n+}\n+\n static void handle_internal_command(int argc, const char **argv)\n {\n \tconst char *cmd = argv[0];\n+\t/* commands must be sorted alphabetically for binary search */\n \tstatic struct cmd_struct commands[] = {\n \t\t{ \"add\", cmd_add, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"annotate\", cmd_annotate, RUN_SETUP },\n@@ -447,12 +452,12 @@ static void handle_internal_command(int argc, const char **argv)\n \t\targv[0] = cmd = \"help\";\n \t}\n \n-\tfor (i = 0; i < ARRAY_SIZE(commands); i++) {\n-\t\tstruct cmd_struct *p = commands+i;\n-\t\tif (strcmp(p->cmd, cmd))\n-\t\t\tcontinue;\n+\tstruct cmd_struct *p = bsearch(cmd, commands,\n+\t\t\t\tARRAY_SIZE(commands), sizeof(struct cmd_struct),\n+\t\t\t\tcompare_internal_command);\n+\n+\tif (p)\n \t\texit(run_builtin(p, argc, argv));\n-\t}\n }\n \n static void execv_dashed_external(const char **argv)\n-- \n1.8.4.rc0.1.g8f6a3e5\n"},{"id":"224172","messageId":"m2k3kc4b6w.fsf@linux-m68k.org","threadId":"34547","inReplyTo":"51F38997.9010507@googlemail.com","subject":"Re: [PATCH] builtins: search builtin commands via binary search.","fromName":"Andreas Schwab","fromEmail":"schwab@linux-m68k.org","sentAt":"2013-07-27T09:13:59Z","receivedAt":"2013-07-27T09:13:59Z","isPatch":true,"sender":{"key":"schwab@linux-m68k.org","avatar":"https://avatars.githubusercontent.com/u/2175493?v=4"},"body":"Stefan Beller <stefanbeller@googlemail.com> writes:\n\n> My approach would have been:\n>\n> sorted_internal_cmds: git.c\n> \t{ awk '/cmd_struct commands/,/};/ { if (match($2,/\"/)) print $2 }' <git.c >builtin.actual && \\\n> \tsort <builtin.actual >builtin.expect && \\\n> \tcmp -s builtin.expect builtin.actual && \\\n> \trm builtin.expect builtin.actual \\\n> \t}\n\nYou need a semicolon before the closing }.\n\nAndreas.\n\n-- \nAndreas Schwab, schwab@linux-m68k.org\nGPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5\n\"And now for something completely different.\"\n"},{"id":"224199","messageId":"CAPig+cSsUJSd2nx6icfbEgv71hBVnBQM8oxpvoqLzqFPkNY4KA@mail.gmail.com","threadId":"34547","inReplyTo":"51F38997.9010507@googlemail.com","subject":"Re: [PATCH] builtins: search builtin commands via binary search.","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2013-07-28T09:05:00Z","receivedAt":"2013-07-28T09:05:00Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sat, Jul 27, 2013 at 4:49 AM, Stefan Beller\n<stefanbeller@googlemail.com> wrote:\n> I was fiddling around with make now to include the suggestion of Eric to\n> check the arguments for being sorted in make. However I do not\n> seem to fully understand the syntax yet.\n> My approach would have been:\n>\n> sorted_internal_cmds: git.c\n>         { awk '/cmd_struct commands/,/};/ { if (match($2,/\"/)) print $2 }' <git.c >builtin.actual && \\\n>         sort <builtin.actual >builtin.expect && \\\n>         cmp -s builtin.expect builtin.actual && \\\n>         rm builtin.expect builtin.actual \\\n>         }\n>\n> all:: sorted_internal_cmds\n>\n> But then there is\n> $ make\n> ...\n>         }\n> /bin/sh: 5: Syntax error: end of file unexpected (expecting \"}\")\n>\n> So I suspect the { within the shell code inside the awk parameter is messing up?\n\nAs Andreas noted, you need a semicolon before the closing shell '}',\nhowever it's not clear why you added the braces since they are not\nneeded. The following works (after fixing whitespace corruption):\n\n-->8--\ndiff --git a/Makefile b/Makefile\nindex ef442eb..82e727c 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1681,6 +1681,15 @@ shell_compatibility_test:\nplease_set_SHELL_PATH_to_a_more_modern_shell\n strip: $(PROGRAMS) git$X\n  $(STRIP) $(STRIP_OPTS) $^\n\n+.PHONY: sorted_internal_cmds\n+all:: sorted_internal_cmds\n+\n+sorted_internal_cmds: git.c\n+ @awk '/cmd_struct commands/,/};/ { if (match($$2,/\"/)) print $$2 }'\n<git.c >builtin.actual && \\\n+ sort <builtin.actual >builtin.expect && \\\n+ cmp -s builtin.expect builtin.actual && \\\n+ rm builtin.expect builtin.actual\n+\n ### Target-specific flags and dependencies\n\n # The generic compilation pattern rule and automatically\n-->8--\n\nNote the $$2 in awk expression. Also the .PHONY is a good idea.\n\nHowever, perhaps, it is better for this check to be part of the test\nsuite. It might look like this (after fixing whitespace corruption):\n\n-->8--\ndiff --git a/t/t0000-basic.sh b/t/t0000-basic.sh\nindex 10be52b..e5ba504 100755\n--- a/t/t0000-basic.sh\n+++ b/t/t0000-basic.sh\n@@ -387,6 +387,14 @@ test_expect_success 'tests clean up even on failures' \"\n ################################################################\n # Basics of the basics\n\n+# git.c commands[] of builtins is properly sorted\n+test_expect_success 'builtin commands[] sorted' '\n+ awk \"/cmd_struct commands/,/};/ { if (match(\\$2,/\\\"/)) print \\$2 }\" \\\n+ <../../git.c >actual && \\\n+ sort <actual >expect && \\\n+ test_cmp expect actual\n+'\n+\n # updating a new file without --add should fail.\n test_expect_success 'git update-index without --add should fail adding' '\n  test_must_fail git update-index should-be-empty\n-->8--\n\nI'm not sure that referencing ../../git.c from within the test suite\nis kosher. Perhaps Jonathan can say something about that.\n"},{"id":"224233","messageId":"7v4nbd1grs.fsf@alter.siamese.dyndns.org","threadId":"34547","inReplyTo":"51F38997.9010507@googlemail.com","subject":"Re: [PATCH] builtins: search builtin commands via binary search.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-07-29T16:18:31Z","receivedAt":"2013-07-29T16:18:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Stefan Beller <stefanbeller@googlemail.com> writes:\n\n> However I could not find a speedup.\n> So if the patch is accepted, it would only be for readability.\n\nThis adds a maintenance burden.  It is very easy for somebody to\nmistakenly run \"sort\" on the region in her editor under non C\nlocale.\n\nPerhaps with a change like the attached, if we were to do so.  Then\nhave a test that runs \"git --check-builtins-sorted\" will ensure we\nwon't ever screw it up.\n\nFOR ILLUSTRATION PURPOSES ONLY: diff --git a/git.c b/git.c\nindex 2025f77..3155273 100644\n--- a/git.c\n+++ b/git.c\n@@ -34,6 +34,150 @@ static void commit_pager_choice(void) {\n \t}\n }\n \n+struct cmd_struct {\n+\tconst char *cmd;\n+\tint (*fn)(int, const char **, const char *);\n+\tint option;\n+};\n+\n+#define RUN_SETUP\t\t(1<<0)\n+#define RUN_SETUP_GENTLY\t(1<<1)\n+#define USE_PAGER\t\t(1<<2)\n+/*\n+ * require working tree to be present -- anything uses this needs\n+ * RUN_SETUP for reading from the configuration file.\n+ */\n+#define NEED_WORK_TREE\t\t(1<<3)\n+\n+static struct cmd_struct builtin_commands[] = {\n+\t{ \"add\", cmd_add, RUN_SETUP | NEED_WORK_TREE },\n+\t{ \"annotate\", cmd_annotate, RUN_SETUP },\n+\t...\n+\t{ \"whatchanged\", cmd_whatchanged, RUN_SETUP },\n+\t{ \"write-tree\", cmd_write_tree, RUN_SETUP },\n+};\n+\n+static void make_sure_the_builtin_array_is_sorted(void)\n+{\n+\tint i;\n+\tfor (i = 0; i < ARRAY_SIZE(builtin_commands) - 1; i++) {\n+\t\tstruct cmd_struct *it = &builtin_commands[i];\n+\t\tif (strcmp(it[0].cmd, it[1].cmd) >= 0)\n+\t\t\tdie(\"BUG: builtin command list not sorted %s > %s\",\n+\t\t\t    it[0].cmd, it[1].cmd);\n+\t}\n+}\n+\n static int handle_options(const char ***argv, int *argc, int *envchanged)\n {\n \tconst char **orig_argv = *argv;\n@@ -62,6 +206,9 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)\n \t\t\t\tputs(git_exec_path());\n \t\t\t\texit(0);\n \t\t\t}\n+\t\t} else if (!strcmp(cmd, \"--check-builtins-sorted\")) {\n+\t\t\tmake_sure_the_builtin_array_is_sorted();\n+\t\t\texit(0);\n \t\t} else if (!strcmp(cmd, \"--html-path\")) {\n \t\t\tputs(system_path(GIT_HTML_PATH));\n \t\t\texit(0);\n@@ -241,21 +388,6 @@ static int handle_alias(int *argcp, const char ***argv)\n \treturn ret;\n }\n \n-#define RUN_SETUP\t\t(1<<0)\n-#define RUN_SETUP_GENTLY\t(1<<1)\n-#define USE_PAGER\t\t(1<<2)\n-/*\n- * require working tree to be present -- anything uses this needs\n- * RUN_SETUP for reading from the configuration file.\n- */\n-#define NEED_WORK_TREE\t\t(1<<3)\n-\n-struct cmd_struct {\n-\tconst char *cmd;\n-\tint (*fn)(int, const char **, const char *);\n-\tint option;\n-};\n-\n static int run_builtin(struct cmd_struct *p, int argc, const char **argv)\n {\n \tint status, help;\n@@ -312,123 +444,6 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)\n static void handle_internal_command(int argc, const char **argv)\n {\n \tconst char *cmd = argv[0];\n-\tstatic struct cmd_struct commands[] = {\n-\t\t{ \"add\", cmd_add, RUN_SETUP | NEED_WORK_TREE },\n-\t\t{ \"annotate\", cmd_annotate, RUN_SETUP },\n-\t\t...\n-\t\t{ \"whatchanged\", cmd_whatchanged, RUN_SETUP },\n-\t\t{ \"write-tree\", cmd_write_tree, RUN_SETUP },\n-\t};\n \tint i;\n \tstatic const char ext[] = STRIP_EXTENSION;\n \n@@ -447,8 +462,8 @@ static void handle_internal_command(int argc, const char **argv)\n \t\targv[0] = cmd = \"help\";\n \t}\n \n-\tfor (i = 0; i < ARRAY_SIZE(commands); i++) {\n-\t\tstruct cmd_struct *p = commands+i;\n+\tfor (i = 0; i < ARRAY_SIZE(builtin_commands); i++) {\n+\t\tstruct cmd_struct *p = builtin_commands+i;\n \t\tif (strcmp(p->cmd, cmd))\n \t\t\tcontinue;\n \t\texit(run_builtin(p, argc, argv));\n"}]}