{"thread":{"id":"10039","subject":"[PATCH 1/4] Add a simple option parser for use by builtin-commit.c.","startedAt":"2007-09-27T04:50:29Z","lastAt":"2007-10-03T22:03:54Z","messageCount":17,"participants":["Kristian Høgsberg","Junio C Hamano","Johannes Schindelin","Jonas Fonseca","Jeff King"],"isPatch":true,"patchVersion":1,"patchTotal":4},"messages":[{"id":"54128","messageId":"1190868632-29287-1-git-send-email-krh@redhat.com","threadId":"10039","inReplyTo":null,"subject":"[PATCH 1/4] Add a simple option parser for use by builtin-commit.c.","fromName":"Kristian Høgsberg","fromEmail":"krh@redhat.com","sentAt":"2007-09-27T04:50:29Z","receivedAt":"2007-09-27T04:50:29Z","isPatch":true,"sender":{"key":"krh@redhat.com","avatar":"https://gravatar.com/avatar/763dee6f9594ac474f725b137a39565792928e583ddf59b32befc2907409027e?d=mp&s=160"},"body":"Signed-off-by: Kristian Høgsberg <krh@redhat.com>\n---\n Makefile        |    2 +-\n parse-options.c |   74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++\n parse-options.h |   29 +++++++++++++++++++++\n 3 files changed, 104 insertions(+), 1 deletions(-)\n create mode 100644 parse-options.c\n create mode 100644 parse-options.h\n\ndiff --git a/Makefile b/Makefile\nindex 62bdac6..d90e959 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -310,7 +310,7 @@ LIB_OBJS = \\\n \talloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \\\n \tcolor.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \\\n \tconvert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \\\n-\ttransport.o bundle.o\n+\ttransport.o bundle.o parse-options.o\n \n BUILTIN_OBJS = \\\n \tbuiltin-add.o \\\ndiff --git a/parse-options.c b/parse-options.c\nnew file mode 100644\nindex 0000000..2fb30cd\n--- /dev/null\n+++ b/parse-options.c\n@@ -0,0 +1,74 @@\n+#include \"git-compat-util.h\"\n+#include \"parse-options.h\"\n+\n+int parse_options(const char ***argv,\n+\t\t  struct option *options, int count,\n+\t\t  const char *usage_string)\n+{\n+\tconst char *value, *eq;\n+\tint i;\n+\n+\tif (**argv == NULL)\n+\t\treturn 0;\n+\tif ((**argv)[0] != '-')\n+\t\treturn 0;\n+\tif (!strcmp(**argv, \"--\"))\n+\t\treturn 0;\n+\n+\tvalue = NULL;\n+\tfor (i = 0; i < count; i++) {\n+\t\tif ((**argv)[1] == '-') {\n+\t\t\tif (!prefixcmp(options[i].long_name, **argv + 2)) {\n+\t\t\t\tif (options[i].type != OPTION_BOOLEAN)\n+\t\t\t\t\tvalue = *++(*argv);\n+\t\t\t\tgoto match;\n+\t\t\t}\n+\n+\t\t\teq = strchr(**argv + 2, '=');\n+\t\t\tif (eq && options[i].type != OPTION_BOOLEAN &&\n+\t\t\t    !strncmp(**argv + 2,\n+\t\t\t\t     options[i].long_name, eq - **argv - 2)) {\n+\t\t\t\tvalue = eq + 1;\n+\t\t\t\tgoto match;\n+\t\t\t}\n+\t\t}\n+\n+\t\tif ((**argv)[1] == options[i].short_name) {\n+\t\t\tif ((**argv)[2] == '\\0') {\n+\t\t\t\tif (options[i].type != OPTION_BOOLEAN)\n+\t\t\t\t\tvalue = *++(*argv);\n+\t\t\t\tgoto match;\n+\t\t\t}\n+\n+\t\t\tif (options[i].type != OPTION_BOOLEAN) {\n+\t\t\t\tvalue = **argv + 2;\n+\t\t\t\tgoto match;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tusage(usage_string);\n+\n+ match:\n+\tswitch (options[i].type) {\n+\tcase OPTION_BOOLEAN:\n+\t\t*(int *)options[i].value = 1;\n+\t\tbreak;\n+\tcase OPTION_STRING:\n+\t\tif (value == NULL)\n+\t\t\tdie(\"option %s requires a value.\", (*argv)[-1]);\n+\t\t*(const char **)options[i].value = value;\n+\t\tbreak;\n+\tcase OPTION_INTEGER:\n+\t\tif (value == NULL)\n+\t\t\tdie(\"option %s requires a value.\", (*argv)[-1]);\n+\t\t*(int *)options[i].value = atoi(value);\n+\t\tbreak;\n+\tdefault:\n+\t\tassert(0);\n+\t}\n+\n+\t(*argv)++;\n+\n+\treturn 1;\n+}\ndiff --git a/parse-options.h b/parse-options.h\nnew file mode 100644\nindex 0000000..39399c3\n--- /dev/null\n+++ b/parse-options.h\n@@ -0,0 +1,29 @@\n+#ifndef PARSE_OPTIONS_H\n+#define PARSE_OPTIONS_H\n+\n+enum option_type {\n+    OPTION_BOOLEAN,\n+    OPTION_STRING,\n+    OPTION_INTEGER,\n+    OPTION_LAST,\n+};\n+\n+struct option {\n+    enum option_type type;\n+    const char *long_name;\n+    char short_name;\n+    void *value;\n+};\n+\n+/* Parse the given options against the list of known options.  The\n+ * order of the option structs matters, in that ambiguous\n+ * abbreviations (eg, --in could be short for --include or\n+ * --interactive) are matched by the first option that share the\n+ * prefix.\n+ */\n+\n+extern int parse_options(const char ***argv,\n+\t\t\t struct option *options, int count,\n+\t\t\t const char *usage_string);\n+\n+#endif\n-- \n1.5.2.GIT\n"},{"id":"54129","messageId":"1190868632-29287-2-git-send-email-krh@redhat.com","threadId":"10039","inReplyTo":"1190868632-29287-1-git-send-email-krh@redhat.com","subject":"[PATCH 2/4] This exports the update() function from builtin-add.c as","fromName":"Kristian Høgsberg","fromEmail":"krh@redhat.com","sentAt":"2007-09-27T04:50:30Z","receivedAt":"2007-09-27T04:50:30Z","isPatch":true,"sender":{"key":"krh@redhat.com","avatar":"https://gravatar.com/avatar/763dee6f9594ac474f725b137a39565792928e583ddf59b32befc2907409027e?d=mp&s=160"},"body":"Signed-off-by: Kristian Høgsberg <krh@redhat.com>\n---\n builtin-add.c |    8 ++++----\n commit.h      |    2 ++\n 2 files changed, 6 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin-add.c b/builtin-add.c\nindex 5e30380..966e145 100644\n--- a/builtin-add.c\n+++ b/builtin-add.c\n@@ -107,7 +107,7 @@ static void update_callback(struct diff_queue_struct *q,\n \t}\n }\n \n-static void update(int verbose, const char *prefix, const char **files)\n+void add_files_to_cache(int verbose, const char *prefix, const char **files)\n {\n \tstruct rev_info rev;\n \tinit_revisions(&rev, prefix);\n@@ -116,8 +116,6 @@ static void update(int verbose, const char *prefix, const char **files)\n \trev.diffopt.output_format = DIFF_FORMAT_CALLBACK;\n \trev.diffopt.format_callback = update_callback;\n \trev.diffopt.format_callback_data = &verbose;\n-\tif (read_cache() < 0)\n-\t\tdie(\"index file corrupt\");\n \trun_diff_files(&rev, 0);\n }\n \n@@ -218,7 +216,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)\n \t}\n \n \tif (take_worktree_changes) {\n-\t\tupdate(verbose, prefix, argv + i);\n+\t\tif (read_cache() < 0)\n+\t\t\tdie(\"index file corrupt\");\n+\t\tadd_files_to_cache(verbose, prefix, argv + i);\n \t\tgoto finish;\n \t}\n \ndiff --git a/commit.h b/commit.h\nindex cc8d890..89caa12 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -130,6 +130,8 @@ extern struct commit_list *get_shallow_commits(struct object_array *heads,\n int in_merge_bases(struct commit *, struct commit **, int);\n \n extern int interactive_add(void);\n+extern void add_files_to_cache(int verbose,\n+\t\t\t       const char *prefix, const char **files);\n extern int rerere(void);\n \n #endif /* COMMIT_H */\n-- \n1.5.2.GIT\n"},{"id":"54130","messageId":"1190868632-29287-3-git-send-email-krh@redhat.com","threadId":"10039","inReplyTo":"1190868632-29287-2-git-send-email-krh@redhat.com","subject":"[PATCH 3/4] Implement git commit as a builtin command.","fromName":"Kristian Høgsberg","fromEmail":"krh@redhat.com","sentAt":"2007-09-27T04:50:31Z","receivedAt":"2007-09-27T04:50:31Z","isPatch":true,"sender":{"key":"krh@redhat.com","avatar":"https://gravatar.com/avatar/763dee6f9594ac474f725b137a39565792928e583ddf59b32befc2907409027e?d=mp&s=160"},"body":"Move git-commit.sh to contrib/examples.\n\nSigned-off-by: Kristian Høgsberg <krh@redhat.com>\n---\n Makefile                       |    9 +-\n builtin-commit.c               |  611 +++++++++++++++++++++++++++++++++++++++\n builtin-tag.c                  |    3 +-\n builtin.h                      |    3 +-\n contrib/examples/git-commit.sh |  627 ++++++++++++++++++++++++++++++++++++++++\n git-commit.sh                  |  627 ----------------------------------------\n git.c                          |    3 +-\n strbuf.h                       |    1 +\n t/t3501-revert-cherry-pick.sh  |    4 +-\n t/t3901-i18n-patch.sh          |    8 +-\n t/test-lib.sh                  |    4 +-\n 11 files changed, 1255 insertions(+), 645 deletions(-)\n create mode 100644 builtin-commit.c\n create mode 100755 contrib/examples/git-commit.sh\n delete mode 100755 git-commit.sh\n\ndiff --git a/Makefile b/Makefile\nindex d90e959..6172589 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -206,7 +206,7 @@ BASIC_LDFLAGS =\n \n SCRIPT_SH = \\\n \tgit-bisect.sh git-checkout.sh \\\n-\tgit-clean.sh git-clone.sh git-commit.sh \\\n+\tgit-clean.sh git-clone.sh \\\n \tgit-ls-remote.sh \\\n \tgit-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \\\n \tgit-pull.sh git-rebase.sh git-rebase--interactive.sh \\\n@@ -254,7 +254,7 @@ EXTRA_PROGRAMS =\n BUILT_INS = \\\n \tgit-format-patch$X git-show$X git-whatchanged$X git-cherry$X \\\n \tgit-get-tar-commit-id$X git-init$X git-repo-config$X \\\n-\tgit-fsck-objects$X git-cherry-pick$X \\\n+\tgit-fsck-objects$X git-cherry-pick$X git-status$X\\\n \t$(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))\n \n # what 'all' will build and 'install' will install, in gitexecdir\n@@ -324,6 +324,7 @@ BUILTIN_OBJS = \\\n \tbuiltin-check-attr.o \\\n \tbuiltin-checkout-index.o \\\n \tbuiltin-check-ref-format.o \\\n+\tbuiltin-commit.o \\\n \tbuiltin-commit-tree.o \\\n \tbuiltin-count-objects.o \\\n \tbuiltin-describe.o \\\n@@ -362,7 +363,6 @@ BUILTIN_OBJS = \\\n \tbuiltin-rev-parse.o \\\n \tbuiltin-revert.o \\\n \tbuiltin-rm.o \\\n-\tbuiltin-runstatus.o \\\n \tbuiltin-shortlog.o \\\n \tbuiltin-show-branch.o \\\n \tbuiltin-stripspace.o \\\n@@ -822,9 +822,6 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl\n \tchmod +x $@+ && \\\n \tmv $@+ $@\n \n-git-status: git-commit\n-\t$(QUIET_GEN)cp $< $@+ && mv $@+ $@\n-\n gitweb/gitweb.cgi: gitweb/gitweb.perl\n \t$(QUIET_GEN)$(RM) $@ $@+ && \\\n \tsed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \\\ndiff --git a/builtin-commit.c b/builtin-commit.c\nnew file mode 100644\nindex 0000000..69e8b19\n--- /dev/null\n+++ b/builtin-commit.c\n@@ -0,0 +1,611 @@\n+/*\n+ * Builtin \"git commit\"\n+ *\n+ * Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>\n+ * Based on git-commit.sh by Junio C Hamano and Linus Torvalds\n+ */\n+\n+#include \"cache.h\"\n+#include \"cache-tree.h\"\n+#include \"builtin.h\"\n+#include \"diff.h\"\n+#include \"diffcore.h\"\n+#include \"commit.h\"\n+#include \"revision.h\"\n+#include \"wt-status.h\"\n+#include \"run-command.h\"\n+#include \"refs.h\"\n+#include \"log-tree.h\"\n+#include \"strbuf.h\"\n+#include \"utf8.h\"\n+#include \"parse-options.h\"\n+\n+static const char builtin_commit_usage[] =\n+\t\"[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [--template <file>] [[-i | -o] <path>...]\";\n+\n+static unsigned char head_sha1[20], merge_head_sha1[20];\n+static char *use_message_buffer;\n+static const char commit_editmsg[] = \"COMMIT_EDITMSG\";\n+static struct lock_file lock_file;\n+\n+static char *logfile, *force_author, *message, *template_file;\n+static char *edit_message, *use_message;\n+static int all, edit_flag, also, interactive, only, no_verify, amend, signoff;\n+static int quiet, verbose, untracked_files;\n+\n+static int no_edit, initial_commit, in_merge;\n+const char *only_include_assumed;\n+\n+static struct option commit_options[] = {\n+\t{ OPTION_STRING, \"file\", 'F', (void *) &logfile },\n+\t{ OPTION_BOOLEAN, \"all\", 'a', &all },\n+\t{ OPTION_STRING, \"author\", 0, (void *) &force_author },\n+\t{ OPTION_BOOLEAN, \"edit\", 'e', &edit_flag },\n+\t{ OPTION_BOOLEAN, \"include\", 'i', &also },\n+\t{ OPTION_BOOLEAN, \"interactive\", 0, &interactive },\n+\t{ OPTION_BOOLEAN, \"only\", 'o', &only },\n+\t{ OPTION_STRING, \"message\", 'm', &message },\n+\t{ OPTION_BOOLEAN, \"no-verify\", 'n', &no_verify },\n+\t{ OPTION_BOOLEAN, \"amend\", 0, &amend },\n+\t{ OPTION_STRING, \"reedit-message\", 'c', &edit_message },\n+\t{ OPTION_STRING, \"reuse-message\", 'C', &use_message },\n+\t{ OPTION_BOOLEAN, \"signoff\", 's', &signoff },\n+\t{ OPTION_BOOLEAN, \"quiet\", 'q', &quiet },\n+\t{ OPTION_BOOLEAN, \"verbose\", 'v', &verbose },\n+\t{ OPTION_BOOLEAN, \"untracked-files\", 0, &untracked_files },\n+\t{ OPTION_STRING, \"template\", 't', &template_file },\n+\t{ OPTION_LAST },\n+};\n+\n+static char *\n+prepare_index(const char **files, const char *prefix)\n+{\n+\tint fd;\n+\tstruct tree *tree;\n+\tstruct lock_file *next_index_lock;\n+\n+\tfd = hold_locked_index(&lock_file, 1);\n+\tif (read_cache() < 0)\n+\t\tdie(\"index file corrupt\");\n+\n+\tif (all) {\n+\t\tadd_files_to_cache(0, prefix, files);\n+\t\tif (write_cache(fd, active_cache, active_nr) || close(fd))\n+\t\t\tdie(\"unable to write new index file\");\n+\t\treturn lock_file.filename;\n+\t} else if (also) {\n+\t\tadd_files_to_cache(fd, prefix, files);\n+\t\tif (write_cache(fd, active_cache, active_nr) || close(fd))\n+\t\t\tdie(\"unable to write new index file\");\n+\t\treturn lock_file.filename;\n+\t}\n+\n+\tif (interactive)\n+\t\tinteractive_add();\n+\n+\tif (*files == NULL) {\n+\t\t/* Commit index as-is. */\n+\t\trollback_lock_file(&lock_file);\n+\t\treturn get_index_file();\n+\t}\n+\n+\t/*\n+\t * FIXME: Warn on unknown files.  Shell script does\n+\t *\n+\t *   commit_only=`git-ls-files --error-unmatch -- \"$@\"`\n+\t */\n+\n+\t/*\n+\t * FIXME: shell script does\n+\t *\n+\t *   git-read-tree --index-output=\"$TMP_INDEX\" -i -m HEAD\n+\t *\n+\t * which warns about unmerged files in the index.\n+\t */\n+\n+\t/* update the user index file */\n+\tadd_files_to_cache(0, prefix, files);\n+\tif (write_cache(fd, active_cache, active_nr) || close(fd))\n+\t\tdie(\"unable to write new index file\");\n+\n+\tif (!initial_commit) {\n+\t\ttree = parse_tree_indirect(head_sha1);\n+\t\tif (!tree)\n+\t\t\tdie(\"failed to unpack HEAD tree object\");\n+\t\tif (read_tree(tree, 0, NULL))\n+\t\t\tdie(\"failed to read HEAD tree object\");\n+\t}\n+\n+\t/* Uh oh, abusing lock_file to create a garbage collected file */\n+\tnext_index_lock = xmalloc(sizeof(*next_index_lock));\n+\tfd = hold_lock_file_for_update(next_index_lock,\n+\t\t\t\t       git_path(\"next-index-%d\", getpid()), 1);\n+\tadd_files_to_cache(0, prefix, files);\n+\tif (write_cache(fd, active_cache, active_nr) || close(fd))\n+\t\tdie(\"unable to write new index file\");\n+\n+\treturn next_index_lock->filename;\n+}\n+\n+static int run_status(FILE *fp, const char *index_file)\n+{\n+\tstruct wt_status s;\n+\n+\twt_status_prepare(&s);\n+\n+\tif (amend) {\n+\t\ts.amend = 1;\n+\t\ts.reference = \"HEAD^1\";\n+\t}\n+\ts.verbose = verbose;\n+\ts.untracked = untracked_files;\n+\ts.index_file = index_file;\n+\ts.fp = fp;\n+\n+\twt_status_print(&s);\n+\n+\treturn s.commitable;\n+}\n+\n+static const char sign_off_header[] = \"Signed-off-by: \";\n+\n+static int prepare_log_message(const char *index_file)\n+{\n+\tstruct stat statbuf;\n+\tint commitable;\n+\tstruct strbuf sb;\n+\tchar *buffer;\n+\tFILE *fp;\n+\n+\tstrbuf_init(&sb, 0);\n+\tif (message) {\n+\t\tstrbuf_add(&sb, message, strlen(message));\n+\t} else if (logfile && !strcmp(logfile, \"-\")) {\n+\t\tif (isatty(0))\n+\t\t\tfprintf(stderr, \"(reading log message from standard input)\\n\");\n+\t\tif (strbuf_read(&sb, 0, 0) < 0)\n+\t\t\tdie(\"could not read log from standard input\");\n+\t} else if (logfile) {\n+\t\tif (strbuf_read_file(&sb, logfile) < 0)\n+\t\t\tdie(\"could not read log file '%s': %s\",\n+\t\t\t    logfile, strerror(errno));\n+\t} else if (use_message) {\n+\t\tbuffer = strstr(use_message_buffer, \"\\n\\n\");\n+\t\tif (!buffer || buffer[2] == '\\0')\n+\t\t\tdie(\"commit has empty message\");\n+\t\tstrbuf_add(&sb, buffer + 2, strlen(buffer + 2));\n+\t} else if (!stat(git_path(\"MERGE_MSG\"), &statbuf)) {\n+\t\tif (strbuf_read_file(&sb, git_path(\"MERGE_MSG\")) < 0)\n+\t\t\tdie(\"could not read MERGE_MSG: %s\", strerror(errno));\n+\t} else if (!stat(git_path(\"SQUASH_MSG\"), &statbuf)) {\n+\t\tif (strbuf_read_file(&sb, git_path(\"SQUASH_MSG\")) < 0)\n+\t\t\tdie(\"could not read SQUASH_MSG: %s\", strerror(errno));\n+\t} else if (!stat(template_file, &statbuf)) {\n+\t\tif (strbuf_read_file(&sb, template_file) < 0)\n+\t\t\tdie(\"could not read %s: %s\",\n+\t\t\t    template_file, strerror(errno));\n+\t}\n+\n+\tfp = fopen(git_path(commit_editmsg), \"w\");\n+\tif (fp == NULL)\n+\t\tdie(\"could not open %s\\n\", git_path(commit_editmsg));\n+\n+\tstripspace(&sb, 0);\n+\tif (fwrite(sb.buf, 1, sb.len, fp) < sb.len)\n+\t\tdie(\"could not write commit template: %s\\n\",\n+\t\t    strerror(errno));\n+\n+\tif (signoff) {\n+\t\tconst char *info, *bol;\n+\n+\t\tinfo = git_committer_info(1);\n+\t\tstrbuf_addch(&sb, '\\0');\n+\t\tbol = strrchr(sb.buf + sb.len - 1, '\\n');\n+\t\tif (!bol || prefixcmp(bol, sign_off_header))\n+\t\t\tfprintf(fp, \"\\n\");\n+\t\tfprintf(fp, \"%s%s\\n\", sign_off_header, git_committer_info(1));\n+\t}\n+\n+\tstrbuf_release(&sb);\n+\n+\tif (in_merge && !no_edit) {\n+\t\tfprintf(fp,\n+\t\t\t\"#\\n\"\n+\t\t\t\"# It looks like you may be committing a MERGE.\\n\"\n+\t\t\t\"# If this is not correct, please remove the file\\n\"\n+\t\t\t\"#\t%s\\n\"\n+\t\t\t\"# and try again.\\n\"\n+\t\t\t\"#\\n\",\n+\t\t\tgit_path(\"MERGE_HEAD\"));\n+\t}\n+\n+\tfprintf(fp,\n+\t\t\"\\n\"\n+\t\t\"# Please enter the commit message for your changes.\\n\"\n+\t\t\"# (Comment lines starting with '#' will not be included)\\n\");\n+\tif (only_include_assumed)\n+\t\tfprintf(fp, \"# %s\\n\", only_include_assumed);\n+\n+\tcommitable = run_status(fp, index_file);\n+\n+\tfclose(fp);\n+\n+\treturn commitable;\n+}\n+\n+/* Find out if the message starting at position 'start' in the strbuf\n+ * contains only whitespace and Signed-off-by lines. */\n+static int message_is_empty(struct strbuf *sb, int start)\n+{\n+\tstruct strbuf tmpl;\n+\tconst char *nl;\n+\tint eol, i;\n+\n+\t/* See if the template is just a prefix of the message. */\n+\tstrbuf_init(&tmpl, 0);\n+\tif (template_file && strbuf_read_file(&tmpl, template_file) > 0) {\n+\t\tstripspace(&tmpl, 1);\n+\t\tif (start + tmpl.len <= sb->len &&\n+\t\t    memcmp(tmpl.buf, sb->buf + start, tmpl.len) == 0)\n+\t\t\tstart += tmpl.len;\n+\t}\n+\tstrbuf_release(&tmpl);\n+\n+\t/* Check if the rest is just whitespace and Signed-of-by's. */\n+\tfor (i = start; i < sb->len; i++) {\n+\t\tnl = memchr(sb->buf + i, '\\n', sb->len - i);\n+\t\tif (nl)\n+\t\t\teol = nl - sb->buf;\n+\t\telse\n+\t\t\teol = sb->len;\n+\n+\t\tif (strlen(sign_off_header) <= eol - i &&\n+\t\t    !prefixcmp(sb->buf + i, sign_off_header)) {\n+\t\t\ti = eol;\n+\t\t\tcontinue;\n+\t\t}\n+\t\twhile (i < eol)\n+\t\t\tif (!isspace(sb->buf[i++]))\n+\t\t\t\treturn 0;\n+\t}\n+\n+\treturn 1;\n+}\n+\n+static void determine_author_info(struct strbuf *sb)\n+{\n+\tchar *p, *eol;\n+\tchar *name = NULL, *email = NULL;\n+\n+\tif (use_message) {\n+\t\tp = strstr(use_message_buffer, \"\\nauthor\");\n+\t\tif (!p)\n+\t\t\tdie(\"invalid commit: %s\\n\", use_message);\n+\t\tp++;\n+\t\teol = strchr(p, '\\n');\n+\t\tif (!eol)\n+\t\t\tdie(\"invalid commit: %s\\n\", use_message);\n+\n+\t\tstrbuf_add(sb, p, eol + 1 - p);\n+\t} else if (force_author) {\n+\t\tconst char *eoname = strstr(force_author, \" <\");\n+\t\tconst char *eomail = strchr(force_author, '>');\n+\n+\t\tif (!eoname || !eomail)\n+\t\t\tdie(\"malformed --author parameter\\n\");\n+\t\tname = xstrndup(force_author, eoname - force_author);\n+\t\temail = xstrndup(eoname + 2, eomail - eoname - 2);\n+\t\tstrbuf_addf(sb, \"author %s\\n\",\n+\t\t\t    fmt_ident(name, email,\n+\t\t\t\t      getenv(\"GIT_AUTHOR_DATE\"), 1));\n+\t\tfree(name);\n+\t\tfree(email);\n+\t} else {\n+\t\tstrbuf_addf(sb, \"author %s\\n\", git_author_info(1));\n+\t}\n+}\n+\n+static void parse_and_validate_options(const char ***argv)\n+{\n+\tint f = 0;\n+\n+\t(*argv)++;\n+\twhile (parse_options(argv, commit_options, ARRAY_SIZE(commit_options),\n+\t\t\t     builtin_commit_usage))\n+\t\t;\n+\n+\tif (logfile || message || use_message)\n+\t\tno_edit = 1;\n+\tif (edit_flag)\n+\t\tno_edit = 0;\n+\n+\tif (get_sha1(\"HEAD\", head_sha1))\n+\t\tinitial_commit = 1;\n+\n+\tif (!get_sha1(\"MERGE_HEAD\", merge_head_sha1))\n+\t\tin_merge = 1;\n+\n+\t/* Sanity check options */\n+\tif (amend && initial_commit)\n+\t\tdie(\"You have nothing to amend.\");\n+\tif (amend && in_merge)\n+\t\tdie(\"You are in the middle of a merger -- cannot amend.\");\n+\n+\tif (use_message)\n+\t\tf++;\n+\tif (edit_message)\n+\t\tf++;\n+\tif (logfile)\n+\t\tf++;\n+\tif (f > 1)\n+\t\tdie(\"Only one of -c/-C/-F can be used.\");\n+\tif (message && f > 0)\n+\t\tdie(\"Option -m cannot be combined with -c/-C/-F.\");\n+\tif (edit_message)\n+\t\tuse_message = edit_message;\n+\tif (amend)\n+\t\tuse_message = \"HEAD\";\n+\tif (use_message) {\n+\t\tunsigned char sha1[20];\n+\t\tstatic char utf8[] = \"UTF-8\";\n+\t\tconst char *out_enc;\n+\t\tchar *enc, *end;\n+\t\tstruct commit *commit;\n+\n+\t\tif (get_sha1(use_message, sha1))\n+\t\t\tdie(\"could not lookup commit %s\", use_message);\n+\t\tcommit = lookup_commit(sha1);\n+\t\tif (!commit || parse_commit(commit))\n+\t\t\tdie(\"could not parse commit %s\", use_message);\n+\n+\t\tenc = strstr(commit->buffer, \"\\nencoding\");\n+\t\tif (enc) {\n+\t\t\tend = strchr(enc + 10, '\\n');\n+\t\t\tenc = xstrndup(enc + 10, end - (enc + 10));\n+\t\t} else {\n+\t\t\tenc = utf8;\n+\t\t}\n+\t\tout_enc = git_commit_encoding ? git_commit_encoding : utf8;\n+\n+\t\tuse_message_buffer =\n+\t\t\treencode_string(commit->buffer, out_enc, enc);\n+\t\tif (enc != utf8)\n+\t\t\tfree(enc);\n+\t}\n+\n+\tif (also && only)\n+\t\tdie(\"Only one of --include/--only can be used.\");\n+\tif (!*argv && (also || (only && !amend)))\n+\t\tdie(\"No paths with --include/--only does not make sense.\");\n+\tif (!*argv && only && amend)\n+\t\tonly_include_assumed = \"Clever... amending the last one with dirty index.\";\n+\tif (*argv && !also && !only) {\n+\t\tonly_include_assumed = \"Explicit paths specified without -i nor -o; assuming --only paths...\";\n+\t\talso = 0;\n+\t}\n+\n+\tif (all && interactive)\n+\t\tdie(\"Cannot use -a, --interactive or -i at the same time.\");\n+\telse if (all && **argv)\n+\t\tdie(\"Paths with -a does not make sense.\");\n+\telse if (interactive && **argv)\n+\t\tdie(\"Paths with --interactive does not make sense.\");\n+}\n+\n+int cmd_status(int argc, const char **argv, const char *prefix)\n+{\n+\tconst char *index_file;\n+\tint commitable;\n+\n+\tgit_config(git_status_config);\n+\n+\tparse_and_validate_options(&argv);\n+\n+\tindex_file = prepare_index(argv, prefix);\n+\n+\tcommitable = run_status(stdout, index_file);\n+\n+\trollback_lock_file(&lock_file);\n+\n+\treturn commitable ? 0 : 1;\n+}\n+\n+static int run_hook(const char *index_file, const char *name, const char *arg)\n+{\n+\tstruct child_process hook;\n+\tconst char *argv[3], *env[2];\n+\tchar index[PATH_MAX];\n+\n+\targv[0] = git_path(\"hooks/%s\", name);\n+\targv[1] = arg;\n+\targv[2] = NULL;\n+\tsnprintf(index, sizeof(index), \"GIT_INDEX_FILE=%s\", index_file);\n+\tenv[0] = index;\n+\tenv[1] = NULL;\n+\n+\tif (access(argv[0], X_OK) < 0)\n+\t\treturn 0;\n+\n+\tmemset(&hook, 0, sizeof(hook));\n+\thook.argv = argv;\n+\thook.no_stdin = 1;\n+\thook.stdout_to_stderr = 1;\n+\thook.env = env;\n+\n+\treturn run_command(&hook);\n+}\n+\n+static void print_summary(const char *prefix, const unsigned char *sha1)\n+{\n+\tstruct rev_info rev;\n+\tstruct commit *commit;\n+\n+\tcommit = lookup_commit(sha1);\n+\tif (!commit)\n+\t\tdie(\"couldn't look up newly created commit\\n\");\n+\tif (!commit || parse_commit(commit))\n+\t\tdie(\"could not parse newly created commit\");\n+\n+\tinit_revisions(&rev, prefix);\n+\tsetup_revisions(0, NULL, &rev, NULL);\n+\n+\trev.abbrev = 0;\n+\trev.diff = 1;\n+\trev.diffopt.output_format =\n+\t\tDIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;\n+\n+\trev.verbose_header = 1;\n+\trev.show_root_diff = 1;\n+\trev.commit_format = get_commit_format(\"format:%h: %s\");\n+\trev.always_show_header = 1;\n+\n+\tprintf(\"Created %scommit \", initial_commit ? \"initial \" : \"\");\n+\n+\tlog_tree_commit(&rev, commit);\n+}\n+\n+int git_commit_config(const char *k, const char *v)\n+{\n+\tif (!strcmp(k, \"commit.template\")) {\n+\t\ttemplate_file = xstrdup(v);\n+\t\treturn 0;\n+\t}\n+\n+\treturn git_status_config(k, v);\n+}\n+\n+static const char commit_utf8_warn[] =\n+\"Warning: commit message does not conform to UTF-8.\\n\"\n+\"You may want to amend it after fixing the message, or set the config\\n\"\n+\"variable i18n.commitencoding to the encoding your project uses.\\n\";\n+\n+int cmd_commit(int argc, const char **argv, const char *prefix)\n+{\n+\tint header_len, parent_count = 0;\n+\tstruct strbuf sb;\n+\tconst char *index_file, *reflog_msg;\n+\tchar *nl, *header_line;\n+\tunsigned char commit_sha1[20];\n+\tstruct ref_lock *ref_lock;\n+\n+\tgit_config(git_commit_config);\n+\n+\tparse_and_validate_options(&argv);\n+\n+\tindex_file = prepare_index(argv, prefix);\n+\n+\tif (run_hook(index_file, \"pre-commit\", NULL))\n+\t\texit(1);\n+\n+\tif (!prepare_log_message(index_file) && !in_merge) {\n+\t\trun_status(stdout, index_file);\n+\t\tunlink(commit_editmsg);\n+\t\treturn 1;\n+\t}\n+\n+\tstrbuf_init(&sb, 0);\n+\n+\t/* Start building up the commit header */\n+\tread_cache_from(index_file);\n+\tactive_cache_tree = cache_tree();\n+\tif (cache_tree_update(active_cache_tree,\n+\t\t\t      active_cache, active_nr, 0, 0) < 0)\n+\t\tdie(\"Error building trees\");\n+\tstrbuf_addf(&sb, \"tree %s\\n\",\n+\t\t    sha1_to_hex(active_cache_tree->sha1));\n+\n+\t/* Determine parents */\n+\tif (initial_commit) {\n+\t\treflog_msg = \"commit (initial)\";\n+\t\tparent_count = 0;\n+\t} else if (amend) {\n+\t\tstruct commit_list *c;\n+\t\tstruct commit *commit;\n+\n+\t\treflog_msg = \"commit (amend)\";\n+\t\tcommit = lookup_commit(head_sha1);\n+\t\tif (!commit || parse_commit(commit))\n+\t\t\tdie(\"could not parse HEAD commit\");\n+\n+\t\tfor (c = commit->parents; c; c = c->next)\n+\t\t\tstrbuf_addf(&sb, \"parent %s\\n\",\n+\t\t\t\t      sha1_to_hex(c->item->object.sha1));\n+\t} else if (in_merge) {\n+\t\tstruct strbuf m;\n+\t\tFILE *fp;\n+\n+\t\treflog_msg = \"commit (merge)\";\n+\t\tstrbuf_addf(&sb, \"parent %s\\n\", sha1_to_hex(head_sha1));\n+\t\tstrbuf_init(&m, 0);\n+\t\tfp = fopen(git_path(\"MERGE_HEAD\"), \"r\");\n+\t\tif (fp == NULL)\n+\t\t\tdie(\"could not open %s for reading: %s\",\n+\t\t\t    git_path(\"MERGE_HEAD\"), strerror(errno));\n+\t\twhile (strbuf_getline(&m, fp, '\\n') != EOF)\n+\t\t\tstrbuf_addf(&sb, \"parent %s\\n\", m.buf);\n+\t\tfclose(fp);\n+\t\tstrbuf_release(&m);\n+\t} else {\n+\t\treflog_msg = \"commit\";\n+\t\tstrbuf_addf(&sb, \"parent %s\\n\", sha1_to_hex(head_sha1));\n+\t}\n+\n+\tdetermine_author_info(&sb);\n+\tstrbuf_addf(&sb, \"committer %s\\n\", git_committer_info(1));\n+\tif (!is_encoding_utf8(git_commit_encoding))\n+\t\tstrbuf_addf(&sb, \"encoding %s\\n\", git_commit_encoding);\n+\tstrbuf_addch(&sb, '\\n');\n+\n+\t/* Get the commit message and validate it */\n+\theader_len = sb.len;\n+\tif (!no_edit) {\n+\t\tfprintf(stderr, \"launching editor, log %s\\n\", logfile);\n+\t\tlaunch_editor(git_path(commit_editmsg), &sb);\n+\t}\n+\telse if (strbuf_read_file(&sb, git_path(commit_editmsg)) < 0)\n+\t\tdie(\"could not read commit message\\n\");\n+\tif (run_hook(index_file, \"commit-msg\", commit_editmsg))\n+\t\texit(1);\n+\tstripspace(&sb, 1);\n+\tif (sb.len < header_len ||\n+\t    message_is_empty(&sb, header_len))\n+\t\tdie(\"* no commit message?  aborting commit.\");\n+\tstrbuf_addch(&sb, '\\0');\n+\tif (is_encoding_utf8(git_commit_encoding) && !is_utf8(sb.buf))\n+\t\tfprintf(stderr, commit_utf8_warn);\n+\n+\tif (write_sha1_file(sb.buf, sb.len - 1, commit_type, commit_sha1))\n+\t\tdie(\"failed to write commit object\");\n+\n+\tref_lock = lock_any_ref_for_update(\"HEAD\",\n+\t\t\t\t\t   initial_commit ? NULL : head_sha1,\n+\t\t\t\t\t   0);\n+\n+\tnl = strchr(sb.buf + header_len, '\\n');\n+\theader_line = xstrndup(sb.buf + header_len,\n+\t\t\t       nl - (sb.buf + header_len));\n+\tstrbuf_release(&sb);\n+\tstrbuf_addf(&sb, \"%s: %s\\n\", reflog_msg, header_line);\n+\tstrbuf_addch(&sb, '\\0');\n+\tfree(header_line);\n+\n+\tif (!ref_lock)\n+\t\tdie(\"cannot lock HEAD ref\");\n+\tif (write_ref_sha1(ref_lock, commit_sha1, sb.buf) < 0)\n+\t\tdie(\"cannot update HEAD ref\");\n+\n+\tunlink(git_path(\"MERGE_HEAD\"));\n+\tunlink(git_path(\"MERGE_MSG\"));\n+\n+\tif (lock_file.filename[0] && commit_locked_index(&lock_file))\n+\t\tdie(\"failed to write new index\");\n+\n+\trerere();\n+\n+\trun_hook(index_file, \"post-commit\", NULL);\n+\n+\tif (!quiet)\n+\t\tprint_summary(prefix, commit_sha1);\n+\n+\treturn 0;\n+}\ndiff --git a/builtin-tag.c b/builtin-tag.c\nindex a1a2cf0..0a36a5d 100644\n--- a/builtin-tag.c\n+++ b/builtin-tag.c\n@@ -17,12 +17,11 @@ static const char builtin_tag_usage[] =\n \n static char signingkey[1000];\n \n-static void launch_editor(const char *path, struct strbuf *buffer)\n+void launch_editor(const char *path, struct strbuf *buffer)\n {\n \tconst char *editor, *terminal;\n \tstruct child_process child;\n \tconst char *args[3];\n-\tint fd;\n \n \teditor = getenv(\"GIT_EDITOR\");\n \tif (!editor && editor_program)\ndiff --git a/builtin.h b/builtin.h\nindex 65cc0fb..470a788 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -23,6 +23,7 @@ extern int cmd_check_attr(int argc, const char **argv, const char *prefix);\n extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);\n extern int cmd_cherry(int argc, const char **argv, const char *prefix);\n extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);\n+extern int cmd_commit(int argc, const char **argv, const char *prefix);\n extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);\n extern int cmd_count_objects(int argc, const char **argv, const char *prefix);\n extern int cmd_describe(int argc, const char **argv, const char *prefix);\n@@ -67,10 +68,10 @@ extern int cmd_rev_list(int argc, const char **argv, const char *prefix);\n extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);\n extern int cmd_revert(int argc, const char **argv, const char *prefix);\n extern int cmd_rm(int argc, const char **argv, const char *prefix);\n-extern int cmd_runstatus(int argc, const char **argv, const char *prefix);\n extern int cmd_shortlog(int argc, const char **argv, const char *prefix);\n extern int cmd_show(int argc, const char **argv, const char *prefix);\n extern int cmd_show_branch(int argc, const char **argv, const char *prefix);\n+extern int cmd_status(int argc, const char **argv, const char *prefix);\n extern int cmd_stripspace(int argc, const char **argv, const char *prefix);\n extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);\n extern int cmd_tag(int argc, const char **argv, const char *prefix);\ndiff --git a/contrib/examples/git-commit.sh b/contrib/examples/git-commit.sh\nnew file mode 100755\nindex 0000000..44ccc44\n--- /dev/null\n+++ b/contrib/examples/git-commit.sh\n@@ -0,0 +1,627 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2005 Linus Torvalds\n+# Copyright (c) 2006 Junio C Hamano\n+\n+USAGE='[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [--template <file>] [[-i | -o] <path>...]'\n+SUBDIRECTORY_OK=Yes\n+. git-sh-setup\n+require_work_tree\n+\n+git rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t\n+\n+case \"$0\" in\n+*status)\n+\tstatus_only=t\n+\t;;\n+*commit)\n+\tstatus_only=\n+\t;;\n+esac\n+\n+refuse_partial () {\n+\techo >&2 \"$1\"\n+\techo >&2 \"You might have meant to say 'git commit -i paths...', perhaps?\"\n+\texit 1\n+}\n+\n+THIS_INDEX=\"$GIT_DIR/index\"\n+NEXT_INDEX=\"$GIT_DIR/next-index$$\"\n+rm -f \"$NEXT_INDEX\"\n+save_index () {\n+\tcp -p \"$THIS_INDEX\" \"$NEXT_INDEX\"\n+}\n+\n+run_status () {\n+\t# If TMP_INDEX is defined, that means we are doing\n+\t# \"--only\" partial commit, and that index file is used\n+\t# to build the tree for the commit.  Otherwise, if\n+\t# NEXT_INDEX exists, that is the index file used to\n+\t# make the commit.  Otherwise we are using as-is commit\n+\t# so the regular index file is what we use to compare.\n+\tif test '' != \"$TMP_INDEX\"\n+\tthen\n+\t\tGIT_INDEX_FILE=\"$TMP_INDEX\"\n+\t\texport GIT_INDEX_FILE\n+\telif test -f \"$NEXT_INDEX\"\n+\tthen\n+\t\tGIT_INDEX_FILE=\"$NEXT_INDEX\"\n+\t\texport GIT_INDEX_FILE\n+\tfi\n+\n+\tif test \"$status_only\" = \"t\" -o \"$use_status_color\" = \"t\"; then\n+\t\tcolor=\n+\telse\n+\t\tcolor=--nocolor\n+\tfi\n+\tgit runstatus ${color} \\\n+\t\t${verbose:+--verbose} \\\n+\t\t${amend:+--amend} \\\n+\t\t${untracked_files:+--untracked}\n+}\n+\n+trap '\n+\ttest -z \"$TMP_INDEX\" || {\n+\t\ttest -f \"$TMP_INDEX\" && rm -f \"$TMP_INDEX\"\n+\t}\n+\trm -f \"$NEXT_INDEX\"\n+' 0\n+\n+################################################################\n+# Command line argument parsing and sanity checking\n+\n+all=\n+also=\n+interactive=\n+only=\n+logfile=\n+use_commit=\n+amend=\n+edit_flag=\n+no_edit=\n+log_given=\n+log_message=\n+verify=t\n+quiet=\n+verbose=\n+signoff=\n+force_author=\n+only_include_assumed=\n+untracked_files=\n+templatefile=\"`git config commit.template`\"\n+while test $# != 0\n+do\n+\tcase \"$1\" in\n+\t-F|--F|-f|--f|--fi|--fil|--file)\n+\t\tcase \"$#\" in 1) usage ;; esac\n+\t\tshift\n+\t\tno_edit=t\n+\t\tlog_given=t$log_given\n+\t\tlogfile=\"$1\"\n+\t\t;;\n+\t-F*|-f*)\n+\t\tno_edit=t\n+\t\tlog_given=t$log_given\n+\t\tlogfile=\"${1#-[Ff]}\"\n+\t\t;;\n+\t--F=*|--f=*|--fi=*|--fil=*|--file=*)\n+\t\tno_edit=t\n+\t\tlog_given=t$log_given\n+\t\tlogfile=\"${1#*=}\"\n+\t\t;;\n+\t-a|--a|--al|--all)\n+\t\tall=t\n+\t\t;;\n+\t--au=*|--aut=*|--auth=*|--autho=*|--author=*)\n+\t\tforce_author=\"${1#*=}\"\n+\t\t;;\n+\t--au|--aut|--auth|--autho|--author)\n+\t\tcase \"$#\" in 1) usage ;; esac\n+\t\tshift\n+\t\tforce_author=\"$1\"\n+\t\t;;\n+\t-e|--e|--ed|--edi|--edit)\n+\t\tedit_flag=t\n+\t\t;;\n+\t-i|--i|--in|--inc|--incl|--inclu|--includ|--include)\n+\t\talso=t\n+\t\t;;\n+\t--int|--inte|--inter|--intera|--interac|--interact|--interacti|\\\n+\t--interactiv|--interactive)\n+\t\tinteractive=t\n+\t\t;;\n+\t-o|--o|--on|--onl|--only)\n+\t\tonly=t\n+\t\t;;\n+\t-m|--m|--me|--mes|--mess|--messa|--messag|--message)\n+\t\tcase \"$#\" in 1) usage ;; esac\n+\t\tshift\n+\t\tlog_given=m$log_given\n+\t\tlog_message=\"${log_message:+${log_message}\n+\n+}$1\"\n+\t\tno_edit=t\n+\t\t;;\n+\t-m*)\n+\t\tlog_given=m$log_given\n+\t\tlog_message=\"${log_message:+${log_message}\n+\n+}${1#-m}\"\n+\t\tno_edit=t\n+\t\t;;\n+\t--m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*)\n+\t\tlog_given=m$log_given\n+\t\tlog_message=\"${log_message:+${log_message}\n+\n+}${1#*=}\"\n+\t\tno_edit=t\n+\t\t;;\n+\t-n|--n|--no|--no-|--no-v|--no-ve|--no-ver|--no-veri|--no-verif|\\\n+\t--no-verify)\n+\t\tverify=\n+\t\t;;\n+\t--a|--am|--ame|--amen|--amend)\n+\t\tamend=t\n+\t\tuse_commit=HEAD\n+\t\t;;\n+\t-c)\n+\t\tcase \"$#\" in 1) usage ;; esac\n+\t\tshift\n+\t\tlog_given=t$log_given\n+\t\tuse_commit=\"$1\"\n+\t\tno_edit=\n+\t\t;;\n+\t--ree=*|--reed=*|--reedi=*|--reedit=*|--reedit-=*|--reedit-m=*|\\\n+\t--reedit-me=*|--reedit-mes=*|--reedit-mess=*|--reedit-messa=*|\\\n+\t--reedit-messag=*|--reedit-message=*)\n+\t\tlog_given=t$log_given\n+\t\tuse_commit=\"${1#*=}\"\n+\t\tno_edit=\n+\t\t;;\n+\t--ree|--reed|--reedi|--reedit|--reedit-|--reedit-m|--reedit-me|\\\n+\t--reedit-mes|--reedit-mess|--reedit-messa|--reedit-messag|\\\n+\t--reedit-message)\n+\t\tcase \"$#\" in 1) usage ;; esac\n+\t\tshift\n+\t\tlog_given=t$log_given\n+\t\tuse_commit=\"$1\"\n+\t\tno_edit=\n+\t\t;;\n+\t-C)\n+\t\tcase \"$#\" in 1) usage ;; esac\n+\t\tshift\n+\t\tlog_given=t$log_given\n+\t\tuse_commit=\"$1\"\n+\t\tno_edit=t\n+\t\t;;\n+\t--reu=*|--reus=*|--reuse=*|--reuse-=*|--reuse-m=*|--reuse-me=*|\\\n+\t--reuse-mes=*|--reuse-mess=*|--reuse-messa=*|--reuse-messag=*|\\\n+\t--reuse-message=*)\n+\t\tlog_given=t$log_given\n+\t\tuse_commit=\"${1#*=}\"\n+\t\tno_edit=t\n+\t\t;;\n+\t--reu|--reus|--reuse|--reuse-|--reuse-m|--reuse-me|--reuse-mes|\\\n+\t--reuse-mess|--reuse-messa|--reuse-messag|--reuse-message)\n+\t\tcase \"$#\" in 1) usage ;; esac\n+\t\tshift\n+\t\tlog_given=t$log_given\n+\t\tuse_commit=\"$1\"\n+\t\tno_edit=t\n+\t\t;;\n+\t-s|--s|--si|--sig|--sign|--signo|--signof|--signoff)\n+\t\tsignoff=t\n+\t\t;;\n+\t-t|--t|--te|--tem|--temp|--templ|--templa|--templat|--template)\n+\t\tcase \"$#\" in 1) usage ;; esac\n+\t\tshift\n+\t\ttemplatefile=\"$1\"\n+\t\tno_edit=\n+\t\t;;\n+\t-q|--q|--qu|--qui|--quie|--quiet)\n+\t\tquiet=t\n+\t\t;;\n+\t-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)\n+\t\tverbose=t\n+\t\t;;\n+\t-u|--u|--un|--unt|--untr|--untra|--untrac|--untrack|--untracke|\\\n+\t--untracked|--untracked-|--untracked-f|--untracked-fi|--untracked-fil|\\\n+\t--untracked-file|--untracked-files)\n+\t\tuntracked_files=t\n+\t\t;;\n+\t--)\n+\t\tshift\n+\t\tbreak\n+\t\t;;\n+\t-*)\n+\t\tusage\n+\t\t;;\n+\t*)\n+\t\tbreak\n+\t\t;;\n+\tesac\n+\tshift\n+done\n+case \"$edit_flag\" in t) no_edit= ;; esac\n+\n+################################################################\n+# Sanity check options\n+\n+case \"$amend,$initial_commit\" in\n+t,t)\n+\tdie \"You do not have anything to amend.\" ;;\n+t,)\n+\tif [ -f \"$GIT_DIR/MERGE_HEAD\" ]; then\n+\t\tdie \"You are in the middle of a merge -- cannot amend.\"\n+\tfi ;;\n+esac\n+\n+case \"$log_given\" in\n+tt*)\n+\tdie \"Only one of -c/-C/-F can be used.\" ;;\n+*tm*|*mt*)\n+\tdie \"Option -m cannot be combined with -c/-C/-F.\" ;;\n+esac\n+\n+case \"$#,$also,$only,$amend\" in\n+*,t,t,*)\n+\tdie \"Only one of --include/--only can be used.\" ;;\n+0,t,,* | 0,,t,)\n+\tdie \"No paths with --include/--only does not make sense.\" ;;\n+0,,t,t)\n+\tonly_include_assumed=\"# Clever... amending the last one with dirty index.\" ;;\n+0,,,*)\n+\t;;\n+*,,,*)\n+\tonly_include_assumed=\"# Explicit paths specified without -i nor -o; assuming --only paths...\"\n+\talso=\n+\t;;\n+esac\n+unset only\n+case \"$all,$interactive,$also,$#\" in\n+*t,*t,*)\n+\tdie \"Cannot use -a, --interactive or -i at the same time.\" ;;\n+t,,[1-9]*)\n+\tdie \"Paths with -a does not make sense.\" ;;\n+,t,[1-9]*)\n+\tdie \"Paths with --interactive does not make sense.\" ;;\n+,,t,0)\n+\tdie \"No paths with -i does not make sense.\" ;;\n+esac\n+\n+if test ! -z \"$templatefile\" -a -z \"$log_given\"\n+then\n+\tif test ! -f \"$templatefile\"\n+\tthen\n+\t\tdie \"Commit template file does not exist.\"\n+\tfi\n+fi\n+\n+################################################################\n+# Prepare index to have a tree to be committed\n+\n+case \"$all,$also\" in\n+t,)\n+\tif test ! -f \"$THIS_INDEX\"\n+\tthen\n+\t\tdie 'nothing to commit (use \"git add file1 file2\" to include for commit)'\n+\tfi\n+\tsave_index &&\n+\t(\n+\t\tcd_to_toplevel &&\n+\t\tGIT_INDEX_FILE=\"$NEXT_INDEX\" &&\n+\t\texport GIT_INDEX_FILE &&\n+\t\tgit diff-files --name-only -z |\n+\t\tgit update-index --remove -z --stdin\n+\t) || exit\n+\t;;\n+,t)\n+\tsave_index &&\n+\tgit ls-files --error-unmatch -- \"$@\" >/dev/null || exit\n+\n+\tgit diff-files --name-only -z -- \"$@\"  |\n+\t(\n+\t\tcd_to_toplevel &&\n+\t\tGIT_INDEX_FILE=\"$NEXT_INDEX\" &&\n+\t\texport GIT_INDEX_FILE &&\n+\t\tgit update-index --remove -z --stdin\n+\t) || exit\n+\t;;\n+,)\n+\tif test \"$interactive\" = t; then\n+\t\tgit add --interactive || exit\n+\tfi\n+\tcase \"$#\" in\n+\t0)\n+\t\t;; # commit as-is\n+\t*)\n+\t\tif test -f \"$GIT_DIR/MERGE_HEAD\"\n+\t\tthen\n+\t\t\trefuse_partial \"Cannot do a partial commit during a merge.\"\n+\t\tfi\n+\n+\t\tTMP_INDEX=\"$GIT_DIR/tmp-index$$\"\n+\t\tW=\n+\t\ttest -z \"$initial_commit\" && W=--with-tree=HEAD\n+\t\tcommit_only=`git ls-files --error-unmatch $W -- \"$@\"` || exit\n+\n+\t\t# Build a temporary index and update the real index\n+\t\t# the same way.\n+\t\tif test -z \"$initial_commit\"\n+\t\tthen\n+\t\t\tGIT_INDEX_FILE=\"$THIS_INDEX\" \\\n+\t\t\tgit read-tree --index-output=\"$TMP_INDEX\" -i -m HEAD\n+\t\telse\n+\t\t\trm -f \"$TMP_INDEX\"\n+\t\tfi || exit\n+\n+\t\tprintf '%s\\n' \"$commit_only\" |\n+\t\tGIT_INDEX_FILE=\"$TMP_INDEX\" \\\n+\t\tgit update-index --add --remove --stdin &&\n+\n+\t\tsave_index &&\n+\t\tprintf '%s\\n' \"$commit_only\" |\n+\t\t(\n+\t\t\tGIT_INDEX_FILE=\"$NEXT_INDEX\"\n+\t\t\texport GIT_INDEX_FILE\n+\t\t\tgit update-index --add --remove --stdin\n+\t\t) || exit\n+\t\t;;\n+\tesac\n+\t;;\n+esac\n+\n+################################################################\n+# If we do as-is commit, the index file will be THIS_INDEX,\n+# otherwise NEXT_INDEX after we make this commit.  We leave\n+# the index as is if we abort.\n+\n+if test -f \"$NEXT_INDEX\"\n+then\n+\tUSE_INDEX=\"$NEXT_INDEX\"\n+else\n+\tUSE_INDEX=\"$THIS_INDEX\"\n+fi\n+\n+case \"$status_only\" in\n+t)\n+\t# This will silently fail in a read-only repository, which is\n+\t# what we want.\n+\tGIT_INDEX_FILE=\"$USE_INDEX\" git update-index -q --unmerged --refresh\n+\trun_status\n+\texit $?\n+\t;;\n+'')\n+\tGIT_INDEX_FILE=\"$USE_INDEX\" git update-index -q --refresh || exit\n+\t;;\n+esac\n+\n+################################################################\n+# Grab commit message, write out tree and make commit.\n+\n+if test t = \"$verify\" && test -x \"$GIT_DIR\"/hooks/pre-commit\n+then\n+    GIT_INDEX_FILE=\"${TMP_INDEX:-${USE_INDEX}}\" \"$GIT_DIR\"/hooks/pre-commit \\\n+    || exit\n+fi\n+\n+if test \"$log_message\" != ''\n+then\n+\tprintf '%s\\n' \"$log_message\"\n+elif test \"$logfile\" != \"\"\n+then\n+\tif test \"$logfile\" = -\n+\tthen\n+\t\ttest -t 0 &&\n+\t\techo >&2 \"(reading log message from standard input)\"\n+\t\tcat\n+\telse\n+\t\tcat <\"$logfile\"\n+\tfi\n+elif test \"$use_commit\" != \"\"\n+then\n+\tencoding=$(git config i18n.commitencoding || echo UTF-8)\n+\tgit show -s --pretty=raw --encoding=\"$encoding\" \"$use_commit\" |\n+\tsed -e '1,/^$/d' -e 's/^    //'\n+elif test -f \"$GIT_DIR/MERGE_MSG\"\n+then\n+\tcat \"$GIT_DIR/MERGE_MSG\"\n+elif test -f \"$GIT_DIR/SQUASH_MSG\"\n+then\n+\tcat \"$GIT_DIR/SQUASH_MSG\"\n+elif test \"$templatefile\" != \"\"\n+then\n+\tcat \"$templatefile\"\n+fi | git stripspace >\"$GIT_DIR\"/COMMIT_EDITMSG\n+\n+case \"$signoff\" in\n+t)\n+\tsign=$(git-var GIT_COMMITTER_IDENT | sed -e '\n+\t\ts/>.*/>/\n+\t\ts/^/Signed-off-by: /\n+\t\t')\n+\tblank_before_signoff=\n+\ttail -n 1 \"$GIT_DIR\"/COMMIT_EDITMSG |\n+\tgrep 'Signed-off-by:' >/dev/null || blank_before_signoff='\n+'\n+\ttail -n 1 \"$GIT_DIR\"/COMMIT_EDITMSG |\n+\tgrep \"$sign\"$ >/dev/null ||\n+\tprintf '%s%s\\n' \"$blank_before_signoff\" \"$sign\" \\\n+\t\t>>\"$GIT_DIR\"/COMMIT_EDITMSG\n+\t;;\n+esac\n+\n+if test -f \"$GIT_DIR/MERGE_HEAD\" && test -z \"$no_edit\"; then\n+\techo \"#\"\n+\techo \"# It looks like you may be committing a MERGE.\"\n+\techo \"# If this is not correct, please remove the file\"\n+\tprintf '%s\\n' \"#\t$GIT_DIR/MERGE_HEAD\"\n+\techo \"# and try again\"\n+\techo \"#\"\n+fi >>\"$GIT_DIR\"/COMMIT_EDITMSG\n+\n+# Author\n+if test '' != \"$use_commit\"\n+then\n+\teval \"$(get_author_ident_from_commit \"$use_commit\")\"\n+\texport GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE\n+fi\n+if test '' != \"$force_author\"\n+then\n+\tGIT_AUTHOR_NAME=`expr \"z$force_author\" : 'z\\(.*[^ ]\\) *<.*'` &&\n+\tGIT_AUTHOR_EMAIL=`expr \"z$force_author\" : '.*\\(<.*\\)'` &&\n+\ttest '' != \"$GIT_AUTHOR_NAME\" &&\n+\ttest '' != \"$GIT_AUTHOR_EMAIL\" ||\n+\tdie \"malformed --author parameter\"\n+\texport GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL\n+fi\n+\n+PARENTS=\"-p HEAD\"\n+if test -z \"$initial_commit\"\n+then\n+\trloga='commit'\n+\tif [ -f \"$GIT_DIR/MERGE_HEAD\" ]; then\n+\t\trloga='commit (merge)'\n+\t\tPARENTS=\"-p HEAD \"`sed -e 's/^/-p /' \"$GIT_DIR/MERGE_HEAD\"`\n+\telif test -n \"$amend\"; then\n+\t\trloga='commit (amend)'\n+\t\tPARENTS=$(git cat-file commit HEAD |\n+\t\t\tsed -n -e '/^$/q' -e 's/^parent /-p /p')\n+\tfi\n+\tcurrent=\"$(git rev-parse --verify HEAD)\"\n+else\n+\tif [ -z \"$(git ls-files)\" ]; then\n+\t\techo >&2 'nothing to commit (use \"git add file1 file2\" to include for commit)'\n+\t\texit 1\n+\tfi\n+\tPARENTS=\"\"\n+\trloga='commit (initial)'\n+\tcurrent=''\n+fi\n+set_reflog_action \"$rloga\"\n+\n+if test -z \"$no_edit\"\n+then\n+\t{\n+\t\techo \"\"\n+\t\techo \"# Please enter the commit message for your changes.\"\n+\t\techo \"# (Comment lines starting with '#' will not be included)\"\n+\t\ttest -z \"$only_include_assumed\" || echo \"$only_include_assumed\"\n+\t\trun_status\n+\t} >>\"$GIT_DIR\"/COMMIT_EDITMSG\n+else\n+\t# we need to check if there is anything to commit\n+\trun_status >/dev/null\n+fi\n+if [ \"$?\" != \"0\" -a ! -f \"$GIT_DIR/MERGE_HEAD\" ]\n+then\n+\trm -f \"$GIT_DIR/COMMIT_EDITMSG\" \"$GIT_DIR/SQUASH_MSG\"\n+\tuse_status_color=t\n+\trun_status\n+\texit 1\n+fi\n+\n+case \"$no_edit\" in\n+'')\n+\tgit-var GIT_AUTHOR_IDENT > /dev/null  || die\n+\tgit-var GIT_COMMITTER_IDENT > /dev/null  || die\n+\tgit_editor \"$GIT_DIR/COMMIT_EDITMSG\"\n+\t;;\n+esac\n+\n+case \"$verify\" in\n+t)\n+\tif test -x \"$GIT_DIR\"/hooks/commit-msg\n+\tthen\n+\t\t\"$GIT_DIR\"/hooks/commit-msg \"$GIT_DIR\"/COMMIT_EDITMSG || exit\n+\tfi\n+esac\n+\n+if test -z \"$no_edit\"\n+then\n+    sed -e '\n+        /^diff --git a\\/.*/{\n+\t    s///\n+\t    q\n+\t}\n+\t/^#/d\n+    ' \"$GIT_DIR\"/COMMIT_EDITMSG\n+else\n+    cat \"$GIT_DIR\"/COMMIT_EDITMSG\n+fi |\n+git stripspace >\"$GIT_DIR\"/COMMIT_MSG\n+\n+# Test whether the commit message has any content we didn't supply.\n+have_commitmsg=\n+grep -v -i '^Signed-off-by' \"$GIT_DIR\"/COMMIT_MSG |\n+\tgit stripspace > \"$GIT_DIR\"/COMMIT_BAREMSG\n+\n+# Is the commit message totally empty?\n+if test -s \"$GIT_DIR\"/COMMIT_BAREMSG\n+then\n+\tif test \"$templatefile\" != \"\"\n+\tthen\n+\t\t# Test whether this is just the unaltered template.\n+\t\tif cnt=`sed -e '/^#/d' < \"$templatefile\" |\n+\t\t\tgit stripspace |\n+\t\t\tdiff \"$GIT_DIR\"/COMMIT_BAREMSG - |\n+\t\t\twc -l` &&\n+\t\t   test 0 -lt $cnt\n+\t\tthen\n+\t\t\thave_commitmsg=t\n+\t\tfi\n+\telse\n+\t\t# No template, so the content in the commit message must\n+\t\t# have come from the user.\n+\t\thave_commitmsg=t\n+\tfi\n+fi\n+\n+rm -f \"$GIT_DIR\"/COMMIT_BAREMSG\n+\n+if test \"$have_commitmsg\" = \"t\"\n+then\n+\tif test -z \"$TMP_INDEX\"\n+\tthen\n+\t\ttree=$(GIT_INDEX_FILE=\"$USE_INDEX\" git write-tree)\n+\telse\n+\t\ttree=$(GIT_INDEX_FILE=\"$TMP_INDEX\" git write-tree) &&\n+\t\trm -f \"$TMP_INDEX\"\n+\tfi &&\n+\tcommit=$(git commit-tree $tree $PARENTS <\"$GIT_DIR/COMMIT_MSG\") &&\n+\trlogm=$(sed -e 1q \"$GIT_DIR\"/COMMIT_MSG) &&\n+\tgit update-ref -m \"$GIT_REFLOG_ACTION: $rlogm\" HEAD $commit \"$current\" &&\n+\trm -f -- \"$GIT_DIR/MERGE_HEAD\" \"$GIT_DIR/MERGE_MSG\" &&\n+\tif test -f \"$NEXT_INDEX\"\n+\tthen\n+\t\tmv \"$NEXT_INDEX\" \"$THIS_INDEX\"\n+\telse\n+\t\t: ;# happy\n+\tfi\n+else\n+\techo >&2 \"* no commit message?  aborting commit.\"\n+\tfalse\n+fi\n+ret=\"$?\"\n+rm -f \"$GIT_DIR/COMMIT_MSG\" \"$GIT_DIR/COMMIT_EDITMSG\" \"$GIT_DIR/SQUASH_MSG\"\n+\n+cd_to_toplevel\n+\n+git rerere\n+\n+if test \"$ret\" = 0\n+then\n+\tgit gc --auto\n+\tif test -x \"$GIT_DIR\"/hooks/post-commit\n+\tthen\n+\t\t\"$GIT_DIR\"/hooks/post-commit\n+\tfi\n+\tif test -z \"$quiet\"\n+\tthen\n+\t\tcommit=`git diff-tree --always --shortstat --pretty=\"format:%h: %s\"\\\n+\t\t       --summary --root HEAD --`\n+\t\techo \"Created${initial_commit:+ initial} commit $commit\"\n+\tfi\n+fi\n+\n+exit \"$ret\"\ndiff --git a/git-commit.sh b/git-commit.sh\ndeleted file mode 100755\nindex 44ccc44..0000000\n--- a/git-commit.sh\n+++ /dev/null\n@@ -1,627 +0,0 @@\n-#!/bin/sh\n-#\n-# Copyright (c) 2005 Linus Torvalds\n-# Copyright (c) 2006 Junio C Hamano\n-\n-USAGE='[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [--template <file>] [[-i | -o] <path>...]'\n-SUBDIRECTORY_OK=Yes\n-. git-sh-setup\n-require_work_tree\n-\n-git rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t\n-\n-case \"$0\" in\n-*status)\n-\tstatus_only=t\n-\t;;\n-*commit)\n-\tstatus_only=\n-\t;;\n-esac\n-\n-refuse_partial () {\n-\techo >&2 \"$1\"\n-\techo >&2 \"You might have meant to say 'git commit -i paths...', perhaps?\"\n-\texit 1\n-}\n-\n-THIS_INDEX=\"$GIT_DIR/index\"\n-NEXT_INDEX=\"$GIT_DIR/next-index$$\"\n-rm -f \"$NEXT_INDEX\"\n-save_index () {\n-\tcp -p \"$THIS_INDEX\" \"$NEXT_INDEX\"\n-}\n-\n-run_status () {\n-\t# If TMP_INDEX is defined, that means we are doing\n-\t# \"--only\" partial commit, and that index file is used\n-\t# to build the tree for the commit.  Otherwise, if\n-\t# NEXT_INDEX exists, that is the index file used to\n-\t# make the commit.  Otherwise we are using as-is commit\n-\t# so the regular index file is what we use to compare.\n-\tif test '' != \"$TMP_INDEX\"\n-\tthen\n-\t\tGIT_INDEX_FILE=\"$TMP_INDEX\"\n-\t\texport GIT_INDEX_FILE\n-\telif test -f \"$NEXT_INDEX\"\n-\tthen\n-\t\tGIT_INDEX_FILE=\"$NEXT_INDEX\"\n-\t\texport GIT_INDEX_FILE\n-\tfi\n-\n-\tif test \"$status_only\" = \"t\" -o \"$use_status_color\" = \"t\"; then\n-\t\tcolor=\n-\telse\n-\t\tcolor=--nocolor\n-\tfi\n-\tgit runstatus ${color} \\\n-\t\t${verbose:+--verbose} \\\n-\t\t${amend:+--amend} \\\n-\t\t${untracked_files:+--untracked}\n-}\n-\n-trap '\n-\ttest -z \"$TMP_INDEX\" || {\n-\t\ttest -f \"$TMP_INDEX\" && rm -f \"$TMP_INDEX\"\n-\t}\n-\trm -f \"$NEXT_INDEX\"\n-' 0\n-\n-################################################################\n-# Command line argument parsing and sanity checking\n-\n-all=\n-also=\n-interactive=\n-only=\n-logfile=\n-use_commit=\n-amend=\n-edit_flag=\n-no_edit=\n-log_given=\n-log_message=\n-verify=t\n-quiet=\n-verbose=\n-signoff=\n-force_author=\n-only_include_assumed=\n-untracked_files=\n-templatefile=\"`git config commit.template`\"\n-while test $# != 0\n-do\n-\tcase \"$1\" in\n-\t-F|--F|-f|--f|--fi|--fil|--file)\n-\t\tcase \"$#\" in 1) usage ;; esac\n-\t\tshift\n-\t\tno_edit=t\n-\t\tlog_given=t$log_given\n-\t\tlogfile=\"$1\"\n-\t\t;;\n-\t-F*|-f*)\n-\t\tno_edit=t\n-\t\tlog_given=t$log_given\n-\t\tlogfile=\"${1#-[Ff]}\"\n-\t\t;;\n-\t--F=*|--f=*|--fi=*|--fil=*|--file=*)\n-\t\tno_edit=t\n-\t\tlog_given=t$log_given\n-\t\tlogfile=\"${1#*=}\"\n-\t\t;;\n-\t-a|--a|--al|--all)\n-\t\tall=t\n-\t\t;;\n-\t--au=*|--aut=*|--auth=*|--autho=*|--author=*)\n-\t\tforce_author=\"${1#*=}\"\n-\t\t;;\n-\t--au|--aut|--auth|--autho|--author)\n-\t\tcase \"$#\" in 1) usage ;; esac\n-\t\tshift\n-\t\tforce_author=\"$1\"\n-\t\t;;\n-\t-e|--e|--ed|--edi|--edit)\n-\t\tedit_flag=t\n-\t\t;;\n-\t-i|--i|--in|--inc|--incl|--inclu|--includ|--include)\n-\t\talso=t\n-\t\t;;\n-\t--int|--inte|--inter|--intera|--interac|--interact|--interacti|\\\n-\t--interactiv|--interactive)\n-\t\tinteractive=t\n-\t\t;;\n-\t-o|--o|--on|--onl|--only)\n-\t\tonly=t\n-\t\t;;\n-\t-m|--m|--me|--mes|--mess|--messa|--messag|--message)\n-\t\tcase \"$#\" in 1) usage ;; esac\n-\t\tshift\n-\t\tlog_given=m$log_given\n-\t\tlog_message=\"${log_message:+${log_message}\n-\n-}$1\"\n-\t\tno_edit=t\n-\t\t;;\n-\t-m*)\n-\t\tlog_given=m$log_given\n-\t\tlog_message=\"${log_message:+${log_message}\n-\n-}${1#-m}\"\n-\t\tno_edit=t\n-\t\t;;\n-\t--m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*)\n-\t\tlog_given=m$log_given\n-\t\tlog_message=\"${log_message:+${log_message}\n-\n-}${1#*=}\"\n-\t\tno_edit=t\n-\t\t;;\n-\t-n|--n|--no|--no-|--no-v|--no-ve|--no-ver|--no-veri|--no-verif|\\\n-\t--no-verify)\n-\t\tverify=\n-\t\t;;\n-\t--a|--am|--ame|--amen|--amend)\n-\t\tamend=t\n-\t\tuse_commit=HEAD\n-\t\t;;\n-\t-c)\n-\t\tcase \"$#\" in 1) usage ;; esac\n-\t\tshift\n-\t\tlog_given=t$log_given\n-\t\tuse_commit=\"$1\"\n-\t\tno_edit=\n-\t\t;;\n-\t--ree=*|--reed=*|--reedi=*|--reedit=*|--reedit-=*|--reedit-m=*|\\\n-\t--reedit-me=*|--reedit-mes=*|--reedit-mess=*|--reedit-messa=*|\\\n-\t--reedit-messag=*|--reedit-message=*)\n-\t\tlog_given=t$log_given\n-\t\tuse_commit=\"${1#*=}\"\n-\t\tno_edit=\n-\t\t;;\n-\t--ree|--reed|--reedi|--reedit|--reedit-|--reedit-m|--reedit-me|\\\n-\t--reedit-mes|--reedit-mess|--reedit-messa|--reedit-messag|\\\n-\t--reedit-message)\n-\t\tcase \"$#\" in 1) usage ;; esac\n-\t\tshift\n-\t\tlog_given=t$log_given\n-\t\tuse_commit=\"$1\"\n-\t\tno_edit=\n-\t\t;;\n-\t-C)\n-\t\tcase \"$#\" in 1) usage ;; esac\n-\t\tshift\n-\t\tlog_given=t$log_given\n-\t\tuse_commit=\"$1\"\n-\t\tno_edit=t\n-\t\t;;\n-\t--reu=*|--reus=*|--reuse=*|--reuse-=*|--reuse-m=*|--reuse-me=*|\\\n-\t--reuse-mes=*|--reuse-mess=*|--reuse-messa=*|--reuse-messag=*|\\\n-\t--reuse-message=*)\n-\t\tlog_given=t$log_given\n-\t\tuse_commit=\"${1#*=}\"\n-\t\tno_edit=t\n-\t\t;;\n-\t--reu|--reus|--reuse|--reuse-|--reuse-m|--reuse-me|--reuse-mes|\\\n-\t--reuse-mess|--reuse-messa|--reuse-messag|--reuse-message)\n-\t\tcase \"$#\" in 1) usage ;; esac\n-\t\tshift\n-\t\tlog_given=t$log_given\n-\t\tuse_commit=\"$1\"\n-\t\tno_edit=t\n-\t\t;;\n-\t-s|--s|--si|--sig|--sign|--signo|--signof|--signoff)\n-\t\tsignoff=t\n-\t\t;;\n-\t-t|--t|--te|--tem|--temp|--templ|--templa|--templat|--template)\n-\t\tcase \"$#\" in 1) usage ;; esac\n-\t\tshift\n-\t\ttemplatefile=\"$1\"\n-\t\tno_edit=\n-\t\t;;\n-\t-q|--q|--qu|--qui|--quie|--quiet)\n-\t\tquiet=t\n-\t\t;;\n-\t-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)\n-\t\tverbose=t\n-\t\t;;\n-\t-u|--u|--un|--unt|--untr|--untra|--untrac|--untrack|--untracke|\\\n-\t--untracked|--untracked-|--untracked-f|--untracked-fi|--untracked-fil|\\\n-\t--untracked-file|--untracked-files)\n-\t\tuntracked_files=t\n-\t\t;;\n-\t--)\n-\t\tshift\n-\t\tbreak\n-\t\t;;\n-\t-*)\n-\t\tusage\n-\t\t;;\n-\t*)\n-\t\tbreak\n-\t\t;;\n-\tesac\n-\tshift\n-done\n-case \"$edit_flag\" in t) no_edit= ;; esac\n-\n-################################################################\n-# Sanity check options\n-\n-case \"$amend,$initial_commit\" in\n-t,t)\n-\tdie \"You do not have anything to amend.\" ;;\n-t,)\n-\tif [ -f \"$GIT_DIR/MERGE_HEAD\" ]; then\n-\t\tdie \"You are in the middle of a merge -- cannot amend.\"\n-\tfi ;;\n-esac\n-\n-case \"$log_given\" in\n-tt*)\n-\tdie \"Only one of -c/-C/-F can be used.\" ;;\n-*tm*|*mt*)\n-\tdie \"Option -m cannot be combined with -c/-C/-F.\" ;;\n-esac\n-\n-case \"$#,$also,$only,$amend\" in\n-*,t,t,*)\n-\tdie \"Only one of --include/--only can be used.\" ;;\n-0,t,,* | 0,,t,)\n-\tdie \"No paths with --include/--only does not make sense.\" ;;\n-0,,t,t)\n-\tonly_include_assumed=\"# Clever... amending the last one with dirty index.\" ;;\n-0,,,*)\n-\t;;\n-*,,,*)\n-\tonly_include_assumed=\"# Explicit paths specified without -i nor -o; assuming --only paths...\"\n-\talso=\n-\t;;\n-esac\n-unset only\n-case \"$all,$interactive,$also,$#\" in\n-*t,*t,*)\n-\tdie \"Cannot use -a, --interactive or -i at the same time.\" ;;\n-t,,[1-9]*)\n-\tdie \"Paths with -a does not make sense.\" ;;\n-,t,[1-9]*)\n-\tdie \"Paths with --interactive does not make sense.\" ;;\n-,,t,0)\n-\tdie \"No paths with -i does not make sense.\" ;;\n-esac\n-\n-if test ! -z \"$templatefile\" -a -z \"$log_given\"\n-then\n-\tif test ! -f \"$templatefile\"\n-\tthen\n-\t\tdie \"Commit template file does not exist.\"\n-\tfi\n-fi\n-\n-################################################################\n-# Prepare index to have a tree to be committed\n-\n-case \"$all,$also\" in\n-t,)\n-\tif test ! -f \"$THIS_INDEX\"\n-\tthen\n-\t\tdie 'nothing to commit (use \"git add file1 file2\" to include for commit)'\n-\tfi\n-\tsave_index &&\n-\t(\n-\t\tcd_to_toplevel &&\n-\t\tGIT_INDEX_FILE=\"$NEXT_INDEX\" &&\n-\t\texport GIT_INDEX_FILE &&\n-\t\tgit diff-files --name-only -z |\n-\t\tgit update-index --remove -z --stdin\n-\t) || exit\n-\t;;\n-,t)\n-\tsave_index &&\n-\tgit ls-files --error-unmatch -- \"$@\" >/dev/null || exit\n-\n-\tgit diff-files --name-only -z -- \"$@\"  |\n-\t(\n-\t\tcd_to_toplevel &&\n-\t\tGIT_INDEX_FILE=\"$NEXT_INDEX\" &&\n-\t\texport GIT_INDEX_FILE &&\n-\t\tgit update-index --remove -z --stdin\n-\t) || exit\n-\t;;\n-,)\n-\tif test \"$interactive\" = t; then\n-\t\tgit add --interactive || exit\n-\tfi\n-\tcase \"$#\" in\n-\t0)\n-\t\t;; # commit as-is\n-\t*)\n-\t\tif test -f \"$GIT_DIR/MERGE_HEAD\"\n-\t\tthen\n-\t\t\trefuse_partial \"Cannot do a partial commit during a merge.\"\n-\t\tfi\n-\n-\t\tTMP_INDEX=\"$GIT_DIR/tmp-index$$\"\n-\t\tW=\n-\t\ttest -z \"$initial_commit\" && W=--with-tree=HEAD\n-\t\tcommit_only=`git ls-files --error-unmatch $W -- \"$@\"` || exit\n-\n-\t\t# Build a temporary index and update the real index\n-\t\t# the same way.\n-\t\tif test -z \"$initial_commit\"\n-\t\tthen\n-\t\t\tGIT_INDEX_FILE=\"$THIS_INDEX\" \\\n-\t\t\tgit read-tree --index-output=\"$TMP_INDEX\" -i -m HEAD\n-\t\telse\n-\t\t\trm -f \"$TMP_INDEX\"\n-\t\tfi || exit\n-\n-\t\tprintf '%s\\n' \"$commit_only\" |\n-\t\tGIT_INDEX_FILE=\"$TMP_INDEX\" \\\n-\t\tgit update-index --add --remove --stdin &&\n-\n-\t\tsave_index &&\n-\t\tprintf '%s\\n' \"$commit_only\" |\n-\t\t(\n-\t\t\tGIT_INDEX_FILE=\"$NEXT_INDEX\"\n-\t\t\texport GIT_INDEX_FILE\n-\t\t\tgit update-index --add --remove --stdin\n-\t\t) || exit\n-\t\t;;\n-\tesac\n-\t;;\n-esac\n-\n-################################################################\n-# If we do as-is commit, the index file will be THIS_INDEX,\n-# otherwise NEXT_INDEX after we make this commit.  We leave\n-# the index as is if we abort.\n-\n-if test -f \"$NEXT_INDEX\"\n-then\n-\tUSE_INDEX=\"$NEXT_INDEX\"\n-else\n-\tUSE_INDEX=\"$THIS_INDEX\"\n-fi\n-\n-case \"$status_only\" in\n-t)\n-\t# This will silently fail in a read-only repository, which is\n-\t# what we want.\n-\tGIT_INDEX_FILE=\"$USE_INDEX\" git update-index -q --unmerged --refresh\n-\trun_status\n-\texit $?\n-\t;;\n-'')\n-\tGIT_INDEX_FILE=\"$USE_INDEX\" git update-index -q --refresh || exit\n-\t;;\n-esac\n-\n-################################################################\n-# Grab commit message, write out tree and make commit.\n-\n-if test t = \"$verify\" && test -x \"$GIT_DIR\"/hooks/pre-commit\n-then\n-    GIT_INDEX_FILE=\"${TMP_INDEX:-${USE_INDEX}}\" \"$GIT_DIR\"/hooks/pre-commit \\\n-    || exit\n-fi\n-\n-if test \"$log_message\" != ''\n-then\n-\tprintf '%s\\n' \"$log_message\"\n-elif test \"$logfile\" != \"\"\n-then\n-\tif test \"$logfile\" = -\n-\tthen\n-\t\ttest -t 0 &&\n-\t\techo >&2 \"(reading log message from standard input)\"\n-\t\tcat\n-\telse\n-\t\tcat <\"$logfile\"\n-\tfi\n-elif test \"$use_commit\" != \"\"\n-then\n-\tencoding=$(git config i18n.commitencoding || echo UTF-8)\n-\tgit show -s --pretty=raw --encoding=\"$encoding\" \"$use_commit\" |\n-\tsed -e '1,/^$/d' -e 's/^    //'\n-elif test -f \"$GIT_DIR/MERGE_MSG\"\n-then\n-\tcat \"$GIT_DIR/MERGE_MSG\"\n-elif test -f \"$GIT_DIR/SQUASH_MSG\"\n-then\n-\tcat \"$GIT_DIR/SQUASH_MSG\"\n-elif test \"$templatefile\" != \"\"\n-then\n-\tcat \"$templatefile\"\n-fi | git stripspace >\"$GIT_DIR\"/COMMIT_EDITMSG\n-\n-case \"$signoff\" in\n-t)\n-\tsign=$(git-var GIT_COMMITTER_IDENT | sed -e '\n-\t\ts/>.*/>/\n-\t\ts/^/Signed-off-by: /\n-\t\t')\n-\tblank_before_signoff=\n-\ttail -n 1 \"$GIT_DIR\"/COMMIT_EDITMSG |\n-\tgrep 'Signed-off-by:' >/dev/null || blank_before_signoff='\n-'\n-\ttail -n 1 \"$GIT_DIR\"/COMMIT_EDITMSG |\n-\tgrep \"$sign\"$ >/dev/null ||\n-\tprintf '%s%s\\n' \"$blank_before_signoff\" \"$sign\" \\\n-\t\t>>\"$GIT_DIR\"/COMMIT_EDITMSG\n-\t;;\n-esac\n-\n-if test -f \"$GIT_DIR/MERGE_HEAD\" && test -z \"$no_edit\"; then\n-\techo \"#\"\n-\techo \"# It looks like you may be committing a MERGE.\"\n-\techo \"# If this is not correct, please remove the file\"\n-\tprintf '%s\\n' \"#\t$GIT_DIR/MERGE_HEAD\"\n-\techo \"# and try again\"\n-\techo \"#\"\n-fi >>\"$GIT_DIR\"/COMMIT_EDITMSG\n-\n-# Author\n-if test '' != \"$use_commit\"\n-then\n-\teval \"$(get_author_ident_from_commit \"$use_commit\")\"\n-\texport GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE\n-fi\n-if test '' != \"$force_author\"\n-then\n-\tGIT_AUTHOR_NAME=`expr \"z$force_author\" : 'z\\(.*[^ ]\\) *<.*'` &&\n-\tGIT_AUTHOR_EMAIL=`expr \"z$force_author\" : '.*\\(<.*\\)'` &&\n-\ttest '' != \"$GIT_AUTHOR_NAME\" &&\n-\ttest '' != \"$GIT_AUTHOR_EMAIL\" ||\n-\tdie \"malformed --author parameter\"\n-\texport GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL\n-fi\n-\n-PARENTS=\"-p HEAD\"\n-if test -z \"$initial_commit\"\n-then\n-\trloga='commit'\n-\tif [ -f \"$GIT_DIR/MERGE_HEAD\" ]; then\n-\t\trloga='commit (merge)'\n-\t\tPARENTS=\"-p HEAD \"`sed -e 's/^/-p /' \"$GIT_DIR/MERGE_HEAD\"`\n-\telif test -n \"$amend\"; then\n-\t\trloga='commit (amend)'\n-\t\tPARENTS=$(git cat-file commit HEAD |\n-\t\t\tsed -n -e '/^$/q' -e 's/^parent /-p /p')\n-\tfi\n-\tcurrent=\"$(git rev-parse --verify HEAD)\"\n-else\n-\tif [ -z \"$(git ls-files)\" ]; then\n-\t\techo >&2 'nothing to commit (use \"git add file1 file2\" to include for commit)'\n-\t\texit 1\n-\tfi\n-\tPARENTS=\"\"\n-\trloga='commit (initial)'\n-\tcurrent=''\n-fi\n-set_reflog_action \"$rloga\"\n-\n-if test -z \"$no_edit\"\n-then\n-\t{\n-\t\techo \"\"\n-\t\techo \"# Please enter the commit message for your changes.\"\n-\t\techo \"# (Comment lines starting with '#' will not be included)\"\n-\t\ttest -z \"$only_include_assumed\" || echo \"$only_include_assumed\"\n-\t\trun_status\n-\t} >>\"$GIT_DIR\"/COMMIT_EDITMSG\n-else\n-\t# we need to check if there is anything to commit\n-\trun_status >/dev/null\n-fi\n-if [ \"$?\" != \"0\" -a ! -f \"$GIT_DIR/MERGE_HEAD\" ]\n-then\n-\trm -f \"$GIT_DIR/COMMIT_EDITMSG\" \"$GIT_DIR/SQUASH_MSG\"\n-\tuse_status_color=t\n-\trun_status\n-\texit 1\n-fi\n-\n-case \"$no_edit\" in\n-'')\n-\tgit-var GIT_AUTHOR_IDENT > /dev/null  || die\n-\tgit-var GIT_COMMITTER_IDENT > /dev/null  || die\n-\tgit_editor \"$GIT_DIR/COMMIT_EDITMSG\"\n-\t;;\n-esac\n-\n-case \"$verify\" in\n-t)\n-\tif test -x \"$GIT_DIR\"/hooks/commit-msg\n-\tthen\n-\t\t\"$GIT_DIR\"/hooks/commit-msg \"$GIT_DIR\"/COMMIT_EDITMSG || exit\n-\tfi\n-esac\n-\n-if test -z \"$no_edit\"\n-then\n-    sed -e '\n-        /^diff --git a\\/.*/{\n-\t    s///\n-\t    q\n-\t}\n-\t/^#/d\n-    ' \"$GIT_DIR\"/COMMIT_EDITMSG\n-else\n-    cat \"$GIT_DIR\"/COMMIT_EDITMSG\n-fi |\n-git stripspace >\"$GIT_DIR\"/COMMIT_MSG\n-\n-# Test whether the commit message has any content we didn't supply.\n-have_commitmsg=\n-grep -v -i '^Signed-off-by' \"$GIT_DIR\"/COMMIT_MSG |\n-\tgit stripspace > \"$GIT_DIR\"/COMMIT_BAREMSG\n-\n-# Is the commit message totally empty?\n-if test -s \"$GIT_DIR\"/COMMIT_BAREMSG\n-then\n-\tif test \"$templatefile\" != \"\"\n-\tthen\n-\t\t# Test whether this is just the unaltered template.\n-\t\tif cnt=`sed -e '/^#/d' < \"$templatefile\" |\n-\t\t\tgit stripspace |\n-\t\t\tdiff \"$GIT_DIR\"/COMMIT_BAREMSG - |\n-\t\t\twc -l` &&\n-\t\t   test 0 -lt $cnt\n-\t\tthen\n-\t\t\thave_commitmsg=t\n-\t\tfi\n-\telse\n-\t\t# No template, so the content in the commit message must\n-\t\t# have come from the user.\n-\t\thave_commitmsg=t\n-\tfi\n-fi\n-\n-rm -f \"$GIT_DIR\"/COMMIT_BAREMSG\n-\n-if test \"$have_commitmsg\" = \"t\"\n-then\n-\tif test -z \"$TMP_INDEX\"\n-\tthen\n-\t\ttree=$(GIT_INDEX_FILE=\"$USE_INDEX\" git write-tree)\n-\telse\n-\t\ttree=$(GIT_INDEX_FILE=\"$TMP_INDEX\" git write-tree) &&\n-\t\trm -f \"$TMP_INDEX\"\n-\tfi &&\n-\tcommit=$(git commit-tree $tree $PARENTS <\"$GIT_DIR/COMMIT_MSG\") &&\n-\trlogm=$(sed -e 1q \"$GIT_DIR\"/COMMIT_MSG) &&\n-\tgit update-ref -m \"$GIT_REFLOG_ACTION: $rlogm\" HEAD $commit \"$current\" &&\n-\trm -f -- \"$GIT_DIR/MERGE_HEAD\" \"$GIT_DIR/MERGE_MSG\" &&\n-\tif test -f \"$NEXT_INDEX\"\n-\tthen\n-\t\tmv \"$NEXT_INDEX\" \"$THIS_INDEX\"\n-\telse\n-\t\t: ;# happy\n-\tfi\n-else\n-\techo >&2 \"* no commit message?  aborting commit.\"\n-\tfalse\n-fi\n-ret=\"$?\"\n-rm -f \"$GIT_DIR/COMMIT_MSG\" \"$GIT_DIR/COMMIT_EDITMSG\" \"$GIT_DIR/SQUASH_MSG\"\n-\n-cd_to_toplevel\n-\n-git rerere\n-\n-if test \"$ret\" = 0\n-then\n-\tgit gc --auto\n-\tif test -x \"$GIT_DIR\"/hooks/post-commit\n-\tthen\n-\t\t\"$GIT_DIR\"/hooks/post-commit\n-\tfi\n-\tif test -z \"$quiet\"\n-\tthen\n-\t\tcommit=`git diff-tree --always --shortstat --pretty=\"format:%h: %s\"\\\n-\t\t       --summary --root HEAD --`\n-\t\techo \"Created${initial_commit:+ initial} commit $commit\"\n-\tfi\n-fi\n-\n-exit \"$ret\"\ndiff --git a/git.c b/git.c\nindex d7c6bca..9565555 100644\n--- a/git.c\n+++ b/git.c\n@@ -320,6 +320,7 @@ static void handle_internal_command(int argc, const char **argv)\n \t\t{ \"check-attr\", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"cherry\", cmd_cherry, RUN_SETUP },\n \t\t{ \"cherry-pick\", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },\n+\t\t{ \"commit\", cmd_commit, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"commit-tree\", cmd_commit_tree, RUN_SETUP },\n \t\t{ \"config\", cmd_config },\n \t\t{ \"count-objects\", cmd_count_objects, RUN_SETUP },\n@@ -368,10 +369,10 @@ static void handle_internal_command(int argc, const char **argv)\n \t\t{ \"rev-parse\", cmd_rev_parse, RUN_SETUP },\n \t\t{ \"revert\", cmd_revert, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"rm\", cmd_rm, RUN_SETUP | NEED_WORK_TREE },\n-\t\t{ \"runstatus\", cmd_runstatus, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"shortlog\", cmd_shortlog, RUN_SETUP | USE_PAGER },\n \t\t{ \"show-branch\", cmd_show_branch, RUN_SETUP },\n \t\t{ \"show\", cmd_show, RUN_SETUP | USE_PAGER },\n+\t\t{ \"status\", cmd_status, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"stripspace\", cmd_stripspace },\n \t\t{ \"symbolic-ref\", cmd_symbolic_ref, RUN_SETUP },\n \t\t{ \"tag\", cmd_tag, RUN_SETUP },\ndiff --git a/strbuf.h b/strbuf.h\nindex 5657e3d..eef4e6d 100644\n--- a/strbuf.h\n+++ b/strbuf.h\n@@ -113,5 +113,6 @@ extern int strbuf_read_file(struct strbuf *sb, const char *path);\n extern int strbuf_getline(struct strbuf *, FILE *, int);\n \n extern void stripspace(struct strbuf *buf, int skip_comments);\n+extern void launch_editor(const char *path, struct strbuf *buffer);\n \n #endif /* STRBUF_H */\ndiff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh\nindex 552af1c..2dbe04f 100755\n--- a/t/t3501-revert-cherry-pick.sh\n+++ b/t/t3501-revert-cherry-pick.sh\n@@ -44,7 +44,7 @@ test_expect_success setup '\n test_expect_success 'cherry-pick after renaming branch' '\n \n \tgit checkout rename2 &&\n-\tEDITOR=: VISUAL=: git cherry-pick added &&\n+\tgit cherry-pick added &&\n \ttest -f opos &&\n \tgrep \"Add extra line at the end\" opos\n \n@@ -53,7 +53,7 @@ test_expect_success 'cherry-pick after renaming branch' '\n test_expect_success 'revert after renaming branch' '\n \n \tgit checkout rename1 &&\n-\tEDITOR=: VISUAL=: git revert added &&\n+\tgit revert added &&\n \ttest -f spoo &&\n \t! grep \"Add extra line at the end\" spoo\n \ndiff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh\nindex 28e9e37..235f372 100755\n--- a/t/t3901-i18n-patch.sh\n+++ b/t/t3901-i18n-patch.sh\n@@ -154,7 +154,7 @@ test_expect_success 'cherry-pick(U/U)' '\n \tgit reset --hard master &&\n \tgit cherry-pick side^ &&\n \tgit cherry-pick side &&\n-\tEDITOR=: VISUAL=: git revert HEAD &&\n+\tgit revert HEAD &&\n \n \tcheck_encoding 3\n '\n@@ -169,7 +169,7 @@ test_expect_success 'cherry-pick(L/L)' '\n \tgit reset --hard master &&\n \tgit cherry-pick side^ &&\n \tgit cherry-pick side &&\n-\tEDITOR=: VISUAL=: git revert HEAD &&\n+\tgit revert HEAD &&\n \n \tcheck_encoding 3 8859\n '\n@@ -184,7 +184,7 @@ test_expect_success 'cherry-pick(U/L)' '\n \tgit reset --hard master &&\n \tgit cherry-pick side^ &&\n \tgit cherry-pick side &&\n-\tEDITOR=: VISUAL=: git revert HEAD &&\n+\tgit revert HEAD &&\n \n \tcheck_encoding 3\n '\n@@ -200,7 +200,7 @@ test_expect_success 'cherry-pick(L/U)' '\n \tgit reset --hard master &&\n \tgit cherry-pick side^ &&\n \tgit cherry-pick side &&\n-\tEDITOR=: VISUAL=: git revert HEAD &&\n+\tgit revert HEAD &&\n \n \tcheck_encoding 3 8859\n '\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex cc1253c..a232bd6 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -9,8 +9,8 @@ LC_ALL=C\n PAGER=cat\n TZ=UTC\n export LANG LC_ALL PAGER TZ\n-EDITOR=:\n-VISUAL=:\n+EDITOR=/bin/true\n+VISUAL=/bin/true\n unset GIT_EDITOR\n unset AUTHOR_DATE\n unset AUTHOR_EMAIL\n-- \n1.5.2.GIT\n"},{"id":"54131","messageId":"1190868632-29287-4-git-send-email-krh@redhat.com","threadId":"10039","inReplyTo":"1190868632-29287-3-git-send-email-krh@redhat.com","subject":"[PATCH 4/4] Move launch_editor() and stripspace() to new file editor.c.","fromName":"Kristian Høgsberg","fromEmail":"krh@redhat.com","sentAt":"2007-09-27T04:50:32Z","receivedAt":"2007-09-27T04:50:32Z","isPatch":true,"sender":{"key":"krh@redhat.com","avatar":"https://gravatar.com/avatar/763dee6f9594ac474f725b137a39565792928e583ddf59b32befc2907409027e?d=mp&s=160"},"body":"Signed-off-by: Kristian Høgsberg <krh@redhat.com>\n---\n Makefile             |    2 +-\n builtin-commit.c     |    1 +\n builtin-stripspace.c |   68 +------------------------------\n builtin-tag.c        |   40 +------------------\n editor.c             |  109 ++++++++++++++++++++++++++++++++++++++++++++++++++\n editor.h             |    9 ++++\n strbuf.h             |    3 -\n 7 files changed, 122 insertions(+), 110 deletions(-)\n create mode 100644 editor.c\n create mode 100644 editor.h\n\ndiff --git a/Makefile b/Makefile\nindex 6172589..f6d991e 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -310,7 +310,7 @@ LIB_OBJS = \\\n \talloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \\\n \tcolor.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \\\n \tconvert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \\\n-\ttransport.o bundle.o parse-options.o\n+\ttransport.o bundle.o parse-options.o editor.o\n \n BUILTIN_OBJS = \\\n \tbuiltin-add.o \\\ndiff --git a/builtin-commit.c b/builtin-commit.c\nindex 69e8b19..7d87812 100644\n--- a/builtin-commit.c\n+++ b/builtin-commit.c\n@@ -19,6 +19,7 @@\n #include \"strbuf.h\"\n #include \"utf8.h\"\n #include \"parse-options.h\"\n+#include \"editor.h\"\n \n static const char builtin_commit_usage[] =\n \t\"[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [--template <file>] [[-i | -o] <path>...]\";\ndiff --git a/builtin-stripspace.c b/builtin-stripspace.c\nindex c0b2130..4e5bb07 100644\n--- a/builtin-stripspace.c\n+++ b/builtin-stripspace.c\n@@ -1,72 +1,6 @@\n #include \"builtin.h\"\n #include \"cache.h\"\n-\n-/*\n- * Returns the length of a line, without trailing spaces.\n- *\n- * If the line ends with newline, it will be removed too.\n- */\n-static size_t cleanup(char *line, size_t len)\n-{\n-\twhile (len) {\n-\t\tunsigned char c = line[len - 1];\n-\t\tif (!isspace(c))\n-\t\t\tbreak;\n-\t\tlen--;\n-\t}\n-\n-\treturn len;\n-}\n-\n-/*\n- * Remove empty lines from the beginning and end\n- * and also trailing spaces from every line.\n- *\n- * Note that the buffer will not be NUL-terminated.\n- *\n- * Turn multiple consecutive empty lines between paragraphs\n- * into just one empty line.\n- *\n- * If the input has only empty lines and spaces,\n- * no output will be produced.\n- *\n- * If last line does not have a newline at the end, one is added.\n- *\n- * Enable skip_comments to skip every line starting with \"#\".\n- */\n-void stripspace(struct strbuf *sb, int skip_comments)\n-{\n-\tint empties = 0;\n-\tsize_t i, j, len, newlen;\n-\tchar *eol;\n-\n-\t/* We may have to add a newline. */\n-\tstrbuf_grow(sb, 1);\n-\n-\tfor (i = j = 0; i < sb->len; i += len, j += newlen) {\n-\t\teol = memchr(sb->buf + i, '\\n', sb->len - i);\n-\t\tlen = eol ? eol - (sb->buf + i) + 1 : sb->len - i;\n-\n-\t\tif (skip_comments && len && sb->buf[i] == '#') {\n-\t\t\tnewlen = 0;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tnewlen = cleanup(sb->buf + i, len);\n-\n-\t\t/* Not just an empty line? */\n-\t\tif (newlen) {\n-\t\t\tif (empties > 0 && j > 0)\n-\t\t\t\tsb->buf[j++] = '\\n';\n-\t\t\tempties = 0;\n-\t\t\tmemmove(sb->buf + j, sb->buf + i, newlen);\n-\t\t\tsb->buf[newlen + j++] = '\\n';\n-\t\t} else {\n-\t\t\tempties++;\n-\t\t}\n-\t}\n-\n-\tstrbuf_setlen(sb, j);\n-}\n+#include \"editor.h\"\n \n int cmd_stripspace(int argc, const char **argv, const char *prefix)\n {\ndiff --git a/builtin-tag.c b/builtin-tag.c\nindex 0a36a5d..c9a0497 100644\n--- a/builtin-tag.c\n+++ b/builtin-tag.c\n@@ -10,6 +10,7 @@\n #include \"builtin.h\"\n #include \"refs.h\"\n #include \"tag.h\"\n+#include \"editor.h\"\n #include \"run-command.h\"\n \n static const char builtin_tag_usage[] =\n@@ -17,45 +18,6 @@ static const char builtin_tag_usage[] =\n \n static char signingkey[1000];\n \n-void launch_editor(const char *path, struct strbuf *buffer)\n-{\n-\tconst char *editor, *terminal;\n-\tstruct child_process child;\n-\tconst char *args[3];\n-\n-\teditor = getenv(\"GIT_EDITOR\");\n-\tif (!editor && editor_program)\n-\t\teditor = editor_program;\n-\tif (!editor)\n-\t\teditor = getenv(\"VISUAL\");\n-\tif (!editor)\n-\t\teditor = getenv(\"EDITOR\");\n-\n-\tterminal = getenv(\"TERM\");\n-\tif (!editor && (!terminal || !strcmp(terminal, \"dumb\"))) {\n-\t\tfprintf(stderr,\n-\t\t\"Terminal is dumb but no VISUAL nor EDITOR defined.\\n\"\n-\t\t\"Please supply the message using either -m or -F option.\\n\");\n-\t\texit(1);\n-\t}\n-\n-\tif (!editor)\n-\t\teditor = \"vi\";\n-\n-\tmemset(&child, 0, sizeof(child));\n-\tchild.argv = args;\n-\targs[0] = editor;\n-\targs[1] = path;\n-\targs[2] = NULL;\n-\n-\tif (run_command(&child))\n-\t\tdie(\"There was a problem with the editor %s.\", editor);\n-\n-\tif (strbuf_read_file(buffer, path) < 0)\n-\t\tdie(\"could not read message file '%s': %s\",\n-\t\t    path, strerror(errno));\n-}\n-\n struct tag_filter {\n \tconst char *pattern;\n \tint lines;\ndiff --git a/editor.c b/editor.c\nnew file mode 100644\nindex 0000000..6bc3033\n--- /dev/null\n+++ b/editor.c\n@@ -0,0 +1,109 @@\n+#include \"git-compat-util.h\"\n+#include \"editor.h\"\n+#include \"run-command.h\"\n+\n+void launch_editor(const char *path, struct strbuf *buffer)\n+{\n+\tconst char *editor, *terminal;\n+\tstruct child_process child;\n+\tconst char *args[3];\n+\n+\teditor = getenv(\"GIT_EDITOR\");\n+\tif (!editor && editor_program)\n+\t\teditor = editor_program;\n+\tif (!editor)\n+\t\teditor = getenv(\"VISUAL\");\n+\tif (!editor)\n+\t\teditor = getenv(\"EDITOR\");\n+\n+\tterminal = getenv(\"TERM\");\n+\tif (!editor && (!terminal || !strcmp(terminal, \"dumb\"))) {\n+\t\tfprintf(stderr,\n+\t\t\"Terminal is dumb but no VISUAL nor EDITOR defined.\\n\"\n+\t\t\"Please supply the message using either -m or -F option.\\n\");\n+\t\texit(1);\n+\t}\n+\n+\tif (!editor)\n+\t\teditor = \"vi\";\n+\n+\tmemset(&child, 0, sizeof(child));\n+\tchild.argv = args;\n+\targs[0] = editor;\n+\targs[1] = path;\n+\targs[2] = NULL;\n+\n+\tif (run_command(&child))\n+\t\tdie(\"There was a problem with the editor %s.\", editor);\n+\n+\tif (strbuf_read_file(buffer, path) < 0)\n+\t\tdie(\"could not read message file '%s': %s\",\n+\t\t    path, strerror(errno));\n+}\n+\n+/*\n+ * Returns the length of a line, without trailing spaces.\n+ *\n+ * If the line ends with newline, it will be removed too.\n+ */\n+static size_t cleanup(char *line, size_t len)\n+{\n+\twhile (len) {\n+\t\tunsigned char c = line[len - 1];\n+\t\tif (!isspace(c))\n+\t\t\tbreak;\n+\t\tlen--;\n+\t}\n+\n+\treturn len;\n+}\n+\n+/*\n+ * Remove empty lines from the beginning and end\n+ * and also trailing spaces from every line.\n+ *\n+ * Note that the buffer will not be NUL-terminated.\n+ *\n+ * Turn multiple consecutive empty lines between paragraphs\n+ * into just one empty line.\n+ *\n+ * If the input has only empty lines and spaces,\n+ * no output will be produced.\n+ *\n+ * If last line does not have a newline at the end, one is added.\n+ *\n+ * Enable skip_comments to skip every line starting with \"#\".\n+ */\n+void stripspace(struct strbuf *sb, int skip_comments)\n+{\n+\tint empties = 0;\n+\tsize_t i, j, len, newlen;\n+\tchar *eol;\n+\n+\t/* We may have to add a newline. */\n+\tstrbuf_grow(sb, 1);\n+\n+\tfor (i = j = 0; i < sb->len; i += len, j += newlen) {\n+\t\teol = memchr(sb->buf + i, '\\n', sb->len - i);\n+\t\tlen = eol ? eol - (sb->buf + i) + 1 : sb->len - i;\n+\n+\t\tif (skip_comments && len && sb->buf[i] == '#') {\n+\t\t\tnewlen = 0;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tnewlen = cleanup(sb->buf + i, len);\n+\n+\t\t/* Not just an empty line? */\n+\t\tif (newlen) {\n+\t\t\tif (empties > 0 && j > 0)\n+\t\t\t\tsb->buf[j++] = '\\n';\n+\t\t\tempties = 0;\n+\t\t\tmemmove(sb->buf + j, sb->buf + i, newlen);\n+\t\t\tsb->buf[newlen + j++] = '\\n';\n+\t\t} else {\n+\t\t\tempties++;\n+\t\t}\n+\t}\n+\n+\tstrbuf_setlen(sb, j);\n+}\ndiff --git a/editor.h b/editor.h\nnew file mode 100644\nindex 0000000..9dc39f5\n--- /dev/null\n+++ b/editor.h\n@@ -0,0 +1,9 @@\n+#ifndef EDITOR_H\n+#define EDITOR_H\n+\n+#include \"cache.h\"\n+\n+extern void launch_editor(const char *path, struct strbuf *buffer);\n+extern void stripspace(struct strbuf *sb, int skip_comments);\n+\n+#endif\ndiff --git a/strbuf.h b/strbuf.h\nindex eef4e6d..d4d9e56 100644\n--- a/strbuf.h\n+++ b/strbuf.h\n@@ -112,7 +112,4 @@ extern int strbuf_read_file(struct strbuf *sb, const char *path);\n \n extern int strbuf_getline(struct strbuf *, FILE *, int);\n \n-extern void stripspace(struct strbuf *buf, int skip_comments);\n-extern void launch_editor(const char *path, struct strbuf *buffer);\n-\n #endif /* STRBUF_H */\n-- \n1.5.2.GIT\n"},{"id":"54147","messageId":"7v7imcv5op.fsf@gitster.siamese.dyndns.org","threadId":"10039","inReplyTo":"1190868632-29287-2-git-send-email-krh@redhat.com","subject":"Re: [PATCH 2/4] This exports the update() function from builtin-add.c as","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-09-27T09:05:58Z","receivedAt":"2007-09-27T09:05:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Kristian Høgsberg <krh@redhat.com> writes:\n\n> Signed-off-by: Kristian Høgsberg <krh@redhat.com>\n\nHuh?  -EPARSE_TITLE_STRING\n\n[PATCH 1/4] Add a simple option parser for use by builtin-commit.c.\n[PATCH 2/4] This exports the update() function from builtin-add.c as\n[PATCH 3/4] Implement git commit as a builtin command.\n[PATCH 4/4] Move launch_editor() and stripspace() to new file editor.c.\n\nLet's step back a bit.  The whole series organization is very\nscrewy.  Especially I do not think 4/4 should be at the end.\n\nReviewing your old series...\n\n * Enable wt-status output to a given FILE pointer.\n * Enable wt-status to run against non-standard index file.\n\nLet's have the above two from the previous series in 'next'.\n\nNow the following five that have been in 'pu' are from the older\nseries:\n\n * Introduce entry point for launching add--interactive.\n * Clean up stripspace a bit, use strbuf even more.\n * Add strbuf_read_file().\n * Export rerere() and launch_editor().\n * Implement git commit as a builtin command.\n\nThe changes to stripspace and strbuf_read_file() are unrelated\nto making the commit command a builtin.  I have extended the\nstrbuf topic with these two and merged the result to 'next'.\n\nI think the right organization for the \"builtin-commit\" series\nshould be:\n\n * merge strbuf topic in kh/commit topic, in order to get the\n   stripspace updates and strbuf_read_file();\n\n * add--interactive entry point change (respin the one from the\n   old series);\n\n * rename update() to add_files_to_cache() and export (respin\n   this [2/4] with a better commit message);\n\n * create a separate rerere() function and export (respin part\n   of old series, with proper refactoring);\n\n   I am not happy with builtin-foo.c calling into something from\n   builtin-bar.c, though.  We probably would want to move\n   rerere() and add_files_to_cache() somewhere else.\n\n * move launch_editor() and stripspace() to create editor.c (new\n   [4/4]);\n\n * add option parser in parse-options.[ch] (new [1/4]);\n\n * finally, create builtin-commit that uses the groundwork laid\n   out above (new [3/4]).\n\nI ended up doing the above up to the rerere() one myself, but\nhaven't done the rest.\n\nI probably would start more aggressively asking the original\nauthor to clean up and resubmit from now on.  I haven't managed\nto scrape enough time for myself to code anything meaningful for\ngit recently, and instead spent too much time fixing up other\npeoples code.\n"},{"id":"54160","messageId":"Pine.LNX.4.64.0709271245570.28395@racer.site","threadId":"10039","inReplyTo":"1190868632-29287-2-git-send-email-krh@redhat.com","subject":"Re: [PATCH 2/4] This exports the update() function from builtin-add.c as","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-09-27T11:47:09Z","receivedAt":"2007-09-27T11:47:09Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 27 Sep 2007, Kristian Høgsberg wrote:\n\n>  builtin-add.c |    8 ++++----\n>  commit.h      |    2 ++\n\nMaybe move it to read-cache.c, just after \"add_file_to_index()\"?  And \nexpose it via cache.h, not commit.h?\n\nCiao,\nDscho\n"},{"id":"54194","messageId":"7vlkarubv2.fsf@gitster.siamese.dyndns.org","threadId":"10039","inReplyTo":"Pine.LNX.4.64.0709271245570.28395@racer.site","subject":"Re: [PATCH 2/4] This exports the update() function from builtin-add.c as","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-09-27T19:50:09Z","receivedAt":"2007-09-27T19:50:09Z","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> On Thu, 27 Sep 2007, Kristian Høgsberg wrote:\n>\n>>  builtin-add.c |    8 ++++----\n>>  commit.h      |    2 ++\n>\n> Maybe move it to read-cache.c, just after \"add_file_to_index()\"?  And \n> expose it via cache.h, not commit.h?\n\nHmmmmmmmm.  read-cache.c has been one of the lowest level files\nthat define atomic operations, similar to sha1_file.c, and this\nfunction is much more porcelain-ish molecule operation.  I'd\nrather not.\n\nWhat other useful molecules would we have and/or need to have by\nsplitting existing standalone commands?  rerere() is another,\nand probably we would need to rip the --with-tree bit from\nls-files out to make it usable from builtin-commit.\n\nI do not think we would want to go to the \"one file per\nfunction\" extreme, either.  Can we have a new file that hold\nthese helper functions for porcelains?\n"},{"id":"54461","messageId":"20070930131133.GA11209@diku.dk","threadId":"10039","inReplyTo":"1190868632-29287-1-git-send-email-krh@redhat.com","subject":"Re: [PATCH 1/4] Add a simple option parser for use by builtin-commit.c.","fromName":"Jonas Fonseca","fromEmail":"fonseca@diku.dk","sentAt":"2007-09-30T13:11:33Z","receivedAt":"2007-09-30T13:11:33Z","isPatch":true,"sender":{"key":"fonseca@diku.dk","avatar":"https://gravatar.com/avatar/f82f3ad698717c51873b020c750a92438c820a24056dc39fe4d07baa10a92264?d=mp&s=160"},"body":"Hello Kristian,\n\nI have some comments on your patch. Some of the \"improvement\" might have\nto wait until after your builtin-commit changes hits git.git. However,\nif we could agree on some of the general changes, I could start porting\nother of the main porcelain commands to use the option parser without\ndepending on the state of the remaining builtin-commit series.\n\nKristian Høgsberg <krh@redhat.com> wrote Thu, Sep 27, 2007:\n> Signed-off-by: Kristian Høgsberg <krh@redhat.com>\n> ---\n>  Makefile        |    2 +-\n>  parse-options.c |   74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++\n>  parse-options.h |   29 +++++++++++++++++++++\n>  3 files changed, 104 insertions(+), 1 deletions(-)\n>  create mode 100644 parse-options.c\n>  create mode 100644 parse-options.h\n> \n> diff --git a/Makefile b/Makefile\n> index 62bdac6..d90e959 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -310,7 +310,7 @@ LIB_OBJS = \\\n>  \talloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \\\n>  \tcolor.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \\\n>  \tconvert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \\\n> -\ttransport.o bundle.o\n> +\ttransport.o bundle.o parse-options.o\n>  \n>  BUILTIN_OBJS = \\\n>  \tbuiltin-add.o \\\n> diff --git a/parse-options.c b/parse-options.c\n> new file mode 100644\n> index 0000000..2fb30cd\n> --- /dev/null\n> +++ b/parse-options.c\n> @@ -0,0 +1,74 @@\n> +#include \"git-compat-util.h\"\n> +#include \"parse-options.h\"\n> +\n> +int parse_options(const char ***argv,\n> +\t\t  struct option *options, int count,\n> +\t\t  const char *usage_string)\n> +{\n> +\tconst char *value, *eq;\n> +\tint i;\n> +\n> +\tif (**argv == NULL)\n> +\t\treturn 0;\n> +\tif ((**argv)[0] != '-')\n> +\t\treturn 0;\n> +\tif (!strcmp(**argv, \"--\"))\n> +\t\treturn 0;\n\nI don't know if this is a bug, but you do not remove \"--\" from argv,\nwhich is later (in the patch that adds builtin-commit.c) passed to\nadd_files_to_cache and then get_pathspec where it is not removed or\ndetected either.\n\n> +\n> +\tvalue = NULL;\n> +\tfor (i = 0; i < count; i++) {\n> +\t\tif ((**argv)[1] == '-') {\n> +\t\t\tif (!prefixcmp(options[i].long_name, **argv + 2)) {\n> +\t\t\t\tif (options[i].type != OPTION_BOOLEAN)\n> +\t\t\t\t\tvalue = *++(*argv);\n> +\t\t\t\tgoto match;\n> +\t\t\t}\n> +\n> +\t\t\teq = strchr(**argv + 2, '=');\n> +\t\t\tif (eq && options[i].type != OPTION_BOOLEAN &&\n> +\t\t\t    !strncmp(**argv + 2,\n> +\t\t\t\t     options[i].long_name, eq - **argv - 2)) {\n> +\t\t\t\tvalue = eq + 1;\n> +\t\t\t\tgoto match;\n> +\t\t\t}\n> +\t\t}\n> +\n> +\t\tif ((**argv)[1] == options[i].short_name) {\n> +\t\t\tif ((**argv)[2] == '\\0') {\n> +\t\t\t\tif (options[i].type != OPTION_BOOLEAN)\n> +\t\t\t\t\tvalue = *++(*argv);\n> +\t\t\t\tgoto match;\n> +\t\t\t}\n> +\n> +\t\t\tif (options[i].type != OPTION_BOOLEAN) {\n> +\t\t\t\tvalue = **argv + 2;\n> +\t\t\t\tgoto match;\n> +\t\t\t}\n> +\t\t}\n> +\t}\n> +\n> +\tusage(usage_string);\n> +\n> + match:\n\nI think the goto can be avoided by simply breaking out of the above loop\nwhen an option has been found and add ...\n\n> +\tswitch (options[i].type) {\n\tcase OPTION_LAST\n\t\tusage(usage_string);\n\t\tbreak;\n\n> +\tcase OPTION_BOOLEAN:\n> +\t\t*(int *)options[i].value = 1;\n> +\t\tbreak;\n\nI've been looking at builtin-blame.c which IMO has some of the most\nobscure option parsing and maybe this can be changed to increment in\norder to support stuff like changing the meaning by passing the same arg\nmultiple times (e.g. \"-C -C -C\") better.\n\nBlame option parsing also sports (enum) flags being masked together,\nthis can of course be rewritten to a boolean option followed by\nmasking when parse_options is done (to keep it sane).\n\n> +\tcase OPTION_STRING:\n> +\t\tif (value == NULL)\n> +\t\t\tdie(\"option %s requires a value.\", (*argv)[-1]);\n\nMaybe change this ...\n\n> +\t\t*(const char **)options[i].value = value;\n> +\t\tbreak;\n> +\tcase OPTION_INTEGER:\n> +\t\tif (value == NULL)\n> +\t\t\tdie(\"option %s requires a value.\", (*argv)[-1]);\n\n... and this to:\n\n\t\tif (!value) {\n\t\t\terror(\"option %s requires a value.\", (*argv)[-1]);\n\t\t\tusage(usage_string);\n\t\t}\n\n> +\t\t*(int *)options[i].value = atoi(value);\n> +\t\tbreak;\n> +\tdefault:\n> +\t\tassert(0);\n> +\t}\n> +\n> +\t(*argv)++;\n> +\n> +\treturn 1;\n> +}\n> diff --git a/parse-options.h b/parse-options.h\n> new file mode 100644\n> index 0000000..39399c3\n> --- /dev/null\n> +++ b/parse-options.h\n> @@ -0,0 +1,29 @@\n> +#ifndef PARSE_OPTIONS_H\n> +#define PARSE_OPTIONS_H\n> +\n> +enum option_type {\n> +    OPTION_BOOLEAN,\n> +    OPTION_STRING,\n> +    OPTION_INTEGER,\n> +    OPTION_LAST,\n> +};\n> +\n> +struct option {\n> +    enum option_type type;\n> +    const char *long_name;\n> +    char short_name;\n> +    void *value;\n> +};\n\nSpace vs tab indentation.\n\nOne of the last things I miss from Cogito is the nice abbreviated help\nmessages that was available via '-h'. I don't know if it would be\nacceptable (at least for the main porcelain commands) to put this\nfunctionality into the option parser by adding a \"description\" member to\nstruct option and have parse_options print a nice:\n\n\t<error message if any>\n\t<usage string>\n\t<option summary>\n\non failure, or, if that is regarded as too verbose, simply when -h is\ndetected.\n\n> +\n> +/* Parse the given options against the list of known options.  The\n> + * order of the option structs matters, in that ambiguous\n> + * abbreviations (eg, --in could be short for --include or\n> + * --interactive) are matched by the first option that share the\n> + * prefix.\n> + */\n\nThis prefix aware option parsing has not been ported over to the other\nbuiltins when they were lifted from shell code. It might be nice to have\nof course. Is it really needed?\n\n> +\n> +extern int parse_options(const char ***argv,\n> +\t\t\t struct option *options, int count,\n> +\t\t\t const char *usage_string);\n\nI think the interface could be improved a bit. For example, it doesn't\nneed to count argument since the last entry in the options array is\nOPTION_LAST and thus the size can be detected that way.\n\nAlso, I think for this to be more usable for other built-in programs it\nshouldn't modify argv, but instead take both argc and argv (so we don't\nneed to have code like \"*++(*argv)\" ;), parse _all_ options in one go,\nand return the index (of argv) for any remaining options.\n\nThen the following:\n\n\twhile (parse_options(argv, commit_options, ARRAY_SIZE(commit_options),\n\t\tbuiltin_commit_usage))\n\t\t;\n\nbecomes:\n\n\tint i;\n\t...\n\ti = parse_options(argc, argv, commit_options, builtin_commit_usage);\n\nThis fits better with how option parsing is currently done. Take\nbuiltin-add for example:\n\n\tfor (i = 1 ; i < argc ; i++) {\n\t\tconst char *arg = argv[i];\n\t\t/* ... */\n\t}\n\tif (argc <= i)\n\t\tusage(builtin_rm_usage);\n\n[ BTW, blame option parsing actually wants to know if \"--\" has been seen,\n  but I think that can be worked around by simply checking argv[i - 1]\n  after calling the option parser. ]\n\n> +\n> +#endif\n\nOK, I will stop these ramblings here. I hope the fact that I read your\npatch both back and forth and added comments in the process didn't make\nit too confusing.\n\n-- \nJonas Fonseca\n"},{"id":"54463","messageId":"Pine.LNX.4.64.0710011114310.28395@racer.site","threadId":"10039","inReplyTo":"20070930131133.GA11209@diku.dk","subject":"Re: [PATCH 1/4] Add a simple option parser for use by builtin-commit.c.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-10-01T10:14:48Z","receivedAt":"2007-10-01T10:14:48Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 30 Sep 2007, Jonas Fonseca wrote:\n\n> Also, I think for this to be more usable for other built-in programs it \n> shouldn't modify argv, but instead take both argc and argv (so we don't \n> need to have code like \"*++(*argv)\" ;), parse _all_ options in one go, \n> and return the index (of argv) for any remaining options.\n\nWe _have_ to modify argv.  For example, \"git log master -p\" is perfectly \nvalid.\n\nCiao,\nDscho\n"},{"id":"54468","messageId":"20071001103148.GC8315@diku.dk","threadId":"10039","inReplyTo":"Pine.LNX.4.64.0710011114310.28395@racer.site","subject":"Re: [PATCH 1/4] Add a simple option parser for use by builtin-commit.c.","fromName":"Jonas Fonseca","fromEmail":"fonseca@diku.dk","sentAt":"2007-10-01T10:31:48Z","receivedAt":"2007-10-01T10:31:48Z","isPatch":true,"sender":{"key":"fonseca@diku.dk","avatar":"https://gravatar.com/avatar/f82f3ad698717c51873b020c750a92438c820a24056dc39fe4d07baa10a92264?d=mp&s=160"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote Mon, Oct 01, 2007:\n> Hi,\n> \n> On Sun, 30 Sep 2007, Jonas Fonseca wrote:\n> \n> > Also, I think for this to be more usable for other built-in programs it \n> > shouldn't modify argv, but instead take both argc and argv (so we don't \n> > need to have code like \"*++(*argv)\" ;), parse _all_ options in one go, \n> > and return the index (of argv) for any remaining options.\n> \n> We _have_ to modify argv.  For example, \"git log master -p\" is perfectly \n> valid.\n\nAh, yes this could be nice to also finally have (more universally) in\ngit. But for this to be possible I don't see any reason for it to modify\nthe pointer to argv. Instead, it can just reshuffle entries in argv.\n\n-- \nJonas Fonseca\n"},{"id":"54472","messageId":"Pine.LNX.4.64.0710011239080.28395@racer.site","threadId":"10039","inReplyTo":"20071001103148.GC8315@diku.dk","subject":"Re: [PATCH 1/4] Add a simple option parser for use by builtin-commit.c.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-10-01T11:39:43Z","receivedAt":"2007-10-01T11:39:43Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 1 Oct 2007, Jonas Fonseca wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote Mon, Oct 01, 2007:\n> \n> > On Sun, 30 Sep 2007, Jonas Fonseca wrote:\n> > \n> > > Also, I think for this to be more usable for other built-in programs \n> > > it shouldn't modify argv, but instead take both argc and argv (so we \n> > > don't need to have code like \"*++(*argv)\" ;), parse _all_ options in \n> > > one go, and return the index (of argv) for any remaining options.\n> > \n> > We _have_ to modify argv.  For example, \"git log master -p\" is \n> > perfectly valid.\n> \n> Ah, yes this could be nice to also finally have (more universally) in \n> git. But for this to be possible I don't see any reason for it to modify \n> the pointer to argv. Instead, it can just reshuffle entries in argv.\n\nIn that case, I misunderstood you.  Indeed, I'd only reshuffle the \nentries of argv.\n\nCiao,\nDscho\n"},{"id":"54488","messageId":"20071001150050.GA27904@coredump.intra.peff.net","threadId":"10039","inReplyTo":"Pine.LNX.4.64.0710011114310.28395@racer.site","subject":"Re: [PATCH 1/4] Add a simple option parser for use by builtin-commit.c.","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2007-10-01T15:00:50Z","receivedAt":"2007-10-01T15:00:50Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Oct 01, 2007 at 11:14:48AM +0100, Johannes Schindelin wrote:\n\n> We _have_ to modify argv.  For example, \"git log master -p\" is perfectly \n> valid.\n\nWe're not going to support POSIXLY_CORRECT!? ;)\n\n-Peff\n"},{"id":"54495","messageId":"1191255975.25093.26.camel@hinata.boston.redhat.com","threadId":"10039","inReplyTo":"20070930131133.GA11209@diku.dk","subject":"Re: [PATCH 1/4] Add a simple option parser for use by builtin-commit.c.","fromName":"Kristian Høgsberg","fromEmail":"krh@redhat.com","sentAt":"2007-10-01T16:26:15Z","receivedAt":"2007-10-01T16:26:15Z","isPatch":true,"sender":{"key":"krh@redhat.com","avatar":"https://gravatar.com/avatar/763dee6f9594ac474f725b137a39565792928e583ddf59b32befc2907409027e?d=mp&s=160"},"body":"On Sun, 2007-09-30 at 15:11 +0200, Jonas Fonseca wrote:\n> Hello Kristian,\n> \n> I have some comments on your patch. Some of the \"improvement\" might have\n> to wait until after your builtin-commit changes hits git.git. However,\n> if we could agree on some of the general changes, I could start porting\n> other of the main porcelain commands to use the option parser without\n> depending on the state of the remaining builtin-commit series.\n\nHi Jonas,\n\nThat's sounds like a good plan.  In fact, in you want to update the\npatch with your changes (they all sound good) and start porting over\nsome of the other builtins feel free.  I don't have much time follow up\non these comments right now, but I will get to it eventually - unless\nyou beat me to it of course ;)  I will update builtin-commit.c to work\nwith whatever changes you introduce once I get around to updating that\npatch.\n\n> Kristian Høgsberg <krh@redhat.com> wrote Thu, Sep 27, 2007:\n> > Signed-off-by: Kristian Høgsberg <krh@redhat.com>\n> > ---\n> >  Makefile        |    2 +-\n> >  parse-options.c |   74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++\n> >  parse-options.h |   29 +++++++++++++++++++++\n> >  3 files changed, 104 insertions(+), 1 deletions(-)\n> >  create mode 100644 parse-options.c\n> >  create mode 100644 parse-options.h\n> > \n> > diff --git a/Makefile b/Makefile\n> > index 62bdac6..d90e959 100644\n> > --- a/Makefile\n> > +++ b/Makefile\n> > @@ -310,7 +310,7 @@ LIB_OBJS = \\\n> >  \talloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \\\n> >  \tcolor.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \\\n> >  \tconvert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \\\n> > -\ttransport.o bundle.o\n> > +\ttransport.o bundle.o parse-options.o\n> >  \n> >  BUILTIN_OBJS = \\\n> >  \tbuiltin-add.o \\\n> > diff --git a/parse-options.c b/parse-options.c\n> > new file mode 100644\n> > index 0000000..2fb30cd\n> > --- /dev/null\n> > +++ b/parse-options.c\n> > @@ -0,0 +1,74 @@\n> > +#include \"git-compat-util.h\"\n> > +#include \"parse-options.h\"\n> > +\n> > +int parse_options(const char ***argv,\n> > +\t\t  struct option *options, int count,\n> > +\t\t  const char *usage_string)\n> > +{\n> > +\tconst char *value, *eq;\n> > +\tint i;\n> > +\n> > +\tif (**argv == NULL)\n> > +\t\treturn 0;\n> > +\tif ((**argv)[0] != '-')\n> > +\t\treturn 0;\n> > +\tif (!strcmp(**argv, \"--\"))\n> > +\t\treturn 0;\n> \n> I don't know if this is a bug, but you do not remove \"--\" from argv,\n> which is later (in the patch that adds builtin-commit.c) passed to\n> add_files_to_cache and then get_pathspec where it is not removed or\n> detected either.\n\nThat's an oversight, good catch.\n\n> > +\n> > +\tvalue = NULL;\n> > +\tfor (i = 0; i < count; i++) {\n> > +\t\tif ((**argv)[1] == '-') {\n> > +\t\t\tif (!prefixcmp(options[i].long_name, **argv + 2)) {\n> > +\t\t\t\tif (options[i].type != OPTION_BOOLEAN)\n> > +\t\t\t\t\tvalue = *++(*argv);\n> > +\t\t\t\tgoto match;\n> > +\t\t\t}\n> > +\n> > +\t\t\teq = strchr(**argv + 2, '=');\n> > +\t\t\tif (eq && options[i].type != OPTION_BOOLEAN &&\n> > +\t\t\t    !strncmp(**argv + 2,\n> > +\t\t\t\t     options[i].long_name, eq - **argv - 2)) {\n> > +\t\t\t\tvalue = eq + 1;\n> > +\t\t\t\tgoto match;\n> > +\t\t\t}\n> > +\t\t}\n> > +\n> > +\t\tif ((**argv)[1] == options[i].short_name) {\n> > +\t\t\tif ((**argv)[2] == '\\0') {\n> > +\t\t\t\tif (options[i].type != OPTION_BOOLEAN)\n> > +\t\t\t\t\tvalue = *++(*argv);\n> > +\t\t\t\tgoto match;\n> > +\t\t\t}\n> > +\n> > +\t\t\tif (options[i].type != OPTION_BOOLEAN) {\n> > +\t\t\t\tvalue = **argv + 2;\n> > +\t\t\t\tgoto match;\n> > +\t\t\t}\n> > +\t\t}\n> > +\t}\n> > +\n> > +\tusage(usage_string);\n> > +\n> > + match:\n> \n> I think the goto can be avoided by simply breaking out of the above loop\n> when an option has been found and add ...\n> \n> > +\tswitch (options[i].type) {\n> \tcase OPTION_LAST\n> \t\tusage(usage_string);\n> \t\tbreak;\n> \n> > +\tcase OPTION_BOOLEAN:\n> > +\t\t*(int *)options[i].value = 1;\n> > +\t\tbreak;\n\nYeah, that looks nicer.  I think the goto structure is a leftover from\nwhen there was more logic between the loop and the switch.  It's good to\nget some fresh eyes on this code.  Junio didn't like the OPTION_LAST\nterminator, so I changed the interface to take a count.  We can do\n\n\tif (i == count)\n\t\tusage();\n\telse switch (options[i].type) {\n\t\t...\n\t}\n\nof course.\n\n> I've been looking at builtin-blame.c which IMO has some of the most\n> obscure option parsing and maybe this can be changed to increment in\n> order to support stuff like changing the meaning by passing the same arg\n> multiple times (e.g. \"-C -C -C\") better.\n\nThat would be fine, yes.\n\n> Blame option parsing also sports (enum) flags being masked together,\n> this can of course be rewritten to a boolean option followed by\n> masking when parse_options is done (to keep it sane).\n\nYup.\n\n> > +\tcase OPTION_STRING:\n> > +\t\tif (value == NULL)\n> > +\t\t\tdie(\"option %s requires a value.\", (*argv)[-1]);\n> \n> Maybe change this ...\n> \n> > +\t\t*(const char **)options[i].value = value;\n> > +\t\tbreak;\n> > +\tcase OPTION_INTEGER:\n> > +\t\tif (value == NULL)\n> > +\t\t\tdie(\"option %s requires a value.\", (*argv)[-1]);\n> \n> ... and this to:\n> \n> \t\tif (!value) {\n> \t\t\terror(\"option %s requires a value.\", (*argv)[-1]);\n> \t\t\tusage(usage_string);\n> \t\t}\n\nSure, that's friendlier.\n\n> > +\t\t*(int *)options[i].value = atoi(value);\n> > +\t\tbreak;\n> > +\tdefault:\n> > +\t\tassert(0);\n> > +\t}\n> > +\n> > +\t(*argv)++;\n> > +\n> > +\treturn 1;\n> > +}\n> > diff --git a/parse-options.h b/parse-options.h\n> > new file mode 100644\n> > index 0000000..39399c3\n> > --- /dev/null\n> > +++ b/parse-options.h\n> > @@ -0,0 +1,29 @@\n> > +#ifndef PARSE_OPTIONS_H\n> > +#define PARSE_OPTIONS_H\n> > +\n> > +enum option_type {\n> > +    OPTION_BOOLEAN,\n> > +    OPTION_STRING,\n> > +    OPTION_INTEGER,\n> > +    OPTION_LAST,\n> > +};\n> > +\n> > +struct option {\n> > +    enum option_type type;\n> > +    const char *long_name;\n> > +    char short_name;\n> > +    void *value;\n> > +};\n> \n> Space vs tab indentation.\n> \n> One of the last things I miss from Cogito is the nice abbreviated help\n> messages that was available via '-h'. I don't know if it would be\n> acceptable (at least for the main porcelain commands) to put this\n> functionality into the option parser by adding a \"description\" member to\n> struct option and have parse_options print a nice:\n> \n> \t<error message if any>\n> \t<usage string>\n> \t<option summary>\n> \n> on failure, or, if that is regarded as too verbose, simply when -h is\n> detected.\n\nYeah, that might be nice.  We can add it in a follow-on patch, if the\nlist agrees that it's a good thing, I guess.\n\n> > +\n> > +/* Parse the given options against the list of known options.  The\n> > + * order of the option structs matters, in that ambiguous\n> > + * abbreviations (eg, --in could be short for --include or\n> > + * --interactive) are matched by the first option that share the\n> > + * prefix.\n> > + */\n> \n> This prefix aware option parsing has not been ported over to the other\n> builtins when they were lifted from shell code. It might be nice to have\n> of course. Is it really needed?\n\nI don't ever use it myself and I think it's more confusing than helpful.\nI only added it to avoid introducing behavior changes in the port.  I\ndon't have strong feelings either way.\n\n> > +\n> > +extern int parse_options(const char ***argv,\n> > +\t\t\t struct option *options, int count,\n> > +\t\t\t const char *usage_string);\n> \n> I think the interface could be improved a bit. For example, it doesn't\n> need to count argument since the last entry in the options array is\n> OPTION_LAST and thus the size can be detected that way.\n\nHehe, yeah, that's how I did it first.  I don't have a strong preference\nfor terminator elements vs. ARRAY_SIZE(), but Junio prefers the\nARRAY_SIZE() approach, I guess.  At this point I'm just trying the get\nthe patches upstream...\n\n> Also, I think for this to be more usable for other built-in programs it\n> shouldn't modify argv, but instead take both argc and argv (so we don't\n> need to have code like \"*++(*argv)\" ;), parse _all_ options in one go,\n> and return the index (of argv) for any remaining options.\n> \n> Then the following:\n> \n> \twhile (parse_options(argv, commit_options, ARRAY_SIZE(commit_options),\n> \t\tbuiltin_commit_usage))\n> \t\t;\n> \n> becomes:\n> \n> \tint i;\n> \t...\n> \ti = parse_options(argc, argv, commit_options, builtin_commit_usage);\n> \n> This fits better with how option parsing is currently done. Take\n> builtin-add for example:\n> \n> \tfor (i = 1 ; i < argc ; i++) {\n> \t\tconst char *arg = argv[i];\n> \t\t/* ... */\n> \t}\n> \tif (argc <= i)\n> \t\tusage(builtin_rm_usage);\n\nNo objections, I think that looks better too.\n\n> [ BTW, blame option parsing actually wants to know if \"--\" has been seen,\n>   but I think that can be worked around by simply checking argv[i - 1]\n>   after calling the option parser. ]\n> \n> > +\n> > +#endif\n> \n> OK, I will stop these ramblings here. I hope the fact that I read your\n> patch both back and forth and added comments in the process didn't make\n> it too confusing.\n\nHeh, that's what I do myself :)\n\nthanks for the comments,\nKristian\n"},{"id":"54499","messageId":"Pine.LNX.4.64.0710011909291.28395@racer.site","threadId":"10039","inReplyTo":"1191255975.25093.26.camel@hinata.boston.redhat.com","subject":"Re: [PATCH 1/4] Add a simple option parser for use by builtin-commit.c.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-10-01T18:13:27Z","receivedAt":"2007-10-01T18:13:27Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 1 Oct 2007, Kristian H?gsberg wrote:\n\n> On Sun, 2007-09-30 at 15:11 +0200, Jonas Fonseca wrote:\n>\n> > One of the last things I miss from Cogito is the nice abbreviated help \n> > messages that was available via '-h'. I don't know if it would be \n> > acceptable (at least for the main porcelain commands) to put this \n> > functionality into the option parser by adding a \"description\" member \n> > to struct option and have parse_options print a nice:\n> > \n> > \t<error message if any>\n> > \t<usage string>\n> > \t<option summary>\n> > \n> > on failure, or, if that is regarded as too verbose, simply when -h is \n> > detected.\n> \n> Yeah, that might be nice.  We can add it in a follow-on patch, if the \n> list agrees that it's a good thing, I guess.\n\nThat's a good idea; I would put the usage string there, too.\n\n> > > +\n> > > +/* Parse the given options against the list of known options.  The\n> > > + * order of the option structs matters, in that ambiguous\n> > > + * abbreviations (eg, --in could be short for --include or\n> > > + * --interactive) are matched by the first option that share the\n> > > + * prefix.\n> > > + */\n> > \n> > This prefix aware option parsing has not been ported over to the other \n> > builtins when they were lifted from shell code. It might be nice to \n> > have of course. Is it really needed?\n> \n> I don't ever use it myself and I think it's more confusing than helpful. \n> I only added it to avoid introducing behavior changes in the port.  I \n> don't have strong feelings either way.\n\nIt might be convenient, but I think that it is really more confusing than \nhelpful, especially with options that share a prefix.  Besides, we have \ngood completion for bash now (and I hear that this \"zsh\" thing also has \nquite good completion), I recommend <TAB> over prefix DWIMery.\n\n> > > +\n> > > +extern int parse_options(const char ***argv,\n> > > +\t\t\t struct option *options, int count,\n> > > +\t\t\t const char *usage_string);\n> > \n> > I think the interface could be improved a bit. For example, it doesn't \n> > need to count argument since the last entry in the options array is \n> > OPTION_LAST and thus the size can be detected that way.\n> \n> Hehe, yeah, that's how I did it first.  I don't have a strong preference \n> for terminator elements vs. ARRAY_SIZE(), but Junio prefers the \n> ARRAY_SIZE() approach, I guess.  At this point I'm just trying the get \n> the patches upstream...\n\nFWIW I like the ARRAY_SIZE() approach better, too, since it is less error \nprone.\n\nCiao,\nDscho\n"},{"id":"54749","messageId":"20071003201129.GB25856@diku.dk","threadId":"10039","inReplyTo":"Pine.LNX.4.64.0710011909291.28395@racer.site","subject":"Re: [PATCH 1/4] Add a simple option parser for use by builtin-commit.c.","fromName":"Jonas Fonseca","fromEmail":"fonseca@diku.dk","sentAt":"2007-10-03T20:11:29Z","receivedAt":"2007-10-03T20:11:29Z","isPatch":true,"sender":{"key":"fonseca@diku.dk","avatar":"https://gravatar.com/avatar/f82f3ad698717c51873b020c750a92438c820a24056dc39fe4d07baa10a92264?d=mp&s=160"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote Mon, Oct 01, 2007:\n> On Mon, 1 Oct 2007, Kristian H?gsberg wrote:\n> > On Sun, 2007-09-30 at 15:11 +0200, Jonas Fonseca wrote:\n> > > > +\n> > > > +extern int parse_options(const char ***argv,\n> > > > +\t\t\t struct option *options, int count,\n> > > > +\t\t\t const char *usage_string);\n> > > \n> > > I think the interface could be improved a bit. For example, it doesn't \n> > > need to count argument since the last entry in the options array is \n> > > OPTION_LAST and thus the size can be detected that way.\n> > \n> > Hehe, yeah, that's how I did it first.  I don't have a strong preference \n> > for terminator elements vs. ARRAY_SIZE(), but Junio prefers the \n> > ARRAY_SIZE() approach, I guess.  At this point I'm just trying the get \n> > the patches upstream...\n> \n> FWIW I like the ARRAY_SIZE() approach better, too, since it is less error \n> prone.\n\nOK, I must have missed that comment. Good point.\n\nThanks for the comments both of you. It's great to have something to\nwork from. However, I also fear it will also require that some extra\nflags or information is added to the option information to make it more\ngenerally usable. But I guess that is easier to discuss in the context\nof a patch.\n\n-- \nJonas Fonseca\n"},{"id":"54768","messageId":"1191448438.7134.8.camel@hinata.boston.redhat.com","threadId":"10039","inReplyTo":"20071003201129.GB25856@diku.dk","subject":"Re: [PATCH 1/4] Add a simple option parser for use by builtin-commit.c.","fromName":"Kristian Høgsberg","fromEmail":"krh@redhat.com","sentAt":"2007-10-03T21:53:58Z","receivedAt":"2007-10-03T21:53:58Z","isPatch":true,"sender":{"key":"krh@redhat.com","avatar":"https://gravatar.com/avatar/763dee6f9594ac474f725b137a39565792928e583ddf59b32befc2907409027e?d=mp&s=160"},"body":"\nOn Wed, 2007-10-03 at 22:11 +0200, Jonas Fonseca wrote:\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote Mon, Oct 01, 2007:\n> > On Mon, 1 Oct 2007, Kristian H?gsberg wrote:\n> > > On Sun, 2007-09-30 at 15:11 +0200, Jonas Fonseca wrote:\n> > > > > +\n> > > > > +extern int parse_options(const char ***argv,\n> > > > > +\t\t\t struct option *options, int count,\n> > > > > +\t\t\t const char *usage_string);\n> > > > \n> > > > I think the interface could be improved a bit. For example, it doesn't \n> > > > need to count argument since the last entry in the options array is \n> > > > OPTION_LAST and thus the size can be detected that way.\n> > > \n> > > Hehe, yeah, that's how I did it first.  I don't have a strong preference \n> > > for terminator elements vs. ARRAY_SIZE(), but Junio prefers the \n> > > ARRAY_SIZE() approach, I guess.  At this point I'm just trying the get \n> > > the patches upstream...\n> > \n> > FWIW I like the ARRAY_SIZE() approach better, too, since it is less error \n> > prone.\n> \n> OK, I must have missed that comment. Good point.\n> \n> Thanks for the comments both of you. It's great to have something to\n> work from. However, I also fear it will also require that some extra\n> flags or information is added to the option information to make it more\n> generally usable. But I guess that is easier to discuss in the context\n> of a patch.\n\nI just sent an updated option parser patch that incorporates your\nsuggestions along with a patch that ports builtin-add.c to use it.  I\nlooked briefly into porting over a few other builtins, but you're right,\nwe need a couple of extra features for this to be really worthwile:\n\n      * OPTION_SET_FLAG: sets the bit (we need to add a bit value that\n        the option parser can or in)\n      * OPTION_CLEAR_FLAG: clear the bit\n      * OPTION_ADD: adds the value to the destination integer\n      * OPTION_CALLBACK: calls the given function when the option is\n        matched.  We'll need this for builtin-grep that has positional\n        args such as --and etc.\n\nAlso, the option parser should probably verify that a string option\nisn't passed more than once.  Bundling of single letter options would be\nnice to add.  But the patch I just sent out should be a good start, and\nit lets us move forward with builtin-commit.c.\n\ncheers,\nKristian\n"},{"id":"54769","messageId":"1191449034.7134.19.camel@hinata.boston.redhat.com","threadId":"10039","inReplyTo":"7v7imcv5op.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 2/4] This exports the update() function from builtin-add.c as","fromName":"Kristian Høgsberg","fromEmail":"krh@redhat.com","sentAt":"2007-10-03T22:03:54Z","receivedAt":"2007-10-03T22:03:54Z","isPatch":true,"sender":{"key":"krh@redhat.com","avatar":"https://gravatar.com/avatar/763dee6f9594ac474f725b137a39565792928e583ddf59b32befc2907409027e?d=mp&s=160"},"body":"On Thu, 2007-09-27 at 02:05 -0700, Junio C Hamano wrote:\n...\n> I think the right organization for the \"builtin-commit\" series\n> should be:\n> \n>  * merge strbuf topic in kh/commit topic, in order to get the\n>    stripspace updates and strbuf_read_file();\n> \n>  * add--interactive entry point change (respin the one from the\n>    old series);\n> \n>  * rename update() to add_files_to_cache() and export (respin\n>    this [2/4] with a better commit message);\n> \n>  * create a separate rerere() function and export (respin part\n>    of old series, with proper refactoring);\n> \n>    I am not happy with builtin-foo.c calling into something from\n>    builtin-bar.c, though.  We probably would want to move\n>    rerere() and add_files_to_cache() somewhere else.\n> \n>  * move launch_editor() and stripspace() to create editor.c (new\n>    [4/4]);\n> \n>  * add option parser in parse-options.[ch] (new [1/4]);\n> \n>  * finally, create builtin-commit that uses the groundwork laid\n>    out above (new [3/4]).\n> \n> I ended up doing the above up to the rerere() one myself, but\n> haven't done the rest.\n\n>From what I see in next today, it looks like we're just missing the\nparse-options patch and builtin-commit patches.  I resent a better\nversion of parse-options and a patch that ports builtin-add.c to the\noption parser.  To use the option parser in more places, we'll probably\nhave to extend it a bit, but the patch is a good start.  Let's get that\nin shape and into next and then I'll send an updated builtin-commit\npatch.\n\nthanks,\nKristian\n"}]}