Merge branch 'jc/revert-merge'
authorJunio C Hamano <gitster@pobox.com>
Sun, 4 Nov 2007 08:26:02 +0000 (01:26 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 4 Nov 2007 08:26:02 +0000 (01:26 -0700)
* jc/revert-merge:
cherry-pick/revert -m: add tests
revert/cherry-pick: work on merge commits as well

Conflicts:

builtin-revert.c

1  2 
builtin-revert.c
diff --combined builtin-revert.c
index a9347cf9c57b2e4d15d37da609a2851e5b1cfc8e,bfed69d7e2992c659c79bd0c35852cc8da30e968..62ab1fa1f484a113f45da96de8cd209a0c5c522e
@@@ -7,7 -7,6 +7,7 @@@
  #include "run-command.h"
  #include "exec_cmd.h"
  #include "utf8.h"
 +#include "parse-options.h"
  
  /*
   * This implements the builtins revert and cherry-pick.
   * Copyright (c) 2005 Junio C Hamano
   */
  
 -static const char *revert_usage = "git-revert [--edit | --no-edit] [-n] [-m parent-number] <commit-ish>";
 +static const char * const revert_usage[] = {
 +      "git-revert [options] <commit-ish>",
 +      NULL
 +};
  
 -static const char *cherry_pick_usage = "git-cherry-pick [--edit] [-n] [-m parent-number] [-r] [-x] <commit-ish>";
 +static const char * const cherry_pick_usage[] = {
 +      "git-cherry-pick [options] <commit-ish>",
 +      NULL
 +};
  
- static int edit, no_replay, no_commit, needed_deref;
 -static int edit;
 -static int replay;
++static int edit, no_replay, no_commit, needed_deref, mainline;
  static enum { REVERT, CHERRY_PICK } action;
 -static int no_commit;
  static struct commit *commit;
 -static int needed_deref;
 -static int mainline;
  
  static const char *me;
  
  #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
  
 -static void parse_options(int argc, const char **argv)
 +static void parse_args(int argc, const char **argv)
  {
 -      const char *usage_str = action == REVERT ?
 -              revert_usage : cherry_pick_usage;
 +      const char * const * usage_str =
 +              action == REVERT ?  revert_usage : cherry_pick_usage;
        unsigned char sha1[20];
        const char *arg;
 -      int i;
 +      int noop;
 +      struct option options[] = {
 +              OPT_BOOLEAN('n', "no-commit", &no_commit, "don't automatically commit"),
 +              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_INTEGER('m', "mainline", &mainline, "parent number"),
 +              OPT_END(),
 +      };
 +
 +      if (parse_options(argc, argv, options, usage_str, 0) != 1)
 +              usage_with_options(usage_str, options);
 +      arg = argv[0];
 -      if (argc < 2)
 -              usage(usage_str);
 -
 -      for (i = 1; i < argc; i++) {
 -              arg = argv[i];
 -              if (arg[0] != '-')
 -                      break;
 -              if (!strcmp(arg, "-n") || !strcmp(arg, "--no-commit"))
 -                      no_commit = 1;
 -              else if (!strcmp(arg, "-e") || !strcmp(arg, "--edit"))
 -                      edit = 1;
 -              else if (!strcmp(arg, "--no-edit"))
 -                      edit = 0;
 -              else if (!strcmp(arg, "-x") || !strcmp(arg, "--i-really-want-"
 -                              "to-expose-my-private-commit-object-name"))
 -                      replay = 0;
 -              else if (!strcmp(arg, "-m") || !strcmp(arg, "--mainline")) {
 -                      if (++i >= argc ||
 -                          strtol_i(argv[i], 10, &mainline) ||
 -                          mainline <= 0)
 -                              usage(usage_str);
 -              }
 -              else if (strcmp(arg, "-r"))
 -                      usage(usage_str);
 -      }
 -      if (i != argc - 1)
 -              usage(usage_str);
 -      arg = argv[argc - 1];
        if (get_sha1(arg, sha1))
                die ("Cannot find '%s'", arg);
        commit = (struct commit *)parse_object(sha1);
@@@ -226,7 -241,7 +228,7 @@@ static int merge_recursive(const char *
  static int revert_or_cherry_pick(int argc, const char **argv)
  {
        unsigned char head[20];
-       struct commit *base, *next;
+       struct commit *base, *next, *parent;
        int i;
        char *oneline, *reencoded_message = NULL;
        const char *message, *encoding;
        git_config(git_default_config);
        me = action == REVERT ? "revert" : "cherry-pick";
        setenv(GIT_REFLOG_ACTION, me, 0);
 -      parse_options(argc, argv);
 +      parse_args(argc, argv);
  
        /* this is copied from the shell script, but it's never triggered... */
 -      if (action == REVERT && replay)
 +      if (action == REVERT && !no_replay)
                die("revert is incompatible with replay");
  
        if (no_commit) {
  
        if (!commit->parents)
                die ("Cannot %s a root commit", me);
-       if (commit->parents->next)
-               die ("Cannot %s a multi-parent commit.", me);
+       if (commit->parents->next) {
+               /* Reverting or cherry-picking a merge commit */
+               int cnt;
+               struct commit_list *p;
+               if (!mainline)
+                       die("Commit %s is a merge but no -m option was given.",
+                           sha1_to_hex(commit->object.sha1));
+               for (cnt = 1, p = commit->parents;
+                    cnt != mainline && p;
+                    cnt++)
+                       p = p->next;
+               if (cnt != mainline || !p)
+                       die("Commit %s does not have parent %d",
+                           sha1_to_hex(commit->object.sha1), mainline);
+               parent = p->item;
+       } else if (0 < mainline)
+               die("Mainline was specified but commit %s is not a merge.",
+                   sha1_to_hex(commit->object.sha1));
+       else
+               parent = commit->parents->item;
        if (!(message = commit->buffer))
                die ("Cannot get commit message for %s",
                                sha1_to_hex(commit->object.sha1));
                char *oneline_body = strchr(oneline, ' ');
  
                base = commit;
-               next = commit->parents->item;
+               next = parent;
                add_to_msg("Revert \"");
                add_to_msg(oneline_body + 1);
                add_to_msg("\"\n\nThis reverts commit ");
                add_to_msg(sha1_to_hex(commit->object.sha1));
                add_to_msg(".\n");
        } else {
-               base = commit->parents->item;
+               base = parent;
                next = commit;
                set_author_ident_env(message);
                add_message_to_msg(message);
 -              if (!replay) {
 +              if (no_replay) {
                        add_to_msg("(cherry picked from commit ");
                        add_to_msg(sha1_to_hex(commit->object.sha1));
                        add_to_msg(")\n");
                        die ("Error wrapping up %s", defmsg);
                fprintf(stderr, "Automatic %s failed.  "
                        "After resolving the conflicts,\n"
 -                      "mark the corrected paths with 'git-add <paths>'\n"
 +                      "mark the corrected paths with 'git add <paths>' "
                        "and commit the result.\n", me);
                if (action == CHERRY_PICK) {
                        fprintf(stderr, "When commiting, use the option "
@@@ -380,14 -416,13 +403,14 @@@ int cmd_revert(int argc, const char **a
  {
        if (isatty(0))
                edit = 1;
 +      no_replay = 1;
        action = REVERT;
        return revert_or_cherry_pick(argc, argv);
  }
  
  int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
  {
 -      replay = 1;
 +      no_replay = 0;
        action = CHERRY_PICK;
        return revert_or_cherry_pick(argc, argv);
  }