{"thread":{"id":"9098","subject":"[PATCH] Implement git commit as a builtin.","startedAt":"2007-07-18T19:19:39Z","lastAt":"2007-07-20T14:45:24Z","messageCount":4,"participants":["Kristian Høgsberg","Nicolas Pitre","Carlos Rica"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"47764","messageId":"11847863792344-git-send-email-krh@redhat.com","threadId":"9098","inReplyTo":null,"subject":"[PATCH] Implement git commit as a builtin.","fromName":"Kristian Høgsberg","fromEmail":"krh@redhat.com","sentAt":"2007-07-18T19:19:39Z","receivedAt":"2007-07-18T19:19:39Z","isPatch":true,"sender":{"key":"krh@redhat.com","avatar":"https://gravatar.com/avatar/763dee6f9594ac474f725b137a39565792928e583ddf59b32befc2907409027e?d=mp&s=160"},"body":"---\nHere's another update on the work in progress.  At this point, the C\nversion is almost complete, there's only a few issues left (look for\nFIXME in builtin-commit.c).  I've added a commit test case which should\nbe split out in a patch on its own, but the good news is that it\nsuccessfully exercises most of the command line options and the C version\npasses.\n\nMy plan for the remainder of the work is still to wrap up the last few\npieces of functionality and then start taking this big patch apart in a\nnumber of more manageable pieces.  However, the bulk of this work will\nstill be a big patch that removes git-commit.sh and adds builtin-commit\nin one swoop.\n\nKristian\n\n\n Makefile              |    9 +-\n builtin-add.c         |   14 +-\n builtin-commit-tree.c |  120 +++++---\n builtin-commit.c      |  746 +++++++++++++++++++++++++++++++++++++++++++++++++\n builtin.h             |    3 +-\n cache.h               |    3 +-\n color.c               |   18 +-\n color.h               |    4 +-\n commit.h              |    9 +\n git-commit.sh         |  658 -------------------------------------------\n git.c                 |    3 +-\n mktag.c               |    8 +-\n sha1_file.c           |   44 ++-\n t/t7800-commit.sh     |  126 +++++++++\n wt-status.c           |   86 +++---\n wt-status.h           |    4 +\n 16 files changed, 1066 insertions(+), 789 deletions(-)\n create mode 100644 builtin-commit.c\n delete mode 100755 git-commit.sh\n create mode 100644 t/t7800-commit.sh\n\ndiff --git a/Makefile b/Makefile\nindex 0f75955..967d5a5 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -198,7 +198,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-fetch.sh \\\n \tgit-ls-remote.sh \\\n \tgit-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \\\n@@ -257,7 +257,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@@ -332,6 +332,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@@ -367,7 +368,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@@ -791,9 +791,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 -f $@ $@+ && \\\n \tsed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \\\ndiff --git a/builtin-add.c b/builtin-add.c\nindex 1591171..bcd796d 100644\n--- a/builtin-add.c\n+++ b/builtin-add.c\n@@ -8,6 +8,7 @@\n #include \"dir.h\"\n #include \"exec_cmd.h\"\n #include \"cache-tree.h\"\n+#include \"run-command.h\"\n #include \"diff.h\"\n #include \"diffcore.h\"\n #include \"commit.h\"\n@@ -148,6 +149,13 @@ static int git_add_config(const char *var, const char *value)\n \treturn git_default_config(var, value);\n }\n \n+int interactive_add(void)\n+{\n+\tconst char *argv[2] = { \"add--interactive\", NULL };\n+\n+\treturn run_command_v_opt(argv, RUN_GIT_CMD);\n+}\n+\n static struct lock_file lock_file;\n \n static const char ignore_warning[] =\n@@ -167,11 +175,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)\n \t\t\tadd_interactive++;\n \t}\n \tif (add_interactive) {\n-\t\tconst char *args[] = { \"add--interactive\", NULL };\n-\n-\t\tif (add_interactive != 1 || argc != 2)\n+\t\tif (argc != 2)\n \t\t\tdie(\"add --interactive does not take any parameters\");\n-\t\texecv_git_cmd(args);\n+\t\tinteractive_add();\n \t\texit(1);\n \t}\n \ndiff --git a/builtin-commit-tree.c b/builtin-commit-tree.c\nindex ccbcbe3..bb20470 100644\n--- a/builtin-commit-tree.c\n+++ b/builtin-commit-tree.c\n@@ -20,17 +20,11 @@ static void init_buffer(char **bufp, unsigned int *sizep)\n \t*sizep = 0;\n }\n \n-static void add_buffer(char **bufp, unsigned int *sizep, const char *fmt, ...)\n+static void add_chunk(char **bufp, unsigned int *sizep, const char *data, int len)\n {\n-\tchar one_line[2048];\n-\tva_list args;\n-\tint len;\n \tunsigned long alloc, size, newsize;\n \tchar *buf;\n \n-\tva_start(args, fmt);\n-\tlen = vsnprintf(one_line, sizeof(one_line), fmt, args);\n-\tva_end(args);\n \tsize = *sizep;\n \tnewsize = size + len + 1;\n \talloc = (size + 32767) & ~32767;\n@@ -41,7 +35,19 @@ static void add_buffer(char **bufp, unsigned int *sizep, const char *fmt, ...)\n \t\t*bufp = buf;\n \t}\n \t*sizep = newsize - 1;\n-\tmemcpy(buf + size, one_line, len);\n+\tmemcpy(buf + size, data, len);\n+}\n+\n+static void add_buffer(char **bufp, unsigned int *sizep, const char *fmt, ...)\n+{\n+\tchar one_line[2048];\n+\tva_list args;\n+\tint len;\n+\n+\tva_start(args, fmt);\n+\tlen = vsnprintf(one_line, sizeof(one_line), fmt, args);\n+\tva_end(args);\n+\tadd_chunk(bufp, sizep, one_line, len);\n }\n \n static void check_valid(unsigned char *sha1, enum object_type expect)\n@@ -81,39 +87,16 @@ static const char commit_utf8_warn[] =\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_tree(int argc, const char **argv, const char *prefix)\n+const unsigned char *\n+create_commit(const unsigned char *tree_sha1,\n+\t      unsigned char parent_sha1[][20], int parents,\n+\t      const char *author_info, const char *committer_info,\n+\t      const char *message, int length)\n {\n-\tint i;\n-\tint parents = 0;\n-\tunsigned char tree_sha1[20];\n-\tunsigned char commit_sha1[20];\n-\tchar comment[1000];\n+\tstatic unsigned char commit_sha1[20];\n+\tint encoding_is_utf8, i;\n \tchar *buffer;\n \tunsigned int size;\n-\tint encoding_is_utf8;\n-\n-\tgit_config(git_default_config);\n-\n-\tif (argc < 2)\n-\t\tusage(commit_tree_usage);\n-\tif (get_sha1(argv[1], tree_sha1))\n-\t\tdie(\"Not a valid object name %s\", argv[1]);\n-\n-\tcheck_valid(tree_sha1, OBJ_TREE);\n-\tfor (i = 2; i < argc; i += 2) {\n-\t\tconst char *a, *b;\n-\t\ta = argv[i]; b = argv[i+1];\n-\t\tif (!b || strcmp(a, \"-p\"))\n-\t\t\tusage(commit_tree_usage);\n-\n-\t\tif (parents >= MAXPARENT)\n-\t\t\tdie(\"Too many parents (%d max)\", MAXPARENT);\n-\t\tif (get_sha1(b, parent_sha1[parents]))\n-\t\t\tdie(\"Not a valid object name %s\", b);\n-\t\tcheck_valid(parent_sha1[parents], OBJ_COMMIT);\n-\t\tif (new_parent(parents))\n-\t\t\tparents++;\n-\t}\n \n \t/* Not having i18n.commitencoding is the same as having utf-8 */\n \tencoding_is_utf8 = is_encoding_utf8(git_commit_encoding);\n@@ -130,26 +113,71 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)\n \t\tadd_buffer(&buffer, &size, \"parent %s\\n\", sha1_to_hex(parent_sha1[i]));\n \n \t/* Person/date information */\n-\tadd_buffer(&buffer, &size, \"author %s\\n\", git_author_info(1));\n-\tadd_buffer(&buffer, &size, \"committer %s\\n\", git_committer_info(1));\n+\tadd_buffer(&buffer, &size, \"author %s\\n\", author_info);\n+\tadd_buffer(&buffer, &size, \"committer %s\\n\", committer_info);\n \tif (!encoding_is_utf8)\n \t\tadd_buffer(&buffer, &size,\n \t\t\t\t\"encoding %s\\n\", git_commit_encoding);\n \tadd_buffer(&buffer, &size, \"\\n\");\n \n \t/* And add the comment */\n-\twhile (fgets(comment, sizeof(comment), stdin) != NULL)\n-\t\tadd_buffer(&buffer, &size, \"%s\", comment);\n+\tadd_chunk(&buffer, &size, message, length);\n \n \t/* And check the encoding */\n \tbuffer[size] = '\\0';\n \tif (encoding_is_utf8 && !is_utf8(buffer))\n \t\tfprintf(stderr, commit_utf8_warn);\n \n-\tif (!write_sha1_file(buffer, size, commit_type, commit_sha1)) {\n-\t\tprintf(\"%s\\n\", sha1_to_hex(commit_sha1));\n-\t\treturn 0;\n+\tif (!write_sha1_file(buffer, size, commit_type, commit_sha1))\n+\t\treturn commit_sha1;\n+\n+\treturn NULL;\n+}\n+\n+int cmd_commit_tree(int argc, const char **argv, const char *prefix)\n+{\n+\tint i;\n+\tint parents = 0;\n+\tunsigned char tree_sha1[20];\n+\tchar *buffer;\n+\tconst unsigned char *commit_sha1;\n+\tunsigned long length;\n+\n+\tgit_config(git_default_config);\n+\n+\tif (argc < 2)\n+\t\tusage(commit_tree_usage);\n+\tif (get_sha1(argv[1], tree_sha1))\n+\t\tdie(\"Not a valid object name %s\", argv[1]);\n+\n+\tcheck_valid(tree_sha1, OBJ_TREE);\n+\tfor (i = 2; i < argc; i += 2) {\n+\t\tconst char *a, *b;\n+\t\ta = argv[i]; b = argv[i+1];\n+\t\tif (!b || strcmp(a, \"-p\"))\n+\t\t\tusage(commit_tree_usage);\n+\n+\t\tif (parents >= MAXPARENT)\n+\t\t\tdie(\"Too many parents (%d max)\", MAXPARENT);\n+\t\tif (get_sha1(b, parent_sha1[parents]))\n+\t\t\tdie(\"Not a valid object name %s\", b);\n+\t\tcheck_valid(parent_sha1[parents], OBJ_COMMIT);\n+\t\tif (new_parent(parents))\n+\t\t\tparents++;\n \t}\n-\telse\n+\n+\tif (read_fd(0, &buffer, &length))\n+\t\tdie(\"Could not read commit message from standard input\");\n+\n+\tcommit_sha1 = create_commit(tree_sha1,\n+\t\t\t\t    parent_sha1, parents,\n+\t\t\t\t    git_author_info(1),\t    \n+\t\t\t\t    git_committer_info(1),\t    \n+\t\t\t\t    buffer, length);\n+\n+\tif (!commit_sha1)\n \t\treturn 1;\n+\n+\tprintf(\"%s\\n\", sha1_to_hex(commit_sha1));\n+\treturn 0;\n }\ndiff --git a/builtin-commit.c b/builtin-commit.c\nnew file mode 100644\nindex 0000000..198d5af\n--- /dev/null\n+++ b/builtin-commit.c\n@@ -0,0 +1,746 @@\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+\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>] [[-i | -o] <path>...]\";\n+\n+static unsigned char head_sha1[20], merge_head_sha1[20];\n+static struct commit *use_message_commit;\n+static const char commit_editmsg[] = \"COMMIT_EDITMSG\";\n+static struct lock_file lock_file;\n+\n+enum option_type {\n+    OPTION_NONE,\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+static int scan_options(const char ***argv, struct option *options)\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; options[i].type != OPTION_LAST; 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_NONE)\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_NONE &&\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_NONE)\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_NONE) {\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(builtin_commit_usage);\n+\n+ match:\n+\tswitch (options[i].type) {\n+\tcase OPTION_NONE:\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+}\n+\n+static char *logfile, *force_author, *message;\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_NONE, \"all\", 'a', &all },\n+\t{ OPTION_STRING, \"author\", 0, (void *) &force_author },\n+\t{ OPTION_NONE, \"edit\", 0, &edit_flag },\n+\t{ OPTION_NONE, \"include\", 'i', &also },\n+\t{ OPTION_NONE, \"interactive\", 0, &interactive },\n+\t{ OPTION_NONE, \"only\", 'o', &only },\n+\t{ OPTION_STRING, \"message\", 'm', &message },\n+\t{ OPTION_NONE, \"no-verify\", 'n', &no_verify },\n+\t{ OPTION_NONE, \"amend\", 0, &amend },\n+\t{ OPTION_STRING, \"reedit-message\", 'c', &edit_message },\n+\t{ OPTION_STRING, \"reuse-message\", 'C', &use_message },\n+\t{ OPTION_NONE, \"signoff\", 's', &signoff },\n+\t{ OPTION_NONE, \"quiet\", 'q', &quiet },\n+\t{ OPTION_NONE, \"verbose\", 'v', &verbose },\n+\t{ OPTION_NONE, \"untracked-files\", 0, &untracked_files },\n+\t{ OPTION_LAST },\n+};\n+\n+/* FIXME: Taken from builtin-add, should be shared. */\n+\n+static void update_callback(struct diff_queue_struct *q,\n+\t\t\t    struct diff_options *opt, void *cbdata)\n+{\n+\tint i, verbose;\n+\n+\tverbose = *((int *)cbdata);\n+\tfor (i = 0; i < q->nr; i++) {\n+\t\tstruct diff_filepair *p = q->queue[i];\n+\t\tconst char *path = p->one->path;\n+\t\tswitch (p->status) {\n+\t\tdefault:\n+\t\t\tdie(\"unexpacted diff status %c\", p->status);\n+\t\tcase DIFF_STATUS_UNMERGED:\n+\t\tcase DIFF_STATUS_MODIFIED:\n+\t\t\tadd_file_to_cache(path, verbose);\n+\t\t\tbreak;\n+\t\tcase DIFF_STATUS_DELETED:\n+\t\t\tremove_file_from_cache(path);\n+\t\t\tif (verbose)\n+\t\t\t\tprintf(\"remove '%s'\\n\", path);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+}\n+\n+static void\n+add_files_to_cache(int fd, const char **files, const char *prefix)\n+{\n+\tstruct rev_info rev;\n+\n+\tinit_revisions(&rev, \"\");\n+\tsetup_revisions(0, NULL, &rev, NULL);\n+\trev.prune_data = get_pathspec(prefix, files);\n+\trev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;\n+\trev.diffopt.format_callback = update_callback;\n+\trev.diffopt.format_callback_data = &verbose;\n+\n+\trun_diff_files(&rev, 0);\n+\n+\tif (write_cache(fd, active_cache, active_nr) || close(fd))\n+\t\tdie(\"unable to write new index file\");\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(fd, files, NULL);\n+\t\treturn lock_file.filename;\n+\t} else if (also) {\n+\t\tadd_files_to_cache(fd, files, prefix);\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(fd, files, prefix);\n+\n+\ttree = parse_tree_indirect(head_sha1);\n+\tif (!tree)\n+\t\tdie(\"failed to unpack HEAD tree object\");\n+\tif (read_tree(tree, 0, NULL))\n+\t\tdie(\"failed to read HEAD tree object\");\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(fd, files, prefix);\n+\n+\treturn next_index_lock->filename;\n+}\n+\n+static int strip_lines(char *buffer, int len)\n+{\n+\tint blank_lines, i, j;\n+\tchar *eol;\n+\n+\tblank_lines = 1;\n+\tfor (i = 0, j = 0; i < len; i++) {\n+\t\tif (blank_lines > 0 && buffer[i] == '#') {\n+\t\t\teol = strchr(buffer + i, '\\n');\n+\t\t\tif (!eol)\n+\t\t\t\tbreak;\n+\n+\t\t\ti = eol - buffer;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (buffer[i] == '\\n') {\n+\t\t\tblank_lines++;\n+\t\t\tif (blank_lines > 1)\n+\t\t\t\tcontinue;\n+\t\t} else {\n+\t\t\tif (blank_lines > 2)\n+\t\t\t\tbuffer[j++] = '\\n';\n+\t\t\tblank_lines = 0;\n+\t\t}\n+\n+\t\tbuffer[j++] = buffer[i];\n+\t}\n+\n+\tif (buffer[j - 1] != '\\n')\n+               buffer[j++] = '\\n';\n+\n+\treturn j;\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+\tchar *buffer = NULL;\n+\tstruct stat statbuf;\n+\tint commitable;\n+\tunsigned long len;\n+\tFILE *fp;\n+\n+\tif (message) {\n+\t\tbuffer = message;\n+\t\tlen = 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 (read_fd(0, &buffer, &len))\n+\t\t\tdie(\"could not read log from standard input\");\n+\t} else if (logfile) {\n+\t\tif (read_path(logfile, &buffer, &len))\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\t/* FIXME: encoding */\n+\t\tbuffer = strstr(use_message_commit->buffer, \"\\n\\n\");\n+\t\tif (!buffer || buffer[2] == '\\0')\n+\t\t\tdie(\"commit has empty message\");\n+\t\tbuffer += 2;\n+\t\tlen = strlen(buffer);\n+\t} else if (!stat(git_path(\"MERGE_MSG\"), &statbuf)) {\n+\t\tif (read_path(git_path(\"MERGE_MSG\"), &buffer, &len))\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 (read_path(git_path(\"SQUASH_MSG\"), &buffer, &len))\n+\t\t\tdie(\"could not read SQUASH_MSG: %s\", 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+\tif (buffer) {\n+\t\tlen = strip_lines(buffer, len);\n+\n+\t\tif (fwrite(buffer, 1, len, fp) < len)\n+\t\t\tdie(\"could not write commit template: %s\\n\",\n+\t\t\t    strerror(errno));\n+\t}\n+\n+\tif (signoff) {\n+\t\tconst char *info, *bol;\n+\n+\t\tinfo = git_committer_info(1);\n+\t\tif (buffer) {\n+\t\t\tbol = strrchr(buffer + len - 1, '\\n');\n+\t\t\tif (!bol || prefixcmp(bol, sign_off_header))\n+\t\t\t\tfprintf(fp, \"\\n\");\n+\t\t}\n+\t\tfprintf(fp, \"Signed-off-by: %s\\n\", git_committer_info(1));\n+\t}\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+struct commit_info_strings {\n+\tconst char *header, *name_env, *email_env, *date_env;\n+} author_info_strings = {\n+\t\"\\nauthor \",\n+\t\"GIT_AUTHOR_NAME\", \"GIT_AUTHOR_EMAIL\", \"GIT_AUTHOR_DATE\"\n+}, committer_info_strings = {\n+\t\"\\ncommitter \",\n+\t\"GIT_COMMITTER_NAME\", \"GIT_COMMITER_EMAIL\", \"GIT_COMMITTER_DATE\"\n+};\n+\n+static char *determine_info(struct commit_info_strings *strings)\n+{\n+\tchar *p, *eol;\n+\tchar *name = NULL, *email = NULL, *date = NULL;\n+\n+\tif (use_message) {\n+\t\tp = strstr(use_message_commit->buffer, strings->header);\n+\t\tif (!p)\n+\t\t\tdie(\"invalid commit: %s\\n\", use_message);\n+\t\tp += strlen(strings->header);\n+\t\teol = strchr(p, '\\n');\n+\t\tif (!eol)\n+\t\t\tdie(\"invalid commit: %s\\n\", use_message);\n+\n+\t\treturn xstrndup(p, eol - p);\n+\t} else if (force_author && strings == &author_info_strings) {\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}\n+\n+\tif (name == NULL)\n+\t\tname = getenv(strings->name_env);\n+\tif (email == NULL)\n+\t\temail = getenv(strings->email_env);\n+\tif (date == NULL)\n+\t\tdate = getenv(strings->date_env);\n+\n+\treturn xstrdup(fmt_ident(name, email, date, 1));\n+}\n+\n+static void parse_and_validate_options(const char ***argv)\n+{\n+\tint f = 0;\n+\n+\tgit_config(git_status_config);\n+\n+\t(*argv)++;\n+\twhile (scan_options(argv, commit_options))\n+\t\t;\n+\n+\tif (logfile || message || use_message)\n+\t\tno_edit = 1;\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 (amend)\n+\t\tf++;\n+\tif (f > 1)\n+\t\tdie(\"Only one of -c/-C/-F/--amend can be used.\");\n+\tif (message && f > 0)\n+\t\tdie(\"Option -m cannot be combined with -c/-C/-F/--amend.\");\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+\n+\t\tif (get_sha1(use_message, sha1))\n+\t\t\tdie(\"could not lookup commit %s\", use_message);\n+\t\tuse_message_commit = lookup_commit(sha1);\n+\t\tif (!use_message_commit || parse_commit(use_message_commit))\n+\t\t\tdie(\"could not parse commit %s\", use_message);\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+\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 void launch_editor(const char *path)\n+{\n+\tconst char *editor, *terminal;\n+\tstruct child_process child;\n+\tconst char *args[3];\n+\n+\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\t\"Terminal is dumb but no VISUAL nor EDITOR defined.\\n\"\n+\t\t\t\"Please supply the commit log message using either\\n\"\n+\t\t\t\"-m or -F option.  A boilerplate log message has\\n\"\n+\t\t\t\"been prepared in $GIT_DIR/COMMIT_EDITMSG\\n\");\n+\t\texit(1);\n+\t}\n+\n+\tif (!editor)\n+\t\teditor = \"vi\";\n+\t    \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(\"could not launch editor %s.\", editor);\n+}\n+\n+static int message_is_empty(const char *buffer, int len)\n+{\n+\tstatic const char signed_off_by[] = \"Signed-off-by: \";\n+\tconst char *nl;\n+\tint eol, i;\n+\n+\tfor (i = 0; i < len; i++) {\n+\t\tnl = memchr(buffer + i, '\\n', len - i);\n+\t\tif (nl)\n+\t\t\teol = nl - buffer;\n+\t\telse\n+\t\t\teol = len;\n+\n+\t\tif (strlen(signed_off_by) <= eol - i &&\n+\t\t    !prefixcmp(buffer + i, signed_off_by)) {\n+\t\t\ti = eol;\n+\t\t\tcontinue;\n+\t\t}\n+\t\twhile (i < eol)\n+\t\t\tif (!isspace(buffer[i++]))\n+\t\t\t    return 0;\n+\t}\n+\n+\treturn 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+#define MAXPARENT (16)\n+static unsigned char parent_sha1[MAXPARENT][20];\n+\n+int cmd_commit(int argc, const char **argv, const char *prefix)\n+{\n+\tint parent_count = 0;\n+\tunsigned long len;\n+\tchar *buffer;\n+\tconst char *index_file, *reflog_msg;\n+\tconst unsigned char *commit_sha1;\n+\tstruct ref_lock *ref_lock;\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)) {\n+\t\trun_status(stdout, index_file);\n+\t\tunlink(commit_editmsg);\n+\t\treturn 1;\n+\t}\n+\n+\tif (!no_edit)\n+\t\tlaunch_editor(git_path(commit_editmsg));\n+\n+\tif (run_hook(index_file, \"commit-msg\", commit_editmsg))\n+\t\texit(1);\n+\n+\tif (read_path(git_path(commit_editmsg), &buffer, &len))\n+\t\tdie(\"could not read commit message file '%s': %s\",\n+\t\t    git_path(commit_editmsg), strerror(errno));\n+\n+\tlen = strip_lines(buffer, len);\n+\n+\tif (message_is_empty(buffer, len))\n+\t\tdie(\"* no commit message?  aborting commit.\");\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+\t\tint i = 0;\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\thashcpy(parent_sha1[i++], c->item->object.sha1);\n+\t\t\tif (i == MAXPARENT)\n+\t\t\t\tdie(\"Too many parents (%d max)\", MAXPARENT);\n+\t\t}\n+\t\tparent_count = i;\n+\t} else if (in_merge) {\n+\t\t/* FIXME: how are merges with more than two parents handled? */\n+\t\treflog_msg = \"commit (merge)\";\n+\t\thashcpy(parent_sha1[0], head_sha1);\n+\t\thashcpy(parent_sha1[1], merge_head_sha1);\n+\t\tparent_count = 2;\n+\t} else {\n+\t\treflog_msg = \"commit\";\n+\t\thashcpy(parent_sha1[0], head_sha1);\n+\t\tparent_count = 1;\n+\t}\n+\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+\n+\tcommit_sha1 = create_commit(active_cache_tree->sha1,\n+\t\t\t\t    parent_sha1, parent_count,\n+\t\t\t\t    determine_info(&author_info_strings),\n+\t\t\t\t    determine_info(&committer_info_strings),\n+\t\t\t\t    buffer, len);\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+\tif (!ref_lock)\n+\t\tdie(\"cannot lock HEAD ref\");\n+\tif (write_ref_sha1(ref_lock, commit_sha1, reflog_msg) < 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+\t/*\n+\t * FIXME:\n+\t *\tif test -d \"$GIT_DIR/rr-cache\"\n+\t *\tthen\n+\t *\t\tgit-rerere\n+\t *\tfi\n+\t */\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.h b/builtin.h\nindex da4834c..7f395da 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@@ -63,10 +64,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_tar_tree(int argc, const char **argv, const char *prefix);\ndiff --git a/cache.h b/cache.h\nindex 5e7381e..0403ada 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -245,7 +245,8 @@ extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat\n extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, int);\n extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);\n extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);\n-extern int read_pipe(int fd, char** return_buf, unsigned long* return_size);\n+extern int read_fd(int fd, char** return_buf, unsigned long* return_size);\n+extern int read_path(const char *path, char** return_buf, unsigned long* return_size);\n extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);\n extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);\n extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);\ndiff --git a/color.c b/color.c\nindex 09d82ee..124ba33 100644\n--- a/color.c\n+++ b/color.c\n@@ -135,39 +135,39 @@ int git_config_colorbool(const char *var, const char *value)\n \treturn git_config_bool(var, value);\n }\n \n-static int color_vprintf(const char *color, const char *fmt,\n+static int color_vfprintf(FILE *fp, const char *color, const char *fmt,\n \t\tva_list args, const char *trail)\n {\n \tint r = 0;\n \n \tif (*color)\n-\t\tr += printf(\"%s\", color);\n-\tr += vprintf(fmt, args);\n+\t\tr += fprintf(fp, \"%s\", color);\n+\tr += vfprintf(fp, fmt, args);\n \tif (*color)\n-\t\tr += printf(\"%s\", COLOR_RESET);\n+\t\tr += fprintf(fp, \"%s\", COLOR_RESET);\n \tif (trail)\n-\t\tr += printf(\"%s\", trail);\n+\t\tr += fprintf(fp, \"%s\", trail);\n \treturn r;\n }\n \n \n \n-int color_printf(const char *color, const char *fmt, ...)\n+int color_fprintf(FILE *fp, const char *color, const char *fmt, ...)\n {\n \tva_list args;\n \tint r;\n \tva_start(args, fmt);\n-\tr = color_vprintf(color, fmt, args, NULL);\n+\tr = color_vfprintf(fp, color, fmt, args, NULL);\n \tva_end(args);\n \treturn r;\n }\n \n-int color_printf_ln(const char *color, const char *fmt, ...)\n+int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...)\n {\n \tva_list args;\n \tint r;\n \tva_start(args, fmt);\n-\tr = color_vprintf(color, fmt, args, \"\\n\");\n+\tr = color_vfprintf(fp, color, fmt, args, \"\\n\");\n \tva_end(args);\n \treturn r;\n }\ndiff --git a/color.h b/color.h\nindex 88bb8ff..6809800 100644\n--- a/color.h\n+++ b/color.h\n@@ -6,7 +6,7 @@\n \n int git_config_colorbool(const char *var, const char *value);\n void color_parse(const char *var, const char *value, char *dst);\n-int color_printf(const char *color, const char *fmt, ...);\n-int color_printf_ln(const char *color, const char *fmt, ...);\n+int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);\n+int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);\n \n #endif /* COLOR_H */\ndiff --git a/commit.h b/commit.h\nindex a313b53..a07e379 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -122,4 +122,13 @@ extern struct commit_list *get_shallow_commits(struct object_array *heads,\n \t\tint depth, int shallow_flag, int not_shallow_flag);\n \n int in_merge_bases(struct commit *, struct commit **, int);\n+\n+const unsigned char *\n+create_commit(const unsigned char *tree_sha1,\n+\t      unsigned char parent_sha1[][20], int parents,\n+\t      const char *author_info, const char *committer_info,\n+\t      const char *message, int length);\n+\n+int interactive_add(void);\n+\n #endif /* COMMIT_H */\ndiff --git a/git-commit.sh b/git-commit.sh\ndeleted file mode 100755\nindex 5547a02..0000000\n--- a/git-commit.sh\n+++ /dev/null\n@@ -1,658 +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>] [[-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-\tcase \"$status_only\" in\n-\tt) color= ;;\n-\t*) color=--nocolor ;;\n-\tesac\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-while case \"$#\" in 0) break;; esac\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\tshift\n-\t\t;;\n-\t-F*|-f*)\n-\t\tno_edit=t\n-\t\tlog_given=t$log_given\n-\t\tlogfile=`expr \"z$1\" : 'z-[Ff]\\(.*\\)'`\n-\t\tshift\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=`expr \"z$1\" : 'z-[^=]*=\\(.*\\)'`\n-\t\tshift\n-\t\t;;\n-\t-a|--a|--al|--all)\n-\t\tall=t\n-\t\tshift\n-\t\t;;\n-\t--au=*|--aut=*|--auth=*|--autho=*|--author=*)\n-\t\tforce_author=`expr \"z$1\" : 'z-[^=]*=\\(.*\\)'`\n-\t\tshift\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\tshift\n-\t\t;;\n-\t-e|--e|--ed|--edi|--edit)\n-\t\tedit_flag=t\n-\t\tshift\n-\t\t;;\n-\t-i|--i|--in|--inc|--incl|--inclu|--includ|--include)\n-\t\talso=t\n-\t\tshift\n-\t\t;;\n-\t--int|--inte|--inter|--intera|--interac|--interact|--interacti|\\\n-\t--interactiv|--interactive)\n-\t\tinteractive=t\n-\t\tshift\n-\t\t;;\n-\t-o|--o|--on|--onl|--only)\n-\t\tonly=t\n-\t\tshift\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\tif test \"$log_message\" = ''\n-\t\tthen\n-\t\t    log_message=\"$1\"\n-\t\telse\n-\t\t    log_message=\"$log_message\n-\n-$1\"\n-\t\tfi\n-\t\tno_edit=t\n-\t\tshift\n-\t\t;;\n-\t-m*)\n-\t\tlog_given=m$log_given\n-\t\tif test \"$log_message\" = ''\n-\t\tthen\n-\t\t    log_message=`expr \"z$1\" : 'z-m\\(.*\\)'`\n-\t\telse\n-\t\t    log_message=\"$log_message\n-\n-`expr \"z$1\" : 'z-m\\(.*\\)'`\"\n-\t\tfi\n-\t\tno_edit=t\n-\t\tshift\n-\t\t;;\n-\t--m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*)\n-\t\tlog_given=m$log_given\n-\t\tif test \"$log_message\" = ''\n-\t\tthen\n-\t\t    log_message=`expr \"z$1\" : 'z-[^=]*=\\(.*\\)'`\n-\t\telse\n-\t\t    log_message=\"$log_message\n-\n-`expr \"z$1\" : 'zq-[^=]*=\\(.*\\)'`\"\n-\t\tfi\n-\t\tno_edit=t\n-\t\tshift\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\tshift\n-\t\t;;\n-\t--a|--am|--ame|--amen|--amend)\n-\t\tamend=t\n-\t\tlog_given=t$log_given\n-\t\tuse_commit=HEAD\n-\t\tshift\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\tshift\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=`expr \"z$1\" : 'z-[^=]*=\\(.*\\)'`\n-\t\tno_edit=\n-\t\tshift\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\tshift\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\tshift\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=`expr \"z$1\" : 'z-[^=]*=\\(.*\\)'`\n-\t\tno_edit=t\n-\t\tshift\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\tshift\n-\t\t;;\n-\t-s|--s|--si|--sig|--sign|--signo|--signof|--signoff)\n-\t\tsignoff=t\n-\t\tshift\n-\t\t;;\n-\t-q|--q|--qu|--qui|--quie|--quiet)\n-\t\tquiet=t\n-\t\tshift\n-\t\t;;\n-\t-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)\n-\t\tverbose=t\n-\t\tshift\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\tshift\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-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/--amend can be used.\" ;;\n-*tm*|*mt*)\n-\tdie \"Option -m cannot be combined with -c/-C/-F/--amend.\" ;;\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-################################################################\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-\t\tTMP_INDEX=\"$GIT_DIR/tmp-index$$\"\n-\t\tcommit_only=`git-ls-files --error-unmatch -- \"$@\"` || 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 --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-\tif test \"$TMP_INDEX\"\n-\tthen\n-\t\tGIT_INDEX_FILE=\"$TMP_INDEX\" \"$GIT_DIR\"/hooks/pre-commit\n-\telse\n-\t\tGIT_INDEX_FILE=\"$USE_INDEX\" \"$GIT_DIR\"/hooks/pre-commit\n-\tfi || 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-fi | git-stripspace >\"$GIT_DIR\"/COMMIT_EDITMSG\n-\n-case \"$signoff\" in\n-t)\n-\tneed_blank_before_signoff=\n-\ttail -n 1 \"$GIT_DIR\"/COMMIT_EDITMSG |\n-\tgrep 'Signed-off-by:' >/dev/null || need_blank_before_signoff=yes\n-\t{\n-\t\ttest -z \"$need_blank_before_signoff\" || echo\n-\t\tgit-var GIT_COMMITTER_IDENT | sed -e '\n-\t\t\ts/>.*/>/\n-\t\t\ts/^/Signed-off-by: /\n-\t\t'\n-\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-\tpick_author_script='\n-\t/^author /{\n-\t\ts/'\\''/'\\''\\\\'\\'\\''/g\n-\t\th\n-\t\ts/^author \\([^<]*\\) <[^>]*> .*$/\\1/\n-\t\ts/'\\''/'\\''\\'\\'\\''/g\n-\t\ts/.*/GIT_AUTHOR_NAME='\\''&'\\''/p\n-\n-\t\tg\n-\t\ts/^author [^<]* <\\([^>]*\\)> .*$/\\1/\n-\t\ts/'\\''/'\\''\\'\\'\\''/g\n-\t\ts/.*/GIT_AUTHOR_EMAIL='\\''&'\\''/p\n-\n-\t\tg\n-\t\ts/^author [^<]* <[^>]*> \\(.*\\)$/\\1/\n-\t\ts/'\\''/'\\''\\'\\'\\''/g\n-\t\ts/.*/GIT_AUTHOR_DATE='\\''&'\\''/p\n-\n-\t\tq\n-\t}\n-\t'\n-\tencoding=$(git config i18n.commitencoding || echo UTF-8)\n-\tset_author_env=`git show -s --pretty=raw --encoding=\"$encoding\" \"$use_commit\" |\n-\tLANG=C LC_ALL=C sed -ne \"$pick_author_script\"`\n-\teval \"$set_author_env\"\n-\texport GIT_AUTHOR_NAME\n-\texport GIT_AUTHOR_EMAIL\n-\texport 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\" -a -z \"$amend\" ]\n-then\n-\trm -f \"$GIT_DIR/COMMIT_EDITMSG\" \"$GIT_DIR/SQUASH_MSG\"\n-\trun_status\n-\texit 1\n-fi\n-\n-case \"$no_edit\" in\n-'')\n-\tcase \"${VISUAL:-$EDITOR},$TERM\" in\n-\t,dumb)\n-\t\techo >&2 \"Terminal is dumb but no VISUAL nor EDITOR defined.\"\n-\t\techo >&2 \"Please supply the commit log message using either\"\n-\t\techo >&2 \"-m or -F option.  A boilerplate log message has\"\n-\t\techo >&2 \"been prepared in $GIT_DIR/COMMIT_EDITMSG\"\n-\t\texit 1\n-\t\t;;\n-\tesac\n-\tgit-var GIT_AUTHOR_IDENT > /dev/null  || die\n-\tgit-var GIT_COMMITTER_IDENT > /dev/null  || die\n-\t${VISUAL:-${EDITOR:-vi}} \"$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-if cnt=`grep -v -i '^Signed-off-by' \"$GIT_DIR\"/COMMIT_MSG |\n-\tgit-stripspace |\n-\twc -l` &&\n-   test 0 -lt $cnt\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=$(cat \"$GIT_DIR\"/COMMIT_MSG | git-commit-tree $tree $PARENTS) &&\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-if test -d \"$GIT_DIR/rr-cache\"\n-then\n-\tgit-rerere\n-fi\n-\n-if test \"$ret\" = 0\n-then\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 29b55a1..4018e3c 100644\n--- a/git.c\n+++ b/git.c\n@@ -237,6 +237,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)\n \t\t{ \"check-attr\", cmd_check_attr, RUN_SETUP | NOT_BARE },\n \t\t{ \"cherry\", cmd_cherry, RUN_SETUP },\n \t\t{ \"cherry-pick\", cmd_cherry_pick, RUN_SETUP | NOT_BARE },\n+\t\t{ \"commit\", cmd_commit, RUN_SETUP },\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@@ -279,10 +280,10 @@ static void handle_internal_command(int argc, const char **argv, char **envp)\n \t\t{ \"rev-parse\", cmd_rev_parse, RUN_SETUP },\n \t\t{ \"revert\", cmd_revert, RUN_SETUP | NOT_BARE },\n \t\t{ \"rm\", cmd_rm, RUN_SETUP | NOT_BARE },\n-\t\t{ \"runstatus\", cmd_runstatus, RUN_SETUP | NOT_BARE },\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 | NOT_BARE },\n \t\t{ \"stripspace\", cmd_stripspace },\n \t\t{ \"symbolic-ref\", cmd_symbolic_ref, RUN_SETUP },\n \t\t{ \"tar-tree\", cmd_tar_tree },\ndiff --git a/mktag.c b/mktag.c\nindex b82e377..26b9ebf 100644\n--- a/mktag.c\n+++ b/mktag.c\n@@ -111,8 +111,8 @@ static int verify_tag(char *buffer, unsigned long size)\n \n int main(int argc, char **argv)\n {\n-\tunsigned long size = 4096;\n-\tchar *buffer = xmalloc(size);\n+\tunsigned long size;\n+\tchar *buffer;\n \tunsigned char result_sha1[20];\n \n \tif (argc != 1)\n@@ -120,10 +120,8 @@ int main(int argc, char **argv)\n \n \tsetup_git_directory();\n \n-\tif (read_pipe(0, &buffer, &size)) {\n-\t\tfree(buffer);\n+\tif (read_fd(0, &buffer, &size))\n \t\tdie(\"could not read from stdin\");\n-\t}\n \n \t/* Verify it for some basic sanity: it needs to start with\n \t   \"object <sha1>\\ntype\\ntagger \" */\ndiff --git a/sha1_file.c b/sha1_file.c\nindex 2b86086..91e8854 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -2286,18 +2286,17 @@ int has_sha1_file(const unsigned char *sha1)\n }\n \n /*\n- * reads from fd as long as possible into a supplied buffer of size bytes.\n- * If necessary the buffer's size is increased using realloc()\n+ * reads from fd as long as possible and allocates a buffer to hold\n+ * the contents.  The buffer and size of the contents is returned in\n+ * *return_buf and *return_size.  In case of failure, the allocated\n+ * buffers are freed, otherwise, the buffer must be freed using xfree.\n  *\n  * returns 0 if anything went fine and -1 otherwise\n- *\n- * NOTE: both buf and size may change, but even when -1 is returned\n- * you still have to free() it yourself.\n  */\n-int read_pipe(int fd, char** return_buf, unsigned long* return_size)\n+int read_fd(int fd, char** return_buf, unsigned long* return_size)\n {\n-\tchar* buf = *return_buf;\n-\tunsigned long size = *return_size;\n+\tunsigned long size = 4096;\n+\tchar* buf = xmalloc(size);\n \tssize_t iret;\n \tunsigned long off = 0;\n \n@@ -2315,21 +2314,38 @@ int read_pipe(int fd, char** return_buf, unsigned long* return_size)\n \t*return_buf = buf;\n \t*return_size = off;\n \n-\tif (iret < 0)\n+\tif (iret < 0) {\n+\t\tfree(buf);\n+\t\treturn -1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int read_path(const char *path, char** return_buf, unsigned long* return_size)\n+{\n+\tint fd; \n+\n+\tfd = open(path, O_RDONLY);\n+\tif (fd < 0)\n+\t\treturn -1;\n+\tif (read_fd(fd, return_buf, return_size)) {\n+\t\tclose(fd);\n \t\treturn -1;\n+\t}\n+\tclose(fd);\n+\n \treturn 0;\n }\n \n int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)\n {\n-\tunsigned long size = 4096;\n-\tchar *buf = xmalloc(size);\n+\tunsigned long size;\n+\tchar *buf;\n \tint ret;\n \n-\tif (read_pipe(fd, &buf, &size)) {\n-\t\tfree(buf);\n+\tif (read_fd(fd, &buf, &size))\n \t\treturn -1;\n-\t}\n \n \tif (!type)\n \t\ttype = blob_type;\ndiff --git a/t/t7800-commit.sh b/t/t7800-commit.sh\nnew file mode 100644\nindex 0000000..1f97caa\n--- /dev/null\n+++ b/t/t7800-commit.sh\n@@ -0,0 +1,126 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2007 Kristian HÃ¸gsberg <krh@redhat.com>\n+#\n+\n+# FIXME: Test the various index usages, -i and -o, test reflog,\n+# signoff, hooks\n+\n+test_description='git-commit'\n+. ./test-lib.sh\n+\n+# Pick a date so we get consistent commits. 7/7/07 means good luck!\n+export GIT_AUTHOR_DATE=\"July 7, 2007\"\n+export GIT_COMMITTER_DATE=\"July 7, 2007\"\n+\n+echo \"bongo bongo\" >file\n+test_expect_success \\\n+\t\"initial status\" \\\n+\t\"git-add file && \\\n+\t git-status | grep 'Initial commit'\"\n+\n+test_expect_failure \\\n+\t\"fail initial amend\" \\\n+\t\"git-commit -m initial --amend\"\n+\n+test_expect_success \\\n+\t\"initial commit\" \\\n+\t\"git-commit -m initial\"\n+\n+test_expect_failure \\\n+\t\"testing nothing to commit\" \\\n+\t\"git-commit -m initial\"\n+\n+echo \"bongo bongo bongo\" >file\n+\n+test_expect_success \\\n+\t\"next commit\" \\\n+\t\"git-commit -m next -a\"\n+\n+echo \"more bongo: bongo bongo bongo bongo\" >file\n+\n+test_expect_failure \\\n+\t\"commit message from non-existing file\" \\\n+\t\"git-commit -F gah -a\"\n+\n+cat >msg <<EOF\n+\t\t\n+\n+  \n+Signed-off-by: hula\n+EOF\n+test_expect_failure \\\n+\t\"empty commit message\" \\\n+\t\"git-commit -F msg -a\"\n+\n+echo \"this is the commit message, coming from a file\" >msg\n+test_expect_success \\\n+\t\"commit message from file\" \\\n+\t\"git-commit -F msg -a\"\n+\n+cat >editor <<\\EOF\n+#!/bin/sh\n+sed -i -e \"s/a file/an amend commit/g\" $1\n+EOF\n+chmod 755 editor\n+\n+test_expect_success \\\n+\t\"amend commit\" \\\n+\t\"VISUAL=./editor git-commit --amend\"\n+\n+echo \"enough with the bongos\" >file\n+test_expect_failure \\\n+\t\"passing --amend and -F\" \\\n+\t\"git-commit -F msg --amend .\"\n+\n+test_expect_success \\\n+\t\"using message from other commit\" \\\n+\t\"git-commit -C HEAD^ .\"\n+\n+cat >editor <<\\EOF\n+#!/bin/sh\n+sed -i -e \"s/amend/older/g\" $1\n+EOF\n+chmod 755 editor\n+\n+echo \"hula hula\" >file\n+test_expect_success \\\n+\t\"editing message from other commit\" \\\n+\t\"VISUAL=./editor git-commit -c HEAD^ -a\"\n+\n+echo \"silly new contents\" >file\n+test_expect_success \\\n+\t\"message from stdin\" \\\n+\t\"echo commit message from stdin | git-commit -F - -a\"\n+\n+echo \"gak\" >file\n+test_expect_success \\\n+\t\"overriding author from command line\" \\\n+\t\"git-commit -m 'author' --author 'Rubber Duck <rduck@convoy.org>' -a\"\n+\n+test_expect_success \\\n+\t\"interactive add\" \\\n+\t\"echo 7 | git-commit --interactive | grep 'What now'\"\n+\n+test_expect_success \\\n+\t\"showing committed revisions\" \\\n+\t\"git-rev-list HEAD >current\"\n+\n+# We could just check the head sha1, but checking each commit makes it\n+# easier to isolate bugs.\n+\n+cat >expected <<\\EOF\n+9a7ad90c32f43aecc081722a72f26ead981bd6fa\n+d5c6c34f0361d64d3d1b1f26736b1ae98e2baa90\n+ca0ddb06fc90f3f857dd623f8418804aad75d9fa\n+9cc5d9b9e5a29f1c46d0d0cf2dd716fb8241316a\n+ca750e97c137587aa181b6b9526b3b04fb9db667\n+4515202a60be41cb1b8f6b89edb3f6948130ac1c\n+7cc9a125522fe28b84cd3f6c7aeef6e5c62b3f8b\n+EOF\n+\n+test_expect_success \\\n+    'validate git-rev-list output.' \\\n+    'diff current expected'\n+\n+test_done\ndiff --git a/wt-status.c b/wt-status.c\nindex 5205420..5bf800a 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -54,29 +54,30 @@ void wt_status_prepare(struct wt_status *s)\n \ts->reference = \"HEAD\";\n }\n \n-static void wt_status_print_cached_header(const char *reference)\n+static void wt_status_print_cached_header(struct wt_status *s)\n {\n \tconst char *c = color(WT_STATUS_HEADER);\n-\tcolor_printf_ln(c, \"# Changes to be committed:\");\n-\tif (reference) {\n-\t\tcolor_printf_ln(c, \"#   (use \\\"git reset %s <file>...\\\" to unstage)\", reference);\n+\tcolor_fprintf_ln(s->fp, c, \"# Changes to be committed:\");\n+\tif (s->reference) {\n+\t\tcolor_fprintf_ln(s->fp, c, \"#   (use \\\"git reset %s <file>...\\\" to unstage)\", s->reference);\n \t} else {\n-\t\tcolor_printf_ln(c, \"#   (use \\\"git rm --cached <file>...\\\" to unstage)\");\n+\t\tcolor_fprintf_ln(s->fp, c, \"#   (use \\\"git rm --cached <file>...\\\" to unstage)\");\n \t}\n-\tcolor_printf_ln(c, \"#\");\n+\tcolor_fprintf_ln(s->fp, c, \"#\");\n }\n \n-static void wt_status_print_header(const char *main, const char *sub)\n+static void wt_status_print_header(struct wt_status *s,\n+\t\t\t\t   const char *main, const char *sub)\n {\n \tconst char *c = color(WT_STATUS_HEADER);\n-\tcolor_printf_ln(c, \"# %s:\", main);\n-\tcolor_printf_ln(c, \"#   (%s)\", sub);\n-\tcolor_printf_ln(c, \"#\");\n+\tcolor_fprintf_ln(s->fp, c, \"# %s:\", main);\n+\tcolor_fprintf_ln(s->fp, c, \"#   (%s)\", sub);\n+\tcolor_fprintf_ln(s->fp, c, \"#\");\n }\n \n-static void wt_status_print_trailer(void)\n+static void wt_status_print_trailer(struct wt_status *s)\n {\n-\tcolor_printf_ln(color(WT_STATUS_HEADER), \"#\");\n+\tcolor_fprintf_ln(s->fp, color(WT_STATUS_HEADER), \"#\");\n }\n \n static const char *quote_crlf(const char *in, char *buf, size_t sz)\n@@ -108,7 +109,8 @@ static const char *quote_crlf(const char *in, char *buf, size_t sz)\n \treturn ret;\n }\n \n-static void wt_status_print_filepair(int t, struct diff_filepair *p)\n+static void wt_status_print_filepair(struct wt_status *s,\n+\t\t\t\t     int t, struct diff_filepair *p)\n {\n \tconst char *c = color(t);\n \tconst char *one, *two;\n@@ -117,36 +119,36 @@ static void wt_status_print_filepair(int t, struct diff_filepair *p)\n \tone = quote_crlf(p->one->path, onebuf, sizeof(onebuf));\n \ttwo = quote_crlf(p->two->path, twobuf, sizeof(twobuf));\n \n-\tcolor_printf(color(WT_STATUS_HEADER), \"#\\t\");\n+\tcolor_fprintf(s->fp, color(WT_STATUS_HEADER), \"#\\t\");\n \tswitch (p->status) {\n \tcase DIFF_STATUS_ADDED:\n-\t\tcolor_printf(c, \"new file:   %s\", one);\n+\t\tcolor_fprintf(s->fp, c, \"new file:   %s\", one);\n \t\tbreak;\n \tcase DIFF_STATUS_COPIED:\n-\t\tcolor_printf(c, \"copied:     %s -> %s\", one, two);\n+\t\tcolor_fprintf(s->fp, c, \"copied:     %s -> %s\", one, two);\n \t\tbreak;\n \tcase DIFF_STATUS_DELETED:\n-\t\tcolor_printf(c, \"deleted:    %s\", one);\n+\t\tcolor_fprintf(s->fp, c, \"deleted:    %s\", one);\n \t\tbreak;\n \tcase DIFF_STATUS_MODIFIED:\n-\t\tcolor_printf(c, \"modified:   %s\", one);\n+\t\tcolor_fprintf(s->fp, c, \"modified:   %s\", one);\n \t\tbreak;\n \tcase DIFF_STATUS_RENAMED:\n-\t\tcolor_printf(c, \"renamed:    %s -> %s\", one, two);\n+\t\tcolor_fprintf(s->fp, c, \"renamed:    %s -> %s\", one, two);\n \t\tbreak;\n \tcase DIFF_STATUS_TYPE_CHANGED:\n-\t\tcolor_printf(c, \"typechange: %s\", one);\n+\t\tcolor_fprintf(s->fp, c, \"typechange: %s\", one);\n \t\tbreak;\n \tcase DIFF_STATUS_UNKNOWN:\n-\t\tcolor_printf(c, \"unknown:    %s\", one);\n+\t\tcolor_fprintf(s->fp, c, \"unknown:    %s\", one);\n \t\tbreak;\n \tcase DIFF_STATUS_UNMERGED:\n-\t\tcolor_printf(c, \"unmerged:   %s\", one);\n+\t\tcolor_fprintf(s->fp, c, \"unmerged:   %s\", one);\n \t\tbreak;\n \tdefault:\n \t\tdie(\"bug: unhandled diff status %c\", p->status);\n \t}\n-\tprintf(\"\\n\");\n+\tfprintf(s->fp, \"\\n\");\n }\n \n static void wt_status_print_updated_cb(struct diff_queue_struct *q,\n@@ -160,14 +162,14 @@ static void wt_status_print_updated_cb(struct diff_queue_struct *q,\n \t\tif (q->queue[i]->status == 'U')\n \t\t\tcontinue;\n \t\tif (!shown_header) {\n-\t\t\twt_status_print_cached_header(s->reference);\n+\t\t\twt_status_print_cached_header(s);\n \t\t\ts->commitable = 1;\n \t\t\tshown_header = 1;\n \t\t}\n-\t\twt_status_print_filepair(WT_STATUS_UPDATED, q->queue[i]);\n+\t\twt_status_print_filepair(s, WT_STATUS_UPDATED, q->queue[i]);\n \t}\n \tif (shown_header)\n-\t\twt_status_print_trailer();\n+\t\twt_status_print_trailer(s);\n }\n \n static void wt_status_print_changed_cb(struct diff_queue_struct *q,\n@@ -184,18 +186,18 @@ static void wt_status_print_changed_cb(struct diff_queue_struct *q,\n \t\t\t\tmsg = use_add_rm_msg;\n \t\t\t\tbreak;\n \t\t\t}\n-\t\twt_status_print_header(\"Changed but not updated\", msg);\n+\t\twt_status_print_header(s, \"Changed but not updated\", msg);\n \t}\n \tfor (i = 0; i < q->nr; i++)\n-\t\twt_status_print_filepair(WT_STATUS_CHANGED, q->queue[i]);\n+\t\twt_status_print_filepair(s, WT_STATUS_CHANGED, q->queue[i]);\n \tif (q->nr)\n-\t\twt_status_print_trailer();\n+\t\twt_status_print_trailer(s);\n }\n \n static void wt_read_cache(struct wt_status *s)\n {\n \tdiscard_cache();\n-\tread_cache();\n+\tread_cache_from(s->index_file);\n }\n \n static void wt_status_print_initial(struct wt_status *s)\n@@ -206,16 +208,16 @@ static void wt_status_print_initial(struct wt_status *s)\n \twt_read_cache(s);\n \tif (active_nr) {\n \t\ts->commitable = 1;\n-\t\twt_status_print_cached_header(NULL);\n+\t\twt_status_print_cached_header(s);\n \t}\n \tfor (i = 0; i < active_nr; i++) {\n-\t\tcolor_printf(color(WT_STATUS_HEADER), \"#\\t\");\n-\t\tcolor_printf_ln(color(WT_STATUS_UPDATED), \"new file: %s\",\n+\t\tcolor_fprintf(s->fp, color(WT_STATUS_HEADER), \"#\\t\");\n+\t\tcolor_fprintf_ln(s->fp, color(WT_STATUS_UPDATED), \"new file: %s\",\n \t\t\t\tquote_crlf(active_cache[i]->name,\n \t\t\t\t\t   buf, sizeof(buf)));\n \t}\n \tif (active_nr)\n-\t\twt_status_print_trailer();\n+\t\twt_status_print_trailer(s);\n }\n \n static void wt_status_print_updated(struct wt_status *s)\n@@ -281,12 +283,12 @@ static void wt_status_print_untracked(struct wt_status *s)\n \t\t}\n \t\tif (!shown_header) {\n \t\t\ts->workdir_untracked = 1;\n-\t\t\twt_status_print_header(\"Untracked files\",\n+\t\t\twt_status_print_header(s, \"Untracked files\",\n \t\t\t\t\t       use_add_to_include_msg);\n \t\t\tshown_header = 1;\n \t\t}\n-\t\tcolor_printf(color(WT_STATUS_HEADER), \"#\\t\");\n-\t\tcolor_printf_ln(color(WT_STATUS_UNTRACKED), \"%.*s\",\n+\t\tcolor_fprintf(s->fp, color(WT_STATUS_HEADER), \"#\\t\");\n+\t\tcolor_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED), \"%.*s\",\n \t\t\t\tent->len, ent->name);\n \t}\n }\n@@ -316,14 +318,14 @@ void wt_status_print(struct wt_status *s)\n \t\t\tbranch_name = \"\";\n \t\t\ton_what = \"Not currently on any branch.\";\n \t\t}\n-\t\tcolor_printf_ln(color(WT_STATUS_HEADER),\n+\t\tcolor_fprintf_ln(s->fp, color(WT_STATUS_HEADER),\n \t\t\t\"# %s%s\", on_what, branch_name);\n \t}\n \n \tif (s->is_initial) {\n-\t\tcolor_printf_ln(color(WT_STATUS_HEADER), \"#\");\n-\t\tcolor_printf_ln(color(WT_STATUS_HEADER), \"# Initial commit\");\n-\t\tcolor_printf_ln(color(WT_STATUS_HEADER), \"#\");\n+\t\tcolor_fprintf_ln(s->fp, color(WT_STATUS_HEADER), \"#\");\n+\t\tcolor_fprintf_ln(s->fp, color(WT_STATUS_HEADER), \"# Initial commit\");\n+\t\tcolor_fprintf_ln(s->fp, color(WT_STATUS_HEADER), \"#\");\n \t\twt_status_print_initial(s);\n \t}\n \telse {\n@@ -337,7 +339,7 @@ void wt_status_print(struct wt_status *s)\n \t\twt_status_print_verbose(s);\n \tif (!s->commitable) {\n \t\tif (s->amend)\n-\t\t\tprintf(\"# No changes\\n\");\n+\t\t\tfprintf(s->fp, \"# No changes\\n\");\n \t\telse if (s->workdir_dirty)\n \t\t\tprintf(\"no changes added to commit (use \\\"git add\\\" and/or \\\"git commit -a\\\")\\n\");\n \t\telse if (s->workdir_untracked)\ndiff --git a/wt-status.h b/wt-status.h\nindex cfea4ae..7744932 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -1,6 +1,8 @@\n #ifndef STATUS_H\n #define STATUS_H\n \n+#include <stdio.h>\n+\n enum color_wt_status {\n \tWT_STATUS_HEADER,\n \tWT_STATUS_UPDATED,\n@@ -19,6 +21,8 @@ struct wt_status {\n \tint commitable;\n \tint workdir_dirty;\n \tint workdir_untracked;\n+\tconst char *index_file;\n+\tFILE *fp;\n };\n \n int git_status_config(const char *var, const char *value);\n-- \n1.5.2.GIT\n"},{"id":"47766","messageId":"alpine.LFD.0.999.0707181540370.22541@xanadu.home","threadId":"9098","inReplyTo":"11847863792344-git-send-email-krh@redhat.com","subject":"Re: [PATCH] Implement git commit as a builtin.","fromName":"Nicolas Pitre","fromEmail":"nico@cam.org","sentAt":"2007-07-18T19:43:38Z","receivedAt":"2007-07-18T19:43:38Z","isPatch":true,"sender":{"key":"nico@fluxnic.net","avatar":"https://avatars.githubusercontent.com/u/702790?v=4"},"body":"On Wed, 18 Jul 2007, Kristian Høgsberg wrote:\n\n> ---\n> Here's another update on the work in progress.  At this point, the C\n> version is almost complete, there's only a few issues left (look for\n> FIXME in builtin-commit.c).  I've added a commit test case which should\n> be split out in a patch on its own, but the good news is that it\n> successfully exercises most of the command line options and the C version\n> passes.\n> \n> My plan for the remainder of the work is still to wrap up the last few\n> pieces of functionality and then start taking this big patch apart in a\n> number of more manageable pieces.  However, the bulk of this work will\n> still be a big patch that removes git-commit.sh and adds builtin-commit\n> in one swoop.\n\nPlease move git-commit.sh to contrib/examples instead of deleting it.\n\n\nNicolas\n"},{"id":"47772","messageId":"1b46aba20707181427y12bd8b62pe30df61219e7c1f7@mail.gmail.com","threadId":"9098","inReplyTo":"11847863792344-git-send-email-krh@redhat.com","subject":"Re: [PATCH] Implement git commit as a builtin.","fromName":"Carlos Rica","fromEmail":"jasampler@gmail.com","sentAt":"2007-07-18T21:27:42Z","receivedAt":"2007-07-18T21:27:42Z","isPatch":true,"sender":{"key":"jasampler@gmail.com","avatar":null},"body":"2007/7/18, Kristian Høgsberg <krh@redhat.com>:\n> +       if (buffer) {\n> +               len = strip_lines(buffer, len);\n> +\n> +               if (fwrite(buffer, 1, len, fp) < len)\n> +                       die(\"could not write commit template: %s\\n\",\n> +                           strerror(errno));\n> +       }\n....\n> +       len = strip_lines(buffer, len);\n> +\n> +       if (message_is_empty(buffer, len))\n> +               die(\"* no commit message?  aborting commit.\");\n> +\n\nHi Kristian, you could call to the new stripspace() function\nin builtin-stripspace.c, to reduce code in your file. The only\nthing you should consider is that the new stripspace()\nremoves always the last '\\n' in the file (if any), so you have to\nadd it when you need.\n\nI sent a patch to change the name of read_pipe with read_fd\n(not accepted yet), and to terminate the buffer with NUL\n(although perhaps you don't need that), and to allow the\nfunction to receive buffer NULL or size 0.\nYour version was a lot different, therefore you would\nneed to set buffer and size before calling it\nand free the buffer if reading failed after that:\nhttp://article.gmane.org/gmane.comp.version-control.git/52835\n\nNow I need to create the new file editor.c to group at\nleast three functions related with reading and editing text.\nThose also will reduce the size of your code and also\ncan be reused from builtin-tag.c: launch_editor,\nread_path (as read_file someone said), and stripspace.\n\nThe first problem now should be how to write a version for\nlaunch_editor() which both builtins (yours and mine) could share,\nbecause the references in your launch_editor() related to commits,\nremoved already in my version for git-tag.\n\nI did this job trying to make easier your porting by moving\nthose parts shared with builtin-tag.c outside your code\nand generating discussion on them, so you\ncan concentrate just in the specific parts and reuse\nthe rest. I hope those can help you.\n\n---\nCarlos Rica\n"},{"id":"47968","messageId":"1184942724.26967.105.camel@hinata.boston.redhat.com","threadId":"9098","inReplyTo":"1b46aba20707181427y12bd8b62pe30df61219e7c1f7@mail.gmail.com","subject":"Re: [PATCH] Implement git commit as a builtin.","fromName":"Kristian Høgsberg","fromEmail":"krh@redhat.com","sentAt":"2007-07-20T14:45:24Z","receivedAt":"2007-07-20T14:45:24Z","isPatch":true,"sender":{"key":"krh@redhat.com","avatar":"https://gravatar.com/avatar/763dee6f9594ac474f725b137a39565792928e583ddf59b32befc2907409027e?d=mp&s=160"},"body":"On Wed, 2007-07-18 at 23:27 +0200, Carlos Rica wrote:\n> 2007/7/18, Kristian Høgsberg <krh@redhat.com>:\n> > +       if (buffer) {\n> > +               len = strip_lines(buffer, len);\n> > +\n> > +               if (fwrite(buffer, 1, len, fp) < len)\n> > +                       die(\"could not write commit template: %s\\n\",\n> > +                           strerror(errno));\n> > +       }\n> ....\n> > +       len = strip_lines(buffer, len);\n> > +\n> > +       if (message_is_empty(buffer, len))\n> > +               die(\"* no commit message?  aborting commit.\");\n> > +\n> \n> Hi Kristian, you could call to the new stripspace() function\n> in builtin-stripspace.c, to reduce code in your file. The only\n> thing you should consider is that the new stripspace()\n> removes always the last '\\n' in the file (if any), so you have to\n> add it when you need.\n\nYup, I'm keeping an eye on all your changes, and it looks like it's all\nfalling in place nicely.  Once I have the last few pieces of\nfunctionality ported over I'll rebase and update builtin-commit to use\nthe new functionality you've landed.\n\nA couple of things that might be interesting right now is: 1) pick out\nthe test case I made for commit and get it upstream, and 2) take the\nchanges I did to wt-status.c (making it able to run on a different index\nand output to a specified FILE pointer) and get that upstream.\n\ncheers,\nKristian\n"}]}