Merge branch 'jc/revert-merge'

* jc/revert-merge:
  cherry-pick/revert -m: add tests
  revert/cherry-pick: work on merge commits as well

Conflicts:

	builtin-revert.c
This commit is contained in:
Junio C Hamano 2007-11-04 01:26:02 -07:00
commit 02273fdbd0
5 changed files with 181 additions and 8 deletions

View File

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

View File

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

View File

@ -30,7 +30,7 @@ static const char * const cherry_pick_usage[] = {
NULL
};
static int edit, no_replay, no_commit, needed_deref;
static int edit, no_replay, no_commit, needed_deref, mainline;
static enum { REVERT, CHERRY_PICK } action;
static struct commit *commit;
@ -50,12 +50,14 @@ static void parse_args(int argc, const char **argv)
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 (get_sha1(arg, sha1))
die ("Cannot find '%s'", arg);
commit = (struct commit *)parse_object(sha1);
@ -226,7 +228,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;
@ -261,8 +263,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));
@ -291,14 +314,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);

View File

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

123
t/t3502-cherry-pick-merge.sh Executable file
View File

@ -0,0 +1,123 @@
#!/bin/sh
test_description='cherry picking and reverting a merge
b---c
/ /
initial---a
'
. ./test-lib.sh
test_expect_success setup '
>A &&
>B &&
git add A B &&
git commit -m "Initial" &&
git tag initial &&
git branch side &&
echo new line >A &&
git commit -m "add line to A" A &&
git tag a &&
git checkout side &&
echo new line >B &&
git commit -m "add line to B" B &&
git tag b &&
git checkout master &&
git merge side &&
git tag c
'
test_expect_success 'cherry-pick a non-merge with -m should fail' '
git reset --hard &&
git checkout a^0 &&
! git cherry-pick -m 1 b &&
git diff --exit-code a
'
test_expect_success 'cherry pick a merge without -m should fail' '
git reset --hard &&
git checkout a^0 &&
! git cherry-pick c &&
git diff --exit-code a
'
test_expect_success 'cherry pick a merge (1)' '
git reset --hard &&
git checkout a^0 &&
git cherry-pick -m 1 c &&
git diff --exit-code c
'
test_expect_success 'cherry pick a merge (2)' '
git reset --hard &&
git checkout b^0 &&
git cherry-pick -m 2 c &&
git diff --exit-code c
'
test_expect_success 'cherry pick a merge relative to nonexistent parent should fail' '
git reset --hard &&
git checkout b^0 &&
! git cherry-pick -m 3 c
'
test_expect_success 'revert a non-merge with -m should fail' '
git reset --hard &&
git checkout c^0 &&
! git revert -m 1 b &&
git diff --exit-code c
'
test_expect_success 'revert a merge without -m should fail' '
git reset --hard &&
git checkout c^0 &&
! git revert c &&
git diff --exit-code c
'
test_expect_success 'revert a merge (1)' '
git reset --hard &&
git checkout c^0 &&
git revert -m 1 c &&
git diff --exit-code a
'
test_expect_success 'revert a merge (2)' '
git reset --hard &&
git checkout c^0 &&
git revert -m 2 c &&
git diff --exit-code b
'
test_expect_success 'revert a merge relative to nonexistent parent should fail' '
git reset --hard &&
git checkout c^0 &&
! git revert -m 3 c &&
git diff --exit-code c
'
test_done