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 <gitster@pobox.com>
This commit is contained in:
Junio C Hamano 2007-10-23 13:33:26 -07:00
Родитель 85b0045505
Коммит 7791ecbc62
4 изменённых файлов: 64 добавлений и 9 удалений

Просмотреть файл

@ -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
-----------
@ -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

Просмотреть файл

@ -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
-----------
@ -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.

Просмотреть файл

@ -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);

Просмотреть файл

@ -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