{"thread":{"id":"26564","subject":"[PATCH v4 0/4] CHERRY_PICK_HEAD","startedAt":"2011-02-20T04:12:25Z","lastAt":"2011-02-20T04:12:29Z","messageCount":5,"participants":["Jay Soffian"],"isPatch":true,"patchVersion":4,"patchTotal":4},"messages":[{"id":"161729","messageId":"1298175149-41178-1-git-send-email-jaysoffian@gmail.com","threadId":"26564","inReplyTo":null,"subject":"[PATCH v4 0/4] CHERRY_PICK_HEAD","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-02-20T04:12:25Z","receivedAt":"2011-02-20T04:12:25Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"As compared to v3:\n\n- Adds pristine-detach to the start of the series. I thought it made sense to\n  refactor before adding the additional tests instead of after.\n\n- Fixes t3404#16. This was due to CHERRY_PICK_HEAD overriding the authorship set\n  by git rebase --interactive when used with --no-commit. Now we just don't\n  write CHERRY_PICK_HEAD when --no-commit is given. This also addresses Junio's\n  concern that doing so would be a regression.\n\n  (That said, I wonder if rebase --interactive should use --reset-author when\n  committing after using cherry-pick --no-commit just in case.)\n\n- Addresses (I think) the various other feedback given in\n  http://thread.gmane.org/gmane.comp.version-control.git/167031\n\nJay Soffian (3):\n  Introduce CHERRY_PICK_HEAD\n  bash: teach __git_ps1 about CHERRY_PICK_HEAD\n  Teach commit about CHERRY_PICK_HEAD\n\nJonathan Nieder (1):\n  t3507: introduce pristine-detach helper\n\n Documentation/git-cherry-pick.txt      |   19 ++++\n Documentation/git-commit.txt           |    7 +-\n Documentation/revisions.txt            |    5 +-\n branch.c                               |    1 +\n builtin/commit.c                       |  156 +++++++++++++++++++++++---------\n builtin/merge.c                        |    7 ++\n builtin/revert.c                       |   75 +++++-----------\n contrib/completion/git-completion.bash |    2 +\n t/t3507-cherry-pick-conflict.sh        |  150 ++++++++++++++++++++-----------\n t/t7509-commit.sh                      |   29 ++++++\n wt-status.c                            |    4 +-\n wt-status.h                            |    9 ++-\n 12 files changed, 308 insertions(+), 156 deletions(-)\n\n-- \n1.7.4.1.51.g615e0\n"},{"id":"161730","messageId":"1298175149-41178-2-git-send-email-jaysoffian@gmail.com","threadId":"26564","inReplyTo":"1298175149-41178-1-git-send-email-jaysoffian@gmail.com","subject":"[PATCH v4 1/4] t3507: introduce pristine-detach helper","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-02-20T04:12:26Z","receivedAt":"2011-02-20T04:12:26Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"From: Jonathan Nieder <jrnieder@gmail.com>\n\nAll the tests in t3507 (cherry-pick with conflicts) begin with the\nsame checkout + read-tree + clean incantation to ensure a predictable\nstarting point.  Factor out a function for that so the interesting\npart of the tests is easier to read.\n\nThe \"update-index --refresh\" and \"diff-index --exit-code HEAD\" are not\nnecessary as the point of this testsuite is not about testing\n\"read-tree --reset\".\n\nImproved-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n t/t3507-cherry-pick-conflict.sh |   76 +++++++++++---------------------------\n 1 files changed, 22 insertions(+), 54 deletions(-)\n\ndiff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh\nindex 607bf25..2313d79 100755\n--- a/t/t3507-cherry-pick-conflict.sh\n+++ b/t/t3507-cherry-pick-conflict.sh\n@@ -11,6 +11,18 @@ test_description='test cherry-pick and revert with conflicts\n \n . ./test-lib.sh\n \n+test_cmp_rev () {\n+\tgit rev-parse --verify \"$1\" >expect.rev &&\n+\tgit rev-parse --verify \"$2\" >actual.rev &&\n+\ttest_cmp expect.rev actual.rev\n+}\n+\n+pristine_detach () {\n+\tgit checkout -f \"$1^0\" &&\n+\tgit read-tree -u --reset HEAD &&\n+\tgit clean -d -f -f -q -x\n+}\n+\n test_expect_success setup '\n \n \techo unrelated >unrelated &&\n@@ -23,13 +35,7 @@ test_expect_success setup '\n '\n \n test_expect_success 'failed cherry-pick does not advance HEAD' '\n-\n-\tgit checkout -f initial^0 &&\n-\tgit read-tree -u --reset HEAD &&\n-\tgit clean -d -f -f -q -x &&\n-\n-\tgit update-index --refresh &&\n-\tgit diff-index --exit-code HEAD &&\n+\tpristine_detach initial &&\n \n \thead=$(git rev-parse HEAD) &&\n \ttest_must_fail git cherry-pick picked &&\n@@ -39,12 +45,7 @@ test_expect_success 'failed cherry-pick does not advance HEAD' '\n '\n \n test_expect_success 'advice from failed cherry-pick' \"\n-\tgit checkout -f initial^0 &&\n-\tgit read-tree -u --reset HEAD &&\n-\tgit clean -d -f -f -q -x &&\n-\n-\tgit update-index --refresh &&\n-\tgit diff-index --exit-code HEAD &&\n+\tpristine_detach initial &&\n \n \tpicked=\\$(git rev-parse --short picked) &&\n \tcat <<-EOF >expected &&\n@@ -59,13 +60,7 @@ test_expect_success 'advice from failed cherry-pick' \"\n \"\n \n test_expect_success 'failed cherry-pick produces dirty index' '\n-\n-\tgit checkout -f initial^0 &&\n-\tgit read-tree -u --reset HEAD &&\n-\tgit clean -d -f -f -q -x &&\n-\n-\tgit update-index --refresh &&\n-\tgit diff-index --exit-code HEAD &&\n+\tpristine_detach initial &&\n \n \ttest_must_fail git cherry-pick picked &&\n \n@@ -74,9 +69,7 @@ test_expect_success 'failed cherry-pick produces dirty index' '\n '\n \n test_expect_success 'failed cherry-pick registers participants in index' '\n-\n-\tgit read-tree -u --reset HEAD &&\n-\tgit clean -d -f -f -q -x &&\n+\tpristine_detach initial &&\n \t{\n \t\tgit checkout base -- foo &&\n \t\tgit ls-files --stage foo &&\n@@ -90,10 +83,7 @@ test_expect_success 'failed cherry-pick registers participants in index' '\n \t\t2 s/ 0\t/ 2\t/\n \t\t3 s/ 0\t/ 3\t/\n \t\" < stages > expected &&\n-\tgit checkout -f initial^0 &&\n-\n-\tgit update-index --refresh &&\n-\tgit diff-index --exit-code HEAD &&\n+\tgit read-tree -u --reset HEAD &&\n \n \ttest_must_fail git cherry-pick picked &&\n \tgit ls-files --stage --unmerged > actual &&\n@@ -102,10 +92,7 @@ test_expect_success 'failed cherry-pick registers participants in index' '\n '\n \n test_expect_success 'failed cherry-pick describes conflict in work tree' '\n-\n-\tgit checkout -f initial^0 &&\n-\tgit read-tree -u --reset HEAD &&\n-\tgit clean -d -f -f -q -x &&\n+\tpristine_detach initial &&\n \tcat <<-EOF > expected &&\n \t<<<<<<< HEAD\n \ta\n@@ -114,9 +101,6 @@ test_expect_success 'failed cherry-pick describes conflict in work tree' '\n \t>>>>>>> objid picked\n \tEOF\n \n-\tgit update-index --refresh &&\n-\tgit diff-index --exit-code HEAD &&\n-\n \ttest_must_fail git cherry-pick picked &&\n \n \tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo > actual &&\n@@ -124,11 +108,8 @@ test_expect_success 'failed cherry-pick describes conflict in work tree' '\n '\n \n test_expect_success 'diff3 -m style' '\n-\n+\tpristine_detach initial &&\n \tgit config merge.conflictstyle diff3 &&\n-\tgit checkout -f initial^0 &&\n-\tgit read-tree -u --reset HEAD &&\n-\tgit clean -d -f -f -q -x &&\n \tcat <<-EOF > expected &&\n \t<<<<<<< HEAD\n \ta\n@@ -139,9 +120,6 @@ test_expect_success 'diff3 -m style' '\n \t>>>>>>> objid picked\n \tEOF\n \n-\tgit update-index --refresh &&\n-\tgit diff-index --exit-code HEAD &&\n-\n \ttest_must_fail git cherry-pick picked &&\n \n \tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo > actual &&\n@@ -149,10 +127,8 @@ test_expect_success 'diff3 -m style' '\n '\n \n test_expect_success 'revert also handles conflicts sanely' '\n-\n \tgit config --unset merge.conflictstyle &&\n-\tgit read-tree -u --reset HEAD &&\n-\tgit clean -d -f -f -q -x &&\n+\tpristine_detach initial &&\n \tcat <<-EOF > expected &&\n \t<<<<<<< HEAD\n \ta\n@@ -173,10 +149,7 @@ test_expect_success 'revert also handles conflicts sanely' '\n \t\t2 s/ 0\t/ 2\t/\n \t\t3 s/ 0\t/ 3\t/\n \t\" < stages > expected-stages &&\n-\tgit checkout -f initial^0 &&\n-\n-\tgit update-index --refresh &&\n-\tgit diff-index --exit-code HEAD &&\n+\tgit read-tree -u --reset HEAD &&\n \n \thead=$(git rev-parse HEAD) &&\n \ttest_must_fail git revert picked &&\n@@ -192,10 +165,8 @@ test_expect_success 'revert also handles conflicts sanely' '\n '\n \n test_expect_success 'revert conflict, diff3 -m style' '\n+\tpristine_detach initial &&\n \tgit config merge.conflictstyle diff3 &&\n-\tgit checkout -f initial^0 &&\n-\tgit read-tree -u --reset HEAD &&\n-\tgit clean -d -f -f -q -x &&\n \tcat <<-EOF > expected &&\n \t<<<<<<< HEAD\n \ta\n@@ -206,9 +177,6 @@ test_expect_success 'revert conflict, diff3 -m style' '\n \t>>>>>>> parent of objid picked\n \tEOF\n \n-\tgit update-index --refresh &&\n-\tgit diff-index --exit-code HEAD &&\n-\n \ttest_must_fail git revert picked &&\n \n \tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo > actual &&\n-- \n1.7.4.1.51.g615e0\n"},{"id":"161731","messageId":"1298175149-41178-3-git-send-email-jaysoffian@gmail.com","threadId":"26564","inReplyTo":"1298175149-41178-1-git-send-email-jaysoffian@gmail.com","subject":"[PATCH v4 2/4] Introduce CHERRY_PICK_HEAD","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-02-20T04:12:27Z","receivedAt":"2011-02-20T04:12:27Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"When a cherry-pick conflicts git advises:\n\n $ git commit -c <original commit id>\n\nto preserve the original commit message and authorship. Instead, let's\nrecord the original commit id in CHERRY_PICK_HEAD and advise:\n\n  $ git commit -c CHERRY_PICK_HEAD\n\nA later patch teaches git to handle the '-c CHERRY_PICK_HEAD' part.\nNote that we record CHERRY_PICK_HEAD even in the case where there\nare no conflicts so that we may use it to communicate authorship to\ncommit; this will then allow us to remove set_author_ident_env from\nrevert.c. However, we do not record CHERRY_PICK_HEAD when --no-commit\nis used, as presumably the user intends to further edit the commit\nand possibly even cherry-pick additional commits on top.\n\nTests and documentation contributed by Jonathan Nieder.\n\nReviewed-by: Jonathan Nieder <jrnieder@gmail.com>\nSigned-off-by: Jay Soffian <jaysoffian@gmail.com>\n---\n Documentation/git-cherry-pick.txt |   19 +++++++++\n Documentation/revisions.txt       |    5 ++-\n branch.c                          |    1 +\n builtin/commit.c                  |    1 +\n builtin/merge.c                   |    7 +++\n builtin/revert.c                  |   27 ++++++++++++-\n t/t3507-cherry-pick-conflict.sh   |   76 ++++++++++++++++++++++++++++++++++++-\n 7 files changed, 132 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt\nindex 749d68a..5d85daa 100644\n--- a/Documentation/git-cherry-pick.txt\n+++ b/Documentation/git-cherry-pick.txt\n@@ -16,6 +16,25 @@ Given one or more existing commits, apply the change each one\n introduces, recording a new commit for each.  This requires your\n working tree to be clean (no modifications from the HEAD commit).\n \n+When it is not obvious how to apply a change, the following\n+happens:\n+\n+1. The current branch and `HEAD` pointer stay at the last commit\n+   successfully made.\n+2. The `CHERRY_PICK_HEAD` ref is set to point at the commit that\n+   introduced the change that is difficult to apply.\n+3. Paths in which the change applied cleanly are updated both\n+   in the index file and in your working tree.\n+4. For conflicting paths, the index file records up to three\n+   versions, as described in the \"TRUE MERGE\" section of\n+   linkgit:git-merge[1].  The working tree files will include\n+   a description of the conflict bracketed by the usual\n+   conflict markers `<<<<<<<` and `>>>>>>>`.\n+5. No other modifications are made.\n+\n+See linkgit:git-merge[1] for some hints on resolving such\n+conflicts.\n+\n OPTIONS\n -------\n <commit>...::\ndiff --git a/Documentation/revisions.txt b/Documentation/revisions.txt\nindex 9e92734..04fceee 100644\n--- a/Documentation/revisions.txt\n+++ b/Documentation/revisions.txt\n@@ -25,7 +25,8 @@ blobs contained in a commit.\n   first match in the following rules:\n \n   . if `$GIT_DIR/<name>` exists, that is what you mean (this is usually\n-    useful only for `HEAD`, `FETCH_HEAD`, `ORIG_HEAD` and `MERGE_HEAD`);\n+    useful only for `HEAD`, `FETCH_HEAD`, `ORIG_HEAD`, `MERGE_HEAD`\n+    and `CHERRY_PICK_HEAD`);\n \n   . otherwise, `refs/<name>` if exists;\n \n@@ -46,6 +47,8 @@ you can change the tip of the branch back to the state before you ran\n them easily.\n MERGE_HEAD records the commit(s) you are merging into your branch\n when you run 'git merge'.\n+CHERRY_PICK_HEAD records the commit you are cherry-picking\n+when you run 'git cherry-pick'.\n +\n Note that any of the `refs/*` cases above may come either from\n the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.\ndiff --git a/branch.c b/branch.c\nindex 93dc866..dc23e95 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -217,6 +217,7 @@ void create_branch(const char *head,\n \n void remove_branch_state(void)\n {\n+\tunlink(git_path(\"CHERRY_PICK_HEAD\"));\n \tunlink(git_path(\"MERGE_HEAD\"));\n \tunlink(git_path(\"MERGE_RR\"));\n \tunlink(git_path(\"MERGE_MSG\"));\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex d7f55e3..7acd559 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1424,6 +1424,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t\tdie(\"cannot update HEAD ref\");\n \t}\n \n+\tunlink(git_path(\"CHERRY_PICK_HEAD\"));\n \tunlink(git_path(\"MERGE_HEAD\"));\n \tunlink(git_path(\"MERGE_MSG\"));\n \tunlink(git_path(\"MERGE_MODE\"));\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 8c58c3c..1b94c94 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -971,6 +971,13 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \t\telse\n \t\t\tdie(\"You have not concluded your merge (MERGE_HEAD exists).\");\n \t}\n+\tif (file_exists(git_path(\"CHERRY_PICK_HEAD\"))) {\n+\t\tif (advice_resolve_conflict)\n+\t\t\tdie(\"You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\\n\"\n+\t\t\t    \"Please, commit your changes before you can merge.\");\n+\t\telse\n+\t\t\tdie(\"You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\");\n+\t}\n \tresolve_undo_clear();\n \n \tif (verbosity < 0)\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex dc1b702..deecaab 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -248,6 +248,22 @@ static void set_author_ident_env(const char *message)\n \t\t\tsha1_to_hex(commit->object.sha1));\n }\n \n+static void write_cherry_pick_head(void)\n+{\n+\tint fd;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\tstrbuf_addf(&buf, \"%s\\n\", sha1_to_hex(commit->object.sha1));\n+\n+\tfd = open(git_path(\"CHERRY_PICK_HEAD\"), O_WRONLY | O_CREAT, 0666);\n+\tif (fd < 0)\n+\t\tdie_errno(\"Could not open '%s' for writing\",\n+\t\t\t  git_path(\"CHERRY_PICK_HEAD\"));\n+\tif (write_in_full(fd, buf.buf, buf.len) != buf.len || close(fd))\n+\t\tdie_errno(\"Could not write to '%s'\", git_path(\"CHERRY_PICK_HEAD\"));\n+\tstrbuf_release(&buf);\n+}\n+\n static void advise(const char *advice, ...)\n {\n \tva_list params;\n@@ -263,6 +279,12 @@ static void print_advice(void)\n \n \tif (msg) {\n \t\tfprintf(stderr, \"%s\\n\", msg);\n+\t\t/*\n+\t\t * A conflict has occured but the porcelain\n+\t\t * (typically rebase --interactive) wants to take care\n+\t\t * of the commit itself so remove CHERRY_PICK_HEAD\n+\t\t */\n+\t\tunlink(git_path(\"CHERRY_PICK_HEAD\"));\n \t\treturn;\n \t}\n \n@@ -270,8 +292,7 @@ static void print_advice(void)\n \tadvise(\"with 'git add <paths>' or 'git rm <paths>'\");\n \n \tif (action == CHERRY_PICK)\n-\t\tadvise(\"and commit the result with 'git commit -c %s'\",\n-\t\t       find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));\n+\t\tadvise(\"and commit the result with 'git commit -c CHERRY_PICK_HEAD'\");\n }\n \n static void write_message(struct strbuf *msgbuf, const char *filename)\n@@ -504,6 +525,8 @@ static int do_pick_commit(void)\n \t\t\tstrbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));\n \t\t\tstrbuf_addstr(&msgbuf, \")\\n\");\n \t\t}\n+\t\tif (!no_commit)\n+\t\t\twrite_cherry_pick_head();\n \t}\n \n \tif (!strategy || !strcmp(strategy, \"recursive\") || action == REVERT) {\ndiff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh\nindex 2313d79..e0bf356 100755\n--- a/t/t3507-cherry-pick-conflict.sh\n+++ b/t/t3507-cherry-pick-conflict.sh\n@@ -52,13 +52,87 @@ test_expect_success 'advice from failed cherry-pick' \"\n \terror: could not apply \\$picked... picked\n \thint: after resolving the conflicts, mark the corrected paths\n \thint: with 'git add <paths>' or 'git rm <paths>'\n-\thint: and commit the result with 'git commit -c \\$picked'\n+\thint: and commit the result with 'git commit -c CHERRY_PICK_HEAD'\n \tEOF\n \ttest_must_fail git cherry-pick picked 2>actual &&\n \n \ttest_cmp expected actual\n \"\n \n+test_expect_success 'failed cherry-pick sets CHERRY_PICK_HEAD' '\n+\tpristine_detach initial &&\n+\ttest_must_fail git cherry-pick picked &&\n+\ttest_cmp_rev picked CHERRY_PICK_HEAD\n+'\n+\n+test_expect_success 'successful cherry-pick does not set CHERRY_PICK_HEAD' '\n+\tpristine_detach initial &&\n+\tgit cherry-pick base &&\n+\ttest_must_fail git rev-parse --verify CHERRY_PICK_HEAD\n+'\n+\n+test_expect_success 'cherry-pick --no-commit does not set CHERRY_PICK_HEAD' '\n+\tpristine_detach initial &&\n+\tgit cherry-pick --no-commit base &&\n+\ttest_must_fail git rev-parse --verify CHERRY_PICK_HEAD\n+'\n+\n+test_expect_success 'GIT_CHERRY_PICK_HELP suppresses CHERRY_PICK_HEAD' '\n+\tpristine_detach initial &&\n+\t(\n+\t\tGIT_CHERRY_PICK_HELP=\"and then do something else\" &&\n+\t\texport GIT_CHERRY_PICK_HELP &&\n+\t\ttest_must_fail git cherry-pick picked\n+\t) &&\n+\ttest_must_fail git rev-parse --verify CHERRY_PICK_HEAD\n+'\n+\n+test_expect_success 'git reset clears CHERRY_PICK_HEAD' '\n+\tpristine_detach initial &&\n+\n+\ttest_must_fail git cherry-pick picked &&\n+\tgit reset &&\n+\n+\ttest_must_fail git rev-parse --verify CHERRY_PICK_HEAD\n+'\n+\n+test_expect_success 'failed commit does not clear CHERRY_PICK_HEAD' '\n+\tpristine_detach initial &&\n+\n+\ttest_must_fail git cherry-pick picked &&\n+\ttest_must_fail git commit &&\n+\n+\ttest_cmp_rev picked CHERRY_PICK_HEAD\n+'\n+\n+test_expect_success 'cancelled commit does not clear CHERRY_PICK_HEAD' '\n+\tpristine_detach initial &&\n+\n+\ttest_must_fail git cherry-pick picked &&\n+\techo resolved >foo &&\n+\tgit add foo &&\n+\tgit update-index --refresh -q &&\n+\ttest_must_fail git diff-index --exit-code HEAD &&\n+\t(\n+\t\tGIT_EDITOR=false &&\n+\t\texport GIT_EDITOR &&\n+\t\ttest_must_fail git commit\n+\t) &&\n+\n+\ttest_cmp_rev picked CHERRY_PICK_HEAD\n+'\n+\n+test_expect_success 'successful commit clears CHERRY_PICK_HEAD' '\n+\tpristine_detach initial &&\n+\n+\ttest_must_fail git cherry-pick picked &&\n+\techo resolved >foo &&\n+\tgit add foo &&\n+\tgit commit &&\n+\n+\ttest_must_fail git rev-parse --verify CHERRY_PICK_HEAD\n+'\n+\n test_expect_success 'failed cherry-pick produces dirty index' '\n \tpristine_detach initial &&\n \n-- \n1.7.4.1.51.g615e0\n"},{"id":"161733","messageId":"1298175149-41178-4-git-send-email-jaysoffian@gmail.com","threadId":"26564","inReplyTo":"1298175149-41178-1-git-send-email-jaysoffian@gmail.com","subject":"[PATCH v4 3/4] bash: teach __git_ps1 about CHERRY_PICK_HEAD","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-02-20T04:12:28Z","receivedAt":"2011-02-20T04:12:28Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"Make the git prompt (when enabled) show a CHERRY-PICKING indicator\nwhen we are in the middle of a conflicted cherry-pick, analogous\nto the existing MERGING and BISECTING flags.\n\nSigned-off-by: Jay Soffian <jaysoffian@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n contrib/completion/git-completion.bash |    2 ++\n 1 files changed, 2 insertions(+), 0 deletions(-)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 893b771..0b0b913 100755\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -246,6 +246,8 @@ __git_ps1 ()\n \t\t\t\tfi\n \t\t\telif [ -f \"$g/MERGE_HEAD\" ]; then\n \t\t\t\tr=\"|MERGING\"\n+\t\t\telif [ -f \"$g/CHERRY_PICK_HEAD\" ]; then\n+\t\t\t\tr=\"|CHERRY-PICKING\"\n \t\t\telif [ -f \"$g/BISECT_LOG\" ]; then\n \t\t\t\tr=\"|BISECTING\"\n \t\t\tfi\n-- \n1.7.4.1.51.g615e0\n"},{"id":"161732","messageId":"1298175149-41178-5-git-send-email-jaysoffian@gmail.com","threadId":"26564","inReplyTo":"1298175149-41178-1-git-send-email-jaysoffian@gmail.com","subject":"[PATCH v4 4/4] Teach commit about CHERRY_PICK_HEAD","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-02-20T04:12:29Z","receivedAt":"2011-02-20T04:12:29Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"Previously the user was advised to use commit -c CHERRY_PICK_HEAD after\na conflicting cherry-pick. While this would preserve the original\ncommit's authorship, it would sadly discard cherry-pick's carefully\ncrafted MERGE_MSG (which contains the list of conflicts as well as the\noriginal commit-id in the case of cherry-pick -x).\n\nOn the other hand, if a bare 'commit' were performed, it would preserve\nthe MERGE_MSG while resetting the authorship.\n\nIn other words, there was no way to simultaneously take the authorship\nfrom CHERRY_PICK_HEAD and the commit message from MERGE_MSG.\n\nThis change fixes that situation. A bare 'commit' will now take the\nauthorship from CHERRY_PICK_HEAD and the commit message from MERGE_MSG.\nIf the user wishes to reset authorship, that must now be done explicitly\nvia --reset-author.\n\nA side-benefit of passing commit authorship along this way is that we\ncan eliminate redundant authorship parsing code from revert.c.\n\n(Also removed an unused include from revert.c)\n\nSigned-off-by: Jay Soffian <jaysoffian@gmail.com>\n---\n Documentation/git-commit.txt    |    7 +-\n builtin/commit.c                |  155 ++++++++++++++++++++++++++++-----------\n builtin/revert.c                |   56 +--------------\n t/t3507-cherry-pick-conflict.sh |    2 +-\n t/t7509-commit.sh               |   29 +++++++\n wt-status.c                     |    4 +-\n wt-status.h                     |    9 ++-\n 7 files changed, 158 insertions(+), 104 deletions(-)\n\ndiff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt\nindex b586c0f..fd6a1f7 100644\n--- a/Documentation/git-commit.txt\n+++ b/Documentation/git-commit.txt\n@@ -84,9 +84,10 @@ OPTIONS\n \tlinkgit:git-rebase[1] for details.\n \n --reset-author::\n-\tWhen used with -C/-c/--amend options, declare that the\n-\tauthorship of the resulting commit now belongs of the committer.\n-\tThis also renews the author timestamp.\n+\tWhen used with -C/-c/--amend options, or when committing after a\n+\ta conflicting cherry-pick, declare that the authorship of the\n+\tresulting commit now belongs of the committer. This also renews\n+\tthe author timestamp.\n \n --short::\n \tWhen doing a dry-run, give the output in the short-format. See\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 7acd559..85e88b7 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -54,9 +54,17 @@ static const char empty_amend_advice[] =\n \"it empty. You can repeat your command with --allow-empty, or you can\\n\"\n \"remove the commit entirely with \\\"git reset HEAD^\\\".\\n\";\n \n+static const char empty_cherry_pick_advice[] =\n+\"The previous cherry-pick is now empty, possibly due to conflict resolution.\\n\"\n+\"If you wish to commit it anyway, use:\\n\"\n+\"\\n\"\n+\"    git commit --allow-empty\\n\"\n+\"\\n\"\n+\"Otherwise, please use 'git reset'\\n\";\n+\n static unsigned char head_sha1[20];\n \n-static char *use_message_buffer;\n+static const char *use_message_buffer;\n static const char commit_editmsg[] = \"COMMIT_EDITMSG\";\n static struct lock_file index_lock; /* real index */\n static struct lock_file false_lock; /* used only for partial commits */\n@@ -68,6 +76,11 @@ static enum {\n \n static const char *logfile, *force_author;\n static const char *template_file;\n+/*\n+ * The _message variables are commit names from which to take\n+ * the commit message and/or authorship.\n+ */\n+static const char *author_message, *author_message_buffer;\n static char *edit_message, *use_message;\n static char *fixup_message, *squash_message;\n static int all, edit_flag, also, interactive, only, amend, signoff;\n@@ -88,7 +101,8 @@ static enum {\n } cleanup_mode;\n static char *cleanup_arg;\n \n-static int use_editor = 1, initial_commit, in_merge, include_status = 1;\n+static enum commit_whence whence;\n+static int use_editor = 1, initial_commit, include_status = 1;\n static int show_ignored_in_status;\n static const char *only_include_assumed;\n static struct strbuf message;\n@@ -163,6 +177,36 @@ static struct option builtin_commit_options[] = {\n \tOPT_END()\n };\n \n+static void determine_whence(struct wt_status *s)\n+{\n+\tif (file_exists(git_path(\"MERGE_HEAD\")))\n+\t\twhence = FROM_MERGE;\n+\telse if (file_exists(git_path(\"CHERRY_PICK_HEAD\")))\n+\t\twhence = FROM_CHERRY_PICK;\n+\telse\n+\t\twhence = FROM_COMMIT;\n+\tif (s)\n+\t\ts->whence = whence;\n+}\n+\n+static const char *whence_s(void)\n+{\n+\tchar *s = \"\";\n+\n+\tswitch (whence) {\n+\tcase FROM_COMMIT:\n+\t\tbreak;\n+\tcase FROM_MERGE:\n+\t\ts = \"merge\";\n+\t\tbreak;\n+\tcase FROM_CHERRY_PICK:\n+\t\ts = \"cherry-pick\";\n+\t\tbreak;\n+\t}\n+\n+\treturn s;\n+}\n+\n static void rollback_index_files(void)\n {\n \tswitch (commit_style) {\n@@ -378,8 +422,8 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, int\n \t */\n \tcommit_style = COMMIT_PARTIAL;\n \n-\tif (in_merge)\n-\t\tdie(\"cannot do a partial commit during a merge.\");\n+\tif (whence != FROM_COMMIT)\n+\t\tdie(\"cannot do a partial commit during a %s.\", whence_s());\n \n \tmemset(&partial, 0, sizeof(partial));\n \tpartial.strdup_strings = 1;\n@@ -469,18 +513,18 @@ static void determine_author_info(struct strbuf *author_ident)\n \temail = getenv(\"GIT_AUTHOR_EMAIL\");\n \tdate = getenv(\"GIT_AUTHOR_DATE\");\n \n-\tif (use_message && !renew_authorship) {\n+\tif (author_message) {\n \t\tconst char *a, *lb, *rb, *eol;\n \n-\t\ta = strstr(use_message_buffer, \"\\nauthor \");\n+\t\ta = strstr(author_message_buffer, \"\\nauthor \");\n \t\tif (!a)\n-\t\t\tdie(\"invalid commit: %s\", use_message);\n+\t\t\tdie(\"invalid commit: %s\", author_message);\n \n \t\tlb = strchrnul(a + strlen(\"\\nauthor \"), '<');\n \t\trb = strchrnul(lb, '>');\n \t\teol = strchrnul(rb, '\\n');\n \t\tif (!*lb || !*rb || !*eol)\n-\t\t\tdie(\"invalid commit: %s\", use_message);\n+\t\t\tdie(\"invalid commit: %s\", author_message);\n \n \t\tif (lb == a + strlen(\"\\nauthor \"))\n \t\t\t/* \\nauthor <foo@example.com> */\n@@ -641,11 +685,15 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t}\n \n \t/*\n-\t * This final case does not modify the template message,\n-\t * it just sets the argument to the prepare-commit-msg hook.\n+\t * The remaining cases don't modify the template message, but\n+\t * just set the argument(s) to the prepare-commit-msg hook.\n \t */\n-\telse if (in_merge)\n+\telse if (whence == FROM_MERGE)\n \t\thook_arg1 = \"merge\";\n+\telse if (whence == FROM_CHERRY_PICK) {\n+\t\thook_arg1 = \"commit\";\n+\t\thook_arg2 = \"CHERRY_PICK_HEAD\";\n+\t}\n \n \tif (squash_message) {\n \t\t/*\n@@ -694,16 +742,18 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \tstrbuf_addstr(&committer_ident, git_committer_info(0));\n \tif (use_editor && include_status) {\n \t\tchar *ai_tmp, *ci_tmp;\n-\t\tif (in_merge)\n+\t\tif (whence != FROM_COMMIT)\n \t\t\tfprintf(fp,\n \t\t\t\t\"#\\n\"\n-\t\t\t\t\"# It looks like you may be committing a MERGE.\\n\"\n+\t\t\t\t\"# It looks like you may be committing a %s.\\n\"\n \t\t\t\t\"# If this is not correct, please remove the file\\n\"\n \t\t\t\t\"#\t%s\\n\"\n \t\t\t\t\"# and try again.\\n\"\n \t\t\t\t\"#\\n\",\n-\t\t\t\tgit_path(\"MERGE_HEAD\"));\n-\n+\t\t\t\twhence_s(),\n+\t\t\t\tgit_path(whence == FROM_MERGE\n+\t\t\t\t\t ? \"MERGE_HEAD\"\n+\t\t\t\t\t : \"CHERRY_PICK_HEAD\"));\n \t\tfprintf(fp,\n \t\t\t\"\\n\"\n \t\t\t\"# Please enter the commit message for your changes.\");\n@@ -766,11 +816,18 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \n \tfclose(fp);\n \n-\tif (!commitable && !in_merge && !allow_empty &&\n+\t/*\n+\t * Reject an attempt to record a non-merge empty commit without\n+\t * explicit --allow-empty. In the cherry-pick case, it may be\n+\t * empty due to conflict resolution, which the user should okay.\n+\t */\n+\tif (!commitable && whence != FROM_MERGE && !allow_empty &&\n \t    !(amend && is_a_merge(head_sha1))) {\n \t\trun_status(stdout, index_file, prefix, 0, s);\n \t\tif (amend)\n \t\t\tfputs(empty_amend_advice, stderr);\n+\t\telse if (whence == FROM_CHERRY_PICK)\n+\t\t\tfputs(empty_cherry_pick_advice, stderr);\n \t\treturn 0;\n \t}\n \n@@ -898,6 +955,28 @@ static void handle_untracked_files_arg(struct wt_status *s)\n \t\tdie(\"Invalid untracked files mode '%s'\", untracked_files_arg);\n }\n \n+static const char *read_commit_message(const char *name)\n+{\n+\tconst char *out_enc, *out;\n+\tstruct commit *commit;\n+\n+\tcommit = lookup_commit_reference_by_name(name);\n+\tif (!commit)\n+\t\tdie(\"could not lookup commit %s\", name);\n+\tout_enc = get_commit_output_encoding();\n+\tout = logmsg_reencode(commit, out_enc);\n+\n+\t/*\n+\t * If we failed to reencode the buffer, just copy it\n+\t * byte for byte so the user can try to fix it up.\n+\t * This also handles the case where input and output\n+\t * encodings are identical.\n+\t */\n+\tif (out == NULL)\n+\t\tout = xstrdup(commit->buffer);\n+\treturn out;\n+}\n+\n static int parse_and_validate_options(int argc, const char *argv[],\n \t\t\t\t      const char * const usage[],\n \t\t\t\t      const char *prefix,\n@@ -927,8 +1006,8 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t/* Sanity check options */\n \tif (amend && initial_commit)\n \t\tdie(\"You have nothing to amend.\");\n-\tif (amend && in_merge)\n-\t\tdie(\"You are in the middle of a merge -- cannot amend.\");\n+\tif (amend && whence != FROM_COMMIT)\n+\t\tdie(\"You are in the middle of a %s -- cannot amend.\", whence_s());\n \tif (fixup_message && squash_message)\n \t\tdie(\"Options --squash and --fixup cannot be used together\");\n \tif (use_message)\n@@ -947,26 +1026,18 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t\tuse_message = edit_message;\n \tif (amend && !use_message && !fixup_message)\n \t\tuse_message = \"HEAD\";\n-\tif (!use_message && renew_authorship)\n+\tif (!use_message && whence != FROM_CHERRY_PICK && renew_authorship)\n \t\tdie(\"--reset-author can be used only with -C, -c or --amend.\");\n \tif (use_message) {\n-\t\tconst char *out_enc;\n-\t\tstruct commit *commit;\n-\n-\t\tcommit = lookup_commit_reference_by_name(use_message);\n-\t\tif (!commit)\n-\t\t\tdie(\"could not lookup commit %s\", use_message);\n-\t\tout_enc = get_commit_output_encoding();\n-\t\tuse_message_buffer = logmsg_reencode(commit, out_enc);\n-\n-\t\t/*\n-\t\t * If we failed to reencode the buffer, just copy it\n-\t\t * byte for byte so the user can try to fix it up.\n-\t\t * This also handles the case where input and output\n-\t\t * encodings are identical.\n-\t\t */\n-\t\tif (use_message_buffer == NULL)\n-\t\t\tuse_message_buffer = xstrdup(commit->buffer);\n+\t\tuse_message_buffer = read_commit_message(use_message);\n+\t\tif (!renew_authorship) {\n+\t\t\tauthor_message = use_message;\n+\t\t\tauthor_message_buffer = use_message_buffer;\n+\t\t}\n+\t}\n+\tif (whence == FROM_CHERRY_PICK && !renew_authorship) {\n+\t\tauthor_message = \"CHERRY_PICK_HEAD\";\n+\t\tauthor_message_buffer = read_commit_message(author_message);\n \t}\n \n \tif (!!also + !!only + !!all + !!interactive > 1)\n@@ -1117,7 +1188,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \twt_status_prepare(&s);\n \tgitmodules_config();\n \tgit_config(git_status_config, &s);\n-\tin_merge = file_exists(git_path(\"MERGE_HEAD\"));\n+\tdetermine_whence(&s);\n \targc = parse_options(argc, argv, prefix,\n \t\t\t     builtin_status_options,\n \t\t\t     builtin_status_usage, 0);\n@@ -1140,7 +1211,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \t}\n \n \ts.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;\n-\ts.in_merge = in_merge;\n \ts.ignore_submodule_arg = ignore_submodule_arg;\n \twt_status_collect(&s);\n \n@@ -1302,8 +1372,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \n \twt_status_prepare(&s);\n \tgit_config(git_commit_config, &s);\n-\tin_merge = file_exists(git_path(\"MERGE_HEAD\"));\n-\ts.in_merge = in_merge;\n+\tdetermine_whence(&s);\n \n \tif (s.use_color == -1)\n \t\ts.use_color = git_use_color_default;\n@@ -1340,7 +1409,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \n \t\tfor (c = commit->parents; c; c = c->next)\n \t\t\tpptr = &commit_list_insert(c->item, pptr)->next;\n-\t} else if (in_merge) {\n+\t} else if (whence == FROM_MERGE) {\n \t\tstruct strbuf m = STRBUF_INIT;\n \t\tFILE *fp;\n \n@@ -1369,7 +1438,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t\t\tparents = reduce_heads(parents);\n \t} else {\n \t\tif (!reflog_msg)\n-\t\t\treflog_msg = \"commit\";\n+\t\t\treflog_msg = (whence == FROM_CHERRY_PICK)\n+\t\t\t\t\t? \"commit (cherry-pick)\"\n+\t\t\t\t\t: \"commit\";\n \t\tpptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;\n \t}\n \ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex deecaab..c57b872 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -3,7 +3,6 @@\n #include \"object.h\"\n #include \"commit.h\"\n #include \"tag.h\"\n-#include \"wt-status.h\"\n #include \"run-command.h\"\n #include \"exec_cmd.h\"\n #include \"utf8.h\"\n@@ -198,56 +197,6 @@ static void add_message_to_msg(struct strbuf *msgbuf, const char *message)\n \tstrbuf_addstr(msgbuf, p);\n }\n \n-static void set_author_ident_env(const char *message)\n-{\n-\tconst char *p = message;\n-\tif (!p)\n-\t\tdie (\"Could not read commit message of %s\",\n-\t\t\t\tsha1_to_hex(commit->object.sha1));\n-\twhile (*p && *p != '\\n') {\n-\t\tconst char *eol;\n-\n-\t\tfor (eol = p; *eol && *eol != '\\n'; eol++)\n-\t\t\t; /* do nothing */\n-\t\tif (!prefixcmp(p, \"author \")) {\n-\t\t\tchar *line, *pend, *email, *timestamp;\n-\n-\t\t\tp += 7;\n-\t\t\tline = xmemdupz(p, eol - p);\n-\t\t\temail = strchr(line, '<');\n-\t\t\tif (!email)\n-\t\t\t\tdie (\"Could not extract author email from %s\",\n-\t\t\t\t\tsha1_to_hex(commit->object.sha1));\n-\t\t\tif (email == line)\n-\t\t\t\tpend = line;\n-\t\t\telse\n-\t\t\t\tfor (pend = email; pend != line + 1 &&\n-\t\t\t\t\t\tisspace(pend[-1]); pend--);\n-\t\t\t\t\t; /* do nothing */\n-\t\t\t*pend = '\\0';\n-\t\t\temail++;\n-\t\t\ttimestamp = strchr(email, '>');\n-\t\t\tif (!timestamp)\n-\t\t\t\tdie (\"Could not extract author time from %s\",\n-\t\t\t\t\tsha1_to_hex(commit->object.sha1));\n-\t\t\t*timestamp = '\\0';\n-\t\t\tfor (timestamp++; *timestamp && isspace(*timestamp);\n-\t\t\t\t\ttimestamp++)\n-\t\t\t\t; /* do nothing */\n-\t\t\tsetenv(\"GIT_AUTHOR_NAME\", line, 1);\n-\t\t\tsetenv(\"GIT_AUTHOR_EMAIL\", email, 1);\n-\t\t\tsetenv(\"GIT_AUTHOR_DATE\", timestamp, 1);\n-\t\t\tfree(line);\n-\t\t\treturn;\n-\t\t}\n-\t\tp = eol;\n-\t\tif (*p == '\\n')\n-\t\t\tp++;\n-\t}\n-\tdie (\"No author information found in %s\",\n-\t\t\tsha1_to_hex(commit->object.sha1));\n-}\n-\n static void write_cherry_pick_head(void)\n {\n \tint fd;\n@@ -290,9 +239,7 @@ static void print_advice(void)\n \n \tadvise(\"after resolving the conflicts, mark the corrected paths\");\n \tadvise(\"with 'git add <paths>' or 'git rm <paths>'\");\n-\n-\tif (action == CHERRY_PICK)\n-\t\tadvise(\"and commit the result with 'git commit -c CHERRY_PICK_HEAD'\");\n+\tadvise(\"and commit the result with 'git commit'\");\n }\n \n static void write_message(struct strbuf *msgbuf, const char *filename)\n@@ -518,7 +465,6 @@ static int do_pick_commit(void)\n \t\tbase_label = msg.parent_label;\n \t\tnext = commit;\n \t\tnext_label = msg.label;\n-\t\tset_author_ident_env(msg.message);\n \t\tadd_message_to_msg(&msgbuf, msg.message);\n \t\tif (no_replay) {\n \t\t\tstrbuf_addstr(&msgbuf, \"(cherry picked from commit \");\ndiff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh\nindex e0bf356..9574180 100755\n--- a/t/t3507-cherry-pick-conflict.sh\n+++ b/t/t3507-cherry-pick-conflict.sh\n@@ -52,7 +52,7 @@ test_expect_success 'advice from failed cherry-pick' \"\n \terror: could not apply \\$picked... picked\n \thint: after resolving the conflicts, mark the corrected paths\n \thint: with 'git add <paths>' or 'git rm <paths>'\n-\thint: and commit the result with 'git commit -c CHERRY_PICK_HEAD'\n+\thint: and commit the result with 'git commit'\n \tEOF\n \ttest_must_fail git cherry-pick picked 2>actual &&\n \ndiff --git a/t/t7509-commit.sh b/t/t7509-commit.sh\nindex 77b6920..b61fd3c 100755\n--- a/t/t7509-commit.sh\n+++ b/t/t7509-commit.sh\n@@ -157,4 +157,33 @@ test_expect_success '--reset-author should be rejected without -c/-C/--amend' '\n \ttest_must_fail git commit -a --reset-author -m done\n '\n \n+test_expect_success 'commit respects CHERRY_PICK_HEAD and MERGE_MSG' '\n+\techo \"cherry-pick 1a\" >>foo &&\n+\ttest_tick &&\n+\tgit commit -am \"cherry-pick 1\" --author=\"Cherry <cherry@pick.er>\" &&\n+\tgit tag cherry-pick-head &&\n+\tgit rev-parse cherry-pick-head >.git/CHERRY_PICK_HEAD &&\n+\techo \"This is a MERGE_MSG\" >.git/MERGE_MSG &&\n+\techo \"cherry-pick 1b\" >>foo &&\n+\ttest_tick &&\n+\tgit commit -a &&\n+\tauthor_header cherry-pick-head >expect &&\n+\tauthor_header HEAD >actual &&\n+\ttest_cmp expect actual &&\n+\n+\techo \"This is a MERGE_MSG\" >expect &&\n+\tmessage_body HEAD >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--reset-author with CHERRY_PICK_HEAD' '\n+\tgit rev-parse cherry-pick-head >.git/CHERRY_PICK_HEAD &&\n+\techo \"cherry-pick 2\" >>foo &&\n+\ttest_tick &&\n+\tgit commit -am \"cherry-pick 2\" --reset-author &&\n+\techo \"author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE\" >expect &&\n+\tauthor_header HEAD >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\ndiff --git a/wt-status.c b/wt-status.c\nindex 123582b..fbaaf54 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -60,7 +60,7 @@ static void wt_status_print_unmerged_header(struct wt_status *s)\n \tcolor_fprintf_ln(s->fp, c, \"# Unmerged paths:\");\n \tif (!advice_status_hints)\n \t\treturn;\n-\tif (s->in_merge)\n+\tif (s->whence != FROM_COMMIT)\n \t\t;\n \telse if (!s->is_initial)\n \t\tcolor_fprintf_ln(s->fp, c, \"#   (use \\\"git reset %s <file>...\\\" to unstage)\", s->reference);\n@@ -77,7 +77,7 @@ static void wt_status_print_cached_header(struct wt_status *s)\n \tcolor_fprintf_ln(s->fp, c, \"# Changes to be committed:\");\n \tif (!advice_status_hints)\n \t\treturn;\n-\tif (s->in_merge)\n+\tif (s->whence != FROM_COMMIT)\n \t\t; /* NEEDSWORK: use \"git reset --unresolve\"??? */\n \telse if (!s->is_initial)\n \t\tcolor_fprintf_ln(s->fp, c, \"#   (use \\\"git reset %s <file>...\\\" to unstage)\", s->reference);\ndiff --git a/wt-status.h b/wt-status.h\nindex 20b17cf..cec482a 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -24,6 +24,13 @@ enum untracked_status_type {\n \tSHOW_ALL_UNTRACKED_FILES\n };\n \n+/* from where does this commit originate */\n+enum commit_whence {\n+\tFROM_COMMIT,     /* normal */\n+\tFROM_MERGE,      /* commit came from merge */\n+\tFROM_CHERRY_PICK /* commit came from cherry-pick */\n+};\n+\n struct wt_status_change_data {\n \tint worktree_status;\n \tint index_status;\n@@ -40,7 +47,7 @@ struct wt_status {\n \tconst char **pathspec;\n \tint verbose;\n \tint amend;\n-\tint in_merge;\n+\tenum commit_whence whence;\n \tint nowarn;\n \tint use_color;\n \tint relative_paths;\n-- \n1.7.4.1.51.g615e0\n"}]}