revert/cherry-pick: work on merge commits as well
[git/git.git] / builtin-revert.c
index a655c8e..bfed69d 100644 (file)
@@ -19,9 +19,9 @@
  * Copyright (c) 2005 Junio C Hamano
  */
 
-static const char *revert_usage = "git-revert [--edit | --no-edit] [-n] <commit-ish>";
+static const char *revert_usage = "git-revert [--edit | --no-edit] [-n] [-m parent-number] <commit-ish>";
 
-static const char *cherry_pick_usage = "git-cherry-pick [--edit] [-n] [-r] [-x] <commit-ish>";
+static const char *cherry_pick_usage = "git-cherry-pick [--edit] [-n] [-m parent-number] [-r] [-x] <commit-ish>";
 
 static int edit;
 static int replay;
@@ -29,6 +29,7 @@ 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;
 
@@ -58,6 +59,12 @@ static void parse_options(int argc, const char **argv)
                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);
        }
@@ -234,7 +241,7 @@ static int merge_recursive(const char *base_sha1,
 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;
@@ -269,8 +276,29 @@ static int revert_or_cherry_pick(int argc, const char **argv)
 
        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));
@@ -299,14 +327,14 @@ static int revert_or_cherry_pick(int argc, const char **argv)
                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);