Skip to content

Commit d7e5c0c

Browse files
jaysoffiangitster
authored andcommitted
Introduce CHERRY_PICK_HEAD
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>
1 parent 2161da1 commit d7e5c0c

File tree

7 files changed

+132
-4
lines changed

7 files changed

+132
-4
lines changed

Documentation/git-cherry-pick.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,25 @@ Given one or more existing commits, apply the change each one
1616
introduces, recording a new commit for each. This requires your
1717
working tree to be clean (no modifications from the HEAD commit).
1818

19+
When it is not obvious how to apply a change, the following
20+
happens:
21+
22+
1. The current branch and `HEAD` pointer stay at the last commit
23+
successfully made.
24+
2. The `CHERRY_PICK_HEAD` ref is set to point at the commit that
25+
introduced the change that is difficult to apply.
26+
3. Paths in which the change applied cleanly are updated both
27+
in the index file and in your working tree.
28+
4. For conflicting paths, the index file records up to three
29+
versions, as described in the "TRUE MERGE" section of
30+
linkgit:git-merge[1]. The working tree files will include
31+
a description of the conflict bracketed by the usual
32+
conflict markers `<<<<<<<` and `>>>>>>>`.
33+
5. No other modifications are made.
34+
35+
See linkgit:git-merge[1] for some hints on resolving such
36+
conflicts.
37+
1938
OPTIONS
2039
-------
2140
<commit>...::

Documentation/revisions.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ blobs contained in a commit.
2525
first match in the following rules:
2626

2727
. if `$GIT_DIR/<name>` exists, that is what you mean (this is usually
28-
useful only for `HEAD`, `FETCH_HEAD`, `ORIG_HEAD` and `MERGE_HEAD`);
28+
useful only for `HEAD`, `FETCH_HEAD`, `ORIG_HEAD`, `MERGE_HEAD`
29+
and `CHERRY_PICK_HEAD`);
2930

3031
. otherwise, `refs/<name>` if exists;
3132

@@ -46,6 +47,8 @@ you can change the tip of the branch back to the state before you ran
4647
them easily.
4748
MERGE_HEAD records the commit(s) you are merging into your branch
4849
when you run 'git merge'.
50+
CHERRY_PICK_HEAD records the commit you are cherry-picking
51+
when you run 'git cherry-pick'.
4952
+
5053
Note that any of the `refs/*` cases above may come either from
5154
the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.

branch.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ void create_branch(const char *head,
217217

218218
void remove_branch_state(void)
219219
{
220+
unlink(git_path("CHERRY_PICK_HEAD"));
220221
unlink(git_path("MERGE_HEAD"));
221222
unlink(git_path("MERGE_RR"));
222223
unlink(git_path("MERGE_MSG"));

builtin/commit.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1424,6 +1424,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
14241424
die("cannot update HEAD ref");
14251425
}
14261426

1427+
unlink(git_path("CHERRY_PICK_HEAD"));
14271428
unlink(git_path("MERGE_HEAD"));
14281429
unlink(git_path("MERGE_MSG"));
14291430
unlink(git_path("MERGE_MODE"));

builtin/merge.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,13 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
969969
else
970970
die("You have not concluded your merge (MERGE_HEAD exists).");
971971
}
972+
if (file_exists(git_path("CHERRY_PICK_HEAD"))) {
973+
if (advice_resolve_conflict)
974+
die("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
975+
"Please, commit your changes before you can merge.");
976+
else
977+
die("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).");
978+
}
972979
resolve_undo_clear();
973980

974981
if (verbosity < 0)

builtin/revert.c

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,22 @@ static void set_author_ident_env(const char *message)
231231
sha1_to_hex(commit->object.sha1));
232232
}
233233

234+
static void write_cherry_pick_head(void)
235+
{
236+
int fd;
237+
struct strbuf buf = STRBUF_INIT;
238+
239+
strbuf_addf(&buf, "%s\n", sha1_to_hex(commit->object.sha1));
240+
241+
fd = open(git_path("CHERRY_PICK_HEAD"), O_WRONLY | O_CREAT, 0666);
242+
if (fd < 0)
243+
die_errno("Could not open '%s' for writing",
244+
git_path("CHERRY_PICK_HEAD"));
245+
if (write_in_full(fd, buf.buf, buf.len) != buf.len || close(fd))
246+
die_errno("Could not write to '%s'", git_path("CHERRY_PICK_HEAD"));
247+
strbuf_release(&buf);
248+
}
249+
234250
static void advise(const char *advice, ...)
235251
{
236252
va_list params;
@@ -246,15 +262,20 @@ static void print_advice(void)
246262

247263
if (msg) {
248264
fprintf(stderr, "%s\n", msg);
265+
/*
266+
* A conflict has occured but the porcelain
267+
* (typically rebase --interactive) wants to take care
268+
* of the commit itself so remove CHERRY_PICK_HEAD
269+
*/
270+
unlink(git_path("CHERRY_PICK_HEAD"));
249271
return;
250272
}
251273

252274
advise("after resolving the conflicts, mark the corrected paths");
253275
advise("with 'git add <paths>' or 'git rm <paths>'");
254276

255277
if (action == CHERRY_PICK)
256-
advise("and commit the result with 'git commit -c %s'",
257-
find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
278+
advise("and commit the result with 'git commit -c CHERRY_PICK_HEAD'");
258279
}
259280

260281
static void write_message(struct strbuf *msgbuf, const char *filename)
@@ -489,6 +510,8 @@ static int do_pick_commit(void)
489510
strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
490511
strbuf_addstr(&msgbuf, ")\n");
491512
}
513+
if (!no_commit)
514+
write_cherry_pick_head();
492515
}
493516

494517
if (!strategy || !strcmp(strategy, "recursive") || action == REVERT) {

t/t3507-cherry-pick-conflict.sh

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,87 @@ test_expect_success 'advice from failed cherry-pick' "
5252
error: could not apply \$picked... picked
5353
hint: after resolving the conflicts, mark the corrected paths
5454
hint: with 'git add <paths>' or 'git rm <paths>'
55-
hint: and commit the result with 'git commit -c \$picked'
55+
hint: and commit the result with 'git commit -c CHERRY_PICK_HEAD'
5656
EOF
5757
test_must_fail git cherry-pick picked 2>actual &&
5858
5959
test_cmp expected actual
6060
"
6161

62+
test_expect_success 'failed cherry-pick sets CHERRY_PICK_HEAD' '
63+
pristine_detach initial &&
64+
test_must_fail git cherry-pick picked &&
65+
test_cmp_rev picked CHERRY_PICK_HEAD
66+
'
67+
68+
test_expect_success 'successful cherry-pick does not set CHERRY_PICK_HEAD' '
69+
pristine_detach initial &&
70+
git cherry-pick base &&
71+
test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
72+
'
73+
74+
test_expect_success 'cherry-pick --no-commit does not set CHERRY_PICK_HEAD' '
75+
pristine_detach initial &&
76+
git cherry-pick --no-commit base &&
77+
test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
78+
'
79+
80+
test_expect_success 'GIT_CHERRY_PICK_HELP suppresses CHERRY_PICK_HEAD' '
81+
pristine_detach initial &&
82+
(
83+
GIT_CHERRY_PICK_HELP="and then do something else" &&
84+
export GIT_CHERRY_PICK_HELP &&
85+
test_must_fail git cherry-pick picked
86+
) &&
87+
test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
88+
'
89+
90+
test_expect_success 'git reset clears CHERRY_PICK_HEAD' '
91+
pristine_detach initial &&
92+
93+
test_must_fail git cherry-pick picked &&
94+
git reset &&
95+
96+
test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
97+
'
98+
99+
test_expect_success 'failed commit does not clear CHERRY_PICK_HEAD' '
100+
pristine_detach initial &&
101+
102+
test_must_fail git cherry-pick picked &&
103+
test_must_fail git commit &&
104+
105+
test_cmp_rev picked CHERRY_PICK_HEAD
106+
'
107+
108+
test_expect_success 'cancelled commit does not clear CHERRY_PICK_HEAD' '
109+
pristine_detach initial &&
110+
111+
test_must_fail git cherry-pick picked &&
112+
echo resolved >foo &&
113+
git add foo &&
114+
git update-index --refresh -q &&
115+
test_must_fail git diff-index --exit-code HEAD &&
116+
(
117+
GIT_EDITOR=false &&
118+
export GIT_EDITOR &&
119+
test_must_fail git commit
120+
) &&
121+
122+
test_cmp_rev picked CHERRY_PICK_HEAD
123+
'
124+
125+
test_expect_success 'successful commit clears CHERRY_PICK_HEAD' '
126+
pristine_detach initial &&
127+
128+
test_must_fail git cherry-pick picked &&
129+
echo resolved >foo &&
130+
git add foo &&
131+
git commit &&
132+
133+
test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
134+
'
135+
62136
test_expect_success 'failed cherry-pick produces dirty index' '
63137
pristine_detach initial &&
64138

0 commit comments

Comments
 (0)