{"thread":{"id":"10568","subject":"[PATCH] Implement git commit as a builtin command.","startedAt":"2007-11-01T19:09:23Z","lastAt":"2007-11-02T15:31:01Z","messageCount":8,"participants":["Kristian Høgsberg","Jakub Narebski","Junio C Hamano","Karl Hasselström"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"57848","messageId":"1193944163-22892-1-git-send-email-krh@redhat.com","threadId":"10568","inReplyTo":null,"subject":"[PATCH] Implement git commit as a builtin command.","fromName":"Kristian Høgsberg","fromEmail":"krh@redhat.com","sentAt":"2007-11-01T19:09:23Z","receivedAt":"2007-11-01T19:09:23Z","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\nHere's the builtin-commit patch again, this time updated to build\nagainst the in-tree strbuf and option parser.  Now that Pierre has\ndone all the work of getting those pieces upstream, I notice that the\nC version ends up 20 lines shorter than the shell script :)\n\nThere's a couple of changes to the test suite at the end, which\nremoves some EDITOR=: VISUAL=: hard-coding in a few tests and then\nsets the default editor to /bin/true.  This is to address the problem\nthat the C version of launch editor doesn't support any kind of shell\nqouting or built-ins.  Should we just use system(3) here?\n\ncheers,\nKristian\n\n Makefile                       |    9 +-\n builtin-commit.c               |  608 ++++++++++++++++++++++++++++++++++++++\n builtin-tag.c                  |    2 +-\n builtin.h                      |    3 +-\n contrib/examples/git-commit.sh |  628 ++++++++++++++++++++++++++++++++++++++++\n git-commit.sh                  |  628 ----------------------------------------\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, 1253 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 042f79e..689eb33 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -209,7 +209,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@@ -256,7 +256,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@@ -326,6 +326,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@@ -364,7 +365,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@@ -830,9 +830,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..56c7427\n--- /dev/null\n+++ b/builtin-commit.c\n@@ -0,0 +1,608 @@\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 <sys/types.h>\n+#include <sys/stat.h>\n+#include <unistd.h>\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 * const builtin_commit_usage[] = {\n+\t\"git-commit [options] [--] <filepattern>...\",\n+\tNULL\n+};\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, amend, signoff;\n+static int quiet, verbose, untracked_files, no_verify;\n+\n+static int no_edit, initial_commit, in_merge;\n+const char *only_include_assumed;\n+\n+static struct option builtin_commit_options[] = {\n+\tOPT__QUIET(&quiet),\n+\tOPT__VERBOSE(&verbose),\n+\tOPT_GROUP(\"Commit message options\"),\n+\n+\tOPT_STRING('F', \"file\", &logfile, \"FILE\", \"read log from file\"),\n+\tOPT_STRING(0, \"author\", &force_author, \"AUTHOR\", \"override author for commit\"),\n+\tOPT_STRING('m', \"message\", &message, \"MESSAGE\", \"specify commit message\"),\n+\tOPT_STRING('c', \"reedit-message\", &edit_message, \"COMMIT\", \"reuse and edit message from specified commit \"),\n+\tOPT_STRING('C', \"reuse-message\", &use_message, \"COMMIT\", \"reuse message from specified commit\"),\n+\tOPT_BOOLEAN('s', \"signoff\", &signoff, \"add Signed-off-by: header\"),\n+\tOPT_STRING('t', \"template\", &template_file, \"FILE\", \"use specified template file\"),\n+\tOPT_BOOLEAN('e', \"edit\", &edit_flag, \"force edit of commit\"),\n+\n+\tOPT_GROUP(\"Commit contents options\"),\n+\tOPT_BOOLEAN('a', \"all\", &all, \"commit all changed files\"),\n+\tOPT_BOOLEAN('i', \"include\", &also, \"add specified files to index for commit\"),\n+\tOPT_BOOLEAN(0, \"interactive\", &interactive, \"interactively add files\"),\n+\tOPT_BOOLEAN('o', \"only\", &only, \"\"),\n+\tOPT_BOOLEAN('n', \"no-verify\", &no_verify, \"bypass pre-commit hook\"),\n+\tOPT_BOOLEAN(0, \"amend\", &amend, \"amend previous commit\"),\n+\tOPT_BOOLEAN(0, \"untracked-files\", &untracked_files, \"show all untracked files\"),\n+\n+\tOPT_END()\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(verbose, NULL, 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(verbose, 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/* update the user index file */\n+\tadd_files_to_cache(verbose, 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(verbose, 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) < 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) < 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) < 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) < 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+\t\t\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) > 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 int parse_and_validate_options(int argc, const char *argv[])\n+{\n+\tint f = 0;\n+\n+\targc = parse_options(argc, argv, builtin_commit_options,\n+\t\t\t     builtin_commit_usage, 0);\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 && argc > 0)\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+\treturn argc;\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+\targc = parse_and_validate_options(argc, 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+\t\t\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+\targc = parse_and_validate_options(argc, argv);\n+\n+\tindex_file = prepare_index(argv, prefix);\n+\n+\tif (!no_verify && 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) < 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+\t\t       \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 66e5a58..e6bae69 100644\n--- a/builtin-tag.c\n+++ b/builtin-tag.c\n@@ -17,7 +17,7 @@ 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;\ndiff --git a/builtin.h b/builtin.h\nindex 9a6213a..0f8456c 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -24,6 +24,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@@ -68,10 +69,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..fcb8443\n--- /dev/null\n+++ b/contrib/examples/git-commit.sh\n@@ -0,0 +1,628 @@\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+TMP_INDEX=\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 fcb8443..0000000\n--- a/git-commit.sh\n+++ /dev/null\n@@ -1,628 +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-TMP_INDEX=\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 4e10581..8522095 100644\n--- a/git.c\n+++ b/git.c\n@@ -298,6 +298,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@@ -346,10 +347,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 9b9e861..9720826 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, size_t hint);\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 714de6e..51cbcc1 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.3.4.206.g58ba4\n"},{"id":"57851","messageId":"fgdc3m$miq$1@ger.gmane.org","threadId":"10568","inReplyTo":"1193944163-22892-1-git-send-email-krh@redhat.com","subject":"Re: [PATCH] Implement git commit as a builtin command.","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2007-11-01T20:14:45Z","receivedAt":"2007-11-01T20:14:45Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Kristian H?gsberg wrote:\n\n> Move git-commit.sh to contrib/examples.\n\nJust a note: you might want to use \"git format-patch -M\".\n\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"57855","messageId":"7vzlxxaeua.fsf@gitster.siamese.dyndns.org","threadId":"10568","inReplyTo":"1193944163-22892-1-git-send-email-krh@redhat.com","subject":"Re: [PATCH] Implement git commit as a builtin command.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-11-01T20:30:21Z","receivedAt":"2007-11-01T20:30:21Z","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> Move git-commit.sh to contrib/examples.\n>\n> Signed-off-by: Kristian Høgsberg <krh@redhat.com>\n> ---\n>\n> Here's the builtin-commit patch again, this time updated to build\n> against the in-tree strbuf and option parser.  Now that Pierre has\n> done all the work of getting those pieces upstream, I notice that the\n> C version ends up 20 lines shorter than the shell script :)\n>\n> There's a couple of changes to the test suite at the end, which\n> removes some EDITOR=: VISUAL=: hard-coding in a few tests and then\n> sets the default editor to /bin/true.  This is to address the problem\n> that the C version of launch editor doesn't support any kind of shell\n> qouting or built-ins.  Should we just use system(3) here?\n\nI do not care much about _how_ it is done, but one of the most\noften typed command in my .history is\n\n\tEDITOR=: git commit\n\nI'd be somewhat unhappy to be forced to retrain my fingers ;-)\n"},{"id":"57900","messageId":"7vr6j97ce6.fsf@gitster.siamese.dyndns.org","threadId":"10568","inReplyTo":"1193944163-22892-1-git-send-email-krh@redhat.com","subject":"Re: [PATCH] Implement git commit as a builtin command.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-11-01T23:51:29Z","receivedAt":"2007-11-01T23:51:29Z","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> @@ -364,7 +365,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\nI did not read in the commit log that runstatus is gone...\n\n> diff --git a/builtin-commit.c b/builtin-commit.c\n> new file mode 100644\n> index 0000000..56c7427\n> --- /dev/null\n> +++ b/builtin-commit.c\n> @@ -0,0 +1,608 @@\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 <sys/types.h>\n> +#include <sys/stat.h>\n> +#include <unistd.h>\n> +\n> +#include \"cache.h\"\n\n\nThe system header files on some systems have a nasty habit of\nchanging the behaviour depending on the order they are included.\nSince 85023577a8f4b540aa64aa37f6f44578c0c305a3 (simplify\ninclusion of system header files) in late 2006, we have a few\nrules for system-header inclusion to avoid the portability\nissues:\n\n (1) sources under compat/, platform sha-1 implementations, and\n     xdelta code are exempt from the following rules;\n\n (2) the first #include must be \"git-compat-util.h\" or one of\n     our own header file that includes it first (e.g. config.h,\n     builtin.h, pkt-line.h);\n\n (3) system headers that are included in \"git-compat-util.h\"\n     need not be included in individual C source files.\n\n (4) \"git-compat-util.h\" does not have to include subsystem\n     specific header files (e.g. expat.h).\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\nDoesn't this break:\n\n    $ git commit --amend --author='A U Thor <author@example.com>'\n\nto fix a misattribution?\n\n> +static int parse_and_validate_options(int argc, const char *argv[])\n> +{\n> ...\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\nA few issues.\n\n * When use_message is set because of --amend that wanted to\n   amend a commit message that was recorded in a corrupt\n   encoding, and iconv() in reencode_string() fails, saying \"I\n   cannot convert that completely\", most of the message can\n   still be salvageable.  This part should allow looser\n   reencoding if the message will be eyeballed and edited by the\n   user.\n\n * We would want to avoid reencoding when !strcmp(out_enc, enc).\n   Both builtin-mailinfo.c and commit.c skip the call to the\n   function at the calling site, but it might make sense to\n   teach reencode_string() about such an optimization.\n"},{"id":"57947","messageId":"20071102092720.GA30468@diana.vm.bytemark.co.uk","threadId":"10568","inReplyTo":"fgdc3m$miq$1@ger.gmane.org","subject":"Re: [PATCH] Implement git commit as a builtin command.","fromName":"Karl Hasselström","fromEmail":"kha@treskal.com","sentAt":"2007-11-02T09:27:20Z","receivedAt":"2007-11-02T09:27:20Z","isPatch":true,"sender":{"key":"kha@treskal.com","avatar":"https://gravatar.com/avatar/f0120c734b5279b345075a28521e1ac66acb20c9913ffe9bf6ae97e53f7f3f13?d=mp&s=160"},"body":"On 2007-11-01 21:14:45 +0100, Jakub Narebski wrote:\n\n> Kristian H?gsberg wrote:\n>\n> > Move git-commit.sh to contrib/examples.\n>\n> Just a note: you might want to use \"git format-patch -M\".\n\nI wonder if it would be such a bad idea to make this the default. It\nseems like _everyone_ who should be using it forget it. And the number\nof patches made for human or git consumption is probably vastly larger\nthan the number of patches made for consumption by other tools.\n\n-- \nKarl Hasselström, kha@treskal.com\n      www.treskal.com/kalle\n"},{"id":"57949","messageId":"7vsl3p3s48.fsf@gitster.siamese.dyndns.org","threadId":"10568","inReplyTo":"20071102092720.GA30468@diana.vm.bytemark.co.uk","subject":"Re: [PATCH] Implement git commit as a builtin command.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-11-02T09:37:43Z","receivedAt":"2007-11-02T09:37:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Karl Hasselström <kha@treskal.com> writes:\n\n> On 2007-11-01 21:14:45 +0100, Jakub Narebski wrote:\n>\n>> Kristian H?gsberg wrote:\n>>\n>> > Move git-commit.sh to contrib/examples.\n>>\n>> Just a note: you might want to use \"git format-patch -M\".\n>\n> I wonder if it would be such a bad idea to make this the default. It\n> seems like _everyone_ who should be using it forget it. And the number\n> of patches made for human or git consumption is probably vastly larger\n> than the number of patches made for consumption by other tools.\n\nOn this mailing list, we can pretty much assume that renaming\ndiff can be applied by people.  But outside world is a different\nstory.\n"},{"id":"57953","messageId":"20071102094249.GB30468@diana.vm.bytemark.co.uk","threadId":"10568","inReplyTo":"7vsl3p3s48.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] Implement git commit as a builtin command.","fromName":"Karl Hasselström","fromEmail":"kha@treskal.com","sentAt":"2007-11-02T09:42:49Z","receivedAt":"2007-11-02T09:42:49Z","isPatch":true,"sender":{"key":"kha@treskal.com","avatar":"https://gravatar.com/avatar/f0120c734b5279b345075a28521e1ac66acb20c9913ffe9bf6ae97e53f7f3f13?d=mp&s=160"},"body":"On 2007-11-02 02:37:43 -0700, Junio C Hamano wrote:\n\n> Karl Hasselström <kha@treskal.com> writes:\n>\n> > I wonder if it would be such a bad idea to make this the default.\n> > It seems like _everyone_ who should be using it forget it. And the\n> > number of patches made for human or git consumption is probably\n> > vastly larger than the number of patches made for consumption by\n> > other tools.\n>\n> On this mailing list, we can pretty much assume that renaming diff\n> can be applied by people. But outside world is a different story.\n\nSo I guess a prerequisite for this is to get git patch format support\ninto GNU patch, then? :-)\n\n-- \nKarl Hasselström, kha@treskal.com\n      www.treskal.com/kalle\n"},{"id":"57995","messageId":"1194017461.25288.13.camel@hinata.boston.redhat.com","threadId":"10568","inReplyTo":"7vr6j97ce6.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] Implement git commit as a builtin command.","fromName":"Kristian Høgsberg","fromEmail":"krh@redhat.com","sentAt":"2007-11-02T15:31:01Z","receivedAt":"2007-11-02T15:31:01Z","isPatch":true,"sender":{"key":"krh@redhat.com","avatar":"https://gravatar.com/avatar/763dee6f9594ac474f725b137a39565792928e583ddf59b32befc2907409027e?d=mp&s=160"},"body":"On Thu, 2007-11-01 at 16:51 -0700, Junio C Hamano wrote:\n\nHi Junio,\n\nThanks for the review (again).  I've split the patch up in a couple of\ntest suite patches (a patch to accept ':' as a no-op editor, fixing\nhard-coding of editor, and a test case for the commit --amend --author\ncase), exporting launch_editor() and a reworked version of the commit\npatch that addresses your comments.\n\n> Kristian Høgsberg <krh@redhat.com> writes:\n> \n> > @@ -364,7 +365,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> \n> I did not read in the commit log that runstatus is gone...\n\nTrue, that should be documented.  Added in the following patch.\n\n> > diff --git a/builtin-commit.c b/builtin-commit.c\n> > new file mode 100644\n> > index 0000000..56c7427\n> > --- /dev/null\n> > +++ b/builtin-commit.c\n> > @@ -0,0 +1,608 @@\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 <sys/types.h>\n> > +#include <sys/stat.h>\n> > +#include <unistd.h>\n> > +\n> > +#include \"cache.h\"\n> \n> \n> The system header files on some systems have a nasty habit of\n> changing the behaviour depending on the order they are included.\n> Since 85023577a8f4b540aa64aa37f6f44578c0c305a3 (simplify\n> inclusion of system header files) in late 2006, we have a few\n> rules for system-header inclusion to avoid the portability\n> issues:\n> \n>  (1) sources under compat/, platform sha-1 implementations, and\n>      xdelta code are exempt from the following rules;\n> \n>  (2) the first #include must be \"git-compat-util.h\" or one of\n>      our own header file that includes it first (e.g. config.h,\n>      builtin.h, pkt-line.h);\n> \n>  (3) system headers that are included in \"git-compat-util.h\"\n>      need not be included in individual C source files.\n> \n>  (4) \"git-compat-util.h\" does not have to include subsystem\n>      specific header files (e.g. expat.h).\n\nAh, yes, this has been pointed out to me before, sorry.  Patch updated.\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> Doesn't this break:\n> \n>     $ git commit --amend --author='A U Thor <author@example.com>'\n> \n> to fix a misattribution?\n\nDang, good catch.  I've fixed it by swapping the (use_message) and\n(force_author) clauses and the new patch adds a test case for this.\n\n> > +static int parse_and_validate_options(int argc, const char *argv[])\n> > +{\n> > ...\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> \n> A few issues.\n> \n>  * When use_message is set because of --amend that wanted to\n>    amend a commit message that was recorded in a corrupt\n>    encoding, and iconv() in reencode_string() fails, saying \"I\n>    cannot convert that completely\", most of the message can\n>    still be salvageable.  This part should allow looser\n>    reencoding if the message will be eyeballed and edited by the\n>    user.\n\nSo in this case we just want to copy the old message byte for byte,\nright?  That's what I have in the updated patch.\n\n>  * We would want to avoid reencoding when !strcmp(out_enc, enc).\n>    Both builtin-mailinfo.c and commit.c skip the call to the\n>    function at the calling site, but it might make sense to\n>    teach reencode_string() about such an optimization.\n\nRight.  I just added the call-site optimization for builtin-commit for\nnow, but I would expect iconv() to be smart in case input and output\nencodings are the same.\n\ncheers,\nKristian\n"}]}