When a cherry-pick conflicts git advises:

 $ git commit -c <original commit id>

to preserve the original commit message and authorship. Instead, let's
record the original commit id in CHERRY_PICK_HEAD and advise:

  $ git commit -c CHERRY_PICK_HEAD

A later patch teaches git to handle the '-c CHERRY_PICK_HEAD' part.
Note that we record CHERRY_PICK_HEAD even in the case where there
are no conflicts so that we may use it to communicate authorship to
commit; this will then allow us to remove set_author_ident_env from
revert.c. However, we do not record CHERRY_PICK_HEAD when --no-commit
is used, as presumably the user intends to further edit the commit
and possibly even cherry-pick additional commits on top.

Tests and documentation contributed by Jonathan Nieder.

Reviewed-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Jay Soffian <jaysoffian@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Jay Soffian 2011-02-19 23:12:27 -05:00 коммит произвёл Junio C Hamano
Родитель 2161da1039
Коммит d7e5c0cbfb
7 изменённых файлов: 132 добавлений и 4 удалений

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

@ -16,6 +16,25 @@ Given one or more existing commits, apply the change each one
introduces, recording a new commit for each. This requires your
working tree to be clean (no modifications from the HEAD commit).
When it is not obvious how to apply a change, the following
happens:
1. The current branch and `HEAD` pointer stay at the last commit
successfully made.
2. The `CHERRY_PICK_HEAD` ref is set to point at the commit that
introduced the change that is difficult to apply.
3. Paths in which the change applied cleanly are updated both
in the index file and in your working tree.
4. For conflicting paths, the index file records up to three
versions, as described in the "TRUE MERGE" section of
linkgit:git-merge[1]. The working tree files will include
a description of the conflict bracketed by the usual
conflict markers `<<<<<<<` and `>>>>>>>`.
5. No other modifications are made.
See linkgit:git-merge[1] for some hints on resolving such
conflicts.
OPTIONS
-------
<commit>...::

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

@ -25,7 +25,8 @@ blobs contained in a commit.
first match in the following rules:
. if `$GIT_DIR/<name>` exists, that is what you mean (this is usually
useful only for `HEAD`, `FETCH_HEAD`, `ORIG_HEAD` and `MERGE_HEAD`);
useful only for `HEAD`, `FETCH_HEAD`, `ORIG_HEAD`, `MERGE_HEAD`
and `CHERRY_PICK_HEAD`);
. otherwise, `refs/<name>` if exists;
@ -46,6 +47,8 @@ you can change the tip of the branch back to the state before you ran
them easily.
MERGE_HEAD records the commit(s) you are merging into your branch
when you run 'git merge'.
CHERRY_PICK_HEAD records the commit you are cherry-picking
when you run 'git cherry-pick'.
+
Note that any of the `refs/*` cases above may come either from
the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.

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

@ -217,6 +217,7 @@ void create_branch(const char *head,
void remove_branch_state(void)
{
unlink(git_path("CHERRY_PICK_HEAD"));
unlink(git_path("MERGE_HEAD"));
unlink(git_path("MERGE_RR"));
unlink(git_path("MERGE_MSG"));

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

@ -1424,6 +1424,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
die("cannot update HEAD ref");
}
unlink(git_path("CHERRY_PICK_HEAD"));
unlink(git_path("MERGE_HEAD"));
unlink(git_path("MERGE_MSG"));
unlink(git_path("MERGE_MODE"));

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

@ -969,6 +969,13 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
else
die("You have not concluded your merge (MERGE_HEAD exists).");
}
if (file_exists(git_path("CHERRY_PICK_HEAD"))) {
if (advice_resolve_conflict)
die("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
"Please, commit your changes before you can merge.");
else
die("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).");
}
resolve_undo_clear();
if (verbosity < 0)

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

@ -231,6 +231,22 @@ static void set_author_ident_env(const char *message)
sha1_to_hex(commit->object.sha1));
}
static void write_cherry_pick_head(void)
{
int fd;
struct strbuf buf = STRBUF_INIT;
strbuf_addf(&buf, "%s\n", sha1_to_hex(commit->object.sha1));
fd = open(git_path("CHERRY_PICK_HEAD"), O_WRONLY | O_CREAT, 0666);
if (fd < 0)
die_errno("Could not open '%s' for writing",
git_path("CHERRY_PICK_HEAD"));
if (write_in_full(fd, buf.buf, buf.len) != buf.len || close(fd))
die_errno("Could not write to '%s'", git_path("CHERRY_PICK_HEAD"));
strbuf_release(&buf);
}
static void advise(const char *advice, ...)
{
va_list params;
@ -246,6 +262,12 @@ static void print_advice(void)
if (msg) {
fprintf(stderr, "%s\n", msg);
/*
* A conflict has occured but the porcelain
* (typically rebase --interactive) wants to take care
* of the commit itself so remove CHERRY_PICK_HEAD
*/
unlink(git_path("CHERRY_PICK_HEAD"));
return;
}
@ -253,8 +275,7 @@ static void print_advice(void)
advise("with 'git add <paths>' or 'git rm <paths>'");
if (action == CHERRY_PICK)
advise("and commit the result with 'git commit -c %s'",
find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
advise("and commit the result with 'git commit -c CHERRY_PICK_HEAD'");
}
static void write_message(struct strbuf *msgbuf, const char *filename)
@ -489,6 +510,8 @@ static int do_pick_commit(void)
strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
strbuf_addstr(&msgbuf, ")\n");
}
if (!no_commit)
write_cherry_pick_head();
}
if (!strategy || !strcmp(strategy, "recursive") || action == REVERT) {

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

@ -52,13 +52,87 @@ test_expect_success 'advice from failed cherry-pick' "
error: could not apply \$picked... picked
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit -c \$picked'
hint: and commit the result with 'git commit -c CHERRY_PICK_HEAD'
EOF
test_must_fail git cherry-pick picked 2>actual &&
test_cmp expected actual
"
test_expect_success 'failed cherry-pick sets CHERRY_PICK_HEAD' '
pristine_detach initial &&
test_must_fail git cherry-pick picked &&
test_cmp_rev picked CHERRY_PICK_HEAD
'
test_expect_success 'successful cherry-pick does not set CHERRY_PICK_HEAD' '
pristine_detach initial &&
git cherry-pick base &&
test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
'
test_expect_success 'cherry-pick --no-commit does not set CHERRY_PICK_HEAD' '
pristine_detach initial &&
git cherry-pick --no-commit base &&
test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
'
test_expect_success 'GIT_CHERRY_PICK_HELP suppresses CHERRY_PICK_HEAD' '
pristine_detach initial &&
(
GIT_CHERRY_PICK_HELP="and then do something else" &&
export GIT_CHERRY_PICK_HELP &&
test_must_fail git cherry-pick picked
) &&
test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
'
test_expect_success 'git reset clears CHERRY_PICK_HEAD' '
pristine_detach initial &&
test_must_fail git cherry-pick picked &&
git reset &&
test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
'
test_expect_success 'failed commit does not clear CHERRY_PICK_HEAD' '
pristine_detach initial &&
test_must_fail git cherry-pick picked &&
test_must_fail git commit &&
test_cmp_rev picked CHERRY_PICK_HEAD
'
test_expect_success 'cancelled commit does not clear CHERRY_PICK_HEAD' '
pristine_detach initial &&
test_must_fail git cherry-pick picked &&
echo resolved >foo &&
git add foo &&
git update-index --refresh -q &&
test_must_fail git diff-index --exit-code HEAD &&
(
GIT_EDITOR=false &&
export GIT_EDITOR &&
test_must_fail git commit
) &&
test_cmp_rev picked CHERRY_PICK_HEAD
'
test_expect_success 'successful commit clears CHERRY_PICK_HEAD' '
pristine_detach initial &&
test_must_fail git cherry-pick picked &&
echo resolved >foo &&
git add foo &&
git commit &&
test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
'
test_expect_success 'failed cherry-pick produces dirty index' '
pristine_detach initial &&