{"thread":{"id":"7028","subject":"[PATCH] Make git-revert & git-cherry-pick a builtin","startedAt":"2007-03-01T04:26:30Z","lastAt":"2007-03-01T22:24:27Z","messageCount":5,"participants":["Johannes Schindelin","Junio C Hamano","Shawn O. Pearce"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"35915","messageId":"Pine.LNX.4.63.0703010526080.22628@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"7028","inReplyTo":null,"subject":"[PATCH] Make git-revert & git-cherry-pick a builtin","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-03-01T04:26:30Z","receivedAt":"2007-03-01T04:26:30Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Makefile         |   11 +-\n builtin-revert.c |  406 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n builtin.h        |    2 +\n git-revert.sh    |  197 --------------------------\n git.c            |    2 +\n 5 files changed, 414 insertions(+), 204 deletions(-)\n create mode 100644 builtin-revert.c\n delete mode 100755 git-revert.sh\n\ndiff --git a/Makefile b/Makefile\nindex e4bb865..6060b48 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -177,7 +177,7 @@ SCRIPT_SH = \\\n \tgit-merge-one-file.sh git-parse-remote.sh \\\n \tgit-pull.sh git-rebase.sh \\\n \tgit-repack.sh git-request-pull.sh git-reset.sh \\\n-\tgit-revert.sh git-sh-setup.sh \\\n+\tgit-sh-setup.sh \\\n \tgit-tag.sh git-verify-tag.sh \\\n \tgit-applymbox.sh git-applypatch.sh git-am.sh \\\n \tgit-merge.sh git-merge-stupid.sh git-merge-octopus.sh \\\n@@ -193,7 +193,7 @@ SCRIPT_PERL = \\\n \n SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \\\n \t  $(patsubst %.perl,%,$(SCRIPT_PERL)) \\\n-\t  git-cherry-pick git-status git-instaweb\n+\t  git-status git-instaweb\n \n # ... and all the rest that could be moved out of bindir to gitexecdir\n PROGRAMS = \\\n@@ -220,7 +220,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 \\\n+\tgit-fsck-objects$X git-cherry-pick$X \\\n \t$(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))\n \n # what 'all' will build and 'install' will install, in gitexecdir\n@@ -313,6 +313,7 @@ BUILTIN_OBJS = \\\n \tbuiltin-rerere.o \\\n \tbuiltin-rev-list.o \\\n \tbuiltin-rev-parse.o \\\n+\tbuiltin-revert.o \\\n \tbuiltin-rm.o \\\n \tbuiltin-runstatus.o \\\n \tbuiltin-shortlog.o \\\n@@ -684,10 +685,6 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl\n \tchmod +x $@+\n \tmv $@+ $@\n \n-git-cherry-pick: git-revert\n-\tcp $< $@+\n-\tmv $@+ $@\n-\n git-status: git-commit\n \tcp $< $@+\n \tmv $@+ $@\ndiff --git a/builtin-revert.c b/builtin-revert.c\nnew file mode 100644\nindex 0000000..bc3cfcb\n--- /dev/null\n+++ b/builtin-revert.c\n@@ -0,0 +1,406 @@\n+#include \"cache.h\"\n+#include \"builtin.h\"\n+#include \"object.h\"\n+#include \"commit.h\"\n+#include \"tag.h\"\n+#include \"wt-status.h\"\n+#include \"run-command.h\"\n+#include \"exec_cmd.h\"\n+#include \"utf8.h\"\n+\n+/*\n+ * This implements the builtins revert and cherry-pick.\n+ *\n+ * Copyright (c) 2007 Johannes E. Schindelin\n+ *\n+ * Based on git-revert.sh, which is\n+ *\n+ * Copyright (c) 2005 Linus Torvalds\n+ * Copyright (c) 2005 Junio C Hamano\n+ */\n+\n+static const char *revert_usage = \"git-revert [--edit | --no-edit] [-n] <commit-ish>\";\n+\n+static const char *cherry_pick_usage = \"git-cherry-pick [--edit] [-n] [-r] [-x] <commit-ish>\";\n+\n+static int edit;\n+static int replay;\n+enum { REVERT, CHERRY_PICK } action;\n+static int no_commit;\n+static struct commit *commit;\n+static int needed_deref;\n+\n+static const char *me;\n+\n+#define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n+\n+static void parse_options(int argc, const char **argv)\n+{\n+\tconst char *usage_str = action == REVERT ?\n+\t\trevert_usage : cherry_pick_usage;\n+\tunsigned char sha1[20];\n+\tconst char *arg;\n+\tint i;\n+\n+\tif (argc < 2)\n+\t\tusage(usage_str);\n+\n+\tfor (i = 1; i < argc - 1; i++) {\n+\t\targ = argv[i];\n+\t\tif (!strcmp(arg, \"-n\") || !strcmp(arg, \"--no-commit\"))\n+\t\t\tno_commit = 1;\n+\t\telse if (!strcmp(arg, \"-e\") || !strcmp(arg, \"--edit\"))\n+\t\t\tedit = 1;\n+\t\telse if (!strcmp(arg, \"--no-edit\"))\n+\t\t\tedit = 0;\n+\t\telse if (!strcmp(arg, \"-x\") || !strcmp(arg, \"--i-really-want-\"\n+\t\t\t\t\"to-expose-my-private-commit-object-name\"))\n+\t\t\treplay = 0;\n+\t\telse if (strcmp(arg, \"-r\"))\n+\t\t\tusage(usage_str);\n+\t}\n+\n+\targ = argv[argc - 1];\n+\tif (get_sha1(arg, sha1))\n+\t\tdie (\"Cannot find '%s'\", arg);\n+\tcommit = (struct commit *)parse_object(sha1);\n+\tif (!commit)\n+\t\tdie (\"Could not find %s\", sha1_to_hex(sha1));\n+\tif (commit->object.type == OBJ_TAG) {\n+\t\tcommit = (struct commit *)\n+\t\t\tderef_tag((struct object *)commit, arg, strlen(arg));\n+\t\tneeded_deref = 1;\n+\t}\n+\tif (commit->object.type != OBJ_COMMIT)\n+\t\tdie (\"'%s' does not point to a commit\", arg);\n+}\n+\n+static char *get_oneline(const char *message)\n+{\n+\tchar *result;\n+\tconst char *p = message, *abbrev, *eol;\n+\tint abbrev_len, oneline_len;\n+\n+\tif (!p)\n+\t\tdie (\"Could not read commit message of %s\",\n+\t\t\t\tsha1_to_hex(commit->object.sha1));\n+\twhile (*p && (*p != '\\n' || p[1] != '\\n'))\n+\t\tp++;\n+\n+\tif (*p) {\n+\t\tp += 2;\n+\t\tfor (eol = p + 1; *eol && *eol != '\\n'; eol++)\n+\t\t\t; /* do nothing */\n+\t} else\n+\t\teol = p;\n+\tabbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);\n+\tabbrev_len = strlen(abbrev);\n+\toneline_len = eol - p;\n+\tresult = xmalloc(abbrev_len + 5 + oneline_len);\n+\tmemcpy(result, abbrev, abbrev_len);\n+\tmemcpy(result + abbrev_len, \"... \", 4);\n+\tmemcpy(result + abbrev_len + 4, p, oneline_len);\n+\tresult[abbrev_len + 4 + oneline_len] = '\\0';\n+\treturn result;\n+}\n+\n+char *get_encoding(const char *message)\n+{\n+\tconst char *p = message, *eol;\n+\n+\tif (!p)\n+\t\tdie (\"Could not read commit message of %s\",\n+\t\t\t\tsha1_to_hex(commit->object.sha1));\n+\twhile (*p && *p != '\\n') {\n+\t\tfor (eol = p + 1; *eol && *eol != '\\n'; eol++)\n+\t\t\t; /* do nothing */\n+\t\tif (!prefixcmp(p, \"encoding \")) {\n+\t\t\tchar *result = xmalloc(eol - 8 - p);\n+\t\t\tstrlcpy(result, p + 9, eol - 8 - p);\n+\t\t\treturn result;\n+\t\t}\n+\t\tp = eol;\n+\t\tif (*p == '\\n')\n+\t\t\tp++;\n+\t}\n+\treturn NULL;\n+}\n+\n+struct lock_file msg_file;\n+static int msg_fd;\n+\n+static void add_to_msg(const char *string)\n+{\n+\tint len = strlen(string);\n+\tif (write_in_full(msg_fd, string, len) < 0)\n+\t\tdie (\"Could not write to .msg\");\n+}\n+\n+static void add_message_to_msg(const char *message)\n+{\n+\tconst char *p = message;\n+\twhile (*p && (*p != '\\n' || p[1] != '\\n'))\n+\t\tp++;\n+\n+\tif (!*p)\n+\t\tadd_to_msg(sha1_to_hex(commit->object.sha1));\n+\n+\tp += 2;\n+\tadd_to_msg(p);\n+\treturn;\n+}\n+\n+static void set_author_ident_env(const char *message)\n+{\n+\tconst char *p = message;\n+\tif (!p)\n+\t\tdie (\"Could not read commit message of %s\",\n+\t\t\t\tsha1_to_hex(commit->object.sha1));\n+\twhile (*p && *p != '\\n') {\n+\t\tconst char *eol;\n+\n+\t\tfor (eol = p; *eol && *eol != '\\n'; eol++)\n+\t\t\t; /* do nothing */\n+\t\tif (!prefixcmp(p, \"author \")) {\n+\t\t\tchar *line, *pend, *email, *timestamp;\n+\n+\t\t\tp += 7;\n+\t\t\tline = xmalloc(eol + 1 - p);\n+\t\t\tmemcpy(line, p, eol - p);\n+\t\t\tline[eol - p] = '\\0';\n+\t\t\temail = strchr(line, '<');\n+\t\t\tif (!email)\n+\t\t\t\tdie (\"Could not extract author email from %s\",\n+\t\t\t\t\tsha1_to_hex(commit->object.sha1));\n+\t\t\tif (email == line)\n+\t\t\t\tpend = line;\n+\t\t\telse\n+\t\t\t\tfor (pend = email; pend != line + 1 &&\n+\t\t\t\t\t\tisspace(pend[-1]); pend--);\n+\t\t\t\t\t; /* do nothing */\n+\t\t\t*pend = '\\0';\n+\t\t\temail++;\n+\t\t\ttimestamp = strchr(email, '>');\n+\t\t\tif (!timestamp)\n+\t\t\t\tdie (\"Could not extract author email from %s\",\n+\t\t\t\t\tsha1_to_hex(commit->object.sha1));\n+\t\t\t*timestamp = '\\0';\n+\t\t\tfor (timestamp++; *timestamp && isspace(*timestamp);\n+\t\t\t\t\ttimestamp++)\n+\t\t\t\t; /* do nothing */\n+\t\t\tsetenv(\"GIT_AUTHOR_NAME\", line, 1);\n+\t\t\tsetenv(\"GIT_AUTHOR_EMAIL\", email, 1);\n+\t\t\tsetenv(\"GIT_AUTHOR_DATE\", timestamp, 1);\n+\t\t\tfree(line);\n+\t\t\treturn;\n+\t\t}\n+\t\tp = eol;\n+\t\tif (*p == '\\n')\n+\t\t\tp++;\n+\t}\n+\tdie (\"No author information found in %s\",\n+\t\t\tsha1_to_hex(commit->object.sha1));\n+}\n+\n+static int merge_recursive(const char *base_sha1,\n+\t\tconst char *head_sha1, const char *head_name,\n+\t\tconst char *next_sha1, const char *next_name)\n+{\n+\tchar buffer[256];\n+\n+\tsprintf(buffer, \"GITHEAD_%s\", head_sha1);\n+\tsetenv(buffer, head_name, 1);\n+\tsprintf(buffer, \"GITHEAD_%s\", next_sha1);\n+\tsetenv(buffer, next_name, 1);\n+\n+\t/*\n+\t * This three way merge is an interesting one.  We are at\n+\t * $head, and would want to apply the change between $commit\n+\t * and $prev on top of us (when reverting), or the change between\n+\t * $prev and $commit on top of us (when cherry-picking or replaying).\n+\t */\n+\n+\treturn run_command_opt(RUN_COMMAND_NO_STDIN | RUN_GIT_CMD,\n+\t\t\t\"merge-recursive\", base_sha1, \"--\",\n+\t\t\thead_sha1, next_sha1, NULL);\n+}\n+\n+static int revert_or_cherry_pick(int argc, const char **argv)\n+{\n+\tunsigned char head[20];\n+\tstruct commit *base, *next;\n+\tint i;\n+\tchar *oneline, *encoding, *reencoded_message = NULL;\n+\tconst char *message;\n+\n+\tgit_config(git_default_config);\n+\tme = action == REVERT ? \"revert\" : \"cherry-pick\";\n+\tsetenv(GIT_REFLOG_ACTION, me, 0);\n+\tparse_options(argc, argv);\n+\n+\t/* this is copied from the shell script, but it's never triggered... */\n+\tif (action == REVERT && replay)\n+\t\tdie(\"revert is incompatible with replay\");\n+\n+\tif (no_commit) {\n+\t\t/*\n+\t\t * We do not intend to commit immediately.  We just want to\n+\t\t * merge the differences in.\n+\t\t */\n+\t\tif (write_tree(head, 0, NULL))\n+\t\t\tdie (\"Your index file is unmerged.\");\n+\t} else {\n+\t\tstruct wt_status s;\n+\n+\t\tif (get_sha1(\"HEAD\", head))\n+\t\t\tdie (\"You do not have a valid HEAD\");\n+\t\twt_status_prepare(&s);\n+\t\tif (s.commitable || s.workdir_dirty)\n+\t\t\tdie (\"Dirty index: cannot %s\", me);\n+\t\tdiscard_cache();\n+\t}\n+\n+\tif (!commit->parents)\n+\t\tdie (\"Cannot %s a root commit\", me);\n+\tif (commit->parents->next)\n+\t\tdie (\"Cannot %s a multi-parent commit.\", me);\n+\tif (!(message = commit->buffer))\n+\t\tdie (\"Cannot get commit message for %s\",\n+\t\t\t\tsha1_to_hex(commit->object.sha1));\n+\n+\t/*\n+\t * \"commit\" is an existing commit.  We would want to apply\n+\t * the difference it introduces since its first parent \"prev\"\n+\t * on top of the current HEAD if we are cherry-pick.  Or the\n+\t * reverse of it if we are revert.\n+\t */\n+\n+\tmsg_fd = hold_lock_file_for_update(&msg_file, \".msg\", 1);\n+\n+\tencoding = get_encoding(message);\n+\tif (!encoding)\n+\t\tencoding = \"utf-8\";\n+\tif (!git_commit_encoding)\n+\t\tgit_commit_encoding = \"utf-8\";\n+\tif ((reencoded_message = reencode_string(message,\n+\t\t\t\t\tgit_commit_encoding, encoding)))\n+\t\tmessage = reencoded_message;\n+\n+\toneline = get_oneline(message);\n+\n+\tif (action == REVERT) {\n+\t\tbase = commit;\n+\t\tnext = commit->parents->item;\n+\t\tadd_to_msg(\"Revert \");\n+\t\tadd_to_msg(find_unique_abbrev(commit->object.sha1,\n+\t\t\t\t\tDEFAULT_ABBREV));\n+\t\tadd_to_msg(oneline);\n+\t\tadd_to_msg(\"\\nThis reverts commit \");\n+\t\tadd_to_msg(sha1_to_hex(commit->object.sha1));\n+\t\tadd_to_msg(\".\\n\");\n+\t} else {\n+\t\tbase = commit->parents->item;\n+\t\tnext = commit;\n+\t\tset_author_ident_env(message);\n+\t\tadd_message_to_msg(message);\n+\t\tif (replay) {\n+\t\t\tadd_to_msg(\"\\n(cherry picked from commit \");\n+\t\t\tadd_to_msg(sha1_to_hex(commit->object.sha1));\n+\t\t\tadd_to_msg(\")\\n\");\n+\t\t}\n+\t}\n+\tif (needed_deref) {\n+\t\tadd_to_msg(\"(original 'git \");\n+\t\tadd_to_msg(me);\n+\t\tadd_to_msg(\"' arguments: \");\n+\t\tfor (i = 0; i < argc; i++) {\n+\t\t\tif (i)\n+\t\t\t\tadd_to_msg(\" \");\n+\t\t\tadd_to_msg(argv[i]);\n+\t\t}\n+\t\tadd_to_msg(\")\\n\");\n+\t}\n+\n+\tif (merge_recursive(sha1_to_hex(base->object.sha1),\n+\t\t\t\tsha1_to_hex(head), \"HEAD\",\n+\t\t\t\tsha1_to_hex(next->object.sha1), oneline))\n+\t\texit(1);\n+\tif (write_tree(head, 0, NULL)) {\n+\t\tconst char *target = git_path(\"MERGE_MSG\");\n+\t\tadd_to_msg(\"\\nConflicts:\\n\\n\");\n+\t\tread_cache();\n+\t\tfor (i = 0; i < active_nr;) {\n+\t\t\tstruct cache_entry *ce = active_cache[i++];\n+\t\t\tif (ce_stage(ce)) {\n+\t\t\t\tadd_to_msg(\"\\t\");\n+\t\t\t\tadd_to_msg(ce->name);\n+\t\t\t\tadd_to_msg(\"\\n\");\n+\t\t\t\twhile (i < active_nr && !strcmp(ce->name,\n+\t\t\t\t\t\tactive_cache[i]->name))\n+\t\t\t\t\ti++;\n+\t\t\t}\n+\t\t}\n+\t\tif (close(msg_fd) || commit_lock_file(&msg_file) < 0)\n+\t\t\tdie (\"Error wrapping up .msg\");\n+\t\tunlink(target);\n+\t\tif (rename(\".msg\", target))\n+\t\t\tdie (\"Could not move .msg to %s\", target);\n+\t\tfprintf(stderr, \"Automatic %s failed.  \"\n+\t\t\t\"After resolving the conflicts,\\n\"\n+\t\t\t\"mark the corrected paths with 'git-add <paths>'\\n\"\n+\t\t\t\"and commit the result.\\n\", me);\n+\t\tif (action == CHERRY_PICK) {\n+\t\t\tfprintf(stderr, \"You may choose to use the following \"\n+\t\t\t\t\"when making the commit:\\n\"\n+\t\t\t\t\"GIT_AUTHOR_NAME=\\\"%s\\\"\\n\",\n+\t\t\t\tgetenv(\"GIT_AUTHOR_NAME\"));\n+\t\t\tfprintf(stderr, \"GIT_AUTHOR_EMAIL=\\\"%s\\\"\\n\",\n+\t\t\t\tgetenv(\"GIT_AUTHOR_EMAIL\"));\n+\t\t\tfprintf(stderr, \"GIT_AUTHOR_DATE=\\\"%s\\\"\\n\"\n+\t\t\t\t\"export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL \"\n+\t\t\t\t\"GIT_AUTHOR_DATE\\n\",\n+\t\t\t\tgetenv(\"GIT_AUTHOR_DATE\"));\n+\t\t}\n+\t\texit(1);\n+\t}\n+\tif (close(msg_fd) || commit_lock_file(&msg_file) < 0)\n+\t\tdie (\"Error wrapping up .msg\");\n+\tfprintf(stderr, \"Finished one %s.\\n\", me);\n+\n+\t/*\n+\t *\n+\t * If we are cherry-pick, and if the merge did not result in\n+\t * hand-editing, we will hit this commit and inherit the original\n+\t * author date and name.\n+\t * If we are revert, or if our cherry-pick results in a hand merge,\n+\t * we had better say that the current user is responsible for that.\n+\t */\n+\n+\tif (!no_commit) {\n+\t\tif (edit)\n+\t\t\treturn execl_git_cmd(\"commit\", \"-n\", \"-F\", \".msg\",\n+\t\t\t\t\"-e\", NULL);\n+\t\telse\n+\t\t\treturn execl_git_cmd(\"commit\", \"-n\", \"-F\", \".msg\",\n+\t\t\t\tNULL);\n+\t}\n+\tif (reencoded_message)\n+\t\tfree(reencoded_message);\n+\n+\treturn 0;\n+}\n+\n+int cmd_revert(int argc, const char **argv, const char *prefix)\n+{\n+\tif (isatty(0))\n+\t\tedit = 1;\n+\taction = REVERT;\n+\treturn revert_or_cherry_pick(argc, argv);\n+}\n+\n+int cmd_cherry_pick(int argc, const char **argv, const char *prefix)\n+{\n+\treplay = 1;\n+\taction = CHERRY_PICK;\n+\treturn revert_or_cherry_pick(argc, argv);\n+}\ndiff --git a/builtin.h b/builtin.h\nindex 48d68c8..1cb64b7 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -24,6 +24,7 @@ extern int cmd_cat_file(int argc, const char **argv, const char *prefix);\n extern int cmd_checkout_index(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_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@@ -61,6 +62,7 @@ extern int cmd_config(int argc, const char **argv, const char *prefix);\n extern int cmd_rerere(int argc, const char **argv, const char *prefix);\n 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);\ndiff --git a/git-revert.sh b/git-revert.sh\ndeleted file mode 100755\nindex 49f0032..0000000\n--- a/git-revert.sh\n+++ /dev/null\n@@ -1,197 +0,0 @@\n-#!/bin/sh\n-#\n-# Copyright (c) 2005 Linus Torvalds\n-# Copyright (c) 2005 Junio C Hamano\n-#\n-\n-case \"$0\" in\n-*-revert* )\n-\ttest -t 0 && edit=-e\n-\treplay=\n-\tme=revert\n-\tUSAGE='[--edit | --no-edit] [-n] <commit-ish>' ;;\n-*-cherry-pick* )\n-\treplay=t\n-\tedit=\n-\tme=cherry-pick\n-\tUSAGE='[--edit] [-n] [-r] [-x] <commit-ish>'  ;;\n-* )\n-\techo >&2 \"What are you talking about?\"\n-\texit 1 ;;\n-esac\n-\n-SUBDIRECTORY_OK=Yes ;# we will cd up\n-. git-sh-setup\n-require_work_tree\n-cd_to_toplevel\n-\n-no_commit=\n-while case \"$#\" in 0) break ;; esac\n-do\n-\tcase \"$1\" in\n-\t-n|--n|--no|--no-|--no-c|--no-co|--no-com|--no-comm|\\\n-\t    --no-commi|--no-commit)\n-\t\tno_commit=t\n-\t\t;;\n-\t-e|--e|--ed|--edi|--edit)\n-\t\tedit=-e\n-\t\t;;\n-\t--n|--no|--no-|--no-e|--no-ed|--no-edi|--no-edit)\n-\t\tedit=\n-\t\t;;\n-\t-r)\n-\t\t: no-op ;;\n-\t-x|--i-really-want-to-expose-my-private-commit-object-name)\n-\t\treplay=\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-\n-set_reflog_action \"$me\"\n-\n-test \"$me,$replay\" = \"revert,t\" && usage\n-\n-case \"$no_commit\" in\n-t)\n-\t# We do not intend to commit immediately.  We just want to\n-\t# merge the differences in.\n-\thead=$(git-write-tree) ||\n-\t\tdie \"Your index file is unmerged.\"\n-\t;;\n-*)\n-\thead=$(git-rev-parse --verify HEAD) ||\n-\t\tdie \"You do not have a valid HEAD\"\n-\tfiles=$(git-diff-index --cached --name-only $head) || exit\n-\tif [ \"$files\" ]; then\n-\t\tdie \"Dirty index: cannot $me (dirty: $files)\"\n-\tfi\n-\t;;\n-esac\n-\n-rev=$(git-rev-parse --verify \"$@\") &&\n-commit=$(git-rev-parse --verify \"$rev^0\") ||\n-\tdie \"Not a single commit $@\"\n-prev=$(git-rev-parse --verify \"$commit^1\" 2>/dev/null) ||\n-\tdie \"Cannot run $me a root commit\"\n-git-rev-parse --verify \"$commit^2\" >/dev/null 2>&1 &&\n-\tdie \"Cannot run $me a multi-parent commit.\"\n-\n-encoding=$(git config i18n.commitencoding || echo UTF-8)\n-\n-# \"commit\" is an existing commit.  We would want to apply\n-# the difference it introduces since its first parent \"prev\"\n-# on top of the current HEAD if we are cherry-pick.  Or the\n-# reverse of it if we are revert.\n-\n-case \"$me\" in\n-revert)\n-\tgit show -s --pretty=oneline --encoding=\"$encoding\" $commit |\n-\tsed -e '\n-\t\ts/^[^ ]* /Revert \"/\n-\t\ts/$/\"/\n-\t'\n-\techo\n-\techo \"This reverts commit $commit.\"\n-\ttest \"$rev\" = \"$commit\" ||\n-\techo \"(original 'git revert' arguments: $@)\"\n-\tbase=$commit next=$prev\n-\t;;\n-\n-cherry-pick)\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-\n-\tlogmsg=`git show -s --pretty=raw --encoding=\"$encoding\" \"$commit\"`\n-\tset_author_env=`echo \"$logmsg\" |\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-\n-\techo \"$logmsg\" |\n-\tsed -e '1,/^$/d' -e 's/^    //'\n-\tcase \"$replay\" in\n-\t'')\n-\t\techo \"(cherry picked from commit $commit)\"\n-\t\ttest \"$rev\" = \"$commit\" ||\n-\t\techo \"(original 'git cherry-pick' arguments: $@)\"\n-\t\t;;\n-\tesac\n-\tbase=$prev next=$commit\n-\t;;\n-\n-esac >.msg\n-\n-eval GITHEAD_$head=HEAD\n-eval GITHEAD_$next='`git show -s \\\n-\t--pretty=oneline --encoding=\"$encoding\" \"$commit\" |\n-\tsed -e \"s/^[^ ]* //\"`'\n-export GITHEAD_$head GITHEAD_$next\n-\n-# This three way merge is an interesting one.  We are at\n-# $head, and would want to apply the change between $commit\n-# and $prev on top of us (when reverting), or the change between\n-# $prev and $commit on top of us (when cherry-picking or replaying).\n-\n-git-merge-recursive $base -- $head $next &&\n-result=$(git-write-tree 2>/dev/null) || {\n-\tmv -f .msg \"$GIT_DIR/MERGE_MSG\"\n-\t{\n-\t    echo '\n-Conflicts:\n-'\n-\t\tgit ls-files --unmerged |\n-\t\tsed -e 's/^[^\t]*\t/\t/' |\n-\t\tuniq\n-\t} >>\"$GIT_DIR/MERGE_MSG\"\n-\techo >&2 \"Automatic $me failed.  After resolving the conflicts,\"\n-\techo >&2 \"mark the corrected paths with 'git-add <paths>'\"\n-\techo >&2 \"and commit the result.\"\n-\tcase \"$me\" in\n-\tcherry-pick)\n-\t\techo >&2 \"You may choose to use the following when making\"\n-\t\techo >&2 \"the commit:\"\n-\t\techo >&2 \"$set_author_env\"\n-\tesac\n-\texit 1\n-}\n-echo >&2 \"Finished one $me.\"\n-\n-# If we are cherry-pick, and if the merge did not result in\n-# hand-editing, we will hit this commit and inherit the original\n-# author date and name.\n-# If we are revert, or if our cherry-pick results in a hand merge,\n-# we had better say that the current user is responsible for that.\n-\n-case \"$no_commit\" in\n-'')\n-\tgit-commit -n -F .msg $edit\n-\trm -f .msg\n-\t;;\n-esac\ndiff --git a/git.c b/git.c\nindex 008e016..fe0ce61 100644\n--- a/git.c\n+++ b/git.c\n@@ -234,6 +234,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)\n \t\t{ \"checkout-index\", cmd_checkout_index, RUN_SETUP },\n \t\t{ \"check-ref-format\", cmd_check_ref_format },\n \t\t{ \"cherry\", cmd_cherry, RUN_SETUP },\n+\t\t{ \"cherry-pick\", cmd_cherry_pick, RUN_SETUP | NOT_BARE },\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@@ -273,6 +274,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)\n \t\t{ \"rerere\", cmd_rerere, RUN_SETUP },\n \t\t{ \"rev-list\", cmd_rev_list, RUN_SETUP },\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-- \n1.5.0.2.809.g0f936-dirty\n"},{"id":"35968","messageId":"7vzm6xqj0d.fsf@assigned-by-dhcp.cox.net","threadId":"7028","inReplyTo":"Pine.LNX.4.63.0703010526080.22628@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] Make git-revert & git-cherry-pick a builtin","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-03-01T11:51:30Z","receivedAt":"2007-03-01T11:51:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  Makefile         |   11 +-\n>  builtin-revert.c |  406 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n>  builtin.h        |    2 +\n>  git-revert.sh    |  197 --------------------------\n>  git.c            |    2 +\n>  5 files changed, 414 insertions(+), 204 deletions(-)\n>  create mode 100644 builtin-revert.c\n>  delete mode 100755 git-revert.sh\n\nThanks.  I haven't looked the patch closely but I trust this\nround is a fairly faithful straight translation from the shell\nscript, which is good.  I have a few gripes on the behaviour of\nthese commands, and it might be worthwhile to address them in\nthe later rounds.\n\n> +static int merge_recursive(const char *base_sha1,\n> +\t\tconst char *head_sha1, const char *head_name,\n> +\t\tconst char *next_sha1, const char *next_name)\n> +{\n> + ...\n> +}\n\nSomehow I would have expected you to call merge-recursive not\nspawn, but this is saner ;-).\n\n> +static int revert_or_cherry_pick(int argc, const char **argv)\n> +{\n> + ...\n> +\tmsg_fd = hold_lock_file_for_update(&msg_file, \".msg\", 1);\n\nAlthough the detailed reason escapes me, we earlier moved the\ntemporary files to $GIT_DIR/.  This .msg in the current\ndirectory somehow survived.  It probably is a good idea to move\nthis to $GIT_DIR/ in later rounds.\n\n> +\t\tif (action == CHERRY_PICK) {\n> +\t\t\tfprintf(stderr, \"You may choose to use the following \"\n> +\t\t\t\t\"when making the commit:\\n\"\n> +\t\t\t\t\"GIT_AUTHOR_NAME=\\\"%s\\\"\\n\",\n> +\t\t\t\tgetenv(\"GIT_AUTHOR_NAME\"));\n> +\t\t\tfprintf(stderr, \"GIT_AUTHOR_EMAIL=\\\"%s\\\"\\n\",\n> +\t\t\t\tgetenv(\"GIT_AUTHOR_EMAIL\"));\n> +\t\t\tfprintf(stderr, \"GIT_AUTHOR_DATE=\\\"%s\\\"\\n\"\n> +\t\t\t\t\"export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL \"\n> +\t\t\t\t\"GIT_AUTHOR_DATE\\n\",\n> +\t\t\t\tgetenv(\"GIT_AUTHOR_DATE\"));\n> +\t\t}\n> +\t\texit(1);\n> +\t}\n\nI am to blame for this crap, but I think this part of code was\ndone before we invented \"git commit -C <commit>\".  It would be a\nlot better to suggest using that command.\n"},{"id":"36009","messageId":"Pine.LNX.4.63.0703012230290.22628@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"7028","inReplyTo":"7vzm6xqj0d.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Make git-revert & git-cherry-pick a builtin","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-03-01T21:37:20Z","receivedAt":"2007-03-01T21:37:20Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 1 Mar 2007, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > ---\n> >  Makefile         |   11 +-\n> >  builtin-revert.c |  406 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n> >  builtin.h        |    2 +\n> >  git-revert.sh    |  197 --------------------------\n> >  git.c            |    2 +\n> >  5 files changed, 414 insertions(+), 204 deletions(-)\n> >  create mode 100644 builtin-revert.c\n> >  delete mode 100755 git-revert.sh\n> \n> Thanks.  I haven't looked the patch closely but I trust this\n> round is a fairly faithful straight translation from the shell\n> script, which is good.\n\nI tried. Of course, it helped tremendously that there were test cases. \nNevertheless would I appreciate review from the list...\n\n> I have a few gripes on the behaviour of these commands, and it might be \n> worthwhile to address them in the later rounds.\n\nYes, that was part of the reason I rewrote these two in C: For example, I \nwould like a little bit better locking (double locking index would be a \ngood start).\n\nOf course, it makes it easier to write cmd_rebase(): I plan to put it into \nthe same file so that the function revert_or_cherry_pick() can easily be \nreused.\n\n> > +static int merge_recursive(const char *base_sha1,\n> > +\t\tconst char *head_sha1, const char *head_name,\n> > +\t\tconst char *next_sha1, const char *next_name)\n> > +{\n> > + ...\n> > +}\n> \n> Somehow I would have expected you to call merge-recursive not spawn, but \n> this is saner ;-).\n\nI briefly considered this, until I realized that merge-recursive is no \nbuiltin yet!\n\nSpeaking of this issue: Would it be conceivable to make the standalones \ninto builtins? (This adds a dependency on libcurl to core Git programs, \nbut I could live with that... It would make a builtin fetch easier, too.)\n\n> > +static int revert_or_cherry_pick(int argc, const char **argv)\n> > +{\n> > + ...\n> > +\tmsg_fd = hold_lock_file_for_update(&msg_file, \".msg\", 1);\n> \n> Although the detailed reason escapes me, we earlier moved the\n> temporary files to $GIT_DIR/.  This .msg in the current\n> directory somehow survived.  It probably is a good idea to move\n> this to $GIT_DIR/ in later rounds.\n\nOkay. I have no idea how much the tests rely on that locations, let alone \nother scripts. Therefore I think it is sane to do it in a separate step.\n\n> > +\t\tif (action == CHERRY_PICK) {\n> > +\t\t\tfprintf(stderr, \"You may choose to use the following \"\n> > +\t\t\t\t\"when making the commit:\\n\"\n> > +\t\t\t\t\"GIT_AUTHOR_NAME=\\\"%s\\\"\\n\",\n> > +\t\t\t\tgetenv(\"GIT_AUTHOR_NAME\"));\n> > +\t\t\tfprintf(stderr, \"GIT_AUTHOR_EMAIL=\\\"%s\\\"\\n\",\n> > +\t\t\t\tgetenv(\"GIT_AUTHOR_EMAIL\"));\n> > +\t\t\tfprintf(stderr, \"GIT_AUTHOR_DATE=\\\"%s\\\"\\n\"\n> > +\t\t\t\t\"export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL \"\n> > +\t\t\t\t\"GIT_AUTHOR_DATE\\n\",\n> > +\t\t\t\tgetenv(\"GIT_AUTHOR_DATE\"));\n> > +\t\t}\n> > +\t\texit(1);\n> > +\t}\n> \n> I am to blame for this crap, but I think this part of code was done \n> before we invented \"git commit -C <commit>\".  It would be a lot better \n> to suggest using that command.\n\nYeah, let's do that, too.\n\nBTW it took me so long because there was a hard-to-catch bug with the \nencodings (of course!). In the end I realized that I only forgot to call \ngit_config(git_default_config)... D'oh.\n\nCiao,\nDscho\n"},{"id":"36012","messageId":"20070301215123.GB9254@spearce.org","threadId":"7028","inReplyTo":"Pine.LNX.4.63.0703012230290.22628@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] Make git-revert & git-cherry-pick a builtin","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-03-01T21:51:23Z","receivedAt":"2007-03-01T21:51:23Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> > > +static int merge_recursive(const char *base_sha1,\n> > > +\t\tconst char *head_sha1, const char *head_name,\n> > > +\t\tconst char *next_sha1, const char *next_name)\n> > > +{\n> > > + ...\n> > > +}\n> > \n> > Somehow I would have expected you to call merge-recursive not spawn, but \n> > this is saner ;-).\n> \n> I briefly considered this, until I realized that merge-recursive is no \n> builtin yet!\n\nHeh, I expected the same thing as Junio when I first read through\nthe patch.  But then I realized that doing so right now might not\nbe a great idea; normally if merge-recursive aborts we say \"fix up\nand commit\" and do a few things still in cherry-pick/revert before\ngiving the shell back to the user.\n\nBut a quick pass through merge-recursive.c says this may actually\nbe possible...\n \n> Speaking of this issue: Would it be conceivable to make the standalones \n> into builtins? (This adds a dependency on libcurl to core Git programs, \n> but I could live with that... It would make a builtin fetch easier, too.)\n\nPlease don't make libcurl required.  I don't build with it on\nSolaris, because its not there, and I don't need it there.\n\n-- \nShawn.\n"},{"id":"36017","messageId":"Pine.LNX.4.63.0703012318020.22628@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"7028","inReplyTo":"20070301215123.GB9254@spearce.org","subject":"Re: [PATCH] Make git-revert & git-cherry-pick a builtin","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-03-01T22:24:27Z","receivedAt":"2007-03-01T22:24:27Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 1 Mar 2007, Shawn O. Pearce wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> > > > +static int merge_recursive(const char *base_sha1,\n> > > > +\t\tconst char *head_sha1, const char *head_name,\n> > > > +\t\tconst char *next_sha1, const char *next_name)\n> > > > +{\n> > > > + ...\n> > > > +}\n> > > \n> > > Somehow I would have expected you to call merge-recursive not spawn, \n> > > but this is saner ;-).\n> > \n> > I briefly considered this, until I realized that merge-recursive is no \n> > builtin yet!\n> \n> Heh, I expected the same thing as Junio when I first read through the \n> patch.  But then I realized that doing so right now might not be a great \n> idea; normally if merge-recursive aborts we say \"fix up and commit\" and \n> do a few things still in cherry-pick/revert before giving the shell back \n> to the user.\n\nIIRC merge-recursive does not quit early with an exit(), rather returns \nthe exit status from the main() routine. So, it would be a matter of \nmaking merge-recursive a builtin, separating the main() method in \nmerge_recursive() and an option parsing cmd_merge_recursive(), and call it \nfrom revert.\n\nI admit that I was a little too uneasy to do that, what with the object \nflags and all, but it _should_ work. After all, merge-recursive is \n_recursive_, so we had to make sure that everything was properly cleaned \nup after each iteration.\n\n> > Speaking of this issue: Would it be conceivable to make the \n> > standalones into builtins? (This adds a dependency on libcurl to core \n> > Git programs, but I could live with that... It would make a builtin \n> > fetch easier, too.)\n> \n> Please don't make libcurl required.  I don't build with it on Solaris, \n> because its not there, and I don't need it there.\n\nI don't mean to make it required. I mean to link git$X to it. That would \nmean that even a silly git-rev-parse dynamically links to libcurl, iff \ncurl support is enabled...\n\nCiao,\nDscho\n"}]}