Browse Source

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
maint
Junio C Hamano 17 years ago
parent
commit
02273fdbd0
  1. 9
      Documentation/git-cherry-pick.txt
  2. 9
      Documentation/git-revert.txt
  3. 35
      builtin-revert.c
  4. 13
      git-compat-util.h
  5. 123
      t/t3502-cherry-pick-merge.sh

9
Documentation/git-cherry-pick.txt

@ -7,7 +7,7 @@ git-cherry-pick - Apply the change introduced by an existing commit @@ -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 @@ -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

9
Documentation/git-revert.txt

@ -7,7 +7,7 @@ git-revert - Revert an existing commit @@ -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 @@ -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.

35
builtin-revert.c

@ -30,7 +30,7 @@ static const char * const cherry_pick_usage[] = { @@ -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) @@ -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, @@ -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) @@ -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) @@ -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);

13
git-compat-util.h

@ -381,4 +381,17 @@ static inline int strtoul_ui(char const *s, int base, unsigned int *result) @@ -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

@ -0,0 +1,123 @@ @@ -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
Loading…
Cancel
Save