From 7791ecbc62b792b3eaa6d722b6dadcea4d0f322d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 23 Oct 2007 13:33:26 -0700 Subject: revert/cherry-pick: work on merge commits as well Usually you cannot revert a merge because you do not know which side of the merge should be considered the mainline (iow, what change to reverse). With this patch, cherry-pick and revert learn -m (--mainline) option that lets you specify the parent number (starting from 1) of the mainline, so that you can: git revert -m 1 $merge to reverse the changes introduced by the $merge commit relative to its first parent, and: git cherry-pick -m 2 $merge to replay the changes introduced by the $merge commit relative to its second parent. Signed-off-by: Junio C Hamano --- builtin-revert.c | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) (limited to 'builtin-revert.c') diff --git a/builtin-revert.c b/builtin-revert.c index a655c8ee2..bfed69d7e 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -19,9 +19,9 @@ * Copyright (c) 2005 Junio C Hamano */ -static const char *revert_usage = "git-revert [--edit | --no-edit] [-n] "; +static const char *revert_usage = "git-revert [--edit | --no-edit] [-n] [-m parent-number] "; -static const char *cherry_pick_usage = "git-cherry-pick [--edit] [-n] [-r] [-x] "; +static const char *cherry_pick_usage = "git-cherry-pick [--edit] [-n] [-m parent-number] [-r] [-x] "; 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); -- cgit v1.2.1