Merge branch 'mv/commit-tree'
[gitweb.git] / builtin-revert.c
index b6dee6a56c21be816725a4366f448dac98e62ae6..36677053f87b107f658f1472369755c4fe957a46 100644 (file)
@@ -9,6 +9,9 @@
 #include "utf8.h"
 #include "parse-options.h"
 #include "cache-tree.h"
+#include "diff.h"
+#include "revision.h"
+#include "rerere.h"
 
 /*
  * This implements the builtins revert and cherry-pick.
  */
 
 static const char * const revert_usage[] = {
-       "git-revert [options] <commit-ish>",
+       "git revert [options] <commit-ish>",
        NULL
 };
 
 static const char * const cherry_pick_usage[] = {
-       "git-cherry-pick [options] <commit-ish>",
+       "git cherry-pick [options] <commit-ish>",
        NULL
 };
 
-static int edit, no_replay, no_commit, mainline;
+static int edit, no_replay, no_commit, mainline, signoff;
 static enum { REVERT, CHERRY_PICK } action;
 static struct commit *commit;
 
@@ -51,6 +54,7 @@ static void parse_args(int argc, const char **argv)
                OPT_BOOLEAN('e', "edit", &edit, "edit the commit message"),
                OPT_BOOLEAN('x', NULL, &no_replay, "append commit name when cherry-picking"),
                OPT_BOOLEAN('r', NULL, &noop, "no-op (backward compatibility)"),
+               OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
                OPT_INTEGER('m', "mainline", &mainline, "parent number"),
                OPT_END(),
        };
@@ -177,7 +181,7 @@ static void set_author_ident_env(const char *message)
                        email++;
                        timestamp = strchr(email, '>');
                        if (!timestamp)
-                               die ("Could not extract author email from %s",
+                               die ("Could not extract author time from %s",
                                        sha1_to_hex(commit->object.sha1));
                        *timestamp = '\0';
                        for (timestamp++; *timestamp && isspace(*timestamp);
@@ -203,6 +207,7 @@ static int merge_recursive(const char *base_sha1,
 {
        char buffer[256];
        const char *argv[6];
+       int i = 0;
 
        sprintf(buffer, "GITHEAD_%s", head_sha1);
        setenv(buffer, head_name, 1);
@@ -215,12 +220,13 @@ static int merge_recursive(const char *base_sha1,
         * and $prev on top of us (when reverting), or the change between
         * $prev and $commit on top of us (when cherry-picking or replaying).
         */
-       argv[0] = "merge-recursive";
-       argv[1] = base_sha1;
-       argv[2] = "--";
-       argv[3] = head_sha1;
-       argv[4] = next_sha1;
-       argv[5] = NULL;
+       argv[i++] = "merge-recursive";
+       if (base_sha1)
+               argv[i++] = base_sha1;
+       argv[i++] = "--";
+       argv[i++] = head_sha1;
+       argv[i++] = next_sha1;
+       argv[i++] = NULL;
 
        return run_command_v_opt(argv, RUN_COMMAND_NO_STDIN | RUN_GIT_CMD);
 }
@@ -246,6 +252,17 @@ static char *help_msg(const unsigned char *sha1)
        return helpbuf;
 }
 
+static int index_is_dirty(void)
+{
+       struct rev_info rev;
+       init_revisions(&rev, NULL);
+       setup_revisions(0, NULL, &rev, "HEAD");
+       DIFF_OPT_SET(&rev.diffopt, QUIET);
+       DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS);
+       run_diff_index(&rev, 1);
+       return !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES);
+}
+
 static int revert_or_cherry_pick(int argc, const char **argv)
 {
        unsigned char head[20];
@@ -255,7 +272,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
        const char *message, *encoding;
        const char *defmsg = xstrdup(git_path("MERGE_MSG"));
 
-       git_config(git_default_config);
+       git_config(git_default_config, NULL);
        me = action == REVERT ? "revert" : "cherry-pick";
        setenv(GIT_REFLOG_ACTION, me, 0);
        parse_args(argc, argv);
@@ -274,19 +291,21 @@ static int revert_or_cherry_pick(int argc, const char **argv)
                if (write_cache_as_tree(head, 0, NULL))
                        die ("Your index file is unmerged.");
        } else {
-               struct wt_status s;
-
                if (get_sha1("HEAD", head))
                        die ("You do not have a valid HEAD");
-               wt_status_prepare(&s);
-               if (s.commitable)
+               if (read_cache() < 0)
+                       die("could not read the index");
+               if (index_is_dirty())
                        die ("Dirty index: cannot %s", me);
                discard_cache();
        }
 
-       if (!commit->parents)
-               die ("Cannot %s a root commit", me);
-       if (commit->parents->next) {
+       if (!commit->parents) {
+               if (action == REVERT)
+                       die ("Cannot revert a root commit");
+               parent = NULL;
+       }
+       else if (commit->parents->next) {
                /* Reverting or cherry-picking a merge commit */
                int cnt;
                struct commit_list *p;
@@ -355,7 +374,8 @@ static int revert_or_cherry_pick(int argc, const char **argv)
                }
        }
 
-       if (merge_recursive(sha1_to_hex(base->object.sha1),
+       if (merge_recursive(base == NULL ?
+                               NULL : sha1_to_hex(base->object.sha1),
                                sha1_to_hex(head), "HEAD",
                                sha1_to_hex(next->object.sha1), oneline) ||
                        write_cache_as_tree(head, 0, NULL)) {
@@ -376,6 +396,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
                        die ("Error wrapping up %s", defmsg);
                fprintf(stderr, "Automatic %s failed.%s\n",
                        me, help_msg(commit->object.sha1));
+               rerere();
                exit(1);
        }
        if (commit_lock_file(&msg_file) < 0)
@@ -392,10 +413,19 @@ static int revert_or_cherry_pick(int argc, const char **argv)
         */
 
        if (!no_commit) {
-               if (edit)
-                       return execl_git_cmd("commit", "-n", NULL);
-               else
-                       return execl_git_cmd("commit", "-n", "-F", defmsg, NULL);
+               /* 6 is max possible length of our args array including NULL */
+               const char *args[6];
+               int i = 0;
+               args[i++] = "commit";
+               args[i++] = "-n";
+               if (signoff)
+                       args[i++] = "-s";
+               if (!edit) {
+                       args[i++] = "-F";
+                       args[i++] = defmsg;
+               }
+               args[i] = NULL;
+               return execv_git_cmd(args);
        }
        free(reencoded_message);