Merge branch 'js/cherry-pick-usability'

* js/cherry-pick-usability:
  Teach commit about CHERRY_PICK_HEAD
  bash: teach __git_ps1 about CHERRY_PICK_HEAD
  Introduce CHERRY_PICK_HEAD
  t3507: introduce pristine-detach helper
This commit is contained in:
Junio C Hamano 2011-03-09 15:56:17 -08:00
commit 66ecd2d053
12 changed files with 307 additions and 155 deletions

View file

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

View file

@ -84,9 +84,10 @@ OPTIONS
linkgit:git-rebase[1] for details.
--reset-author::
When used with -C/-c/--amend options, declare that the
authorship of the resulting commit now belongs of the committer.
This also renews the author timestamp.
When used with -C/-c/--amend options, or when committing after a
a conflicting cherry-pick, declare that the authorship of the
resulting commit now belongs of the committer. This also renews
the author timestamp.
--short::
When doing a dry-run, give the output in the short-format. See

View file

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

View 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"));

View file

@ -54,9 +54,17 @@ static const char empty_amend_advice[] =
"it empty. You can repeat your command with --allow-empty, or you can\n"
"remove the commit entirely with \"git reset HEAD^\".\n";
static const char empty_cherry_pick_advice[] =
"The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
"If you wish to commit it anyway, use:\n"
"\n"
" git commit --allow-empty\n"
"\n"
"Otherwise, please use 'git reset'\n";
static unsigned char head_sha1[20];
static char *use_message_buffer;
static const char *use_message_buffer;
static const char commit_editmsg[] = "COMMIT_EDITMSG";
static struct lock_file index_lock; /* real index */
static struct lock_file false_lock; /* used only for partial commits */
@ -68,6 +76,11 @@ static enum {
static const char *logfile, *force_author;
static const char *template_file;
/*
* The _message variables are commit names from which to take
* the commit message and/or authorship.
*/
static const char *author_message, *author_message_buffer;
static char *edit_message, *use_message;
static char *fixup_message, *squash_message;
static int all, edit_flag, also, interactive, only, amend, signoff;
@ -88,7 +101,8 @@ static enum {
} cleanup_mode;
static char *cleanup_arg;
static int use_editor = 1, initial_commit, in_merge, include_status = 1;
static enum commit_whence whence;
static int use_editor = 1, initial_commit, include_status = 1;
static int show_ignored_in_status;
static const char *only_include_assumed;
static struct strbuf message;
@ -163,6 +177,36 @@ static struct option builtin_commit_options[] = {
OPT_END()
};
static void determine_whence(struct wt_status *s)
{
if (file_exists(git_path("MERGE_HEAD")))
whence = FROM_MERGE;
else if (file_exists(git_path("CHERRY_PICK_HEAD")))
whence = FROM_CHERRY_PICK;
else
whence = FROM_COMMIT;
if (s)
s->whence = whence;
}
static const char *whence_s(void)
{
char *s = "";
switch (whence) {
case FROM_COMMIT:
break;
case FROM_MERGE:
s = "merge";
break;
case FROM_CHERRY_PICK:
s = "cherry-pick";
break;
}
return s;
}
static void rollback_index_files(void)
{
switch (commit_style) {
@ -378,8 +422,8 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, int
*/
commit_style = COMMIT_PARTIAL;
if (in_merge)
die("cannot do a partial commit during a merge.");
if (whence != FROM_COMMIT)
die("cannot do a partial commit during a %s.", whence_s());
memset(&partial, 0, sizeof(partial));
partial.strdup_strings = 1;
@ -469,18 +513,18 @@ static void determine_author_info(struct strbuf *author_ident)
email = getenv("GIT_AUTHOR_EMAIL");
date = getenv("GIT_AUTHOR_DATE");
if (use_message && !renew_authorship) {
if (author_message) {
const char *a, *lb, *rb, *eol;
a = strstr(use_message_buffer, "\nauthor ");
a = strstr(author_message_buffer, "\nauthor ");
if (!a)
die("invalid commit: %s", use_message);
die("invalid commit: %s", author_message);
lb = strchrnul(a + strlen("\nauthor "), '<');
rb = strchrnul(lb, '>');
eol = strchrnul(rb, '\n');
if (!*lb || !*rb || !*eol)
die("invalid commit: %s", use_message);
die("invalid commit: %s", author_message);
if (lb == a + strlen("\nauthor "))
/* \nauthor <foo@example.com> */
@ -641,11 +685,15 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
}
/*
* This final case does not modify the template message,
* it just sets the argument to the prepare-commit-msg hook.
* The remaining cases don't modify the template message, but
* just set the argument(s) to the prepare-commit-msg hook.
*/
else if (in_merge)
else if (whence == FROM_MERGE)
hook_arg1 = "merge";
else if (whence == FROM_CHERRY_PICK) {
hook_arg1 = "commit";
hook_arg2 = "CHERRY_PICK_HEAD";
}
if (squash_message) {
/*
@ -694,16 +742,18 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
strbuf_addstr(&committer_ident, git_committer_info(0));
if (use_editor && include_status) {
char *ai_tmp, *ci_tmp;
if (in_merge)
if (whence != FROM_COMMIT)
fprintf(fp,
"#\n"
"# It looks like you may be committing a MERGE.\n"
"# It looks like you may be committing a %s.\n"
"# If this is not correct, please remove the file\n"
"# %s\n"
"# and try again.\n"
"#\n",
git_path("MERGE_HEAD"));
whence_s(),
git_path(whence == FROM_MERGE
? "MERGE_HEAD"
: "CHERRY_PICK_HEAD"));
fprintf(fp,
"\n"
"# Please enter the commit message for your changes.");
@ -766,11 +816,18 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
fclose(fp);
if (!commitable && !in_merge && !allow_empty &&
/*
* Reject an attempt to record a non-merge empty commit without
* explicit --allow-empty. In the cherry-pick case, it may be
* empty due to conflict resolution, which the user should okay.
*/
if (!commitable && whence != FROM_MERGE && !allow_empty &&
!(amend && is_a_merge(head_sha1))) {
run_status(stdout, index_file, prefix, 0, s);
if (amend)
fputs(empty_amend_advice, stderr);
else if (whence == FROM_CHERRY_PICK)
fputs(empty_cherry_pick_advice, stderr);
return 0;
}
@ -898,6 +955,28 @@ static void handle_untracked_files_arg(struct wt_status *s)
die("Invalid untracked files mode '%s'", untracked_files_arg);
}
static const char *read_commit_message(const char *name)
{
const char *out_enc, *out;
struct commit *commit;
commit = lookup_commit_reference_by_name(name);
if (!commit)
die("could not lookup commit %s", name);
out_enc = get_commit_output_encoding();
out = logmsg_reencode(commit, out_enc);
/*
* If we failed to reencode the buffer, just copy it
* byte for byte so the user can try to fix it up.
* This also handles the case where input and output
* encodings are identical.
*/
if (out == NULL)
out = xstrdup(commit->buffer);
return out;
}
static int parse_and_validate_options(int argc, const char *argv[],
const char * const usage[],
const char *prefix,
@ -927,8 +1006,8 @@ static int parse_and_validate_options(int argc, const char *argv[],
/* Sanity check options */
if (amend && initial_commit)
die("You have nothing to amend.");
if (amend && in_merge)
die("You are in the middle of a merge -- cannot amend.");
if (amend && whence != FROM_COMMIT)
die("You are in the middle of a %s -- cannot amend.", whence_s());
if (fixup_message && squash_message)
die("Options --squash and --fixup cannot be used together");
if (use_message)
@ -947,26 +1026,18 @@ static int parse_and_validate_options(int argc, const char *argv[],
use_message = edit_message;
if (amend && !use_message && !fixup_message)
use_message = "HEAD";
if (!use_message && renew_authorship)
if (!use_message && whence != FROM_CHERRY_PICK && renew_authorship)
die("--reset-author can be used only with -C, -c or --amend.");
if (use_message) {
const char *out_enc;
struct commit *commit;
commit = lookup_commit_reference_by_name(use_message);
if (!commit)
die("could not lookup commit %s", use_message);
out_enc = get_commit_output_encoding();
use_message_buffer = logmsg_reencode(commit, out_enc);
/*
* If we failed to reencode the buffer, just copy it
* byte for byte so the user can try to fix it up.
* This also handles the case where input and output
* encodings are identical.
*/
if (use_message_buffer == NULL)
use_message_buffer = xstrdup(commit->buffer);
use_message_buffer = read_commit_message(use_message);
if (!renew_authorship) {
author_message = use_message;
author_message_buffer = use_message_buffer;
}
}
if (whence == FROM_CHERRY_PICK && !renew_authorship) {
author_message = "CHERRY_PICK_HEAD";
author_message_buffer = read_commit_message(author_message);
}
if (!!also + !!only + !!all + !!interactive > 1)
@ -1117,7 +1188,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
wt_status_prepare(&s);
gitmodules_config();
git_config(git_status_config, &s);
in_merge = file_exists(git_path("MERGE_HEAD"));
determine_whence(&s);
argc = parse_options(argc, argv, prefix,
builtin_status_options,
builtin_status_usage, 0);
@ -1140,7 +1211,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)
}
s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
s.in_merge = in_merge;
s.ignore_submodule_arg = ignore_submodule_arg;
wt_status_collect(&s);
@ -1302,8 +1372,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
wt_status_prepare(&s);
git_config(git_commit_config, &s);
in_merge = file_exists(git_path("MERGE_HEAD"));
s.in_merge = in_merge;
determine_whence(&s);
if (s.use_color == -1)
s.use_color = git_use_color_default;
@ -1340,7 +1409,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
for (c = commit->parents; c; c = c->next)
pptr = &commit_list_insert(c->item, pptr)->next;
} else if (in_merge) {
} else if (whence == FROM_MERGE) {
struct strbuf m = STRBUF_INIT;
FILE *fp;
@ -1369,7 +1438,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
parents = reduce_heads(parents);
} else {
if (!reflog_msg)
reflog_msg = "commit";
reflog_msg = (whence == FROM_CHERRY_PICK)
? "commit (cherry-pick)"
: "commit";
pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;
}
@ -1424,6 +1495,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"));

View file

@ -999,6 +999,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)

View file

@ -3,7 +3,6 @@
#include "object.h"
#include "commit.h"
#include "tag.h"
#include "wt-status.h"
#include "run-command.h"
#include "exec_cmd.h"
#include "utf8.h"
@ -198,54 +197,20 @@ static void add_message_to_msg(struct strbuf *msgbuf, const char *message)
strbuf_addstr(msgbuf, p);
}
static void set_author_ident_env(const char *message)
static void write_cherry_pick_head(void)
{
const char *p = message;
if (!p)
die ("Could not read commit message of %s",
sha1_to_hex(commit->object.sha1));
while (*p && *p != '\n') {
const char *eol;
int fd;
struct strbuf buf = STRBUF_INIT;
for (eol = p; *eol && *eol != '\n'; eol++)
; /* do nothing */
if (!prefixcmp(p, "author ")) {
char *line, *pend, *email, *timestamp;
strbuf_addf(&buf, "%s\n", sha1_to_hex(commit->object.sha1));
p += 7;
line = xmemdupz(p, eol - p);
email = strchr(line, '<');
if (!email)
die ("Could not extract author email from %s",
sha1_to_hex(commit->object.sha1));
if (email == line)
pend = line;
else
for (pend = email; pend != line + 1 &&
isspace(pend[-1]); pend--);
; /* do nothing */
*pend = '\0';
email++;
timestamp = strchr(email, '>');
if (!timestamp)
die ("Could not extract author time from %s",
sha1_to_hex(commit->object.sha1));
*timestamp = '\0';
for (timestamp++; *timestamp && isspace(*timestamp);
timestamp++)
; /* do nothing */
setenv("GIT_AUTHOR_NAME", line, 1);
setenv("GIT_AUTHOR_EMAIL", email, 1);
setenv("GIT_AUTHOR_DATE", timestamp, 1);
free(line);
return;
}
p = eol;
if (*p == '\n')
p++;
}
die ("No author information found in %s",
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, ...)
@ -263,15 +228,18 @@ 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;
}
advise("after resolving the conflicts, mark the corrected paths");
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'");
}
static void write_message(struct strbuf *msgbuf, const char *filename)
@ -497,13 +465,14 @@ static int do_pick_commit(void)
base_label = msg.parent_label;
next = commit;
next_label = msg.label;
set_author_ident_env(msg.message);
add_message_to_msg(&msgbuf, msg.message);
if (no_replay) {
strbuf_addstr(&msgbuf, "(cherry picked from commit ");
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) {

View file

@ -246,6 +246,8 @@ __git_ps1 ()
fi
elif [ -f "$g/MERGE_HEAD" ]; then
r="|MERGING"
elif [ -f "$g/CHERRY_PICK_HEAD" ]; then
r="|CHERRY-PICKING"
elif [ -f "$g/BISECT_LOG" ]; then
r="|BISECTING"
fi

View file

@ -11,6 +11,18 @@ test_description='test cherry-pick and revert with conflicts
. ./test-lib.sh
test_cmp_rev () {
git rev-parse --verify "$1" >expect.rev &&
git rev-parse --verify "$2" >actual.rev &&
test_cmp expect.rev actual.rev
}
pristine_detach () {
git checkout -f "$1^0" &&
git read-tree -u --reset HEAD &&
git clean -d -f -f -q -x
}
test_expect_success setup '
echo unrelated >unrelated &&
@ -23,13 +35,7 @@ test_expect_success setup '
'
test_expect_success 'failed cherry-pick does not advance HEAD' '
git checkout -f initial^0 &&
git read-tree -u --reset HEAD &&
git clean -d -f -f -q -x &&
git update-index --refresh &&
git diff-index --exit-code HEAD &&
pristine_detach initial &&
head=$(git rev-parse HEAD) &&
test_must_fail git cherry-pick picked &&
@ -39,33 +45,96 @@ test_expect_success 'failed cherry-pick does not advance HEAD' '
'
test_expect_success 'advice from failed cherry-pick' "
git checkout -f initial^0 &&
git read-tree -u --reset HEAD &&
git clean -d -f -f -q -x &&
git update-index --refresh &&
git diff-index --exit-code HEAD &&
pristine_detach initial &&
picked=\$(git rev-parse --short picked) &&
cat <<-EOF >expected &&
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'
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' '
git checkout -f initial^0 &&
git read-tree -u --reset HEAD &&
git clean -d -f -f -q -x &&
git update-index --refresh &&
git diff-index --exit-code HEAD &&
pristine_detach initial &&
test_must_fail git cherry-pick picked &&
@ -74,9 +143,7 @@ test_expect_success 'failed cherry-pick produces dirty index' '
'
test_expect_success 'failed cherry-pick registers participants in index' '
git read-tree -u --reset HEAD &&
git clean -d -f -f -q -x &&
pristine_detach initial &&
{
git checkout base -- foo &&
git ls-files --stage foo &&
@ -90,10 +157,7 @@ test_expect_success 'failed cherry-pick registers participants in index' '
2 s/ 0 / 2 /
3 s/ 0 / 3 /
" < stages > expected &&
git checkout -f initial^0 &&
git update-index --refresh &&
git diff-index --exit-code HEAD &&
git read-tree -u --reset HEAD &&
test_must_fail git cherry-pick picked &&
git ls-files --stage --unmerged > actual &&
@ -102,10 +166,7 @@ test_expect_success 'failed cherry-pick registers participants in index' '
'
test_expect_success 'failed cherry-pick describes conflict in work tree' '
git checkout -f initial^0 &&
git read-tree -u --reset HEAD &&
git clean -d -f -f -q -x &&
pristine_detach initial &&
cat <<-EOF > expected &&
<<<<<<< HEAD
a
@ -114,9 +175,6 @@ test_expect_success 'failed cherry-pick describes conflict in work tree' '
>>>>>>> objid picked
EOF
git update-index --refresh &&
git diff-index --exit-code HEAD &&
test_must_fail git cherry-pick picked &&
sed "s/[a-f0-9]*\.\.\./objid/" foo > actual &&
@ -124,11 +182,8 @@ test_expect_success 'failed cherry-pick describes conflict in work tree' '
'
test_expect_success 'diff3 -m style' '
pristine_detach initial &&
git config merge.conflictstyle diff3 &&
git checkout -f initial^0 &&
git read-tree -u --reset HEAD &&
git clean -d -f -f -q -x &&
cat <<-EOF > expected &&
<<<<<<< HEAD
a
@ -139,9 +194,6 @@ test_expect_success 'diff3 -m style' '
>>>>>>> objid picked
EOF
git update-index --refresh &&
git diff-index --exit-code HEAD &&
test_must_fail git cherry-pick picked &&
sed "s/[a-f0-9]*\.\.\./objid/" foo > actual &&
@ -149,10 +201,8 @@ test_expect_success 'diff3 -m style' '
'
test_expect_success 'revert also handles conflicts sanely' '
git config --unset merge.conflictstyle &&
git read-tree -u --reset HEAD &&
git clean -d -f -f -q -x &&
pristine_detach initial &&
cat <<-EOF > expected &&
<<<<<<< HEAD
a
@ -173,10 +223,7 @@ test_expect_success 'revert also handles conflicts sanely' '
2 s/ 0 / 2 /
3 s/ 0 / 3 /
" < stages > expected-stages &&
git checkout -f initial^0 &&
git update-index --refresh &&
git diff-index --exit-code HEAD &&
git read-tree -u --reset HEAD &&
head=$(git rev-parse HEAD) &&
test_must_fail git revert picked &&
@ -192,10 +239,8 @@ test_expect_success 'revert also handles conflicts sanely' '
'
test_expect_success 'revert conflict, diff3 -m style' '
pristine_detach initial &&
git config merge.conflictstyle diff3 &&
git checkout -f initial^0 &&
git read-tree -u --reset HEAD &&
git clean -d -f -f -q -x &&
cat <<-EOF > expected &&
<<<<<<< HEAD
a
@ -206,9 +251,6 @@ test_expect_success 'revert conflict, diff3 -m style' '
>>>>>>> parent of objid picked
EOF
git update-index --refresh &&
git diff-index --exit-code HEAD &&
test_must_fail git revert picked &&
sed "s/[a-f0-9]*\.\.\./objid/" foo > actual &&

View file

@ -157,4 +157,33 @@ test_expect_success '--reset-author should be rejected without -c/-C/--amend' '
test_must_fail git commit -a --reset-author -m done
'
test_expect_success 'commit respects CHERRY_PICK_HEAD and MERGE_MSG' '
echo "cherry-pick 1a" >>foo &&
test_tick &&
git commit -am "cherry-pick 1" --author="Cherry <cherry@pick.er>" &&
git tag cherry-pick-head &&
git rev-parse cherry-pick-head >.git/CHERRY_PICK_HEAD &&
echo "This is a MERGE_MSG" >.git/MERGE_MSG &&
echo "cherry-pick 1b" >>foo &&
test_tick &&
git commit -a &&
author_header cherry-pick-head >expect &&
author_header HEAD >actual &&
test_cmp expect actual &&
echo "This is a MERGE_MSG" >expect &&
message_body HEAD >actual &&
test_cmp expect actual
'
test_expect_success '--reset-author with CHERRY_PICK_HEAD' '
git rev-parse cherry-pick-head >.git/CHERRY_PICK_HEAD &&
echo "cherry-pick 2" >>foo &&
test_tick &&
git commit -am "cherry-pick 2" --reset-author &&
echo "author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE" >expect &&
author_header HEAD >actual &&
test_cmp expect actual
'
test_done

View file

@ -60,7 +60,7 @@ static void wt_status_print_unmerged_header(struct wt_status *s)
color_fprintf_ln(s->fp, c, "# Unmerged paths:");
if (!advice_status_hints)
return;
if (s->in_merge)
if (s->whence != FROM_COMMIT)
;
else if (!s->is_initial)
color_fprintf_ln(s->fp, c, "# (use \"git reset %s <file>...\" to unstage)", s->reference);
@ -77,7 +77,7 @@ static void wt_status_print_cached_header(struct wt_status *s)
color_fprintf_ln(s->fp, c, "# Changes to be committed:");
if (!advice_status_hints)
return;
if (s->in_merge)
if (s->whence != FROM_COMMIT)
; /* NEEDSWORK: use "git reset --unresolve"??? */
else if (!s->is_initial)
color_fprintf_ln(s->fp, c, "# (use \"git reset %s <file>...\" to unstage)", s->reference);

View file

@ -24,6 +24,13 @@ enum untracked_status_type {
SHOW_ALL_UNTRACKED_FILES
};
/* from where does this commit originate */
enum commit_whence {
FROM_COMMIT, /* normal */
FROM_MERGE, /* commit came from merge */
FROM_CHERRY_PICK /* commit came from cherry-pick */
};
struct wt_status_change_data {
int worktree_status;
int index_status;
@ -40,7 +47,7 @@ struct wt_status {
const char **pathspec;
int verbose;
int amend;
int in_merge;
enum commit_whence whence;
int nowarn;
int use_color;
int relative_paths;