Skip to content

Commit

Permalink
revert/cherry-pick: work on merge commits as well
Browse files Browse the repository at this point in the history
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 <gitster@pobox.com>
  • Loading branch information
Junio C Hamano committed Oct 27, 2007
1 parent 85b0045 commit 7791ecb
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 9 deletions.
9 changes: 8 additions & 1 deletion Documentation/git-cherry-pick.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ git-cherry-pick - Apply the change introduced by an existing commit

SYNOPSIS
--------
'git-cherry-pick' [--edit] [-n] [-x] <commit>
'git-cherry-pick' [--edit] [-n] [-m parent-number] [-x] <commit>

DESCRIPTION
-----------
Expand Down Expand Up @@ -44,6 +44,13 @@ OPTIONS
described above, and `-r` was to disable it. Now the
default is not to do `-x` so this option is a no-op.

-m parent-number|--mainline parent-number::
Usually you cannot revert a merge because you do not know which
side of the merge should be considered the mainline. This
option specifies the parent number (starting from 1) of
the mainline and allows cherry-pick to replay the change
relative to the specified parent.

-n|--no-commit::
Usually the command automatically creates a commit with
a commit log message stating which commit was
Expand Down
9 changes: 8 additions & 1 deletion Documentation/git-revert.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ git-revert - Revert an existing commit

SYNOPSIS
--------
'git-revert' [--edit | --no-edit] [-n] <commit>
'git-revert' [--edit | --no-edit] [-n] [-m parent-number] <commit>

DESCRIPTION
-----------
Expand All @@ -27,6 +27,13 @@ OPTIONS
message prior committing the revert. This is the default if
you run the command from a terminal.

-m parent-number|--mainline parent-number::
Usually you cannot revert a merge because you do not know which
side of the merge should be considered the mainline. This
option specifies the parent number (starting from 1) of
the mainline and allows revert to reverse the change
relative to the specified parent.

--no-edit::
With this option, `git-revert` will not start the commit
message editor.
Expand Down
42 changes: 35 additions & 7 deletions builtin-revert.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,17 @@
* 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;
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;

Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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);
Expand Down
13 changes: 13 additions & 0 deletions git-compat-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -381,4 +381,17 @@ static inline int strtoul_ui(char const *s, int base, unsigned int *result)
return 0;
}

static inline int strtol_i(char const *s, int base, int *result)
{
long ul;
char *p;

errno = 0;
ul = strtol(s, &p, base);
if (errno || *p || p == s || (int) ul != ul)
return -1;
*result = ul;
return 0;
}

#endif

0 comments on commit 7791ecb

Please sign in to comment.