Skip to content

Commit

Permalink
Merge branch 'jc/revert-merge'
Browse files Browse the repository at this point in the history
* jc/revert-merge:
  cherry-pick/revert -m: add tests
  revert/cherry-pick: work on merge commits as well

Conflicts:

	builtin-revert.c
  • Loading branch information
Junio C Hamano committed Nov 4, 2007
2 parents c8a140f + 6232b34 commit 02273fd
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 8 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
35 changes: 29 additions & 6 deletions builtin-revert.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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);
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
123 changes: 123 additions & 0 deletions t/t3502-cherry-pick-merge.sh
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 02273fd

Please sign in to comment.