{"thread":{"id":"49830","subject":"[RFC PATCH 0/2] Fix scissors bug during merge conflict","startedAt":"2018-11-14T05:24:58Z","lastAt":"2019-04-18T17:21:16Z","messageCount":105,"participants":["Denton Liu","Junio C Hamano","SZEDER Gábor","Eric Sunshine","Phillip Wood","Ramsay Jones"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"363315","messageId":"cover.1542172724.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":null,"subject":"[RFC PATCH 0/2] Fix scissors bug during merge conflict","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2018-11-14T05:24:45Z","receivedAt":"2018-11-14T05:24:58Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"I discovered a bug in Git a while ago where if one is using\ncommit.cleanup = scissors, if making a commit after a merge conflict,\nthe scissors line will be placed after the `Conflicts:` section. As a\nresult, a careless Git user (e.g. me) may accidentally commit the\n`Conflicts:` section.\n\nHere is a test case to replicate the behaviour:\n\n\tmkdir test\n\tcd test/\n\tgit init\n\tgit config commit.cleanup scissors\n\ttouch a\n\tgit add a\n\tgit commit -m 'test'\n\techo a > a\n\tgit commit -am 'test2'\n\tgit checkout -b new HEAD^\n\techo b > a\n\tgit commit -am 'test3'\n\tgit merge master\n\techo c > a\n\tgit add a\n\tgit commit # look at the commit message here\n\nAnd the resulting message that's sent to the text editor:\n\n\tMerge branch 'master' into new\n\n\t# Conflicts:\n\t#\ta\n\t# ------------------------ >8 ------------------------\n\t# Do not modify or remove the line above.\n\t# Everything below it will be ignored.\n\t#\n\t# It looks like you may be committing a merge.\n\t# If this is not correct, please remove the file\n\t#\t.git/MERGE_HEAD\n\t# and try again.\n\n\n\t# Please enter the commit message for your changes. Lines starting\n\t# with '#' will be kept; you may remove them yourself if you want to.\n\t# An empty message aborts the commit.\n\t#\n\t# On branch new\n\t# All conflicts fixed but you are still merging.\n\t#\n\t# Changes to be committed:\n\t#\tmodified:   a\n\t#\n\nWith this fix, the message becomes the following:\n\n\tMerge branch 'master' into new\n\n\t# ------------------------ >8 ------------------------\n\t# Do not modify or remove the line above.\n\t# Everything below it will be ignored.\n\t#\n\t# Conflicts:\n\t#\ta\n\t#\n\t# It looks like you may be committing a merge.\n\t# If this is not correct, please remove the file\n\t#\t.git/MERGE_HEAD\n\t# and try again.\n\n\n\t# Please enter the commit message for your changes. Lines starting\n\t# with '#' will be kept; you may remove them yourself if you want to.\n\t# An empty message aborts the commit.\n\t#\n\t# On branch new\n\t# All conflicts fixed but you are still merging.\n\t#\n\t# Changes to be committed:\n\t#\tmodified:   a\n\t#\n\nLet me know what you think of the change. Of course, documentation and\ntesting will come after this leaves the RFC phase.\n\nDenton Liu (2):\n  commit: don't add scissors line if one exists\n  merge: add scissors line on merge conflict\n\n builtin/commit.c | 11 +++++++++--\n builtin/merge.c  | 16 ++++++++++++++++\n 2 files changed, 25 insertions(+), 2 deletions(-)\n\n-- \n2.19.1\n\n"},{"id":"363316","messageId":"1c16b9497bd630f0636aa7729082da7a90ba42d9.1542172724.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1542172724.git.liu.denton@gmail.com","subject":"[RFC PATCH 1/2] commit: don't add scissors line if one exists","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2018-11-14T05:24:53Z","receivedAt":"2018-11-14T05:24:58Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"If commit.cleanup = scissors is specified, don't produce a scissors line\nif one already exists in the commit message.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/commit.c | 11 +++++++++--\n 1 file changed, 9 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 0d9828e29e..e486246329 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -659,6 +659,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \tconst char *hook_arg2 = NULL;\n \tint clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);\n \tint old_display_comment_prefix;\n+\tint contains_scissors = 0;\n \n \t/* This checks and barfs if author is badly specified */\n \tdetermine_author_info(author_ident);\n@@ -742,6 +743,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\tclean_message_contents = 0;\n \t}\n \n+\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\twt_status_locate_end(sb.buf, sb.len) != sb.len)\n+\t    contains_scissors = 1;\n+\n \t/*\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@@ -798,7 +803,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\tstruct ident_split ci, ai;\n \n \t\tif (whence != FROM_COMMIT) {\n-\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n+\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\t\t\t!contains_scissors)\n \t\t\t\twt_status_add_cut_line(s->fp);\n \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL,\n \t\t\t    whence == FROM_MERGE\n@@ -824,7 +830,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\t\t  \" Lines starting\\nwith '%c' will be ignored, and an empty\"\n \t\t\t\t  \" message aborts the commit.\\n\"), comment_line_char);\n \t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n-\t\t\t whence == FROM_COMMIT)\n+\t\t\t whence == FROM_COMMIT &&\n+\t\t\t !contains_scissors)\n \t\t\twt_status_add_cut_line(s->fp);\n \t\telse /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n \t\t\tstatus_printf(s, GIT_COLOR_NORMAL,\n-- \n2.19.1\n\n"},{"id":"363317","messageId":"a1d4f535416689f3e5e629bcafd01515ad8dd6ae.1542172724.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1542172724.git.liu.denton@gmail.com","subject":"[RFC PATCH 2/2] merge: add scissors line on merge conflict","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2018-11-14T05:25:09Z","receivedAt":"2018-11-14T05:25:14Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"This fixes a bug where the scissors line is placed after the Conflicts:\nsection, in the case where a merge conflict occurs and\nmerge.cleanup = scissors.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/merge.c | 16 ++++++++++++++++\n 1 file changed, 16 insertions(+)\n\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 8f4a5065c2..c5010cee5e 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -36,6 +36,7 @@\n #include \"packfile.h\"\n #include \"tag.h\"\n #include \"alias.h\"\n+#include \"wt-status.h\"\n \n #define DEFAULT_TWOHEAD (1<<0)\n #define DEFAULT_OCTOPUS (1<<1)\n@@ -96,6 +97,9 @@ enum ff_type {\n \n static enum ff_type fast_forward = FF_ALLOW;\n \n+const char *cleanup_arg;\n+int put_scissors;\n+\n static int option_parse_message(const struct option *opt,\n \t\t\t\tconst char *arg, int unset)\n {\n@@ -243,6 +247,7 @@ static struct option builtin_merge_options[] = {\n \t\tN_(\"perform a commit if the merge succeeds (default)\")),\n \tOPT_BOOL('e', \"edit\", &option_edit,\n \t\tN_(\"edit message before committing\")),\n+\tOPT_STRING(0, \"cleanup\", &cleanup_arg, N_(\"default\"), N_(\"how to strip spaces and #comments from message\")),\n \tOPT_SET_INT(0, \"ff\", &fast_forward, N_(\"allow fast-forward (default)\"), FF_ALLOW),\n \tOPT_SET_INT_F(0, \"ff-only\", &fast_forward,\n \t\t      N_(\"abort if fast-forward is not possible\"),\n@@ -606,6 +611,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n \t\treturn git_config_string(&pull_twohead, k, v);\n \telse if (!strcmp(k, \"pull.octopus\"))\n \t\treturn git_config_string(&pull_octopus, k, v);\n+\telse if (!strcmp(k, \"commit.cleanup\"))\n+\t\treturn git_config_string(&cleanup_arg, k, v);\n \telse if (!strcmp(k, \"merge.renormalize\"))\n \t\toption_renormalize = git_config_bool(k, v);\n \telse if (!strcmp(k, \"merge.ff\")) {\n@@ -894,6 +901,13 @@ static int suggest_conflicts(void)\n \tfilename = git_path_merge_msg(the_repository);\n \tfp = xfopen(filename, \"a\");\n \n+\tif (put_scissors) {\n+\t    fputc('\\n', fp);\n+\t    wt_status_add_cut_line(fp);\n+\t    /* comments out the newline from append_conflicts_hint */\n+\t    fputc(comment_line_char, fp);\n+\t}\n+\n \tappend_conflicts_hint(&msgbuf);\n \tfputs(msgbuf.buf, fp);\n \tstrbuf_release(&msgbuf);\n@@ -1402,6 +1416,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \tif (option_edit < 0)\n \t\toption_edit = default_edit_option();\n \n+\tput_scissors = cleanup_arg && !strcmp(cleanup_arg, \"scissors\");\n+\n \tif (!use_strategies) {\n \t\tif (!remoteheads)\n \t\t\t; /* already up-to-date */\n-- \n2.19.1\n\n"},{"id":"363332","messageId":"xmqq1s7onlic.fsf@gitster-ct.c.googlers.com","threadId":"49830","inReplyTo":"cover.1542172724.git.liu.denton@gmail.com","subject":"Re: [RFC PATCH 0/2] Fix scissors bug during merge conflict","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-14T07:52:59Z","receivedAt":"2018-11-14T07:53:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Denton Liu <liu.denton@gmail.com> writes:\n\n> With this fix, the message becomes the following:\n>\n> \tMerge branch 'master' into new\n>\n> \t# ------------------------ >8 ------------------------\n> \t# Do not modify or remove the line above.\n> \t# Everything below it will be ignored.\n> \t#\n> \t# Conflicts:\n> \t#\ta\n\nI have a mixed feeling about this change and I certainly would not\ncall it a \"fix\".\n\nThe reason why we give the list of conflicted paths that is\ncommented out is to remind the user of them in order to help them\ndescribe what area of the codebase had overlapping changes, why, and\nhow the overlap was resolved, which is relevant information when\nsomebody else later needs to dig into the history to understand how\nthe code came into today's shape and why.  For that reason, if a\ncareless user left conflicts list behind without describing these\ndetails about the merge, it might be better to have the unexplained\nlist in the merge than nothing.\n\nIn theory, the above argument applies the same way for the paths to\nbe committed, but the list is fairly trivial to recreate with \"git\ndiff $it^!\", unlike \"which paths had conflict\", which can only be\nfound out by recreating the auto-merge.  So in practice, the paths\nthat had conflicts is more worth showing than the paths that were\nmodified.\n\nSo, I dunno.  If we value the \"more expensive list to reproduce\",\nthe fix might be not to place it (and possibly the comments and\neverything under the scissors line) behind a \"# \" comment char on\nthe line, without moving its position.\n\n.\n\n\n"},{"id":"363333","messageId":"xmqqwopgm6bb.fsf@gitster-ct.c.googlers.com","threadId":"49830","inReplyTo":"1c16b9497bd630f0636aa7729082da7a90ba42d9.1542172724.git.liu.denton@gmail.com","subject":"Re: [RFC PATCH 1/2] commit: don't add scissors line if one exists","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-14T08:06:32Z","receivedAt":"2018-11-14T08:06:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Denton Liu <liu.denton@gmail.com> writes:\n\n> If commit.cleanup = scissors is specified, don't produce a scissors line\n> if one already exists in the commit message.\n\nIt is good that you won't have two such lines in the end result, but\nis this (1) hiding real problem under the rug? (2) losing information?\n\nIf the current invocation of \"git commit\" added a scissors line in\nthe buffer to be edited already, and we are adding another one in\nthis function, is it possible that the real problem that somebody\nelse has called wt_status_add_cut_line() before this function is\ncalled, in which case that other caller is what we need to fix,\ninstead of this one?\n\nIf the existing line in the buffer came from the end user (perhaps\nit was given from \"-F <file>\", etc., with \"-e\" option) or --amend,\nhow can we be sure if it is OK to lose everything after that\nscissors looking line?  In other words, the scissors looking line\nmay just be part of the log message, in which case we may want to\nquote/escape it, so that the true scissors we append at a later\nplace in the buffer would be noticed without losing the text before\nand after that scissors looking line we already had when this\nfunction was called?\n\n"},{"id":"363334","messageId":"20181114081001.GA1348@archbookpro.localdomain","threadId":"49830","inReplyTo":"xmqq1s7onlic.fsf@gitster-ct.c.googlers.com","subject":"Re: [RFC PATCH 0/2] Fix scissors bug during merge conflict","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2018-11-14T08:10:01Z","receivedAt":"2018-11-14T08:10:06Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"On Wed, Nov 14, 2018 at 04:52:59PM +0900, Junio C Hamano wrote:\n> Denton Liu <liu.denton@gmail.com> writes:\n> \n> > With this fix, the message becomes the following:\n> >\n> > \tMerge branch 'master' into new\n> >\n> > \t# ------------------------ >8 ------------------------\n> > \t# Do not modify or remove the line above.\n> > \t# Everything below it will be ignored.\n> > \t#\n> > \t# Conflicts:\n> > \t#\ta\n> \n> I have a mixed feeling about this change and I certainly would not\n> call it a \"fix\".\n> \n> The reason why we give the list of conflicted paths that is\n> commented out is to remind the user of them in order to help them\n> describe what area of the codebase had overlapping changes, why, and\n> how the overlap was resolved, which is relevant information when\n> somebody else later needs to dig into the history to understand how\n> the code came into today's shape and why.  For that reason, if a\n> careless user left conflicts list behind without describing these\n> details about the merge, it might be better to have the unexplained\n> list in the merge than nothing.\n> \n\nThe reason why I implemented it this way is because the default\ncleanup setting (strip) produces this message:\n\n\tMerge branch 'master' into new\n\n\t# Conflicts:\n\t#\ta\n\t#\n\t# It looks like you may be committing a merge.\n\t# If this is not correct, please remove the file\n\t#\t.git/MERGE_HEAD\n\t# and try again.\n\n\n\t# Please enter the commit message for your changes. Lines starting\n\t# with '#' will be ignored, and an empty message aborts the commit.\n\t#\n\t# On branch new\n\t# All conflicts fixed but you are still merging.\n\t#\n\t# Changes to be committed:\n\t#\tmodified:   a\n\t#\n\nWhich makes it seem like the `Conflicts:` section should be removed by\ndefault.\n\n> In theory, the above argument applies the same way for the paths to\n> be committed, but the list is fairly trivial to recreate with \"git\n> diff $it^!\", unlike \"which paths had conflict\", which can only be\n> found out by recreating the auto-merge.  So in practice, the paths\n> that had conflicts is more worth showing than the paths that were\n> modified.\n> \n> So, I dunno.  If we value the \"more expensive list to reproduce\",\n> the fix might be not to place it (and possibly the comments and\n> everything under the scissors line) behind a \"# \" comment char on\n> the line, without moving its position.\n\nIf I understood correctly, then I have no strong opinions between\nuncommenting the Conflicts section by default and this change; I just\nthink it'd be nice to have behaviour that's consistent.\n\n> \n> .\n> \n> \n"},{"id":"363386","messageId":"20181114180608.GA2299@archbookpro.localdomain","threadId":"49830","inReplyTo":"xmqqwopgm6bb.fsf@gitster-ct.c.googlers.com","subject":"Re: [RFC PATCH 1/2] commit: don't add scissors line if one exists","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2018-11-14T18:06:08Z","receivedAt":"2018-11-14T18:06:14Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"On Wed, Nov 14, 2018 at 05:06:32PM +0900, Junio C Hamano wrote:\n> Denton Liu <liu.denton@gmail.com> writes:\n> \n> > If commit.cleanup = scissors is specified, don't produce a scissors line\n> > if one already exists in the commit message.\n> \n> It is good that you won't have two such lines in the end result, but\n> is this (1) hiding real problem under the rug? (2) losing information?\n> \n> If the current invocation of \"git commit\" added a scissors line in\n> the buffer to be edited already, and we are adding another one in\n> this function, is it possible that the real problem that somebody\n> else has called wt_status_add_cut_line() before this function is\n> called, in which case that other caller is what we need to fix,\n> instead of this one?\n> \n\nIn patch 2/2, I intentionally inserted a scissors line into MERGE_MSG so\nthis patch ensures that we don't get duplicate scissors.\n\n> If the existing line in the buffer came from the end user (perhaps\n> it was given from \"-F <file>\", etc., with \"-e\" option) or --amend,\n> how can we be sure if it is OK to lose everything after that\n> scissors looking line?  In other words, the scissors looking line\n> may just be part of the log message, in which case we may want to\n> quote/escape it, so that the true scissors we append at a later\n> place in the buffer would be noticed without losing the text before\n> and after that scissors looking line we already had when this\n> function was called?\n> \n\nWith the existing behaviour, any messages that contain a scissors\nlooking line will get cut at the earliest scissors anyway, so I believe\nthat this patch would not change the behaviour. If the users were\ndealing with commit messages with a scissors looking line, the current\nbehaviour already requires users to be extra careful to ensure that the\nscissors don't get accidentally removed so in the interest of preserving\nthe existing behaviour, I don't think that any extra information would\nbe lost from this patch.\n"},{"id":"363459","messageId":"xmqqzhu9lmsi.fsf@gitster-ct.c.googlers.com","threadId":"49830","inReplyTo":"20181114180608.GA2299@archbookpro.localdomain","subject":"Re: [RFC PATCH 1/2] commit: don't add scissors line if one exists","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-16T03:32:45Z","receivedAt":"2018-11-16T03:32:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Denton Liu <liu.denton@gmail.com> writes:\n\n>> If the current invocation of \"git commit\" added a scissors line in\n>> the buffer to be edited already, and we are adding another one in\n>> this function, is it possible that the real problem that somebody\n>> else has called wt_status_add_cut_line() before this function is\n>> called, in which case that other caller is what we need to fix,\n>> instead of this one?\n>> \n>\n> In patch 2/2, I intentionally inserted a scissors line into MERGE_MSG so\n> this patch ensures that we don't get duplicate scissors.\n\nThat is exactly what the paragraph you are responding to questions.\nIs the code that adds a scissors line before this function is called\ndone the right way?  Shouldn't it be doing something differnetly?\nLooking for an existing scissors looking line in this function does\nnot let this function differenciate two cases, i.e. we deliberately\nadded one already before calling this function (in which case this\nfunction should not add another one), or we didn't add anything on\nour own, but the material supplied by the end user had one (in which\ncase, not adding ours is losing information---imagine that the user\nnotices a scissors-looking line that came from the original maerial\nand want to munge it, as it is part of proper message, so that it\nwould remain in the committed result, but because [PATCH 1/2]\nstopped adding a scissors line at the right location, the user would\nhave to guess where to add one).\n\nThere must be an explicit way (e.g. a bit in a flag word parameter\ngiven to this function) for the caller who knows when the new code\nin [PATCH 2/2] triggers, to tell this function not to add another\none, instead of a sloppy (and less efficient) \"lets's scan to see if\nthere already is a scissors looking line\".\n\n> With the existing behaviour, any messages that contain a scissors\n> looking line will get cut at the earliest scissors anyway, so I believe\n> that this patch would not change the behaviour. If the users were\n> dealing with commit messages with a scissors looking line, the current\n> behaviour already requires users to be extra careful to ensure that the\n> scissors don't get accidentally removed so in the interest of preserving\n> the existing behaviour, I don't think that any extra information would\n> be lost from this patch.\n\nDoing the \"is there already a scissors looing line\" approach will\n*make* it harder to fix that issue, so the patch is making things\nworse.\n"},{"id":"363514","messageId":"cover.1542380929.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1542172724.git.liu.denton@gmail.com","subject":"[PATCH v2 0/2] Fix scissors bug during merge conflict","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2018-11-16T15:19:01Z","receivedAt":"2018-11-16T15:19:07Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Thanks for your feedback, Junio.\n\nI tried to reroll the patch by adding another option into the MERGE_MODE\nfile but unfortunately, it didn't work completely because doing `merge\n--squash` doesn't produce a MERGE_MODE. In addition to this, because of\nthe way merge and commit were structured, I needed to reorder a lot of\ncalls because some variables were only being set after I needed them.\nUnless we want to produce a MERGE_MODE during --squash (which I don't\nthink is worth it) I don't think that this is the way to go.\n\nInstead, I just refined my first approach and only checked the contents\nof MERGE_MSG for a scissors line. The MERGE_MSG is going to be\nmachine-generated anyway so we should be safe from accidentally ignoring\na human-placed scissors line.\n\nChanges since V1:\n-----------------\n* Only check MERGE_MSG for a scissors line instead of all prepended files\n* Make a variable static in merge where appropriate\n* Add passthrough options in pull\n* Add documentation for the new option\n* Add tests to ensure desired behaviour\n\nDenton Liu (2):\n  commit: don't add scissors line if one exists\n  merge: add scissors line on merge conflict\n\n Documentation/merge-options.txt |  6 +++++\n builtin/commit.c                | 15 +++++++++--\n builtin/merge.c                 | 16 +++++++++++\n builtin/pull.c                  |  6 +++++\n t/t7600-merge.sh                | 48 +++++++++++++++++++++++++++++++++\n 5 files changed, 89 insertions(+), 2 deletions(-)\n\n-- \n2.19.1\n\n"},{"id":"363515","messageId":"1698fe0d7b7356ea1762f410767dcaf2807ea5c2.1542380865.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1542380865.git.liu.denton@gmail.com","subject":"[PATCH v2 1/2] commit: don't add scissors line if one exists in MERGE_MSG","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2018-11-16T15:19:19Z","receivedAt":"2018-11-16T15:19:25Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"If commit.cleanup = scissors is specified, don't produce a scissors line\nif one already exists in the MERGE_MSG file.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/commit.c | 15 +++++++++++++--\n 1 file changed, 13 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 0d9828e29e..a74a324b7a 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -659,6 +659,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \tconst char *hook_arg2 = NULL;\n \tint clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);\n \tint old_display_comment_prefix;\n+\tint merge_contains_scissors = 0;\n \n \t/* This checks and barfs if author is badly specified */\n \tdetermine_author_info(author_ident);\n@@ -719,6 +720,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\tstrbuf_addbuf(&sb, &message);\n \t\thook_arg1 = \"message\";\n \t} else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {\n+\t\tsize_t merge_msg_start;\n+\n \t\t/*\n \t\t * prepend SQUASH_MSG here if it exists and a\n \t\t * \"merge --squash\" was originally performed\n@@ -729,8 +732,14 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\thook_arg1 = \"squash\";\n \t\t} else\n \t\t\thook_arg1 = \"merge\";\n+\n+\t\tmerge_msg_start = sb.len;\n \t\tif (strbuf_read_file(&sb, git_path_merge_msg(the_repository), 0) < 0)\n \t\t\tdie_errno(_(\"could not read MERGE_MSG\"));\n+\n+\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\t    wt_status_locate_end(sb.buf + merge_msg_start, sb.len - merge_msg_start) < sb.len - merge_msg_start)\n+\t\t\tmerge_contains_scissors = 1;\n \t} else if (!stat(git_path_squash_msg(the_repository), &statbuf)) {\n \t\tif (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)\n \t\t\tdie_errno(_(\"could not read SQUASH_MSG\"));\n@@ -798,7 +807,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\tstruct ident_split ci, ai;\n \n \t\tif (whence != FROM_COMMIT) {\n-\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n+\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\t\t\t!merge_contains_scissors)\n \t\t\t\twt_status_add_cut_line(s->fp);\n \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL,\n \t\t\t    whence == FROM_MERGE\n@@ -824,7 +834,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\t\t  \" Lines starting\\nwith '%c' will be ignored, and an empty\"\n \t\t\t\t  \" message aborts the commit.\\n\"), comment_line_char);\n \t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n-\t\t\t whence == FROM_COMMIT)\n+\t\t\t whence == FROM_COMMIT &&\n+\t\t\t !merge_contains_scissors)\n \t\t\twt_status_add_cut_line(s->fp);\n \t\telse /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n \t\t\tstatus_printf(s, GIT_COLOR_NORMAL,\n-- \n2.19.1\n\n"},{"id":"363516","messageId":"cd62694df6be87700e10439716d7faf5ddce8cb4.1542380929.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1542380929.git.liu.denton@gmail.com","subject":"[PATCH v2 2/2] merge: add scissors line on merge conflict","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2018-11-16T15:20:11Z","receivedAt":"2018-11-16T15:20:16Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"This fixes a bug where the scissors line is placed after the Conflicts:\nsection, in the case where a merge conflict occurs and\nmerge.cleanup = scissors.\n\nIn addition, we give pull the passthrough option of --cleanup so that it\ncan also take advantage of this change.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/merge-options.txt |  6 +++++\n builtin/merge.c                 | 16 +++++++++++\n builtin/pull.c                  |  6 +++++\n t/t7600-merge.sh                | 48 +++++++++++++++++++++++++++++++++\n 4 files changed, 76 insertions(+)\n\ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 63a3fc0954..115e0ca6f0 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -27,6 +27,12 @@ they run `git merge`. To make it easier to adjust such scripts to the\n updated behaviour, the environment variable `GIT_MERGE_AUTOEDIT` can be\n set to `no` at the beginning of them.\n \n+--cleanup=<mode>::\n+\tThis option determines how the merge message will be cleaned up\n+\tbefore being passed on. Specifically, if the '<mode>' is given a\n+\tvalue of `scissors`, scissors will be prepended to the message in\n+\tthe case of a merge conflict. See also linkgit:git-commit[1].\n+\n --ff::\n \tWhen the merge resolves as a fast-forward, only update the branch\n \tpointer, without creating a merge commit.  This is the default\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 8f4a5065c2..23a6e6bb93 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -36,6 +36,7 @@\n #include \"packfile.h\"\n #include \"tag.h\"\n #include \"alias.h\"\n+#include \"wt-status.h\"\n \n #define DEFAULT_TWOHEAD (1<<0)\n #define DEFAULT_OCTOPUS (1<<1)\n@@ -96,6 +97,9 @@ enum ff_type {\n \n static enum ff_type fast_forward = FF_ALLOW;\n \n+static const char *cleanup_arg;\n+static int put_scissors;\n+\n static int option_parse_message(const struct option *opt,\n \t\t\t\tconst char *arg, int unset)\n {\n@@ -243,6 +247,7 @@ static struct option builtin_merge_options[] = {\n \t\tN_(\"perform a commit if the merge succeeds (default)\")),\n \tOPT_BOOL('e', \"edit\", &option_edit,\n \t\tN_(\"edit message before committing\")),\n+\tOPT_STRING(0, \"cleanup\", &cleanup_arg, N_(\"default\"), N_(\"how to strip spaces and #comments from message\")),\n \tOPT_SET_INT(0, \"ff\", &fast_forward, N_(\"allow fast-forward (default)\"), FF_ALLOW),\n \tOPT_SET_INT_F(0, \"ff-only\", &fast_forward,\n \t\t      N_(\"abort if fast-forward is not possible\"),\n@@ -606,6 +611,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n \t\treturn git_config_string(&pull_twohead, k, v);\n \telse if (!strcmp(k, \"pull.octopus\"))\n \t\treturn git_config_string(&pull_octopus, k, v);\n+\telse if (!strcmp(k, \"commit.cleanup\"))\n+\t\treturn git_config_string(&cleanup_arg, k, v);\n \telse if (!strcmp(k, \"merge.renormalize\"))\n \t\toption_renormalize = git_config_bool(k, v);\n \telse if (!strcmp(k, \"merge.ff\")) {\n@@ -894,6 +901,13 @@ static int suggest_conflicts(void)\n \tfilename = git_path_merge_msg(the_repository);\n \tfp = xfopen(filename, \"a\");\n \n+\tif (put_scissors) {\n+\t    fputc('\\n', fp);\n+\t    wt_status_add_cut_line(fp);\n+\t    /* comments out the newline from append_conflicts_hint */\n+\t    fputc(comment_line_char, fp);\n+\t}\n+\n \tappend_conflicts_hint(&msgbuf);\n \tfputs(msgbuf.buf, fp);\n \tstrbuf_release(&msgbuf);\n@@ -1402,6 +1416,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \tif (option_edit < 0)\n \t\toption_edit = default_edit_option();\n \n+\tput_scissors = cleanup_arg && !strcmp(cleanup_arg, \"scissors\");\n+\n \tif (!use_strategies) {\n \t\tif (!remoteheads)\n \t\t\t; /* already up-to-date */\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 681c127a07..88245bce0e 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -95,6 +95,7 @@ static char *opt_signoff;\n static char *opt_squash;\n static char *opt_commit;\n static char *opt_edit;\n+static char *opt_cleanup;\n static char *opt_ff;\n static char *opt_verify_signatures;\n static int opt_autostash = -1;\n@@ -162,6 +163,9 @@ static struct option pull_options[] = {\n \tOPT_PASSTHRU(0, \"edit\", &opt_edit, NULL,\n \t\tN_(\"edit message before committing\"),\n \t\tPARSE_OPT_NOARG),\n+\tOPT_PASSTHRU(0, \"cleanup\", &opt_cleanup, NULL,\n+\t\tN_(\"how to strip spaces and #comments from message\"),\n+\t\tPARSE_OPT_NOARG),\n \tOPT_PASSTHRU(0, \"ff\", &opt_ff, NULL,\n \t\tN_(\"allow fast-forward\"),\n \t\tPARSE_OPT_NOARG),\n@@ -625,6 +629,8 @@ static int run_merge(void)\n \t\targv_array_push(&args, opt_commit);\n \tif (opt_edit)\n \t\targv_array_push(&args, opt_edit);\n+\tif (opt_cleanup)\n+\t\targv_array_push(&args, opt_cleanup);\n \tif (opt_ff)\n \t\targv_array_push(&args, opt_ff);\n \tif (opt_verify_signatures)\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex 106148254d..0d3db34f08 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -247,6 +247,54 @@ test_expect_success 'merge --squash c3 with c7' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'merge c3 with c7 with commit.cleanup = scissors' '\n+\tgit config commit.cleanup scissors &&\n+\tgit reset --hard c3 &&\n+\ttest_must_fail git merge c7 &&\n+\tcat result.9z >file &&\n+\tgit commit --no-edit -a &&\n+\n+\t{\n+\t\tcat <<-EOF\n+\t\tMerge tag '\"'\"'c7'\"'\"'\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfile\n+\t\tEOF\n+\t} >expect &&\n+\tgit cat-file commit HEAD | sed -e '1,/^$/d' >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'merge c3 with c7 with --squash commit.cleanup = scissors' '\n+\tgit config commit.cleanup scissors &&\n+\tgit reset --hard c3 &&\n+\ttest_must_fail git merge --squash c7 &&\n+\tcat result.9z >file &&\n+\tgit commit --no-edit -a &&\n+\n+\t{\n+\t\tcat <<-EOF\n+\t\tSquashed commit of the following:\n+\n+\t\t$(git show -s c7)\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfile\n+\t\tEOF\n+\t} >expect &&\n+\tgit cat-file commit HEAD | sed -e '1,/^$/d' >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_debug 'git log --graph --decorate --oneline --all'\n \n test_expect_success 'merge c1 with c2 and c3' '\n-- \n2.19.1\n\n"},{"id":"363546","messageId":"xmqq36s0i0vg.fsf@gitster-ct.c.googlers.com","threadId":"49830","inReplyTo":"1698fe0d7b7356ea1762f410767dcaf2807ea5c2.1542380865.git.liu.denton@gmail.com","subject":"Re: [PATCH v2 1/2] commit: don't add scissors line if one exists in MERGE_MSG","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-17T08:06:43Z","receivedAt":"2018-11-17T08:06:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Denton Liu <liu.denton@gmail.com> writes:\n\n> If commit.cleanup = scissors is specified, don't produce a scissors line\n> if one already exists in the MERGE_MSG file.\n\nAre we already sometimes adding a scissors line in that file?  The\nimpression I was getting was that the change in the step 2/2 is the\nonly reason why this becomes necessary.  In which case this change\nmakes no sense as a standalone patch and requires 2/2 to be a\nsensible change, no?\n\n> @@ -798,7 +807,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n>  \t\tstruct ident_split ci, ai;\n>  \n>  \t\tif (whence != FROM_COMMIT) {\n> -\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n> +\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n> +\t\t\t\t!merge_contains_scissors)\n>  \t\t\t\twt_status_add_cut_line(s->fp);\n>  \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL,\n>  \t\t\t    whence == FROM_MERGE\n\nThis one is done before we show a block of text, which looks correct.\n\n> @@ -824,7 +834,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n>  \t\t\t\t  \" Lines starting\\nwith '%c' will be ignored, and an empty\"\n>  \t\t\t\t  \" message aborts the commit.\\n\"), comment_line_char);\n>  \t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n> -\t\t\t whence == FROM_COMMIT)\n> +\t\t\t whence == FROM_COMMIT &&\n> +\t\t\t !merge_contains_scissors)\n>  \t\t\twt_status_add_cut_line(s->fp);\n>  \t\telse /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n>  \t\t\tstatus_printf(s, GIT_COLOR_NORMAL,\n\nThe correctness of this one in an if/elseif/else cascade is less\nclear.  When \"contains scissors\" logic does not kick in, we just\nhave a scissors line here (i.e. the original behaviour).  Now when\nthe new logic kicks in, we not just omit the (extra) scissors line,\nbut also say \"Please enter the commit message...\" which is the\nmessage used with the \"comment line char\" mode of the clean-up.\n\nI wonder if this is what you really meant to have instead:\n\n>  \t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n> -\t\t\t whence == FROM_COMMIT)\n> - \t\t\twt_status_add_cut_line(s->fp);\n> +\t\t\t whence == FROM_COMMIT) {\n> +\t\t\t if (!merge_contains_scissors)\n> +\t\t\t\twt_status_add_cut_line(s->fp);\n> +\t\t}\n>  \t\telse /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n>  \t\t\tstatus_printf(s, GIT_COLOR_NORMAL,\n\nThat is, the only behaviour change in \"merge contains scissors\"\nmode is to omit the cut line and nothing else.\n"},{"id":"363562","messageId":"cover.1542496915.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1542380929.git.liu.denton@gmail.com","subject":"[PATCH v3 0/1] Fix scissors bug during merge conflict","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2018-11-17T23:32:17Z","receivedAt":"2018-11-17T23:32:23Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"On Sat, Nov 17, 2018 at 05:06:43PM +0900, Junio C Hamano wrote:\n> Are we already sometimes adding a scissors line in that file?  The\n> impression I was getting was that the change in the step 2/2 is the\n> only reason why this becomes necessary.  In which case this change\n> makes no sense as a standalone patch and requires 2/2 to be a\n> sensible change, no?\n> \n\nMy mistake, I guess I went a little overboard trying to split my\ncontribution into digestable patches.\n\n> > @@ -798,7 +807,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n> >  \t\tstruct ident_split ci, ai;\n> >  \n> >  \t\tif (whence != FROM_COMMIT) {\n> > -\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n> > +\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n> > +\t\t\t\t!merge_contains_scissors)\n> >  \t\t\t\twt_status_add_cut_line(s->fp);\n> >  \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL,\n> >  \t\t\t    whence == FROM_MERGE\n> \n> This one is done before we show a block of text, which looks correct.\n> \n> > @@ -824,7 +834,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n> >  \t\t\t\t  \" Lines starting\\nwith '%c' will be ignored, and an empty\"\n> >  \t\t\t\t  \" message aborts the commit.\\n\"), comment_line_char);\n> >  \t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n> > -\t\t\t whence == FROM_COMMIT)\n> > +\t\t\t whence == FROM_COMMIT &&\n> > +\t\t\t !merge_contains_scissors)\n> >  \t\t\twt_status_add_cut_line(s->fp);\n> >  \t\telse /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n> >  \t\t\tstatus_printf(s, GIT_COLOR_NORMAL,\n> \n> The correctness of this one in an if/elseif/else cascade is less\n> clear.  When \"contains scissors\" logic does not kick in, we just\n> have a scissors line here (i.e. the original behaviour).  Now when\n> the new logic kicks in, we not just omit the (extra) scissors line,\n> but also say \"Please enter the commit message...\" which is the\n> message used with the \"comment line char\" mode of the clean-up.\n> \n> I wonder if this is what you really meant to have instead:\n> \n> >  \t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n> > -\t\t\t whence == FROM_COMMIT)\n> > - \t\t\twt_status_add_cut_line(s->fp);\n> > +\t\t\t whence == FROM_COMMIT) {\n> > +\t\t\t if (!merge_contains_scissors)\n> > +\t\t\t\twt_status_add_cut_line(s->fp);\n> > +\t\t}\n> >  \t\telse /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n> >  \t\t\tstatus_printf(s, GIT_COLOR_NORMAL,\n> \n> That is, the only behaviour change in \"merge contains scissors\"\n> mode is to omit the cut line and nothing else.\n\nThanks for catching this. I noticed that the originally behaviour is\nbuggy too: in the case where we're merging a commit and scissors are\nprinted from the `if (whence != FROM_COMMIT)` block, the original\nbehaviour would drop us into the else (COMMIT_MSG_CLEANUP_SPACE)\nstatement, which is undesired. With this fix, the message about `#`\n_not_ being removed is now silenced in both cases.\n\nChanges since V1:\n\t* Only check MERGE_MSG for a scissors line instead of all prepended\n\t  files\n\t* Make a variable static in merge where appropriate\n\t* Add passthrough options in pull\n\t* Add documentation for the new option\n\t* Add tests to ensure desired behaviour\n\nChanges since V2:\n\t* Merge both patches into one patch\n\t* Fix bug in help message printing logic\n\nDenton Liu (1):\n  merge: add scissors line on merge conflict\n\n Documentation/merge-options.txt |  6 +++++\n builtin/commit.c                | 20 ++++++++++----\n builtin/merge.c                 | 16 +++++++++++\n builtin/pull.c                  |  6 +++++\n t/t7600-merge.sh                | 48 +++++++++++++++++++++++++++++++++\n 5 files changed, 91 insertions(+), 5 deletions(-)\n\n-- \n2.19.1\n\n"},{"id":"363563","messageId":"5c1aa82794da23077ef4ec4e836ee221c64162ef.1542496915.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1542496915.git.liu.denton@gmail.com","subject":"[PATCH v3 1/1] merge: add scissors line on merge conflict","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2018-11-17T23:32:33Z","receivedAt":"2018-11-17T23:32:41Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"This fixes a bug where the scissors line is placed after the Conflicts:\nsection, in the case where a merge conflict occurs and\nmerge.cleanup = scissors.\n\nNext, if commit.cleanup = scissors is specified, don't produce a\nscissors line in commit if one already exists in the MERGE_MSG file.\n\nFinally, we give pull the passthrough option of --cleanup so that it\ncan also take advantage of this change.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/merge-options.txt |  6 +++++\n builtin/commit.c                | 20 ++++++++++----\n builtin/merge.c                 | 16 +++++++++++\n builtin/pull.c                  |  6 +++++\n t/t7600-merge.sh                | 48 +++++++++++++++++++++++++++++++++\n 5 files changed, 91 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 63a3fc0954..115e0ca6f0 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -27,6 +27,12 @@ they run `git merge`. To make it easier to adjust such scripts to the\n updated behaviour, the environment variable `GIT_MERGE_AUTOEDIT` can be\n set to `no` at the beginning of them.\n \n+--cleanup=<mode>::\n+\tThis option determines how the merge message will be cleaned up\n+\tbefore being passed on. Specifically, if the '<mode>' is given a\n+\tvalue of `scissors`, scissors will be prepended to the message in\n+\tthe case of a merge conflict. See also linkgit:git-commit[1].\n+\n --ff::\n \tWhen the merge resolves as a fast-forward, only update the branch\n \tpointer, without creating a merge commit.  This is the default\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 0d9828e29e..7902645bc9 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -659,6 +659,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \tconst char *hook_arg2 = NULL;\n \tint clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);\n \tint old_display_comment_prefix;\n+\tint merge_contains_scissors = 0;\n \n \t/* This checks and barfs if author is badly specified */\n \tdetermine_author_info(author_ident);\n@@ -719,6 +720,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\tstrbuf_addbuf(&sb, &message);\n \t\thook_arg1 = \"message\";\n \t} else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {\n+\t\tsize_t merge_msg_start;\n+\n \t\t/*\n \t\t * prepend SQUASH_MSG here if it exists and a\n \t\t * \"merge --squash\" was originally performed\n@@ -729,8 +732,14 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\thook_arg1 = \"squash\";\n \t\t} else\n \t\t\thook_arg1 = \"merge\";\n+\n+\t\tmerge_msg_start = sb.len;\n \t\tif (strbuf_read_file(&sb, git_path_merge_msg(the_repository), 0) < 0)\n \t\t\tdie_errno(_(\"could not read MERGE_MSG\"));\n+\n+\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\t    wt_status_locate_end(sb.buf + merge_msg_start, sb.len - merge_msg_start) < sb.len - merge_msg_start)\n+\t\t\tmerge_contains_scissors = 1;\n \t} else if (!stat(git_path_squash_msg(the_repository), &statbuf)) {\n \t\tif (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)\n \t\t\tdie_errno(_(\"could not read SQUASH_MSG\"));\n@@ -798,7 +807,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\tstruct ident_split ci, ai;\n \n \t\tif (whence != FROM_COMMIT) {\n-\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n+\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\t\t\t!merge_contains_scissors)\n \t\t\t\twt_status_add_cut_line(s->fp);\n \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL,\n \t\t\t    whence == FROM_MERGE\n@@ -823,10 +833,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\nwith '%c' will be ignored, and an empty\"\n \t\t\t\t  \" message aborts the commit.\\n\"), comment_line_char);\n-\t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n-\t\t\t whence == FROM_COMMIT)\n-\t\t\twt_status_add_cut_line(s->fp);\n-\t\telse /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n+\t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {\n+\t\t\tif (whence == FROM_COMMIT && !merge_contains_scissors)\n+\t\t\t\twt_status_add_cut_line(s->fp);\n+\t\t} else /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n \t\t\tstatus_printf(s, GIT_COLOR_NORMAL,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\n\"\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 8f4a5065c2..23a6e6bb93 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -36,6 +36,7 @@\n #include \"packfile.h\"\n #include \"tag.h\"\n #include \"alias.h\"\n+#include \"wt-status.h\"\n \n #define DEFAULT_TWOHEAD (1<<0)\n #define DEFAULT_OCTOPUS (1<<1)\n@@ -96,6 +97,9 @@ enum ff_type {\n \n static enum ff_type fast_forward = FF_ALLOW;\n \n+static const char *cleanup_arg;\n+static int put_scissors;\n+\n static int option_parse_message(const struct option *opt,\n \t\t\t\tconst char *arg, int unset)\n {\n@@ -243,6 +247,7 @@ static struct option builtin_merge_options[] = {\n \t\tN_(\"perform a commit if the merge succeeds (default)\")),\n \tOPT_BOOL('e', \"edit\", &option_edit,\n \t\tN_(\"edit message before committing\")),\n+\tOPT_STRING(0, \"cleanup\", &cleanup_arg, N_(\"default\"), N_(\"how to strip spaces and #comments from message\")),\n \tOPT_SET_INT(0, \"ff\", &fast_forward, N_(\"allow fast-forward (default)\"), FF_ALLOW),\n \tOPT_SET_INT_F(0, \"ff-only\", &fast_forward,\n \t\t      N_(\"abort if fast-forward is not possible\"),\n@@ -606,6 +611,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n \t\treturn git_config_string(&pull_twohead, k, v);\n \telse if (!strcmp(k, \"pull.octopus\"))\n \t\treturn git_config_string(&pull_octopus, k, v);\n+\telse if (!strcmp(k, \"commit.cleanup\"))\n+\t\treturn git_config_string(&cleanup_arg, k, v);\n \telse if (!strcmp(k, \"merge.renormalize\"))\n \t\toption_renormalize = git_config_bool(k, v);\n \telse if (!strcmp(k, \"merge.ff\")) {\n@@ -894,6 +901,13 @@ static int suggest_conflicts(void)\n \tfilename = git_path_merge_msg(the_repository);\n \tfp = xfopen(filename, \"a\");\n \n+\tif (put_scissors) {\n+\t    fputc('\\n', fp);\n+\t    wt_status_add_cut_line(fp);\n+\t    /* comments out the newline from append_conflicts_hint */\n+\t    fputc(comment_line_char, fp);\n+\t}\n+\n \tappend_conflicts_hint(&msgbuf);\n \tfputs(msgbuf.buf, fp);\n \tstrbuf_release(&msgbuf);\n@@ -1402,6 +1416,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \tif (option_edit < 0)\n \t\toption_edit = default_edit_option();\n \n+\tput_scissors = cleanup_arg && !strcmp(cleanup_arg, \"scissors\");\n+\n \tif (!use_strategies) {\n \t\tif (!remoteheads)\n \t\t\t; /* already up-to-date */\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 681c127a07..88245bce0e 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -95,6 +95,7 @@ static char *opt_signoff;\n static char *opt_squash;\n static char *opt_commit;\n static char *opt_edit;\n+static char *opt_cleanup;\n static char *opt_ff;\n static char *opt_verify_signatures;\n static int opt_autostash = -1;\n@@ -162,6 +163,9 @@ static struct option pull_options[] = {\n \tOPT_PASSTHRU(0, \"edit\", &opt_edit, NULL,\n \t\tN_(\"edit message before committing\"),\n \t\tPARSE_OPT_NOARG),\n+\tOPT_PASSTHRU(0, \"cleanup\", &opt_cleanup, NULL,\n+\t\tN_(\"how to strip spaces and #comments from message\"),\n+\t\tPARSE_OPT_NOARG),\n \tOPT_PASSTHRU(0, \"ff\", &opt_ff, NULL,\n \t\tN_(\"allow fast-forward\"),\n \t\tPARSE_OPT_NOARG),\n@@ -625,6 +629,8 @@ static int run_merge(void)\n \t\targv_array_push(&args, opt_commit);\n \tif (opt_edit)\n \t\targv_array_push(&args, opt_edit);\n+\tif (opt_cleanup)\n+\t\targv_array_push(&args, opt_cleanup);\n \tif (opt_ff)\n \t\targv_array_push(&args, opt_ff);\n \tif (opt_verify_signatures)\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex 106148254d..0d3db34f08 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -247,6 +247,54 @@ test_expect_success 'merge --squash c3 with c7' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'merge c3 with c7 with commit.cleanup = scissors' '\n+\tgit config commit.cleanup scissors &&\n+\tgit reset --hard c3 &&\n+\ttest_must_fail git merge c7 &&\n+\tcat result.9z >file &&\n+\tgit commit --no-edit -a &&\n+\n+\t{\n+\t\tcat <<-EOF\n+\t\tMerge tag '\"'\"'c7'\"'\"'\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfile\n+\t\tEOF\n+\t} >expect &&\n+\tgit cat-file commit HEAD | sed -e '1,/^$/d' >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'merge c3 with c7 with --squash commit.cleanup = scissors' '\n+\tgit config commit.cleanup scissors &&\n+\tgit reset --hard c3 &&\n+\ttest_must_fail git merge --squash c7 &&\n+\tcat result.9z >file &&\n+\tgit commit --no-edit -a &&\n+\n+\t{\n+\t\tcat <<-EOF\n+\t\tSquashed commit of the following:\n+\n+\t\t$(git show -s c7)\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfile\n+\t\tEOF\n+\t} >expect &&\n+\tgit cat-file commit HEAD | sed -e '1,/^$/d' >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_debug 'git log --graph --decorate --oneline --all'\n \n test_expect_success 'merge c1 with c2 and c3' '\n-- \n2.19.1\n\n"},{"id":"363566","messageId":"xmqqwopag9kb.fsf@gitster-ct.c.googlers.com","threadId":"49830","inReplyTo":"cover.1542496915.git.liu.denton@gmail.com","subject":"Re: [PATCH v3 0/1] Fix scissors bug during merge conflict","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-18T06:54:12Z","receivedAt":"2018-11-18T06:54:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Denton Liu <liu.denton@gmail.com> writes:\n\n>> I wonder if this is what you really meant to have instead:\n>> \n>> >  \t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n>> > -\t\t\t whence == FROM_COMMIT)\n>> > - \t\t\twt_status_add_cut_line(s->fp);\n>> > +\t\t\t whence == FROM_COMMIT) {\n>> > +\t\t\t if (!merge_contains_scissors)\n>> > +\t\t\t\twt_status_add_cut_line(s->fp);\n>> > +\t\t}\n>> >  \t\telse /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n>> >  \t\t\tstatus_printf(s, GIT_COLOR_NORMAL,\n>> \n>> That is, the only behaviour change in \"merge contains scissors\"\n>> mode is to omit the cut line and nothing else.\n>\n> Thanks for catching this. I noticed that the originally behaviour is\n> buggy too: in the case where we're merging a commit and scissors are\n> printed from the `if (whence != FROM_COMMIT)` block, the original\n> behaviour would drop us into the else (COMMIT_MSG_CLEANUP_SPACE)\n> statement, which is undesired.\n\nThe original calls add-cut-line in the \"whence != FROM_COMMIT\" when\ncleanup_mode is CLEANUP_SCISSORS (and then in that block it also adds\nthe message about committing a merge or cherry-pick).  After that,\nthe original does the three-arm if/else if/else cascade and we end\nup showing the \"lines starting with # will be kept\" message.\n\nYeah, you read the original correctly and I agree that in that block\nthe right thing to do is not to say anything in CLEANUP_SCISSORS\nmode.\n\nThanks for carefully reading the code again.\n\n\n"},{"id":"363577","messageId":"20181118141853.GR30222@szeder.dev","threadId":"49830","inReplyTo":"5c1aa82794da23077ef4ec4e836ee221c64162ef.1542496915.git.liu.denton@gmail.com","subject":"Re: [PATCH v3 1/1] merge: add scissors line on merge conflict","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2018-11-18T14:18:53Z","receivedAt":"2018-11-18T14:19:01Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Sat, Nov 17, 2018 at 06:32:33PM -0500, Denton Liu wrote:\n> diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\n> index 106148254d..0d3db34f08 100755\n> --- a/t/t7600-merge.sh\n> +++ b/t/t7600-merge.sh\n> @@ -247,6 +247,54 @@ test_expect_success 'merge --squash c3 with c7' '\n>  \ttest_cmp expect actual\n>  '\n>  \n> +test_expect_success 'merge c3 with c7 with commit.cleanup = scissors' '\n> +\tgit config commit.cleanup scissors &&\n> +\tgit reset --hard c3 &&\n> +\ttest_must_fail git merge c7 &&\n> +\tcat result.9z >file &&\n> +\tgit commit --no-edit -a &&\n> +\n> +\t{\n> +\t\tcat <<-EOF\n> +\t\tMerge tag '\"'\"'c7'\"'\"'\n> +\n> +\t\t# ------------------------ >8 ------------------------\n> +\t\t# Do not modify or remove the line above.\n> +\t\t# Everything below it will be ignored.\n\nNote that these two lines of advice text are translated; see the\nconsequences below.\n\n> +\t\t#\n> +\t\t# Conflicts:\n> +\t\t#\tfile\n> +\t\tEOF\n> +\t} >expect &&\n\nThe {...} block is unnecessary, because there is only a single command\nin there.\n\n> +\tgit cat-file commit HEAD | sed -e '1,/^$/d' >actual &&\n\nPlease don't run git commands upstream of a pipe, because the pipe\nhides their exit code.  Furthermore, put the sed script inside double\nquotes, because the whole test is already in a single-quoted block.\n\nI presume you wrote the test this way because you simply followed suit\nof the previous test 'merge --squash c3 with c7', which did all the\nsame.  Bonus points for a preparatory patch that cleans up the\nprevious test ;)\n\n> +\ttest_cmp expect actual\n\nBut most importantly, here 'test_cmp' compares translated advice text\nas well, which fails in the GETTEXT_POISON build.  Use 'test_i18ncmp'\ninstead.\n\n> +'\n> +\n> +test_expect_success 'merge c3 with c7 with --squash commit.cleanup = scissors' '\n> +\tgit config commit.cleanup scissors &&\n> +\tgit reset --hard c3 &&\n> +\ttest_must_fail git merge --squash c7 &&\n> +\tcat result.9z >file &&\n> +\tgit commit --no-edit -a &&\n> +\n> +\t{\n> +\t\tcat <<-EOF\n> +\t\tSquashed commit of the following:\n> +\n> +\t\t$(git show -s c7)\n> +\n> +\t\t# ------------------------ >8 ------------------------\n> +\t\t# Do not modify or remove the line above.\n> +\t\t# Everything below it will be ignored.\n> +\t\t#\n> +\t\t# Conflicts:\n> +\t\t#\tfile\n> +\t\tEOF\n> +\t} >expect &&\n> +\tgit cat-file commit HEAD | sed -e '1,/^$/d' >actual &&\n> +\ttest_cmp expect actual\n\nLikewise.\n\n> +'\n> +\n>  test_debug 'git log --graph --decorate --oneline --all'\n>  \n>  test_expect_success 'merge c1 with c2 and c3' '\n> -- \n> 2.19.1\n> \n"},{"id":"363799","messageId":"cover.1542768902.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1542496915.git.liu.denton@gmail.com","subject":"[PATCH v4 0/2] Fix scissors bug during merge conflict","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2018-11-21T03:13:07Z","receivedAt":"2018-11-21T03:13:13Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Thanks for catching that, Szeder. I tested this revised patch under\nGETTEXT_POISON and it should be passing tests now.\n\nChanges since V1:\n\t* Only check MERGE_MSG for a scissors line instead of all prepended\n\t  files\n\t* Make a variable static in merge where appropriate\n\t* Add passthrough options in pull\n\t* Add documentation for the new option\n\t* Add tests to ensure desired behaviour\n\nChanges since V2:\n\t* Merge both patches into one patch\n\t* Fix bug in help message printing logic\n\nChanges since V3:\n\t* Add patch to cleanup 'merge --squash c3 with c7' test in t7600\n\t* Use test_i18ncmp instead of test_cmp to pass GETTEXT_POISON tests\n\nDenton Liu (2):\n  t7600: clean up 'merge --squash c3 with c7' test\n  merge: add scissors line on merge conflict\n\n Documentation/merge-options.txt |  6 ++++\n builtin/commit.c                | 20 +++++++++----\n builtin/merge.c                 | 16 ++++++++++\n builtin/pull.c                  |  6 ++++\n t/t7600-merge.sh                | 53 ++++++++++++++++++++++++++++++---\n 5 files changed, 92 insertions(+), 9 deletions(-)\n\n-- \n2.19.1\n\n"},{"id":"363800","messageId":"efa96543f6bad23830fba1e3c82b070769a946a3.1542768902.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1542768902.git.liu.denton@gmail.com","subject":"[PATCH v4 1/2] t7600: clean up 'merge --squash c3 with c7' test","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2018-11-21T03:13:18Z","receivedAt":"2018-11-21T03:13:32Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"This cleans up the original test by removing some unnecessary braces and\nremoving a pipe.\n\nHelped-by: SZEDER Gábor <szeder.dev@gmail.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t7600-merge.sh | 7 +++----\n 1 file changed, 3 insertions(+), 4 deletions(-)\n\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex 106148254d..d879efd330 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -233,8 +233,7 @@ test_expect_success 'merge --squash c3 with c7' '\n \tcat result.9z >file &&\n \tgit commit --no-edit -a &&\n \n-\t{\n-\t\tcat <<-EOF\n+\tcat >expect <<-EOF &&\n \t\tSquashed commit of the following:\n \n \t\t$(git show -s c7)\n@@ -242,8 +241,8 @@ test_expect_success 'merge --squash c3 with c7' '\n \t\t# Conflicts:\n \t\t#\tfile\n \t\tEOF\n-\t} >expect &&\n-\tgit cat-file commit HEAD | sed -e '1,/^$/d' >actual &&\n+\tgit cat-file commit HEAD >tmp &&\n+\tsed -e '1,/^$/d' <tmp >actual &&\n \ttest_cmp expect actual\n '\n \n-- \n2.19.1\n\n"},{"id":"363801","messageId":"d20751672c238e2f2f54f20c27bf01faa9455445.1542768902.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1542768902.git.liu.denton@gmail.com","subject":"[PATCH v4 2/2] merge: add scissors line on merge conflict","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2018-11-21T03:13:26Z","receivedAt":"2018-11-21T03:13:32Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"This fixes a bug where the scissors line is placed after the Conflicts:\nsection, in the case where a merge conflict occurs and\nmerge.cleanup = scissors.\n\nNext, if commit.cleanup = scissors is specified, don't produce a\nscissors line in commit if one already exists in the MERGE_MSG file.\n\nFinally, we give pull the passthrough option of --cleanup so that it\ncan also take advantage of this change.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/merge-options.txt |  6 +++++\n builtin/commit.c                | 20 ++++++++++----\n builtin/merge.c                 | 16 ++++++++++++\n builtin/pull.c                  |  6 +++++\n t/t7600-merge.sh                | 46 +++++++++++++++++++++++++++++++++\n 5 files changed, 89 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 63a3fc0954..115e0ca6f0 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -27,6 +27,12 @@ they run `git merge`. To make it easier to adjust such scripts to the\n updated behaviour, the environment variable `GIT_MERGE_AUTOEDIT` can be\n set to `no` at the beginning of them.\n \n+--cleanup=<mode>::\n+\tThis option determines how the merge message will be cleaned up\n+\tbefore being passed on. Specifically, if the '<mode>' is given a\n+\tvalue of `scissors`, scissors will be prepended to the message in\n+\tthe case of a merge conflict. See also linkgit:git-commit[1].\n+\n --ff::\n \tWhen the merge resolves as a fast-forward, only update the branch\n \tpointer, without creating a merge commit.  This is the default\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 0d9828e29e..7902645bc9 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -659,6 +659,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \tconst char *hook_arg2 = NULL;\n \tint clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);\n \tint old_display_comment_prefix;\n+\tint merge_contains_scissors = 0;\n \n \t/* This checks and barfs if author is badly specified */\n \tdetermine_author_info(author_ident);\n@@ -719,6 +720,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\tstrbuf_addbuf(&sb, &message);\n \t\thook_arg1 = \"message\";\n \t} else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {\n+\t\tsize_t merge_msg_start;\n+\n \t\t/*\n \t\t * prepend SQUASH_MSG here if it exists and a\n \t\t * \"merge --squash\" was originally performed\n@@ -729,8 +732,14 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\thook_arg1 = \"squash\";\n \t\t} else\n \t\t\thook_arg1 = \"merge\";\n+\n+\t\tmerge_msg_start = sb.len;\n \t\tif (strbuf_read_file(&sb, git_path_merge_msg(the_repository), 0) < 0)\n \t\t\tdie_errno(_(\"could not read MERGE_MSG\"));\n+\n+\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\t    wt_status_locate_end(sb.buf + merge_msg_start, sb.len - merge_msg_start) < sb.len - merge_msg_start)\n+\t\t\tmerge_contains_scissors = 1;\n \t} else if (!stat(git_path_squash_msg(the_repository), &statbuf)) {\n \t\tif (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)\n \t\t\tdie_errno(_(\"could not read SQUASH_MSG\"));\n@@ -798,7 +807,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\tstruct ident_split ci, ai;\n \n \t\tif (whence != FROM_COMMIT) {\n-\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n+\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\t\t\t!merge_contains_scissors)\n \t\t\t\twt_status_add_cut_line(s->fp);\n \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL,\n \t\t\t    whence == FROM_MERGE\n@@ -823,10 +833,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\nwith '%c' will be ignored, and an empty\"\n \t\t\t\t  \" message aborts the commit.\\n\"), comment_line_char);\n-\t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n-\t\t\t whence == FROM_COMMIT)\n-\t\t\twt_status_add_cut_line(s->fp);\n-\t\telse /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n+\t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {\n+\t\t\tif (whence == FROM_COMMIT && !merge_contains_scissors)\n+\t\t\t\twt_status_add_cut_line(s->fp);\n+\t\t} else /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n \t\t\tstatus_printf(s, GIT_COLOR_NORMAL,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\n\"\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 8f4a5065c2..23a6e6bb93 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -36,6 +36,7 @@\n #include \"packfile.h\"\n #include \"tag.h\"\n #include \"alias.h\"\n+#include \"wt-status.h\"\n \n #define DEFAULT_TWOHEAD (1<<0)\n #define DEFAULT_OCTOPUS (1<<1)\n@@ -96,6 +97,9 @@ enum ff_type {\n \n static enum ff_type fast_forward = FF_ALLOW;\n \n+static const char *cleanup_arg;\n+static int put_scissors;\n+\n static int option_parse_message(const struct option *opt,\n \t\t\t\tconst char *arg, int unset)\n {\n@@ -243,6 +247,7 @@ static struct option builtin_merge_options[] = {\n \t\tN_(\"perform a commit if the merge succeeds (default)\")),\n \tOPT_BOOL('e', \"edit\", &option_edit,\n \t\tN_(\"edit message before committing\")),\n+\tOPT_STRING(0, \"cleanup\", &cleanup_arg, N_(\"default\"), N_(\"how to strip spaces and #comments from message\")),\n \tOPT_SET_INT(0, \"ff\", &fast_forward, N_(\"allow fast-forward (default)\"), FF_ALLOW),\n \tOPT_SET_INT_F(0, \"ff-only\", &fast_forward,\n \t\t      N_(\"abort if fast-forward is not possible\"),\n@@ -606,6 +611,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n \t\treturn git_config_string(&pull_twohead, k, v);\n \telse if (!strcmp(k, \"pull.octopus\"))\n \t\treturn git_config_string(&pull_octopus, k, v);\n+\telse if (!strcmp(k, \"commit.cleanup\"))\n+\t\treturn git_config_string(&cleanup_arg, k, v);\n \telse if (!strcmp(k, \"merge.renormalize\"))\n \t\toption_renormalize = git_config_bool(k, v);\n \telse if (!strcmp(k, \"merge.ff\")) {\n@@ -894,6 +901,13 @@ static int suggest_conflicts(void)\n \tfilename = git_path_merge_msg(the_repository);\n \tfp = xfopen(filename, \"a\");\n \n+\tif (put_scissors) {\n+\t    fputc('\\n', fp);\n+\t    wt_status_add_cut_line(fp);\n+\t    /* comments out the newline from append_conflicts_hint */\n+\t    fputc(comment_line_char, fp);\n+\t}\n+\n \tappend_conflicts_hint(&msgbuf);\n \tfputs(msgbuf.buf, fp);\n \tstrbuf_release(&msgbuf);\n@@ -1402,6 +1416,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \tif (option_edit < 0)\n \t\toption_edit = default_edit_option();\n \n+\tput_scissors = cleanup_arg && !strcmp(cleanup_arg, \"scissors\");\n+\n \tif (!use_strategies) {\n \t\tif (!remoteheads)\n \t\t\t; /* already up-to-date */\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 681c127a07..88245bce0e 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -95,6 +95,7 @@ static char *opt_signoff;\n static char *opt_squash;\n static char *opt_commit;\n static char *opt_edit;\n+static char *opt_cleanup;\n static char *opt_ff;\n static char *opt_verify_signatures;\n static int opt_autostash = -1;\n@@ -162,6 +163,9 @@ static struct option pull_options[] = {\n \tOPT_PASSTHRU(0, \"edit\", &opt_edit, NULL,\n \t\tN_(\"edit message before committing\"),\n \t\tPARSE_OPT_NOARG),\n+\tOPT_PASSTHRU(0, \"cleanup\", &opt_cleanup, NULL,\n+\t\tN_(\"how to strip spaces and #comments from message\"),\n+\t\tPARSE_OPT_NOARG),\n \tOPT_PASSTHRU(0, \"ff\", &opt_ff, NULL,\n \t\tN_(\"allow fast-forward\"),\n \t\tPARSE_OPT_NOARG),\n@@ -625,6 +629,8 @@ static int run_merge(void)\n \t\targv_array_push(&args, opt_commit);\n \tif (opt_edit)\n \t\targv_array_push(&args, opt_edit);\n+\tif (opt_cleanup)\n+\t\targv_array_push(&args, opt_cleanup);\n \tif (opt_ff)\n \t\targv_array_push(&args, opt_ff);\n \tif (opt_verify_signatures)\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex d879efd330..2cb57e5878 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -246,6 +246,52 @@ test_expect_success 'merge --squash c3 with c7' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'merge c3 with c7 with commit.cleanup = scissors' '\n+\tgit config commit.cleanup scissors &&\n+\tgit reset --hard c3 &&\n+\ttest_must_fail git merge c7 &&\n+\tcat result.9z >file &&\n+\tgit commit --no-edit -a &&\n+\n+\tcat >expect <<-EOF &&\n+\t\tMerge tag '\"'\"'c7'\"'\"'\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfile\n+\t\tEOF\n+\tgit cat-file commit HEAD >tmp &&\n+\tsed -e '1,/^$/d' <tmp >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n+test_expect_success 'merge c3 with c7 with --squash commit.cleanup = scissors' '\n+\tgit config commit.cleanup scissors &&\n+\tgit reset --hard c3 &&\n+\ttest_must_fail git merge --squash c7 &&\n+\tcat result.9z >file &&\n+\tgit commit --no-edit -a &&\n+\n+\tcat >expect <<-EOF &&\n+\t\tSquashed commit of the following:\n+\n+\t\t$(git show -s c7)\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfile\n+\t\tEOF\n+\tgit cat-file commit HEAD >tmp &&\n+\tsed -e '1,/^$/d' <tmp >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n test_debug 'git log --graph --decorate --oneline --all'\n \n test_expect_success 'merge c1 with c2 and c3' '\n-- \n2.19.1\n\n"},{"id":"363814","messageId":"xmqqefbe7otv.fsf@gitster-ct.c.googlers.com","threadId":"49830","inReplyTo":"cover.1542768902.git.liu.denton@gmail.com","subject":"Re: [PATCH v4 0/2] Fix scissors bug during merge conflict","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-21T09:38:20Z","receivedAt":"2018-11-21T09:38:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Denton Liu <liu.denton@gmail.com> writes:\n\n> Changes since V3:\n> \t* Add patch to cleanup 'merge --squash c3 with c7' test in t7600\n> \t* Use test_i18ncmp instead of test_cmp to pass GETTEXT_POISON tests\n\nQueued. Thanks, both.\n"},{"id":"363859","messageId":"20181122011055.GA1584@archbookpro.localdomain","threadId":"49830","inReplyTo":"xmqqefbe7otv.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v4 0/2] Fix scissors bug during merge conflict","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2018-11-22T01:10:55Z","receivedAt":"2018-11-22T01:11:00Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"On Wed, Nov 21, 2018 at 06:38:20PM +0900, Junio C Hamano wrote:\n> Denton Liu <liu.denton@gmail.com> writes:\n> \n> > Changes since V3:\n> > \t* Add patch to cleanup 'merge --squash c3 with c7' test in t7600\n> > \t* Use test_i18ncmp instead of test_cmp to pass GETTEXT_POISON tests\n> \n> Queued. Thanks, both.\n\nI just realised that there is a slight problem with the proposed change.\nWhen we do a merge and there are no merge conflicts, at the end of the\nmerge, we get dropped into an editor with this text:\n\n\tMerge branch 'master' into new\n\n\t# Please enter a commit message to explain why this merge is necessary,\n\t# especially if it merges an updated upstream into a topic branch.\n\t#\n\t# Lines starting with '#' will be ignored, and an empty message aborts\n\t# the commit.\n\nNote that in git-merge, the cleanup only removes commented lines and\nthis cannot be configured to be scissors or whatever else. I think that\nthe fact that it's not configurable isn't a problem; most hardcore\ncommit message editing happens in git-commit anyway.\n\nHowever, since we taught git-merge the --cleanup option, this might be\nmisleading for the end-user since they would expect the MERGE_MSG to be\ncleaned up as specified.\n\nI see two resolutions for this. We can either rename --cleanup more\nprecisely so users won't be confused (perhaps something like\n--merge-conflict-scissors but a lot more snappy) or we can actually make\ngit-merge respect the cleanup option and post-process the message\naccording to the specified cleanup rule.\n\nI would personally think the first option is better than the second but\nI'd like to hear your thoughts.\n\nThanks!\n"},{"id":"363979","messageId":"xmqqin0n44dv.fsf@gitster-ct.c.googlers.com","threadId":"49830","inReplyTo":"20181122011055.GA1584@archbookpro.localdomain","subject":"Re: [PATCH v4 0/2] Fix scissors bug during merge conflict","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-11-24T02:05:00Z","receivedAt":"2018-11-24T02:05:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Denton Liu <liu.denton@gmail.com> writes:\n\n> I just realised that there is a slight problem with the proposed change.\n> When we do a merge and there are no merge conflicts, at the end of the\n> merge, we get dropped into an editor with this text:\n>\n> \tMerge branch 'master' into new\n>\n> \t# Please enter a commit message to explain why this merge is necessary,\n> \t# especially if it merges an updated upstream into a topic branch.\n> \t#\n> \t# Lines starting with '#' will be ignored, and an empty message aborts\n> \t# the commit.\n>\n> Note that in git-merge, the cleanup only removes commented lines and\n> this cannot be configured to be scissors or whatever else. I think that\n> the fact that it's not configurable isn't a problem; most hardcore\n> commit message editing happens in git-commit anyway.\n\nOK.\n\n> However, since we taught git-merge the --cleanup option, this might be\n> misleading for the end-user since they would expect the MERGE_MSG to be\n> cleaned up as specified.\n>\n> I see two resolutions for this. We can either rename --cleanup more\n> precisely so users won't be confused (perhaps something like\n> --merge-conflict-scissors but a lot more snappy) or we can actually make\n> git-merge respect the cleanup option and post-process the message\n> according to the specified cleanup rule.\n\nThe former certainly would be simpler to implement, but feels more\nlike an excuse for not doing the right thing to me, when I put\nmyself in shoes of users who use 'scissors' clean-up option in\ncommit.  I dunno.\n\n"},{"id":"365801","messageId":"cover.1545745331.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1542768902.git.liu.denton@gmail.com","subject":"[PATCH v5 0/4] Add git-merge --cleanup support","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2018-12-25T13:55:35Z","receivedAt":"2018-12-25T13:56:19Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Merry Christmas, everyone!\n\nI've finally had some free time to throw together the changes I promised\na couple weeks ago, so now git-merge will support --cleanup just like\ngit-commit.\n\nNot related to this but while I was making my changes, I noticed that -F\nand -m don't die properly in git-merge when they accept invalid\narguments. Instead, they just print an error message to stderr and the\nprogram keeps running. Is this the expected behaviour?\n\nChanges since V1:\n\t* Only check MERGE_MSG for a scissors line instead of all prepended\n\t  files\n\t* Make a variable static in merge where appropriate\n\t* Add passthrough options in pull\n\t* Add documentation for the new option\n\t* Add tests to ensure desired behaviour\n\nChanges since V2:\n\t* Merge both patches into one patch\n\t* Fix bug in help message printing logic\n\nChanges since V3:\n\t* Add patch to cleanup 'merge --squash c3 with c7' test in t7600\n\t* Use test_i18ncmp instead of test_cmp to pass GETTEXT_POISON tests\n\nChanges since V4:\n\t* Add patches so that git-merge messages will be cleaned up like in\n      git-commit\n\nDenton Liu (4):\n  commit: extract cleanup_mode functions to sequencer\n  t7600: clean up 'merge --squash c3 with c7' test\n  merge: cleanup messages like commit\n  merge: add scissors line on merge conflict\n\n Documentation/merge-options.txt |  7 ++++\n builtin/commit.c                | 41 +++++++++-------------\n builtin/merge.c                 | 45 ++++++++++++++++++++----\n builtin/pull.c                  |  6 ++++\n sequencer.c                     | 29 ++++++++++++++++\n sequencer.h                     |  4 +++\n t/t7600-merge.sh                | 53 +++++++++++++++++++++++++---\n t/t7604-merge-custom-message.sh | 61 +++++++++++++++++++++++++++++++++\n wt-status.c                     | 12 +++++--\n wt-status.h                     |  1 +\n 10 files changed, 221 insertions(+), 38 deletions(-)\n\n-- \n2.20.1\n\n"},{"id":"365802","messageId":"006f2a7039a6f370f7b84552d5d19526564229e9.1545745331.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1545745331.git.liu.denton@gmail.com","subject":"[PATCH v5 1/4] commit: extract cleanup_mode functions to sequencer","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2018-12-25T13:55:47Z","receivedAt":"2018-12-25T13:56:19Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Signed-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/commit.c | 21 ++-------------------\n sequencer.c      | 29 +++++++++++++++++++++++++++++\n sequencer.h      |  4 ++++\n 3 files changed, 35 insertions(+), 19 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex c021b119bb..9eb745b7ce 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1164,20 +1164,7 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t\tdie(_(\"Only one of --include/--only/--all/--interactive/--patch can be used.\"));\n \tif (argc == 0 && (also || (only && !amend && !allow_empty)))\n \t\tdie(_(\"No paths with --include/--only does not make sense.\"));\n-\tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n-\t\tcleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_ALL :\n-\t\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n-\telse if (!strcmp(cleanup_arg, \"verbatim\"))\n-\t\tcleanup_mode = COMMIT_MSG_CLEANUP_NONE;\n-\telse if (!strcmp(cleanup_arg, \"whitespace\"))\n-\t\tcleanup_mode = COMMIT_MSG_CLEANUP_SPACE;\n-\telse if (!strcmp(cleanup_arg, \"strip\"))\n-\t\tcleanup_mode = COMMIT_MSG_CLEANUP_ALL;\n-\telse if (!strcmp(cleanup_arg, \"scissors\"))\n-\t\tcleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n-\t\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n-\telse\n-\t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n+\tcleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);\n \n \thandle_untracked_files_arg(s);\n \n@@ -1614,11 +1601,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"could not read commit message: %s\"), strerror(saved_errno));\n \t}\n \n-\tif (verbose || /* Truncate the message just before the diff, if any. */\n-\t    cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n-\t\tstrbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));\n-\tif (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)\n-\t\tstrbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);\n+\tcleanup_message(&sb, cleanup_mode, verbose);\n \n \tif (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {\n \t\trollback_index_files();\ndiff --git a/sequencer.c b/sequencer.c\nindex e1a4dd15f1..9c4eba06dc 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -506,6 +506,25 @@ static int fast_forward_to(const struct object_id *to, const struct object_id *f\n \treturn 0;\n }\n \n+enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n+\tint use_editor)\n+{\n+\tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n+\t\treturn use_editor ? COMMIT_MSG_CLEANUP_ALL :\n+\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n+\telse if (!strcmp(cleanup_arg, \"verbatim\"))\n+\t\treturn COMMIT_MSG_CLEANUP_NONE;\n+\telse if (!strcmp(cleanup_arg, \"whitespace\"))\n+\t\treturn COMMIT_MSG_CLEANUP_SPACE;\n+\telse if (!strcmp(cleanup_arg, \"strip\"))\n+\t\treturn COMMIT_MSG_CLEANUP_ALL;\n+\telse if (!strcmp(cleanup_arg, \"scissors\"))\n+\t\treturn use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n+\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n+\telse\n+\t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n+}\n+\n void append_conflicts_hint(struct strbuf *msgbuf)\n {\n \tint i;\n@@ -1003,6 +1022,16 @@ static int rest_is_empty(const struct strbuf *sb, int start)\n \treturn 1;\n }\n \n+void cleanup_message(struct strbuf *msgbuf,\n+\tenum commit_msg_cleanup_mode cleanup_mode, int verbose)\n+{\n+\tif (verbose || /* Truncate the message just before the diff, if any. */\n+\t    cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n+\t\tstrbuf_setlen(msgbuf, wt_status_locate_end(msgbuf->buf, msgbuf->len));\n+\tif (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)\n+\t\tstrbuf_stripspace(msgbuf, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);\n+}\n+\n /*\n  * Find out if the message in the strbuf contains only whitespace and\n  * Signed-off-by lines.\ndiff --git a/sequencer.h b/sequencer.h\nindex 5071a73563..e6d937dd3c 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -112,7 +112,11 @@ extern const char sign_off_header[];\n  */\n void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);\n \n+enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n+\tint use_editor);\n void append_conflicts_hint(struct strbuf *msgbuf);\n+void cleanup_message(struct strbuf *msgbuf,\n+\tenum commit_msg_cleanup_mode cleanup_mode, int verbose);\n int message_is_empty(const struct strbuf *sb,\n \t\t     enum commit_msg_cleanup_mode cleanup_mode);\n int template_untouched(const struct strbuf *sb, const char *template_file,\n-- \n2.20.1\n\n"},{"id":"365803","messageId":"4361c3a31a703502fa7210bc2ac7b88a08b70126.1545745331.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1545745331.git.liu.denton@gmail.com","subject":"[PATCH v5 2/4] t7600: clean up 'merge --squash c3 with c7' test","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2018-12-25T13:56:05Z","receivedAt":"2018-12-25T13:56:19Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"This cleans up the original test by removing some unnecessary braces and\nremoving a pipe.\n\nHelped-by: SZEDER Gábor <szeder.dev@gmail.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t7600-merge.sh | 7 +++----\n 1 file changed, 3 insertions(+), 4 deletions(-)\n\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex 106148254d..d879efd330 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -233,8 +233,7 @@ test_expect_success 'merge --squash c3 with c7' '\n \tcat result.9z >file &&\n \tgit commit --no-edit -a &&\n \n-\t{\n-\t\tcat <<-EOF\n+\tcat >expect <<-EOF &&\n \t\tSquashed commit of the following:\n \n \t\t$(git show -s c7)\n@@ -242,8 +241,8 @@ test_expect_success 'merge --squash c3 with c7' '\n \t\t# Conflicts:\n \t\t#\tfile\n \t\tEOF\n-\t} >expect &&\n-\tgit cat-file commit HEAD | sed -e '1,/^$/d' >actual &&\n+\tgit cat-file commit HEAD >tmp &&\n+\tsed -e '1,/^$/d' <tmp >actual &&\n \ttest_cmp expect actual\n '\n \n-- \n2.20.1\n\n"},{"id":"365804","messageId":"421942870ce23ed425139187332b9ebcd784124e.1545745331.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1545745331.git.liu.denton@gmail.com","subject":"[PATCH v5 3/4] merge: cleanup messages like commit","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2018-12-25T13:56:13Z","receivedAt":"2018-12-25T13:56:19Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"This change allows git-merge messages to be cleaned up with the\ncommit.cleanup configuration or --cleanup option, just like how\ngit-commit does it.\n\nWe also give git-pull the passthrough option of --cleanup so that it can\nalso take advantage of this change.\n\nFinally, add testing to ensure that messages are properly cleaned up.\nNote that some newlines that were added to the commit message were\nremoved so that if a file were read via -F, it would be copied\nfaithfully.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/merge-options.txt |  5 +++\n builtin/merge.c                 | 31 +++++++++++++----\n builtin/pull.c                  |  6 ++++\n t/t7604-merge-custom-message.sh | 61 +++++++++++++++++++++++++++++++++\n wt-status.c                     | 12 +++++--\n wt-status.h                     |  1 +\n 6 files changed, 106 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 63a3fc0954..22783651b6 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -27,6 +27,11 @@ they run `git merge`. To make it easier to adjust such scripts to the\n updated behaviour, the environment variable `GIT_MERGE_AUTOEDIT` can be\n set to `no` at the beginning of them.\n \n+--cleanup=<mode>::\n+\tThis option determines how the merge message will be cleaned up\n+\tbefore commiting or being passed on. See linkgit:git-commit[1] for more\n+\tdetails.\n+\n --ff::\n \tWhen the merge resolves as a fast-forward, only update the branch\n \tpointer, without creating a merge commit.  This is the default\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex c3c976d471..ab0db46ede 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -37,6 +37,7 @@\n #include \"tag.h\"\n #include \"alias.h\"\n #include \"commit-reach.h\"\n+#include \"wt-status.h\"\n \n #define DEFAULT_TWOHEAD (1<<0)\n #define DEFAULT_OCTOPUS (1<<1)\n@@ -97,6 +98,9 @@ enum ff_type {\n \n static enum ff_type fast_forward = FF_ALLOW;\n \n+static const char *cleanup_arg;\n+static enum commit_msg_cleanup_mode cleanup_mode;\n+\n static int option_parse_message(const struct option *opt,\n \t\t\t\tconst char *arg, int unset)\n {\n@@ -245,6 +249,7 @@ static struct option builtin_merge_options[] = {\n \t\tN_(\"perform a commit if the merge succeeds (default)\")),\n \tOPT_BOOL('e', \"edit\", &option_edit,\n \t\tN_(\"edit message before committing\")),\n+\tOPT_STRING(0, \"cleanup\", &cleanup_arg, N_(\"default\"), N_(\"how to strip spaces and #comments from message\")),\n \tOPT_SET_INT(0, \"ff\", &fast_forward, N_(\"allow fast-forward (default)\"), FF_ALLOW),\n \tOPT_SET_INT_F(0, \"ff-only\", &fast_forward,\n \t\t      N_(\"abort if fast-forward is not possible\"),\n@@ -608,6 +613,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n \t\treturn git_config_string(&pull_twohead, k, v);\n \telse if (!strcmp(k, \"pull.octopus\"))\n \t\treturn git_config_string(&pull_octopus, k, v);\n+\telse if (!strcmp(k, \"commit.cleanup\"))\n+\t\treturn git_config_string(&cleanup_arg, k, v);\n \telse if (!strcmp(k, \"merge.renormalize\"))\n \t\toption_renormalize = git_config_bool(k, v);\n \telse if (!strcmp(k, \"merge.ff\")) {\n@@ -793,23 +800,32 @@ static void abort_commit(struct commit_list *remoteheads, const char *err_msg)\n \texit(1);\n }\n \n+static const char comment_line_explanation[] =\n+N_(\"Lines starting with '%c' will be ignored.\\n\");\n+\n static const char merge_editor_comment[] =\n N_(\"Please enter a commit message to explain why this merge is necessary,\\n\"\n    \"especially if it merges an updated upstream into a topic branch.\\n\"\n    \"\\n\"\n-   \"Lines starting with '%c' will be ignored, and an empty message aborts\\n\"\n-   \"the commit.\\n\");\n+   \"An empty message aborts the commit.\\n\");\n \n static void write_merge_heads(struct commit_list *);\n static void prepare_to_commit(struct commit_list *remoteheads)\n {\n \tstruct strbuf msg = STRBUF_INIT;\n \tstrbuf_addbuf(&msg, &merge_msg);\n-\tstrbuf_addch(&msg, '\\n');\n \tif (squash)\n \t\tBUG(\"the control must not reach here under --squash\");\n-\tif (0 < option_edit)\n-\t\tstrbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);\n+\tif (0 < option_edit) {\n+\t\tstrbuf_addch(&msg, '\\n');\n+\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n+\t\t\twt_status_append_cut_line(&msg);\n+\t\telse\n+\t\t\tstrbuf_commented_addf(&msg, _(comment_line_explanation), comment_line_char);\n+\n+\t\tstrbuf_commented_addf(&msg, \"\\n\");\n+\t\tstrbuf_commented_addf(&msg, _(merge_editor_comment));\n+\t}\n \tif (signoff)\n \t\tappend_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);\n \twrite_merge_heads(remoteheads);\n@@ -828,7 +844,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)\n \t\tabort_commit(remoteheads, NULL);\n \n \tread_merge_msg(&msg);\n-\tstrbuf_stripspace(&msg, 0 < option_edit);\n+\tcleanup_message(&msg, cleanup_mode, 0);\n \tif (!msg.len)\n \t\tabort_commit(remoteheads, _(\"Empty commit message.\"));\n \tstrbuf_release(&merge_msg);\n@@ -876,7 +892,6 @@ static int finish_automerge(struct commit *head,\n \tparents = remoteheads;\n \tif (!head_subsumed || fast_forward == FF_NO)\n \t\tcommit_list_insert(head, &parents);\n-\tstrbuf_addch(&merge_msg, '\\n');\n \tprepare_to_commit(remoteheads);\n \tif (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,\n \t\t\t&result_commit, NULL, sign_commit))\n@@ -1385,6 +1400,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \tif (option_edit < 0)\n \t\toption_edit = default_edit_option();\n \n+\tcleanup_mode = get_cleanup_mode(cleanup_arg, 0 < option_edit);\n+\n \tif (!use_strategies) {\n \t\tif (!remoteheads)\n \t\t\t; /* already up-to-date */\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 1b90622b13..938be2f610 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -96,6 +96,7 @@ static char *opt_signoff;\n static char *opt_squash;\n static char *opt_commit;\n static char *opt_edit;\n+static char *opt_cleanup;\n static char *opt_ff;\n static char *opt_verify_signatures;\n static int opt_autostash = -1;\n@@ -163,6 +164,9 @@ static struct option pull_options[] = {\n \tOPT_PASSTHRU(0, \"edit\", &opt_edit, NULL,\n \t\tN_(\"edit message before committing\"),\n \t\tPARSE_OPT_NOARG),\n+\tOPT_PASSTHRU(0, \"cleanup\", &opt_cleanup, NULL,\n+\t\tN_(\"how to strip spaces and #comments from message\"),\n+\t\tPARSE_OPT_NOARG),\n \tOPT_PASSTHRU(0, \"ff\", &opt_ff, NULL,\n \t\tN_(\"allow fast-forward\"),\n \t\tPARSE_OPT_NOARG),\n@@ -639,6 +643,8 @@ static int run_merge(void)\n \t\targv_array_push(&args, opt_commit);\n \tif (opt_edit)\n \t\targv_array_push(&args, opt_edit);\n+\tif (opt_cleanup)\n+\t\targv_array_push(&args, opt_cleanup);\n \tif (opt_ff)\n \t\targv_array_push(&args, opt_ff);\n \tif (opt_verify_signatures)\ndiff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh\nindex 89619cf446..3fcd58eb2c 100755\n--- a/t/t7604-merge-custom-message.sh\n+++ b/t/t7604-merge-custom-message.sh\n@@ -47,4 +47,65 @@ test_expect_success 'merge --log appends to custom message' '\n \ttest_cmp exp.log actual\n '\n \n+mesg_with_comment_and_newlines='\n+# text\n+\n+'\n+\n+test_expect_success 'prepare file with comment line and trailing newlines'  '\n+\tprintf \"%s\" \"$mesg_with_comment_and_newlines\" >expect\n+'\n+\n+test_expect_success 'cleanup commit messages (verbatim option)' '\n+\tgit reset --hard c1 &&\n+\tgit merge --cleanup=verbatim -F expect c2 &&\n+\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'cleanup commit messages (whitespace option)' '\n+\tgit reset --hard c1 &&\n+\t{ echo;echo \"# text\";echo; } >text &&\n+\techo \"# text\" >expect &&\n+\tgit merge --cleanup=whitespace -F text c2 &&\n+\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\ttest_cmp expect actual\n+\n+'\n+\n+test_expect_success 'cleanup merge messages (scissors option)' '\n+\tgit reset --hard c1 &&\n+\tcat >text <<EOF &&\n+\n+# to be kept\n+\n+  # ------------------------ >8 ------------------------\n+# to be kept, too\n+# ------------------------ >8 ------------------------\n+to be removed\n+# ------------------------ >8 ------------------------\n+to be removed, too\n+EOF\n+\n+\tcat >expect <<EOF &&\n+# to be kept\n+\n+  # ------------------------ >8 ------------------------\n+# to be kept, too\n+EOF\n+\tgit merge --cleanup=scissors -e -F text c2 &&\n+\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'cleanup commit messages (strip option)' '\n+\tgit reset --hard c1 &&\n+\t{ echo;echo \"# text\";echo sample;echo; } >text &&\n+\techo sample >expect &&\n+\tgit merge --cleanup=strip -F text c2 &&\n+\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\ttest_cmp expect actual\n+\n+'\n+\n test_done\ndiff --git a/wt-status.c b/wt-status.c\nindex a24711374c..3dc2ff9a8d 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -991,13 +991,19 @@ size_t wt_status_locate_end(const char *s, size_t len)\n \treturn len;\n }\n \n-void wt_status_add_cut_line(FILE *fp)\n+void wt_status_append_cut_line(struct strbuf *buf)\n {\n \tconst char *explanation = _(\"Do not modify or remove the line above.\\nEverything below it will be ignored.\");\n+\n+\tstrbuf_commented_addf(buf, \"%s\", cut_line);\n+\tstrbuf_add_commented_lines(buf, explanation, strlen(explanation));\n+}\n+\n+void wt_status_add_cut_line(FILE *fp)\n+{\n \tstruct strbuf buf = STRBUF_INIT;\n \n-\tfprintf(fp, \"%c %s\", comment_line_char, cut_line);\n-\tstrbuf_add_commented_lines(&buf, explanation, strlen(explanation));\n+\twt_status_append_cut_line(&buf);\n \tfputs(buf.buf, fp);\n \tstrbuf_release(&buf);\n }\ndiff --git a/wt-status.h b/wt-status.h\nindex 1fcf93afbf..739a3e4dfb 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -127,6 +127,7 @@ struct wt_status {\n };\n \n size_t wt_status_locate_end(const char *s, size_t len);\n+void wt_status_append_cut_line(struct strbuf *buf);\n void wt_status_add_cut_line(FILE *fp);\n void wt_status_prepare(struct wt_status *s);\n void wt_status_print(struct wt_status *s);\n-- \n2.20.1\n\n"},{"id":"365805","messageId":"a97b9f99baa9e2ed85972e2034f0676991f086f6.1545745331.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1545745331.git.liu.denton@gmail.com","subject":"[PATCH v5 4/4] merge: add scissors line on merge conflict","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2018-12-25T13:56:20Z","receivedAt":"2018-12-25T13:56:26Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"This fixes a bug where the scissors line is placed after the Conflicts:\nsection, in the case where a merge conflict occurs and\ncommit.cleanup = scissors.\n\nNext, if commit.cleanup = scissors is specified, don't produce a\nscissors line in commit if one already exists in the MERGE_MSG file.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/merge-options.txt |  4 ++-\n builtin/commit.c                | 20 ++++++++++----\n builtin/merge.c                 | 14 ++++++++++\n t/t7600-merge.sh                | 46 +++++++++++++++++++++++++++++++++\n 4 files changed, 78 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 22783651b6..c2a263ba74 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -30,7 +30,9 @@ set to `no` at the beginning of them.\n --cleanup=<mode>::\n \tThis option determines how the merge message will be cleaned up\n \tbefore commiting or being passed on. See linkgit:git-commit[1] for more\n-\tdetails.\n+\tdetails. In addition, if the '<mode>' is given a value of `scissors`,\n+\tscissors will be prepended to MERGE_MSG before being passed on in the case\n+\tof a merge conflict.\n \n --ff::\n \tWhen the merge resolves as a fast-forward, only update the branch\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 9eb745b7ce..7923282ebb 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -665,6 +665,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \tconst char *hook_arg2 = NULL;\n \tint clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);\n \tint old_display_comment_prefix;\n+\tint merge_contains_scissors = 0;\n \n \t/* This checks and barfs if author is badly specified */\n \tdetermine_author_info(author_ident);\n@@ -725,6 +726,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\tstrbuf_addbuf(&sb, &message);\n \t\thook_arg1 = \"message\";\n \t} else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {\n+\t\tsize_t merge_msg_start;\n+\n \t\t/*\n \t\t * prepend SQUASH_MSG here if it exists and a\n \t\t * \"merge --squash\" was originally performed\n@@ -735,8 +738,14 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\thook_arg1 = \"squash\";\n \t\t} else\n \t\t\thook_arg1 = \"merge\";\n+\n+\t\tmerge_msg_start = sb.len;\n \t\tif (strbuf_read_file(&sb, git_path_merge_msg(the_repository), 0) < 0)\n \t\t\tdie_errno(_(\"could not read MERGE_MSG\"));\n+\n+\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\t    wt_status_locate_end(sb.buf + merge_msg_start, sb.len - merge_msg_start) < sb.len - merge_msg_start)\n+\t\t\tmerge_contains_scissors = 1;\n \t} else if (!stat(git_path_squash_msg(the_repository), &statbuf)) {\n \t\tif (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)\n \t\t\tdie_errno(_(\"could not read SQUASH_MSG\"));\n@@ -804,7 +813,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\tstruct ident_split ci, ai;\n \n \t\tif (whence != FROM_COMMIT) {\n-\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n+\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\t\t\t!merge_contains_scissors)\n \t\t\t\twt_status_add_cut_line(s->fp);\n \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL,\n \t\t\t    whence == FROM_MERGE\n@@ -829,10 +839,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\nwith '%c' will be ignored, and an empty\"\n \t\t\t\t  \" message aborts the commit.\\n\"), comment_line_char);\n-\t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n-\t\t\t whence == FROM_COMMIT)\n-\t\t\twt_status_add_cut_line(s->fp);\n-\t\telse /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n+\t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {\n+\t\t\tif (whence == FROM_COMMIT && !merge_contains_scissors)\n+\t\t\t\twt_status_add_cut_line(s->fp);\n+\t\t} else /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n \t\t\tstatus_printf(s, GIT_COLOR_NORMAL,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\n\"\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex ab0db46ede..6514267efc 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -912,6 +912,20 @@ static int suggest_conflicts(void)\n \tfilename = git_path_merge_msg(the_repository);\n \tfp = xfopen(filename, \"a\");\n \n+\t/*\n+\t * We can't use cleanup_mode because if we're not using the editor,\n+\t * get_cleanup_mode will return COMMIT_MSG_CLEANUP_SPACE instead, even\n+\t * though the message is meant to be processed later by git-commit.\n+\t * Thus, we will get the cleanup mode is returned we _are_ using an\n+\t * editor.\n+\t */\n+\tif (get_cleanup_mode(cleanup_arg, 1) == COMMIT_MSG_CLEANUP_SCISSORS) {\n+\t    fputc('\\n', fp);\n+\t    wt_status_add_cut_line(fp);\n+\t    /* comments out the newline from append_conflicts_hint */\n+\t    fputc(comment_line_char, fp);\n+\t}\n+\n \tappend_conflicts_hint(&msgbuf);\n \tfputs(msgbuf.buf, fp);\n \tstrbuf_release(&msgbuf);\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex d879efd330..2cb57e5878 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -246,6 +246,52 @@ test_expect_success 'merge --squash c3 with c7' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'merge c3 with c7 with commit.cleanup = scissors' '\n+\tgit config commit.cleanup scissors &&\n+\tgit reset --hard c3 &&\n+\ttest_must_fail git merge c7 &&\n+\tcat result.9z >file &&\n+\tgit commit --no-edit -a &&\n+\n+\tcat >expect <<-EOF &&\n+\t\tMerge tag '\"'\"'c7'\"'\"'\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfile\n+\t\tEOF\n+\tgit cat-file commit HEAD >tmp &&\n+\tsed -e '1,/^$/d' <tmp >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n+test_expect_success 'merge c3 with c7 with --squash commit.cleanup = scissors' '\n+\tgit config commit.cleanup scissors &&\n+\tgit reset --hard c3 &&\n+\ttest_must_fail git merge --squash c7 &&\n+\tcat result.9z >file &&\n+\tgit commit --no-edit -a &&\n+\n+\tcat >expect <<-EOF &&\n+\t\tSquashed commit of the following:\n+\n+\t\t$(git show -s c7)\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfile\n+\t\tEOF\n+\tgit cat-file commit HEAD >tmp &&\n+\tsed -e '1,/^$/d' <tmp >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n test_debug 'git log --graph --decorate --oneline --all'\n \n test_expect_success 'merge c1 with c2 and c3' '\n-- \n2.20.1\n\n"},{"id":"367398","messageId":"cover.1548219737.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1545745331.git.liu.denton@gmail.com","subject":"[PATCH v6 0/4] Add git-merge --cleanup support","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-01-23T05:06:15Z","receivedAt":"2019-01-23T05:06:22Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"There were some merge conflicts since v5 so I've rebased the last few\npatches on the latest master and fixed the conflicts.\n\nNot related to this but while I was making my changes, I noticed that -F\nand -m don't die properly in git-merge when they accept invalid\narguments. Instead, they just print an error message to stderr and the\nprogram keeps running. Is this the expected behaviour?\n\n\nChanges since V1:\n\t* Only check MERGE_MSG for a scissors line instead of all prepended\n\t  files\n\t* Make a variable static in merge where appropriate\n\t* Add passthrough options in pull\n\t* Add documentation for the new option\n\t* Add tests to ensure desired behaviour\n\nChanges since V2:\n\t* Merge both patches into one patch\n\t* Fix bug in help message printing logic\n\nChanges since V3:\n\t* Add patch to cleanup 'merge --squash c3 with c7' test in t7600\n\t* Use test_i18ncmp instead of test_cmp to pass GETTEXT_POISON tests\n\nChanges since V4:\n\t* Add patches so that git-merge messages will be cleaned up like in\n\t  git-commit\n\nChanges since V5:\n\t* Rebase on latest master and resolve merge conflicts\n\n\nDenton Liu (4):\n  commit: extract cleanup_mode functions to sequencer\n  t7600: clean up 'merge --squash c3 with c7' test\n  merge: cleanup messages like commit\n  merge: add scissors line on merge conflict\n\n Documentation/merge-options.txt |  7 ++++\n builtin/commit.c                | 41 +++++++++-------------\n builtin/merge.c                 | 45 ++++++++++++++++++++----\n builtin/pull.c                  |  6 ++++\n sequencer.c                     | 29 ++++++++++++++++\n sequencer.h                     |  4 +++\n t/t7600-merge.sh                | 53 +++++++++++++++++++++++++---\n t/t7604-merge-custom-message.sh | 61 +++++++++++++++++++++++++++++++++\n wt-status.c                     | 12 +++++--\n wt-status.h                     |  1 +\n 10 files changed, 221 insertions(+), 38 deletions(-)\n\n-- \n2.20.1.3.ge68552ea27.dirty\n\n"},{"id":"367399","messageId":"69199be34dbe0648f88fa7eb5f0cc11ed1e7feb7.1548219737.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1548219737.git.liu.denton@gmail.com","subject":"[PATCH v6 1/4] commit: extract cleanup_mode functions to sequencer","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-01-23T05:06:18Z","receivedAt":"2019-01-23T05:06:24Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Signed-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/commit.c | 21 ++-------------------\n sequencer.c      | 29 +++++++++++++++++++++++++++++\n sequencer.h      |  4 ++++\n 3 files changed, 35 insertions(+), 19 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 004b816635..3ac0a21a22 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1165,20 +1165,7 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t\tdie(_(\"Only one of --include/--only/--all/--interactive/--patch can be used.\"));\n \tif (argc == 0 && (also || (only && !amend && !allow_empty)))\n \t\tdie(_(\"No paths with --include/--only does not make sense.\"));\n-\tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n-\t\tcleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_ALL :\n-\t\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n-\telse if (!strcmp(cleanup_arg, \"verbatim\"))\n-\t\tcleanup_mode = COMMIT_MSG_CLEANUP_NONE;\n-\telse if (!strcmp(cleanup_arg, \"whitespace\"))\n-\t\tcleanup_mode = COMMIT_MSG_CLEANUP_SPACE;\n-\telse if (!strcmp(cleanup_arg, \"strip\"))\n-\t\tcleanup_mode = COMMIT_MSG_CLEANUP_ALL;\n-\telse if (!strcmp(cleanup_arg, \"scissors\"))\n-\t\tcleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n-\t\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n-\telse\n-\t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n+\tcleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);\n \n \thandle_untracked_files_arg(s);\n \n@@ -1615,11 +1602,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"could not read commit message: %s\"), strerror(saved_errno));\n \t}\n \n-\tif (verbose || /* Truncate the message just before the diff, if any. */\n-\t    cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n-\t\tstrbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));\n-\tif (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)\n-\t\tstrbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);\n+\tcleanup_message(&sb, cleanup_mode, verbose);\n \n \tif (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {\n \t\trollback_index_files();\ndiff --git a/sequencer.c b/sequencer.c\nindex f5370f4965..4f79892801 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -510,6 +510,25 @@ static int fast_forward_to(struct repository *r,\n \treturn 0;\n }\n \n+enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n+\tint use_editor)\n+{\n+\tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n+\t\treturn use_editor ? COMMIT_MSG_CLEANUP_ALL :\n+\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n+\telse if (!strcmp(cleanup_arg, \"verbatim\"))\n+\t\treturn COMMIT_MSG_CLEANUP_NONE;\n+\telse if (!strcmp(cleanup_arg, \"whitespace\"))\n+\t\treturn COMMIT_MSG_CLEANUP_SPACE;\n+\telse if (!strcmp(cleanup_arg, \"strip\"))\n+\t\treturn COMMIT_MSG_CLEANUP_ALL;\n+\telse if (!strcmp(cleanup_arg, \"scissors\"))\n+\t\treturn use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n+\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n+\telse\n+\t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n+}\n+\n void append_conflicts_hint(struct index_state *istate,\n \t\t\t   struct strbuf *msgbuf)\n {\n@@ -1012,6 +1031,16 @@ static int rest_is_empty(const struct strbuf *sb, int start)\n \treturn 1;\n }\n \n+void cleanup_message(struct strbuf *msgbuf,\n+\tenum commit_msg_cleanup_mode cleanup_mode, int verbose)\n+{\n+\tif (verbose || /* Truncate the message just before the diff, if any. */\n+\t    cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n+\t\tstrbuf_setlen(msgbuf, wt_status_locate_end(msgbuf->buf, msgbuf->len));\n+\tif (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)\n+\t\tstrbuf_stripspace(msgbuf, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);\n+}\n+\n /*\n  * Find out if the message in the strbuf contains only whitespace and\n  * Signed-off-by lines.\ndiff --git a/sequencer.h b/sequencer.h\nindex 9d83f0f3e9..57ad6fc7de 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -115,7 +115,11 @@ extern const char sign_off_header[];\n  */\n void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);\n \n+enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n+\tint use_editor);\n void append_conflicts_hint(struct index_state *istate, struct strbuf *msgbuf);\n+void cleanup_message(struct strbuf *msgbuf,\n+\tenum commit_msg_cleanup_mode cleanup_mode, int verbose);\n int message_is_empty(const struct strbuf *sb,\n \t\t     enum commit_msg_cleanup_mode cleanup_mode);\n int template_untouched(const struct strbuf *sb, const char *template_file,\n-- \n2.20.1.3.ge68552ea27.dirty\n\n"},{"id":"367400","messageId":"2b982fd6a1f443696b82a79cabedc16f6307d89b.1548219737.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1548219737.git.liu.denton@gmail.com","subject":"[PATCH v6 2/4] t7600: clean up 'merge --squash c3 with c7' test","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-01-23T05:06:21Z","receivedAt":"2019-01-23T05:06:26Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"This cleans up the original test by removing some unnecessary braces and\nremoving a pipe.\n\nHelped-by: SZEDER Gábor <szeder.dev@gmail.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t7600-merge.sh | 7 +++----\n 1 file changed, 3 insertions(+), 4 deletions(-)\n\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex 106148254d..d879efd330 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -233,8 +233,7 @@ test_expect_success 'merge --squash c3 with c7' '\n \tcat result.9z >file &&\n \tgit commit --no-edit -a &&\n \n-\t{\n-\t\tcat <<-EOF\n+\tcat >expect <<-EOF &&\n \t\tSquashed commit of the following:\n \n \t\t$(git show -s c7)\n@@ -242,8 +241,8 @@ test_expect_success 'merge --squash c3 with c7' '\n \t\t# Conflicts:\n \t\t#\tfile\n \t\tEOF\n-\t} >expect &&\n-\tgit cat-file commit HEAD | sed -e '1,/^$/d' >actual &&\n+\tgit cat-file commit HEAD >tmp &&\n+\tsed -e '1,/^$/d' <tmp >actual &&\n \ttest_cmp expect actual\n '\n \n-- \n2.20.1.3.ge68552ea27.dirty\n\n"},{"id":"367401","messageId":"ac7a9b7abb4c0457ccb657c84456ea094a00c5a9.1548219737.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1548219737.git.liu.denton@gmail.com","subject":"[PATCH v6 3/4] merge: cleanup messages like commit","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-01-23T05:06:23Z","receivedAt":"2019-01-23T05:06:28Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"This change allows git-merge messages to be cleaned up with the\ncommit.cleanup configuration or --cleanup option, just like how\ngit-commit does it.\n\nWe also give git-pull the passthrough option of --cleanup so that it can\nalso take advantage of this change.\n\nFinally, add testing to ensure that messages are properly cleaned up.\nNote that some newlines that were added to the commit message were\nremoved so that if a file were read via -F, it would be copied\nfaithfully.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/merge-options.txt |  5 +++\n builtin/merge.c                 | 31 +++++++++++++----\n builtin/pull.c                  |  6 ++++\n t/t7604-merge-custom-message.sh | 61 +++++++++++++++++++++++++++++++++\n wt-status.c                     | 12 +++++--\n wt-status.h                     |  1 +\n 6 files changed, 106 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 63a3fc0954..22783651b6 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -27,6 +27,11 @@ they run `git merge`. To make it easier to adjust such scripts to the\n updated behaviour, the environment variable `GIT_MERGE_AUTOEDIT` can be\n set to `no` at the beginning of them.\n \n+--cleanup=<mode>::\n+\tThis option determines how the merge message will be cleaned up\n+\tbefore commiting or being passed on. See linkgit:git-commit[1] for more\n+\tdetails.\n+\n --ff::\n \tWhen the merge resolves as a fast-forward, only update the branch\n \tpointer, without creating a merge commit.  This is the default\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex dc0b7cc521..19fb3abb77 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -37,6 +37,7 @@\n #include \"tag.h\"\n #include \"alias.h\"\n #include \"commit-reach.h\"\n+#include \"wt-status.h\"\n \n #define DEFAULT_TWOHEAD (1<<0)\n #define DEFAULT_OCTOPUS (1<<1)\n@@ -97,6 +98,9 @@ enum ff_type {\n \n static enum ff_type fast_forward = FF_ALLOW;\n \n+static const char *cleanup_arg;\n+static enum commit_msg_cleanup_mode cleanup_mode;\n+\n static int option_parse_message(const struct option *opt,\n \t\t\t\tconst char *arg, int unset)\n {\n@@ -245,6 +249,7 @@ static struct option builtin_merge_options[] = {\n \t\tN_(\"perform a commit if the merge succeeds (default)\")),\n \tOPT_BOOL('e', \"edit\", &option_edit,\n \t\tN_(\"edit message before committing\")),\n+\tOPT_STRING(0, \"cleanup\", &cleanup_arg, N_(\"default\"), N_(\"how to strip spaces and #comments from message\")),\n \tOPT_SET_INT(0, \"ff\", &fast_forward, N_(\"allow fast-forward (default)\"), FF_ALLOW),\n \tOPT_SET_INT_F(0, \"ff-only\", &fast_forward,\n \t\t      N_(\"abort if fast-forward is not possible\"),\n@@ -608,6 +613,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n \t\treturn git_config_string(&pull_twohead, k, v);\n \telse if (!strcmp(k, \"pull.octopus\"))\n \t\treturn git_config_string(&pull_octopus, k, v);\n+\telse if (!strcmp(k, \"commit.cleanup\"))\n+\t\treturn git_config_string(&cleanup_arg, k, v);\n \telse if (!strcmp(k, \"merge.renormalize\"))\n \t\toption_renormalize = git_config_bool(k, v);\n \telse if (!strcmp(k, \"merge.ff\")) {\n@@ -793,23 +800,32 @@ static void abort_commit(struct commit_list *remoteheads, const char *err_msg)\n \texit(1);\n }\n \n+static const char comment_line_explanation[] =\n+N_(\"Lines starting with '%c' will be ignored.\\n\");\n+\n static const char merge_editor_comment[] =\n N_(\"Please enter a commit message to explain why this merge is necessary,\\n\"\n    \"especially if it merges an updated upstream into a topic branch.\\n\"\n    \"\\n\"\n-   \"Lines starting with '%c' will be ignored, and an empty message aborts\\n\"\n-   \"the commit.\\n\");\n+   \"An empty message aborts the commit.\\n\");\n \n static void write_merge_heads(struct commit_list *);\n static void prepare_to_commit(struct commit_list *remoteheads)\n {\n \tstruct strbuf msg = STRBUF_INIT;\n \tstrbuf_addbuf(&msg, &merge_msg);\n-\tstrbuf_addch(&msg, '\\n');\n \tif (squash)\n \t\tBUG(\"the control must not reach here under --squash\");\n-\tif (0 < option_edit)\n-\t\tstrbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);\n+\tif (0 < option_edit) {\n+\t\tstrbuf_addch(&msg, '\\n');\n+\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n+\t\t\twt_status_append_cut_line(&msg);\n+\t\telse\n+\t\t\tstrbuf_commented_addf(&msg, _(comment_line_explanation), comment_line_char);\n+\n+\t\tstrbuf_commented_addf(&msg, \"\\n\");\n+\t\tstrbuf_commented_addf(&msg, _(merge_editor_comment));\n+\t}\n \tif (signoff)\n \t\tappend_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);\n \twrite_merge_heads(remoteheads);\n@@ -828,7 +844,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)\n \t\tabort_commit(remoteheads, NULL);\n \n \tread_merge_msg(&msg);\n-\tstrbuf_stripspace(&msg, 0 < option_edit);\n+\tcleanup_message(&msg, cleanup_mode, 0);\n \tif (!msg.len)\n \t\tabort_commit(remoteheads, _(\"Empty commit message.\"));\n \tstrbuf_release(&merge_msg);\n@@ -876,7 +892,6 @@ static int finish_automerge(struct commit *head,\n \tparents = remoteheads;\n \tif (!head_subsumed || fast_forward == FF_NO)\n \t\tcommit_list_insert(head, &parents);\n-\tstrbuf_addch(&merge_msg, '\\n');\n \tprepare_to_commit(remoteheads);\n \tif (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,\n \t\t\t&result_commit, NULL, sign_commit))\n@@ -1385,6 +1400,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \tif (option_edit < 0)\n \t\toption_edit = default_edit_option();\n \n+\tcleanup_mode = get_cleanup_mode(cleanup_arg, 0 < option_edit);\n+\n \tif (!use_strategies) {\n \t\tif (!remoteheads)\n \t\t\t; /* already up-to-date */\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 74808b9455..0cb458137b 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -96,6 +96,7 @@ static char *opt_signoff;\n static char *opt_squash;\n static char *opt_commit;\n static char *opt_edit;\n+static char *opt_cleanup;\n static char *opt_ff;\n static char *opt_verify_signatures;\n static int opt_autostash = -1;\n@@ -163,6 +164,9 @@ static struct option pull_options[] = {\n \tOPT_PASSTHRU(0, \"edit\", &opt_edit, NULL,\n \t\tN_(\"edit message before committing\"),\n \t\tPARSE_OPT_NOARG),\n+\tOPT_PASSTHRU(0, \"cleanup\", &opt_cleanup, NULL,\n+\t\tN_(\"how to strip spaces and #comments from message\"),\n+\t\tPARSE_OPT_NOARG),\n \tOPT_PASSTHRU(0, \"ff\", &opt_ff, NULL,\n \t\tN_(\"allow fast-forward\"),\n \t\tPARSE_OPT_NOARG),\n@@ -639,6 +643,8 @@ static int run_merge(void)\n \t\targv_array_push(&args, opt_commit);\n \tif (opt_edit)\n \t\targv_array_push(&args, opt_edit);\n+\tif (opt_cleanup)\n+\t\targv_array_push(&args, opt_cleanup);\n \tif (opt_ff)\n \t\targv_array_push(&args, opt_ff);\n \tif (opt_verify_signatures)\ndiff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh\nindex 89619cf446..3fcd58eb2c 100755\n--- a/t/t7604-merge-custom-message.sh\n+++ b/t/t7604-merge-custom-message.sh\n@@ -47,4 +47,65 @@ test_expect_success 'merge --log appends to custom message' '\n \ttest_cmp exp.log actual\n '\n \n+mesg_with_comment_and_newlines='\n+# text\n+\n+'\n+\n+test_expect_success 'prepare file with comment line and trailing newlines'  '\n+\tprintf \"%s\" \"$mesg_with_comment_and_newlines\" >expect\n+'\n+\n+test_expect_success 'cleanup commit messages (verbatim option)' '\n+\tgit reset --hard c1 &&\n+\tgit merge --cleanup=verbatim -F expect c2 &&\n+\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'cleanup commit messages (whitespace option)' '\n+\tgit reset --hard c1 &&\n+\t{ echo;echo \"# text\";echo; } >text &&\n+\techo \"# text\" >expect &&\n+\tgit merge --cleanup=whitespace -F text c2 &&\n+\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\ttest_cmp expect actual\n+\n+'\n+\n+test_expect_success 'cleanup merge messages (scissors option)' '\n+\tgit reset --hard c1 &&\n+\tcat >text <<EOF &&\n+\n+# to be kept\n+\n+  # ------------------------ >8 ------------------------\n+# to be kept, too\n+# ------------------------ >8 ------------------------\n+to be removed\n+# ------------------------ >8 ------------------------\n+to be removed, too\n+EOF\n+\n+\tcat >expect <<EOF &&\n+# to be kept\n+\n+  # ------------------------ >8 ------------------------\n+# to be kept, too\n+EOF\n+\tgit merge --cleanup=scissors -e -F text c2 &&\n+\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'cleanup commit messages (strip option)' '\n+\tgit reset --hard c1 &&\n+\t{ echo;echo \"# text\";echo sample;echo; } >text &&\n+\techo sample >expect &&\n+\tgit merge --cleanup=strip -F text c2 &&\n+\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\ttest_cmp expect actual\n+\n+'\n+\n test_done\ndiff --git a/wt-status.c b/wt-status.c\nindex 0fe3bcd4cd..2768205d78 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -995,13 +995,19 @@ size_t wt_status_locate_end(const char *s, size_t len)\n \treturn len;\n }\n \n-void wt_status_add_cut_line(FILE *fp)\n+void wt_status_append_cut_line(struct strbuf *buf)\n {\n \tconst char *explanation = _(\"Do not modify or remove the line above.\\nEverything below it will be ignored.\");\n+\n+\tstrbuf_commented_addf(buf, \"%s\", cut_line);\n+\tstrbuf_add_commented_lines(buf, explanation, strlen(explanation));\n+}\n+\n+void wt_status_add_cut_line(FILE *fp)\n+{\n \tstruct strbuf buf = STRBUF_INIT;\n \n-\tfprintf(fp, \"%c %s\", comment_line_char, cut_line);\n-\tstrbuf_add_commented_lines(&buf, explanation, strlen(explanation));\n+\twt_status_append_cut_line(&buf);\n \tfputs(buf.buf, fp);\n \tstrbuf_release(&buf);\n }\ndiff --git a/wt-status.h b/wt-status.h\nindex 3a95975032..64f1ddc9fd 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -129,6 +129,7 @@ struct wt_status {\n };\n \n size_t wt_status_locate_end(const char *s, size_t len);\n+void wt_status_append_cut_line(struct strbuf *buf);\n void wt_status_add_cut_line(FILE *fp);\n void wt_status_prepare(struct repository *r, struct wt_status *s);\n void wt_status_print(struct wt_status *s);\n-- \n2.20.1.3.ge68552ea27.dirty\n\n"},{"id":"367402","messageId":"39640b9e9e1171f1978082dc15b70b9277fa30d0.1548219737.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1548219737.git.liu.denton@gmail.com","subject":"[PATCH v6 4/4] merge: add scissors line on merge conflict","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-01-23T05:06:26Z","receivedAt":"2019-01-23T05:06:31Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"This fixes a bug where the scissors line is placed after the Conflicts:\nsection, in the case where a merge conflict occurs and\ncommit.cleanup = scissors.\n\nNext, if commit.cleanup = scissors is specified, don't produce a\nscissors line in commit if one already exists in the MERGE_MSG file.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/merge-options.txt |  4 ++-\n builtin/commit.c                | 20 ++++++++++----\n builtin/merge.c                 | 14 ++++++++++\n t/t7600-merge.sh                | 46 +++++++++++++++++++++++++++++++++\n 4 files changed, 78 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 22783651b6..c2a263ba74 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -30,7 +30,9 @@ set to `no` at the beginning of them.\n --cleanup=<mode>::\n \tThis option determines how the merge message will be cleaned up\n \tbefore commiting or being passed on. See linkgit:git-commit[1] for more\n-\tdetails.\n+\tdetails. In addition, if the '<mode>' is given a value of `scissors`,\n+\tscissors will be prepended to MERGE_MSG before being passed on in the case\n+\tof a merge conflict.\n \n --ff::\n \tWhen the merge resolves as a fast-forward, only update the branch\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 3ac0a21a22..eb549b57a3 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -665,6 +665,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \tconst char *hook_arg2 = NULL;\n \tint clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);\n \tint old_display_comment_prefix;\n+\tint merge_contains_scissors = 0;\n \n \t/* This checks and barfs if author is badly specified */\n \tdetermine_author_info(author_ident);\n@@ -725,6 +726,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\tstrbuf_addbuf(&sb, &message);\n \t\thook_arg1 = \"message\";\n \t} else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {\n+\t\tsize_t merge_msg_start;\n+\n \t\t/*\n \t\t * prepend SQUASH_MSG here if it exists and a\n \t\t * \"merge --squash\" was originally performed\n@@ -735,8 +738,14 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\thook_arg1 = \"squash\";\n \t\t} else\n \t\t\thook_arg1 = \"merge\";\n+\n+\t\tmerge_msg_start = sb.len;\n \t\tif (strbuf_read_file(&sb, git_path_merge_msg(the_repository), 0) < 0)\n \t\t\tdie_errno(_(\"could not read MERGE_MSG\"));\n+\n+\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\t    wt_status_locate_end(sb.buf + merge_msg_start, sb.len - merge_msg_start) < sb.len - merge_msg_start)\n+\t\t\tmerge_contains_scissors = 1;\n \t} else if (!stat(git_path_squash_msg(the_repository), &statbuf)) {\n \t\tif (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)\n \t\t\tdie_errno(_(\"could not read SQUASH_MSG\"));\n@@ -804,7 +813,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\tstruct ident_split ci, ai;\n \n \t\tif (whence != FROM_COMMIT) {\n-\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n+\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\t\t\t!merge_contains_scissors)\n \t\t\t\twt_status_add_cut_line(s->fp);\n \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL,\n \t\t\t    whence == FROM_MERGE\n@@ -829,10 +839,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\nwith '%c' will be ignored, and an empty\"\n \t\t\t\t  \" message aborts the commit.\\n\"), comment_line_char);\n-\t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n-\t\t\t whence == FROM_COMMIT)\n-\t\t\twt_status_add_cut_line(s->fp);\n-\t\telse /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n+\t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {\n+\t\t\tif (whence == FROM_COMMIT && !merge_contains_scissors)\n+\t\t\t\twt_status_add_cut_line(s->fp);\n+\t\t} else /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n \t\t\tstatus_printf(s, GIT_COLOR_NORMAL,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\n\"\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 19fb3abb77..fbf4a24850 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -912,6 +912,20 @@ static int suggest_conflicts(void)\n \tfilename = git_path_merge_msg(the_repository);\n \tfp = xfopen(filename, \"a\");\n \n+\t/*\n+\t * We can't use cleanup_mode because if we're not using the editor,\n+\t * get_cleanup_mode will return COMMIT_MSG_CLEANUP_SPACE instead, even\n+\t * though the message is meant to be processed later by git-commit.\n+\t * Thus, we will get the cleanup mode is returned we _are_ using an\n+\t * editor.\n+\t */\n+\tif (get_cleanup_mode(cleanup_arg, 1) == COMMIT_MSG_CLEANUP_SCISSORS) {\n+\t    fputc('\\n', fp);\n+\t    wt_status_add_cut_line(fp);\n+\t    /* comments out the newline from append_conflicts_hint */\n+\t    fputc(comment_line_char, fp);\n+\t}\n+\n \tappend_conflicts_hint(&the_index, &msgbuf);\n \tfputs(msgbuf.buf, fp);\n \tstrbuf_release(&msgbuf);\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex d879efd330..2cb57e5878 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -246,6 +246,52 @@ test_expect_success 'merge --squash c3 with c7' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'merge c3 with c7 with commit.cleanup = scissors' '\n+\tgit config commit.cleanup scissors &&\n+\tgit reset --hard c3 &&\n+\ttest_must_fail git merge c7 &&\n+\tcat result.9z >file &&\n+\tgit commit --no-edit -a &&\n+\n+\tcat >expect <<-EOF &&\n+\t\tMerge tag '\"'\"'c7'\"'\"'\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfile\n+\t\tEOF\n+\tgit cat-file commit HEAD >tmp &&\n+\tsed -e '1,/^$/d' <tmp >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n+test_expect_success 'merge c3 with c7 with --squash commit.cleanup = scissors' '\n+\tgit config commit.cleanup scissors &&\n+\tgit reset --hard c3 &&\n+\ttest_must_fail git merge --squash c7 &&\n+\tcat result.9z >file &&\n+\tgit commit --no-edit -a &&\n+\n+\tcat >expect <<-EOF &&\n+\t\tSquashed commit of the following:\n+\n+\t\t$(git show -s c7)\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfile\n+\t\tEOF\n+\tgit cat-file commit HEAD >tmp &&\n+\tsed -e '1,/^$/d' <tmp >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n test_debug 'git log --graph --decorate --oneline --all'\n \n test_expect_success 'merge c1 with c2 and c3' '\n-- \n2.20.1.3.ge68552ea27.dirty\n\n"},{"id":"371106","messageId":"cover.1552275703.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1548219737.git.liu.denton@gmail.com","subject":"[PATCH v7 0/8] Fix scissors bug during conflict","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-11T03:42:27Z","receivedAt":"2019-03-11T03:42:33Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"This is a complete replacement for dl/merge-cleanup-scissors-fix.\n\nPrevious discussion on the cherry-pick/revert changes can be found here[1].\n\nChanges since revert/cherry-pick v3:\n\n* Rebased on top of latest master\n* Reordered and squashed patches\n* Added populate_opts_cb and save_opts to save default_msg_cleanup at Phillip's suggestion\n\n[1]: https://public-inbox.org/git/cover.1551867827.git.liu.denton@gmail.com/T/#u\n\n\nDenton Liu (8):\n  t7600: clean up 'merge --squash c3 with c7' test\n  t3507: cleanup space after redirection operators\n  commit: extract cleanup_mode functions to sequencer\n  sequencer.c: remove duplicate code\n  merge: cleanup messages like commit\n  merge: add scissors line on merge conflict\n  sequencer.c: define get_config_from_cleanup\n  cherry-pick/revert: add scissors line on merge conflict\n\n Documentation/git-cherry-pick.txt |   7 ++\n Documentation/git-revert.txt      |   7 ++\n Documentation/merge-options.txt   |   7 ++\n builtin/commit.c                  |  45 +++++------\n builtin/merge.c                   |  40 ++++++++--\n builtin/pull.c                    |   6 ++\n builtin/rebase--interactive.c     |   2 +-\n builtin/revert.c                  |   5 ++\n sequencer.c                       | 102 ++++++++++++++++++++-----\n sequencer.h                       |  10 ++-\n t/t3507-cherry-pick-conflict.sh   | 120 +++++++++++++++++++++++++-----\n t/t7600-merge.sh                  |  53 ++++++++++++-\n t/t7604-merge-custom-message.sh   |  61 +++++++++++++++\n wt-status.c                       |  12 ++-\n wt-status.h                       |   1 +\n 15 files changed, 398 insertions(+), 80 deletions(-)\n\n\nbase-commit: 6e0cc6776106079ed4efa0cc9abace4107657abf\n-- \n2.21.0.370.g4fdb13b891\n\n"},{"id":"371107","messageId":"6716d28a5187c44c1d90f5ce840c44441f62352c.1552275703.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1552275703.git.liu.denton@gmail.com","subject":"[PATCH v7 1/8] t7600: clean up 'merge --squash c3 with c7' test","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-11T03:42:30Z","receivedAt":"2019-03-11T03:42:36Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"This cleans up the original test by removing some unnecessary braces and\nremoving a pipe.\n\nHelped-by: SZEDER Gábor <szeder.dev@gmail.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t7600-merge.sh | 7 +++----\n 1 file changed, 3 insertions(+), 4 deletions(-)\n\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex 106148254d..d879efd330 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -233,8 +233,7 @@ test_expect_success 'merge --squash c3 with c7' '\n \tcat result.9z >file &&\n \tgit commit --no-edit -a &&\n \n-\t{\n-\t\tcat <<-EOF\n+\tcat >expect <<-EOF &&\n \t\tSquashed commit of the following:\n \n \t\t$(git show -s c7)\n@@ -242,8 +241,8 @@ test_expect_success 'merge --squash c3 with c7' '\n \t\t# Conflicts:\n \t\t#\tfile\n \t\tEOF\n-\t} >expect &&\n-\tgit cat-file commit HEAD | sed -e '1,/^$/d' >actual &&\n+\tgit cat-file commit HEAD >tmp &&\n+\tsed -e '1,/^$/d' <tmp >actual &&\n \ttest_cmp expect actual\n '\n \n-- \n2.21.0.370.g4fdb13b891\n\n"},{"id":"371108","messageId":"0929ecc5493f666509903571304cdce81573a6fb.1552275703.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1552275703.git.liu.denton@gmail.com","subject":"[PATCH v7 2/8] t3507: cleanup space after redirection operators","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-11T03:42:33Z","receivedAt":"2019-03-11T03:42:38Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Signed-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t3507-cherry-pick-conflict.sh | 34 ++++++++++++++++-----------------\n 1 file changed, 17 insertions(+), 17 deletions(-)\n\ndiff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh\nindex 0db166152a..74ff925526 100755\n--- a/t/t3507-cherry-pick-conflict.sh\n+++ b/t/t3507-cherry-pick-conflict.sh\n@@ -88,7 +88,7 @@ test_expect_success 'cherry-pick --no-commit does not set CHERRY_PICK_HEAD' '\n \n test_expect_success 'cherry-pick w/dirty tree does not set CHERRY_PICK_HEAD' '\n \tpristine_detach initial &&\n-\techo foo > foo &&\n+\techo foo >foo &&\n \ttest_must_fail git cherry-pick base &&\n \ttest_must_fail git rev-parse --verify CHERRY_PICK_HEAD\n '\n@@ -96,7 +96,7 @@ test_expect_success 'cherry-pick w/dirty tree does not set CHERRY_PICK_HEAD' '\n test_expect_success \\\n \t'cherry-pick --strategy=resolve w/dirty tree does not set CHERRY_PICK_HEAD' '\n \tpristine_detach initial &&\n-\techo foo > foo &&\n+\techo foo >foo &&\n \ttest_must_fail git cherry-pick --strategy=resolve base &&\n \ttest_must_fail git rev-parse --verify CHERRY_PICK_HEAD\n '\n@@ -175,23 +175,23 @@ test_expect_success 'failed cherry-pick registers participants in index' '\n \t\tgit ls-files --stage foo &&\n \t\tgit checkout picked -- foo &&\n \t\tgit ls-files --stage foo\n-\t} > stages &&\n+\t} >stages &&\n \tsed \"\n \t\t1 s/ 0\t/ 1\t/\n \t\t2 s/ 0\t/ 2\t/\n \t\t3 s/ 0\t/ 3\t/\n-\t\" < stages > expected &&\n+\t\" <stages >expected &&\n \tgit read-tree -u --reset HEAD &&\n \n \ttest_must_fail git cherry-pick picked &&\n-\tgit ls-files --stage --unmerged > actual &&\n+\tgit ls-files --stage --unmerged >actual &&\n \n \ttest_cmp expected actual\n '\n \n test_expect_success 'failed cherry-pick describes conflict in work tree' '\n \tpristine_detach initial &&\n-\tcat <<-EOF > expected &&\n+\tcat <<-EOF >expected &&\n \t<<<<<<< HEAD\n \ta\n \t=======\n@@ -201,14 +201,14 @@ test_expect_success 'failed cherry-pick describes conflict in work tree' '\n \n \ttest_must_fail git cherry-pick picked &&\n \n-\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo > actual &&\n+\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo >actual &&\n \ttest_cmp expected actual\n '\n \n test_expect_success 'diff3 -m style' '\n \tpristine_detach initial &&\n \tgit config merge.conflictstyle diff3 &&\n-\tcat <<-EOF > expected &&\n+\tcat <<-EOF >expected &&\n \t<<<<<<< HEAD\n \ta\n \t||||||| parent of objid picked\n@@ -220,14 +220,14 @@ test_expect_success 'diff3 -m style' '\n \n \ttest_must_fail git cherry-pick picked &&\n \n-\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo > actual &&\n+\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo >actual &&\n \ttest_cmp expected actual\n '\n \n test_expect_success 'revert also handles conflicts sanely' '\n \tgit config --unset merge.conflictstyle &&\n \tpristine_detach initial &&\n-\tcat <<-EOF > expected &&\n+\tcat <<-EOF >expected &&\n \t<<<<<<< HEAD\n \ta\n \t=======\n@@ -241,24 +241,24 @@ test_expect_success 'revert also handles conflicts sanely' '\n \t\tgit ls-files --stage foo &&\n \t\tgit checkout base -- foo &&\n \t\tgit ls-files --stage foo\n-\t} > stages &&\n+\t} >stages &&\n \tsed \"\n \t\t1 s/ 0\t/ 1\t/\n \t\t2 s/ 0\t/ 2\t/\n \t\t3 s/ 0\t/ 3\t/\n-\t\" < stages > expected-stages &&\n+\t\" <stages >expected-stages &&\n \tgit read-tree -u --reset HEAD &&\n \n \thead=$(git rev-parse HEAD) &&\n \ttest_must_fail git revert picked &&\n \tnewhead=$(git rev-parse HEAD) &&\n-\tgit ls-files --stage --unmerged > actual-stages &&\n+\tgit ls-files --stage --unmerged >actual-stages &&\n \n \ttest \"$head\" = \"$newhead\" &&\n \ttest_must_fail git update-index --refresh -q &&\n \ttest_must_fail git diff-index --exit-code HEAD &&\n \ttest_cmp expected-stages actual-stages &&\n-\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo > actual &&\n+\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -284,7 +284,7 @@ test_expect_success 'revert --no-commit sets REVERT_HEAD' '\n \n test_expect_success 'revert w/dirty tree does not set REVERT_HEAD' '\n \tpristine_detach base &&\n-\techo foo > foo &&\n+\techo foo >foo &&\n \ttest_must_fail git revert base &&\n \ttest_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&\n \ttest_must_fail git rev-parse --verify REVERT_HEAD\n@@ -319,7 +319,7 @@ test_expect_success 'failed commit does not clear REVERT_HEAD' '\n test_expect_success 'revert conflict, diff3 -m style' '\n \tpristine_detach initial &&\n \tgit config merge.conflictstyle diff3 &&\n-\tcat <<-EOF > expected &&\n+\tcat <<-EOF >expected &&\n \t<<<<<<< HEAD\n \ta\n \t||||||| objid picked\n@@ -331,7 +331,7 @@ test_expect_success 'revert conflict, diff3 -m style' '\n \n \ttest_must_fail git revert picked &&\n \n-\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo > actual &&\n+\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo >actual &&\n \ttest_cmp expected actual\n '\n \n-- \n2.21.0.370.g4fdb13b891\n\n"},{"id":"371109","messageId":"9a6ad0b890b3ad5169854122f5bd429ce5ce8324.1552275703.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1552275703.git.liu.denton@gmail.com","subject":"[PATCH v7 3/8] commit: extract cleanup_mode functions to sequencer","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-11T03:42:36Z","receivedAt":"2019-03-11T03:42:41Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Signed-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/commit.c | 25 ++-----------------------\n sequencer.c      | 29 +++++++++++++++++++++++++++++\n sequencer.h      |  6 ++++++\n 3 files changed, 37 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex f17537474a..0df15e4851 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1172,24 +1172,7 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t\tdie(_(\"Only one of --include/--only/--all/--interactive/--patch can be used.\"));\n \tif (argc == 0 && (also || (only && !amend && !allow_empty)))\n \t\tdie(_(\"No paths with --include/--only does not make sense.\"));\n-\tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n-\t\tcleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_ALL :\n-\t\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n-\telse if (!strcmp(cleanup_arg, \"verbatim\"))\n-\t\tcleanup_mode = COMMIT_MSG_CLEANUP_NONE;\n-\telse if (!strcmp(cleanup_arg, \"whitespace\"))\n-\t\tcleanup_mode = COMMIT_MSG_CLEANUP_SPACE;\n-\telse if (!strcmp(cleanup_arg, \"strip\"))\n-\t\tcleanup_mode = COMMIT_MSG_CLEANUP_ALL;\n-\telse if (!strcmp(cleanup_arg, \"scissors\"))\n-\t\tcleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n-\t\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n-\t/*\n-\t * Please update _git_commit() in git-completion.bash when you\n-\t * add new options.\n-\t */\n-\telse\n-\t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n+\tcleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);\n \n \thandle_untracked_files_arg(s);\n \n@@ -1626,11 +1609,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"could not read commit message: %s\"), strerror(saved_errno));\n \t}\n \n-\tif (verbose || /* Truncate the message just before the diff, if any. */\n-\t    cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n-\t\tstrbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));\n-\tif (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)\n-\t\tstrbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);\n+\tcleanup_message(&sb, cleanup_mode, verbose);\n \n \tif (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {\n \t\trollback_index_files();\ndiff --git a/sequencer.c b/sequencer.c\nindex 95dda23eee..224c823b43 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -511,6 +511,25 @@ static int fast_forward_to(struct repository *r,\n \treturn 0;\n }\n \n+enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n+\tint use_editor)\n+{\n+\tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n+\t\treturn use_editor ? COMMIT_MSG_CLEANUP_ALL :\n+\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n+\telse if (!strcmp(cleanup_arg, \"verbatim\"))\n+\t\treturn COMMIT_MSG_CLEANUP_NONE;\n+\telse if (!strcmp(cleanup_arg, \"whitespace\"))\n+\t\treturn COMMIT_MSG_CLEANUP_SPACE;\n+\telse if (!strcmp(cleanup_arg, \"strip\"))\n+\t\treturn COMMIT_MSG_CLEANUP_ALL;\n+\telse if (!strcmp(cleanup_arg, \"scissors\"))\n+\t\treturn use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n+\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n+\telse\n+\t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n+}\n+\n void append_conflicts_hint(struct index_state *istate,\n \t\t\t   struct strbuf *msgbuf)\n {\n@@ -1013,6 +1032,16 @@ static int rest_is_empty(const struct strbuf *sb, int start)\n \treturn 1;\n }\n \n+void cleanup_message(struct strbuf *msgbuf,\n+\tenum commit_msg_cleanup_mode cleanup_mode, int verbose)\n+{\n+\tif (verbose || /* Truncate the message just before the diff, if any. */\n+\t    cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n+\t\tstrbuf_setlen(msgbuf, wt_status_locate_end(msgbuf->buf, msgbuf->len));\n+\tif (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)\n+\t\tstrbuf_stripspace(msgbuf, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);\n+}\n+\n /*\n  * Find out if the message in the strbuf contains only whitespace and\n  * Signed-off-by lines.\ndiff --git a/sequencer.h b/sequencer.h\nindex 4d505b3590..eb9bd97ef3 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -116,6 +116,12 @@ int rearrange_squash(struct repository *r);\n void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);\n \n void append_conflicts_hint(struct index_state *istate, struct strbuf *msgbuf);\n+enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n+\tint use_editor);\n+\n+void cleanup_message(struct strbuf *msgbuf,\n+\tenum commit_msg_cleanup_mode cleanup_mode, int verbose);\n+\n int message_is_empty(const struct strbuf *sb,\n \t\t     enum commit_msg_cleanup_mode cleanup_mode);\n int template_untouched(const struct strbuf *sb, const char *template_file,\n-- \n2.21.0.370.g4fdb13b891\n\n"},{"id":"371110","messageId":"6d5ce3ffffe5ef6c23b12538650b2cdf86e4cac9.1552275703.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1552275703.git.liu.denton@gmail.com","subject":"[PATCH v7 4/8] sequencer.c: remove duplicate code","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-11T03:42:38Z","receivedAt":"2019-03-11T03:42:43Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Since we implemented get_cleanup_mode, we had some duplicate code in\ngit_sequencer_config which essentially performed the same operations.\nRefactor git_sequencer_config to take advantage of the logic already in\nget_cleanup_mode.\n\nNote that we had to introduce a separate argument to get_cleanup_mode\nindicating whether to die or not on an invalid value. This is because if\nwe are parsing a config, we do not want to die but instead, we only want\nto warn the user, whereas if we are parsing a command-line option, we\nwould like to actually die.\n\nFinally, this is almost a no-op refactor but not quite. Previously, in\nthe case that an invalid value is presented, default_msg_cleanup would\nnot be set. We change the behaviour so that default_msg_cleanup will now\ntake on the value as if \"default\" were provided as the cleanup_arg.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/commit.c |  2 +-\n sequencer.c      | 20 +++++++-------------\n sequencer.h      |  2 +-\n 3 files changed, 9 insertions(+), 15 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 0df15e4851..81e3bd21ca 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1172,7 +1172,7 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t\tdie(_(\"Only one of --include/--only/--all/--interactive/--patch can be used.\"));\n \tif (argc == 0 && (also || (only && !amend && !allow_empty)))\n \t\tdie(_(\"No paths with --include/--only does not make sense.\"));\n-\tcleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);\n+\tcleanup_mode = get_cleanup_mode(cleanup_arg, use_editor, 1);\n \n \thandle_untracked_files_arg(s);\n \ndiff --git a/sequencer.c b/sequencer.c\nindex 224c823b43..612621f221 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -172,17 +172,7 @@ static int git_sequencer_config(const char *k, const char *v, void *cb)\n \t\tif (status)\n \t\t\treturn status;\n \n-\t\tif (!strcmp(s, \"verbatim\"))\n-\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;\n-\t\telse if (!strcmp(s, \"whitespace\"))\n-\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;\n-\t\telse if (!strcmp(s, \"strip\"))\n-\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;\n-\t\telse if (!strcmp(s, \"scissors\"))\n-\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;\n-\t\telse\n-\t\t\twarning(_(\"invalid commit message cleanup mode '%s'\"),\n-\t\t\t\t  s);\n+\t\topts->default_msg_cleanup = get_cleanup_mode(s, 0, 0);\n \n \t\tfree((char *)s);\n \t\treturn status;\n@@ -512,7 +502,7 @@ static int fast_forward_to(struct repository *r,\n }\n \n enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n-\tint use_editor)\n+\tint use_editor, int die_on_error)\n {\n \tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n \t\treturn use_editor ? COMMIT_MSG_CLEANUP_ALL :\n@@ -526,7 +516,11 @@ enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n \telse if (!strcmp(cleanup_arg, \"scissors\"))\n \t\treturn use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n \t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n-\telse\n+\telse if (!die_on_error) {\n+\t\twarning(_(\"Invalid cleanup mode %s, falling back to default\"), cleanup_arg);\n+\t\treturn use_editor ? COMMIT_MSG_CLEANUP_ALL :\n+\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n+\t} else\n \t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n }\n \ndiff --git a/sequencer.h b/sequencer.h\nindex eb9bd97ef3..e7908f558e 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -117,7 +117,7 @@ void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);\n \n void append_conflicts_hint(struct index_state *istate, struct strbuf *msgbuf);\n enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n-\tint use_editor);\n+\tint use_editor, int die_on_error);\n \n void cleanup_message(struct strbuf *msgbuf,\n \tenum commit_msg_cleanup_mode cleanup_mode, int verbose);\n-- \n2.21.0.370.g4fdb13b891\n\n"},{"id":"371111","messageId":"b2b82954e7060a3f5d47f77e4743493ff4256bd6.1552275703.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1552275703.git.liu.denton@gmail.com","subject":"[PATCH v7 5/8] merge: cleanup messages like commit","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-11T03:42:41Z","receivedAt":"2019-03-11T03:42:46Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"This change allows git-merge messages to be cleaned up with the\ncommit.cleanup configuration or --cleanup option, just like how\ngit-commit does it.\n\nWe also give git-pull the passthrough option of --cleanup so that it can\nalso take advantage of this change.\n\nFinally, add testing to ensure that messages are properly cleaned up.\nNote that some newlines that were added to the commit message were\nremoved so that if a file were read via -F, it would be copied\nfaithfully.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/merge-options.txt |  5 +++\n builtin/merge.c                 | 31 +++++++++++++----\n builtin/pull.c                  |  6 ++++\n t/t7604-merge-custom-message.sh | 61 +++++++++++++++++++++++++++++++++\n wt-status.c                     | 12 +++++--\n wt-status.h                     |  1 +\n 6 files changed, 106 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 92a7d936c1..646100ea9a 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -32,6 +32,11 @@ they run `git merge`. To make it easier to adjust such scripts to the\n updated behaviour, the environment variable `GIT_MERGE_AUTOEDIT` can be\n set to `no` at the beginning of them.\n \n+--cleanup=<mode>::\n+\tThis option determines how the merge message will be cleaned up\n+\tbefore commiting or being passed on. See linkgit:git-commit[1] for more\n+\tdetails.\n+\n --ff::\n \tWhen the merge resolves as a fast-forward, only update the branch\n \tpointer, without creating a merge commit.  This is the default\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 5ce8946d39..4f5fcf5ce9 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -38,6 +38,7 @@\n #include \"tag.h\"\n #include \"alias.h\"\n #include \"commit-reach.h\"\n+#include \"wt-status.h\"\n \n #define DEFAULT_TWOHEAD (1<<0)\n #define DEFAULT_OCTOPUS (1<<1)\n@@ -98,6 +99,9 @@ enum ff_type {\n \n static enum ff_type fast_forward = FF_ALLOW;\n \n+static const char *cleanup_arg;\n+static enum commit_msg_cleanup_mode cleanup_mode;\n+\n static int option_parse_message(const struct option *opt,\n \t\t\t\tconst char *arg, int unset)\n {\n@@ -249,6 +253,7 @@ static struct option builtin_merge_options[] = {\n \t\tN_(\"perform a commit if the merge succeeds (default)\")),\n \tOPT_BOOL('e', \"edit\", &option_edit,\n \t\tN_(\"edit message before committing\")),\n+\tOPT_STRING(0, \"cleanup\", &cleanup_arg, N_(\"default\"), N_(\"how to strip spaces and #comments from message\")),\n \tOPT_SET_INT(0, \"ff\", &fast_forward, N_(\"allow fast-forward (default)\"), FF_ALLOW),\n \tOPT_SET_INT_F(0, \"ff-only\", &fast_forward,\n \t\t      N_(\"abort if fast-forward is not possible\"),\n@@ -612,6 +617,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n \t\treturn git_config_string(&pull_twohead, k, v);\n \telse if (!strcmp(k, \"pull.octopus\"))\n \t\treturn git_config_string(&pull_octopus, k, v);\n+\telse if (!strcmp(k, \"commit.cleanup\"))\n+\t\treturn git_config_string(&cleanup_arg, k, v);\n \telse if (!strcmp(k, \"merge.renormalize\"))\n \t\toption_renormalize = git_config_bool(k, v);\n \telse if (!strcmp(k, \"merge.ff\")) {\n@@ -797,23 +804,32 @@ static void abort_commit(struct commit_list *remoteheads, const char *err_msg)\n \texit(1);\n }\n \n+static const char comment_line_explanation[] =\n+N_(\"Lines starting with '%c' will be ignored.\\n\");\n+\n static const char merge_editor_comment[] =\n N_(\"Please enter a commit message to explain why this merge is necessary,\\n\"\n    \"especially if it merges an updated upstream into a topic branch.\\n\"\n    \"\\n\"\n-   \"Lines starting with '%c' will be ignored, and an empty message aborts\\n\"\n-   \"the commit.\\n\");\n+   \"An empty message aborts the commit.\\n\");\n \n static void write_merge_heads(struct commit_list *);\n static void prepare_to_commit(struct commit_list *remoteheads)\n {\n \tstruct strbuf msg = STRBUF_INIT;\n \tstrbuf_addbuf(&msg, &merge_msg);\n-\tstrbuf_addch(&msg, '\\n');\n \tif (squash)\n \t\tBUG(\"the control must not reach here under --squash\");\n-\tif (0 < option_edit)\n-\t\tstrbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);\n+\tif (0 < option_edit) {\n+\t\tstrbuf_addch(&msg, '\\n');\n+\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n+\t\t\twt_status_append_cut_line(&msg);\n+\t\telse\n+\t\t\tstrbuf_commented_addf(&msg, _(comment_line_explanation), comment_line_char);\n+\n+\t\tstrbuf_commented_addf(&msg, \"\\n\");\n+\t\tstrbuf_commented_addf(&msg, _(merge_editor_comment));\n+\t}\n \tif (signoff)\n \t\tappend_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);\n \twrite_merge_heads(remoteheads);\n@@ -832,7 +848,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)\n \t\tabort_commit(remoteheads, NULL);\n \n \tread_merge_msg(&msg);\n-\tstrbuf_stripspace(&msg, 0 < option_edit);\n+\tcleanup_message(&msg, cleanup_mode, 0);\n \tif (!msg.len)\n \t\tabort_commit(remoteheads, _(\"Empty commit message.\"));\n \tstrbuf_release(&merge_msg);\n@@ -880,7 +896,6 @@ static int finish_automerge(struct commit *head,\n \tparents = remoteheads;\n \tif (!head_subsumed || fast_forward == FF_NO)\n \t\tcommit_list_insert(head, &parents);\n-\tstrbuf_addch(&merge_msg, '\\n');\n \tprepare_to_commit(remoteheads);\n \tif (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,\n \t\t\t&result_commit, NULL, sign_commit))\n@@ -1389,6 +1404,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \tif (option_edit < 0)\n \t\toption_edit = default_edit_option();\n \n+\tcleanup_mode = get_cleanup_mode(cleanup_arg, 0 < option_edit, 1);\n+\n \tif (!use_strategies) {\n \t\tif (!remoteheads)\n \t\t\t; /* already up-to-date */\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 33db889955..3ec7882375 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -101,6 +101,7 @@ static char *opt_signoff;\n static char *opt_squash;\n static char *opt_commit;\n static char *opt_edit;\n+static char *opt_cleanup;\n static char *opt_ff;\n static char *opt_verify_signatures;\n static int opt_autostash = -1;\n@@ -168,6 +169,9 @@ static struct option pull_options[] = {\n \tOPT_PASSTHRU(0, \"edit\", &opt_edit, NULL,\n \t\tN_(\"edit message before committing\"),\n \t\tPARSE_OPT_NOARG),\n+\tOPT_PASSTHRU(0, \"cleanup\", &opt_cleanup, NULL,\n+\t\tN_(\"how to strip spaces and #comments from message\"),\n+\t\tPARSE_OPT_NOARG),\n \tOPT_PASSTHRU(0, \"ff\", &opt_ff, NULL,\n \t\tN_(\"allow fast-forward\"),\n \t\tPARSE_OPT_NOARG),\n@@ -644,6 +648,8 @@ static int run_merge(void)\n \t\targv_array_push(&args, opt_commit);\n \tif (opt_edit)\n \t\targv_array_push(&args, opt_edit);\n+\tif (opt_cleanup)\n+\t\targv_array_push(&args, opt_cleanup);\n \tif (opt_ff)\n \t\targv_array_push(&args, opt_ff);\n \tif (opt_verify_signatures)\ndiff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh\nindex 89619cf446..3fcd58eb2c 100755\n--- a/t/t7604-merge-custom-message.sh\n+++ b/t/t7604-merge-custom-message.sh\n@@ -47,4 +47,65 @@ test_expect_success 'merge --log appends to custom message' '\n \ttest_cmp exp.log actual\n '\n \n+mesg_with_comment_and_newlines='\n+# text\n+\n+'\n+\n+test_expect_success 'prepare file with comment line and trailing newlines'  '\n+\tprintf \"%s\" \"$mesg_with_comment_and_newlines\" >expect\n+'\n+\n+test_expect_success 'cleanup commit messages (verbatim option)' '\n+\tgit reset --hard c1 &&\n+\tgit merge --cleanup=verbatim -F expect c2 &&\n+\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'cleanup commit messages (whitespace option)' '\n+\tgit reset --hard c1 &&\n+\t{ echo;echo \"# text\";echo; } >text &&\n+\techo \"# text\" >expect &&\n+\tgit merge --cleanup=whitespace -F text c2 &&\n+\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\ttest_cmp expect actual\n+\n+'\n+\n+test_expect_success 'cleanup merge messages (scissors option)' '\n+\tgit reset --hard c1 &&\n+\tcat >text <<EOF &&\n+\n+# to be kept\n+\n+  # ------------------------ >8 ------------------------\n+# to be kept, too\n+# ------------------------ >8 ------------------------\n+to be removed\n+# ------------------------ >8 ------------------------\n+to be removed, too\n+EOF\n+\n+\tcat >expect <<EOF &&\n+# to be kept\n+\n+  # ------------------------ >8 ------------------------\n+# to be kept, too\n+EOF\n+\tgit merge --cleanup=scissors -e -F text c2 &&\n+\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'cleanup commit messages (strip option)' '\n+\tgit reset --hard c1 &&\n+\t{ echo;echo \"# text\";echo sample;echo; } >text &&\n+\techo sample >expect &&\n+\tgit merge --cleanup=strip -F text c2 &&\n+\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\ttest_cmp expect actual\n+\n+'\n+\n test_done\ndiff --git a/wt-status.c b/wt-status.c\nindex 445a36204a..b81fcd428d 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1006,13 +1006,19 @@ size_t wt_status_locate_end(const char *s, size_t len)\n \treturn len;\n }\n \n-void wt_status_add_cut_line(FILE *fp)\n+void wt_status_append_cut_line(struct strbuf *buf)\n {\n \tconst char *explanation = _(\"Do not modify or remove the line above.\\nEverything below it will be ignored.\");\n+\n+\tstrbuf_commented_addf(buf, \"%s\", cut_line);\n+\tstrbuf_add_commented_lines(buf, explanation, strlen(explanation));\n+}\n+\n+void wt_status_add_cut_line(FILE *fp)\n+{\n \tstruct strbuf buf = STRBUF_INIT;\n \n-\tfprintf(fp, \"%c %s\", comment_line_char, cut_line);\n-\tstrbuf_add_commented_lines(&buf, explanation, strlen(explanation));\n+\twt_status_append_cut_line(&buf);\n \tfputs(buf.buf, fp);\n \tstrbuf_release(&buf);\n }\ndiff --git a/wt-status.h b/wt-status.h\nindex 3a95975032..64f1ddc9fd 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -129,6 +129,7 @@ struct wt_status {\n };\n \n size_t wt_status_locate_end(const char *s, size_t len);\n+void wt_status_append_cut_line(struct strbuf *buf);\n void wt_status_add_cut_line(FILE *fp);\n void wt_status_prepare(struct repository *r, struct wt_status *s);\n void wt_status_print(struct wt_status *s);\n-- \n2.21.0.370.g4fdb13b891\n\n"},{"id":"371112","messageId":"b6aca21639eb96b65f201f4a3526d8d4a4180c59.1552275703.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1552275703.git.liu.denton@gmail.com","subject":"[PATCH v7 6/8] merge: add scissors line on merge conflict","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-11T03:42:43Z","receivedAt":"2019-03-11T03:42:48Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"This fixes a bug where the scissors line is placed after the Conflicts:\nsection, in the case where a merge conflict occurs and\ncommit.cleanup = scissors.\n\nNext, if commit.cleanup = scissors is specified, don't produce a\nscissors line in commit if one already exists in the MERGE_MSG file.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/merge-options.txt |  4 ++-\n builtin/commit.c                | 20 ++++++++++----\n builtin/merge.c                 | 14 ++++++++++\n t/t7600-merge.sh                | 46 +++++++++++++++++++++++++++++++++\n 4 files changed, 78 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 646100ea9a..c7b889d6f5 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -35,7 +35,9 @@ set to `no` at the beginning of them.\n --cleanup=<mode>::\n \tThis option determines how the merge message will be cleaned up\n \tbefore commiting or being passed on. See linkgit:git-commit[1] for more\n-\tdetails.\n+\tdetails. In addition, if the '<mode>' is given a value of `scissors`,\n+\tscissors will be appended to MERGE_MSG before being passed on in the\n+\tcase of a merge conflict.\n \n --ff::\n \tWhen the merge resolves as a fast-forward, only update the branch\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 81e3bd21ca..d8c4626a68 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -668,6 +668,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \tconst char *hook_arg2 = NULL;\n \tint clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);\n \tint old_display_comment_prefix;\n+\tint merge_contains_scissors = 0;\n \n \t/* This checks and barfs if author is badly specified */\n \tdetermine_author_info(author_ident);\n@@ -728,6 +729,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\tstrbuf_addbuf(&sb, &message);\n \t\thook_arg1 = \"message\";\n \t} else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {\n+\t\tsize_t merge_msg_start;\n+\n \t\t/*\n \t\t * prepend SQUASH_MSG here if it exists and a\n \t\t * \"merge --squash\" was originally performed\n@@ -738,8 +741,14 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\thook_arg1 = \"squash\";\n \t\t} else\n \t\t\thook_arg1 = \"merge\";\n+\n+\t\tmerge_msg_start = sb.len;\n \t\tif (strbuf_read_file(&sb, git_path_merge_msg(the_repository), 0) < 0)\n \t\t\tdie_errno(_(\"could not read MERGE_MSG\"));\n+\n+\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\t    wt_status_locate_end(sb.buf + merge_msg_start, sb.len - merge_msg_start) < sb.len - merge_msg_start)\n+\t\t\tmerge_contains_scissors = 1;\n \t} else if (!stat(git_path_squash_msg(the_repository), &statbuf)) {\n \t\tif (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)\n \t\t\tdie_errno(_(\"could not read SQUASH_MSG\"));\n@@ -807,7 +816,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\tstruct ident_split ci, ai;\n \n \t\tif (whence != FROM_COMMIT) {\n-\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n+\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\t\t\t!merge_contains_scissors)\n \t\t\t\twt_status_add_cut_line(s->fp);\n \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL,\n \t\t\t    whence == FROM_MERGE\n@@ -832,10 +842,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\nwith '%c' will be ignored, and an empty\"\n \t\t\t\t  \" message aborts the commit.\\n\"), comment_line_char);\n-\t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n-\t\t\t whence == FROM_COMMIT)\n-\t\t\twt_status_add_cut_line(s->fp);\n-\t\telse /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n+\t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {\n+\t\t\tif (whence == FROM_COMMIT && !merge_contains_scissors)\n+\t\t\t\twt_status_add_cut_line(s->fp);\n+\t\t} else /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n \t\t\tstatus_printf(s, GIT_COLOR_NORMAL,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\n\"\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 4f5fcf5ce9..b620d4bfcb 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -916,6 +916,20 @@ static int suggest_conflicts(void)\n \tfilename = git_path_merge_msg(the_repository);\n \tfp = xfopen(filename, \"a\");\n \n+\t/*\n+\t * We can't use cleanup_mode because if we're not using the editor,\n+\t * get_cleanup_mode will return COMMIT_MSG_CLEANUP_SPACE instead, even\n+\t * though the message is meant to be processed later by git-commit.\n+\t * Thus, we will get the cleanup mode which is returned when we _are_ using\n+\t * an editor.\n+\t */\n+\tif (get_cleanup_mode(cleanup_arg, 1, 1) == COMMIT_MSG_CLEANUP_SCISSORS) {\n+\t    fputc('\\n', fp);\n+\t    wt_status_add_cut_line(fp);\n+\t    /* comments out the newline from append_conflicts_hint */\n+\t    fputc(comment_line_char, fp);\n+\t}\n+\n \tappend_conflicts_hint(&the_index, &msgbuf);\n \tfputs(msgbuf.buf, fp);\n \tstrbuf_release(&msgbuf);\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex d879efd330..2cb57e5878 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -246,6 +246,52 @@ test_expect_success 'merge --squash c3 with c7' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'merge c3 with c7 with commit.cleanup = scissors' '\n+\tgit config commit.cleanup scissors &&\n+\tgit reset --hard c3 &&\n+\ttest_must_fail git merge c7 &&\n+\tcat result.9z >file &&\n+\tgit commit --no-edit -a &&\n+\n+\tcat >expect <<-EOF &&\n+\t\tMerge tag '\"'\"'c7'\"'\"'\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfile\n+\t\tEOF\n+\tgit cat-file commit HEAD >tmp &&\n+\tsed -e '1,/^$/d' <tmp >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n+test_expect_success 'merge c3 with c7 with --squash commit.cleanup = scissors' '\n+\tgit config commit.cleanup scissors &&\n+\tgit reset --hard c3 &&\n+\ttest_must_fail git merge --squash c7 &&\n+\tcat result.9z >file &&\n+\tgit commit --no-edit -a &&\n+\n+\tcat >expect <<-EOF &&\n+\t\tSquashed commit of the following:\n+\n+\t\t$(git show -s c7)\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfile\n+\t\tEOF\n+\tgit cat-file commit HEAD >tmp &&\n+\tsed -e '1,/^$/d' <tmp >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n test_debug 'git log --graph --decorate --oneline --all'\n \n test_expect_success 'merge c1 with c2 and c3' '\n-- \n2.21.0.370.g4fdb13b891\n\n"},{"id":"371113","messageId":"ced1df0fc76c97c9896b6cbb5b4154532461716d.1552275703.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1552275703.git.liu.denton@gmail.com","subject":"[PATCH v7 7/8] sequencer.c: define get_config_from_cleanup","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-11T03:42:46Z","receivedAt":"2019-03-11T03:42:51Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Define a function which allows us to get the string configuration value\nof a enum commit_msg_cleanup_mode. This is done by refactoring\nget_cleanup_mode such that it uses a lookup table to find the mappings\nbetween string and enum and then using the same LUT in reverse to define\nget_config_from_cleanup.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 67 +++++++++++++++++++++++++++++++++++++++++------------\n sequencer.h |  1 +\n 2 files changed, 53 insertions(+), 15 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 612621f221..5d94e2c865 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -160,6 +160,22 @@ static GIT_PATH_FUNC(rebase_path_strategy_opts, \"rebase-merge/strategy_opts\")\n static GIT_PATH_FUNC(rebase_path_allow_rerere_autoupdate, \"rebase-merge/allow_rerere_autoupdate\")\n static GIT_PATH_FUNC(rebase_path_reschedule_failed_exec, \"rebase-merge/reschedule-failed-exec\")\n \n+struct cleanup_config_mapping {\n+    const char *config_value;\n+    enum commit_msg_cleanup_mode editor_cleanup;\n+    enum commit_msg_cleanup_mode no_editor_cleanup;\n+};\n+\n+/* note that we assume that cleanup_config_mapping[0] contains the default settings */\n+struct cleanup_config_mapping cleanup_config_mappings[] = {\n+\t{ \"default\", COMMIT_MSG_CLEANUP_ALL, COMMIT_MSG_CLEANUP_SPACE },\n+\t{ \"verbatim\", COMMIT_MSG_CLEANUP_NONE, COMMIT_MSG_CLEANUP_NONE },\n+\t{ \"whitespace\", COMMIT_MSG_CLEANUP_SPACE, COMMIT_MSG_CLEANUP_SPACE },\n+\t{ \"strip\", COMMIT_MSG_CLEANUP_ALL, COMMIT_MSG_CLEANUP_ALL },\n+\t{ \"scissors\", COMMIT_MSG_CLEANUP_SCISSORS, COMMIT_MSG_CLEANUP_SPACE },\n+\t{ NULL, 0, 0 }\n+};\n+\n static int git_sequencer_config(const char *k, const char *v, void *cb)\n {\n \tstruct replay_opts *opts = cb;\n@@ -504,26 +520,42 @@ static int fast_forward_to(struct repository *r,\n enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n \tint use_editor, int die_on_error)\n {\n-\tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n-\t\treturn use_editor ? COMMIT_MSG_CLEANUP_ALL :\n-\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n-\telse if (!strcmp(cleanup_arg, \"verbatim\"))\n-\t\treturn COMMIT_MSG_CLEANUP_NONE;\n-\telse if (!strcmp(cleanup_arg, \"whitespace\"))\n-\t\treturn COMMIT_MSG_CLEANUP_SPACE;\n-\telse if (!strcmp(cleanup_arg, \"strip\"))\n-\t\treturn COMMIT_MSG_CLEANUP_ALL;\n-\telse if (!strcmp(cleanup_arg, \"scissors\"))\n-\t\treturn use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n-\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n-\telse if (!die_on_error) {\n+\tstruct cleanup_config_mapping *default_mapping = &cleanup_config_mappings[0];\n+\tstruct cleanup_config_mapping *current_mapping;\n+\n+\tif (!cleanup_arg) {\n+\t\treturn use_editor ? default_mapping->editor_cleanup :\n+\t\t\t\t    default_mapping->no_editor_cleanup;\n+\t}\n+\n+\tfor (current_mapping = cleanup_config_mappings; current_mapping->config_value; current_mapping++) {\n+\t\tif (!strcmp(cleanup_arg, current_mapping->config_value)) {\n+\t\t\treturn use_editor ? current_mapping->editor_cleanup :\n+\t\t\t\t\t    current_mapping->no_editor_cleanup;\n+\t\t}\n+\t}\n+\n+\tif (!die_on_error) {\n \t\twarning(_(\"Invalid cleanup mode %s, falling back to default\"), cleanup_arg);\n-\t\treturn use_editor ? COMMIT_MSG_CLEANUP_ALL :\n-\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n+\t\treturn use_editor ? default_mapping->editor_cleanup :\n+\t\t\t\t    default_mapping->no_editor_cleanup;\n \t} else\n \t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n }\n \n+const char *get_config_from_cleanup(enum commit_msg_cleanup_mode cleanup_mode)\n+{\n+\tstruct cleanup_config_mapping *current_mapping;\n+\n+\tfor (current_mapping = &cleanup_config_mappings[1]; current_mapping->config_value; current_mapping++) {\n+\t\tif (cleanup_mode == current_mapping->editor_cleanup) {\n+\t\t\treturn current_mapping->config_value;\n+\t\t}\n+\t}\n+\n+\tBUG(_(\"invalid cleanup_mode provided\"));\n+}\n+\n void append_conflicts_hint(struct index_state *istate,\n \t\t\t   struct strbuf *msgbuf)\n {\n@@ -2350,6 +2382,8 @@ static int populate_opts_cb(const char *key, const char *value, void *data)\n \t\topts->allow_rerere_auto =\n \t\t\tgit_config_bool_or_int(key, value, &error_flag) ?\n \t\t\t\tRERERE_AUTOUPDATE : RERERE_NOAUTOUPDATE;\n+\telse if (!strcmp(key, \"options.default-msg-cleanup\"))\n+\t\topts->default_msg_cleanup = get_cleanup_mode(value, 1, 1);\n \telse\n \t\treturn error(_(\"invalid key: %s\"), key);\n \n@@ -2754,6 +2788,9 @@ static int save_opts(struct replay_opts *opts)\n \t\tres |= git_config_set_in_file_gently(opts_file, \"options.allow-rerere-auto\",\n \t\t\t\t\t\t     opts->allow_rerere_auto == RERERE_AUTOUPDATE ?\n \t\t\t\t\t\t     \"true\" : \"false\");\n+\n+\tres |= git_config_set_in_file_gently(opts_file, \"options.default-msg-cleanup\",\n+\t\t\t\t\t     get_config_from_cleanup(opts->default_msg_cleanup));\n \treturn res;\n }\n \ndiff --git a/sequencer.h b/sequencer.h\nindex e7908f558e..e3c1f44807 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -118,6 +118,7 @@ void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);\n void append_conflicts_hint(struct index_state *istate, struct strbuf *msgbuf);\n enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n \tint use_editor, int die_on_error);\n+const char *get_config_from_cleanup(enum commit_msg_cleanup_mode cleanup_mode);\n \n void cleanup_message(struct strbuf *msgbuf,\n \tenum commit_msg_cleanup_mode cleanup_mode, int verbose);\n-- \n2.21.0.370.g4fdb13b891\n\n"},{"id":"371114","messageId":"5b30d939674542b2cfeeeef8341ab25c7d6c0543.1552275703.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1552275703.git.liu.denton@gmail.com","subject":"[PATCH v7 8/8] cherry-pick/revert: add scissors line on merge conflict","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-11T03:42:49Z","receivedAt":"2019-03-11T03:42:54Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Fix a bug where the scissors line is placed after the Conflicts:\nsection, in the case where a merge conflict occurs and\ncommit.cleanup = scissors.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/git-cherry-pick.txt |  7 +++\n Documentation/git-revert.txt      |  7 +++\n builtin/merge.c                   |  9 +---\n builtin/rebase--interactive.c     |  2 +-\n builtin/revert.c                  |  5 ++\n sequencer.c                       | 22 +++++---\n sequencer.h                       |  3 +-\n t/t3507-cherry-pick-conflict.sh   | 86 +++++++++++++++++++++++++++++++\n 8 files changed, 123 insertions(+), 18 deletions(-)\n\ndiff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt\nindex b8cfeec67e..b4ff8e136d 100644\n--- a/Documentation/git-cherry-pick.txt\n+++ b/Documentation/git-cherry-pick.txt\n@@ -57,6 +57,13 @@ OPTIONS\n \tWith this option, 'git cherry-pick' will let you edit the commit\n \tmessage prior to committing.\n \n+--cleanup=<mode>::\n+\tThis option determines how the commit message will be cleaned up before\n+\tbeing passed on. See linkgit:git-commit[1] for more details. In\n+\taddition, if the '<mode>' is given a value of `scissors`, scissors will\n+\tbe appended to MERGE_MSG before being passed on in the case of a\n+\tconflict.\n+\n -x::\n \tWhen recording the commit, append a line that says\n \t\"(cherry picked from commit ...)\" to the original commit\ndiff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt\nindex 837707a8fd..bd4ad395a9 100644\n--- a/Documentation/git-revert.txt\n+++ b/Documentation/git-revert.txt\n@@ -66,6 +66,13 @@ more details.\n \tWith this option, 'git revert' will not start the commit\n \tmessage editor.\n \n+--cleanup=<mode>::\n+\tThis option determines how the commit message will be cleaned up before\n+\tbeing passed on. See linkgit:git-commit[1] for more details. In\n+\taddition, if the '<mode>' is given a value of `scissors`, scissors will\n+\tbe appended to MERGE_MSG before being passed on in the case of a\n+\tconflict.\n+\n -n::\n --no-commit::\n \tUsually the command automatically creates some commits with\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex b620d4bfcb..671d6a19a4 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -923,14 +923,7 @@ static int suggest_conflicts(void)\n \t * Thus, we will get the cleanup mode which is returned when we _are_ using\n \t * an editor.\n \t */\n-\tif (get_cleanup_mode(cleanup_arg, 1, 1) == COMMIT_MSG_CLEANUP_SCISSORS) {\n-\t    fputc('\\n', fp);\n-\t    wt_status_add_cut_line(fp);\n-\t    /* comments out the newline from append_conflicts_hint */\n-\t    fputc(comment_line_char, fp);\n-\t}\n-\n-\tappend_conflicts_hint(&the_index, &msgbuf);\n+\tappend_conflicts_hint(&the_index, &msgbuf, get_cleanup_mode(cleanup_arg, 1, 1));\n \tfputs(msgbuf.buf, fp);\n \tstrbuf_release(&msgbuf);\n \tfclose(fp);\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 888390f911..cf2151b271 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -199,10 +199,10 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n+\topts.action = REPLAY_INTERACTIVE_REBASE;\n \tsequencer_init_config(&opts);\n \tgit_config_get_bool(\"rebase.abbreviatecommands\", &abbreviate_commands);\n \n-\topts.action = REPLAY_INTERACTIVE_REBASE;\n \topts.allow_ff = 1;\n \topts.allow_empty = 1;\n \ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex a47b53ceaf..e8168e0214 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -96,11 +96,13 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)\n {\n \tconst char * const * usage_str = revert_or_cherry_pick_usage(opts);\n \tconst char *me = action_name(opts);\n+\tconst char *cleanup_arg = NULL;\n \tint cmd = 0;\n \tstruct option base_options[] = {\n \t\tOPT_CMDMODE(0, \"quit\", &cmd, N_(\"end revert or cherry-pick sequence\"), 'q'),\n \t\tOPT_CMDMODE(0, \"continue\", &cmd, N_(\"resume revert or cherry-pick sequence\"), 'c'),\n \t\tOPT_CMDMODE(0, \"abort\", &cmd, N_(\"cancel revert or cherry-pick sequence\"), 'a'),\n+\t\tOPT_STRING(0, \"cleanup\", &cleanup_arg, N_(\"default\"), N_(\"how to strip spaces and #comments from message\")),\n \t\tOPT_BOOL('n', \"no-commit\", &opts->no_commit, N_(\"don't automatically commit\")),\n \t\tOPT_BOOL('e', \"edit\", &opts->edit, N_(\"edit the commit message\")),\n \t\tOPT_NOOP_NOARG('r', NULL),\n@@ -137,6 +139,9 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)\n \tif (opts->keep_redundant_commits)\n \t\topts->allow_empty = 1;\n \n+\tif (cleanup_arg)\n+\t\topts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1, 1);\n+\n \t/* Check for incompatible command line arguments */\n \tif (cmd) {\n \t\tchar *this_operation;\ndiff --git a/sequencer.c b/sequencer.c\nindex 5d94e2c865..496554a474 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -176,6 +176,11 @@ struct cleanup_config_mapping cleanup_config_mappings[] = {\n \t{ NULL, 0, 0 }\n };\n \n+static inline int is_rebase_i(const struct replay_opts *opts)\n+{\n+\treturn opts->action == REPLAY_INTERACTIVE_REBASE;\n+}\n+\n static int git_sequencer_config(const char *k, const char *v, void *cb)\n {\n \tstruct replay_opts *opts = cb;\n@@ -188,7 +193,7 @@ static int git_sequencer_config(const char *k, const char *v, void *cb)\n \t\tif (status)\n \t\t\treturn status;\n \n-\t\topts->default_msg_cleanup = get_cleanup_mode(s, 0, 0);\n+\t\topts->default_msg_cleanup = get_cleanup_mode(s, !is_rebase_i(opts), 0);\n \n \t\tfree((char *)s);\n \t\treturn status;\n@@ -212,11 +217,6 @@ void sequencer_init_config(struct replay_opts *opts)\n \tgit_config(git_sequencer_config, opts);\n }\n \n-static inline int is_rebase_i(const struct replay_opts *opts)\n-{\n-\treturn opts->action == REPLAY_INTERACTIVE_REBASE;\n-}\n-\n static const char *get_dir(const struct replay_opts *opts)\n {\n \tif (is_rebase_i(opts))\n@@ -557,10 +557,16 @@ const char *get_config_from_cleanup(enum commit_msg_cleanup_mode cleanup_mode)\n }\n \n void append_conflicts_hint(struct index_state *istate,\n-\t\t\t   struct strbuf *msgbuf)\n+\t\tstruct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode)\n {\n \tint i;\n \n+\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {\n+\t\tstrbuf_addch(msgbuf, '\\n');\n+\t\twt_status_append_cut_line(msgbuf);\n+\t\tstrbuf_addch(msgbuf, comment_line_char);\n+\t}\n+\n \tstrbuf_addch(msgbuf, '\\n');\n \tstrbuf_commented_addf(msgbuf, \"Conflicts:\\n\");\n \tfor (i = 0; i < istate->cache_nr;) {\n@@ -628,7 +634,7 @@ static int do_recursive_merge(struct repository *r,\n \t\t\t_(action_name(opts)));\n \n \tif (!clean)\n-\t\tappend_conflicts_hint(r->index, msgbuf);\n+\t\tappend_conflicts_hint(r->index, msgbuf, opts->default_msg_cleanup);\n \n \treturn !clean;\n }\ndiff --git a/sequencer.h b/sequencer.h\nindex e3c1f44807..95ff9db508 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -115,7 +115,8 @@ int rearrange_squash(struct repository *r);\n  */\n void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);\n \n-void append_conflicts_hint(struct index_state *istate, struct strbuf *msgbuf);\n+void append_conflicts_hint(struct index_state *istate,\n+\t\tstruct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode);\n enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n \tint use_editor, int die_on_error);\n const char *get_config_from_cleanup(enum commit_msg_cleanup_mode cleanup_mode);\ndiff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh\nindex 74ff925526..c3894ca9d6 100755\n--- a/t/t3507-cherry-pick-conflict.sh\n+++ b/t/t3507-cherry-pick-conflict.sh\n@@ -189,6 +189,46 @@ test_expect_success 'failed cherry-pick registers participants in index' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success \\\n+\t'cherry-pick conflict, ensure commit.cleanup = scissors places scissors line properly' '\n+\tpristine_detach initial &&\n+\tgit config commit.cleanup scissors &&\n+\tcat <<-EOF >expected &&\n+\t\tpicked\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfoo\n+\t\tEOF\n+\n+\ttest_must_fail git cherry-pick picked &&\n+\n+\ttest_i18ncmp expected .git/MERGE_MSG\n+'\n+\n+test_expect_success \\\n+\t'cherry-pick conflict, ensure cleanup=scissors places scissors line properly' '\n+\tpristine_detach initial &&\n+\tgit config --unset commit.cleanup &&\n+\tcat <<-EOF >expected &&\n+\t\tpicked\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfoo\n+\t\tEOF\n+\n+\ttest_must_fail git cherry-pick --cleanup=scissors picked &&\n+\n+\ttest_i18ncmp expected .git/MERGE_MSG\n+'\n+\n test_expect_success 'failed cherry-pick describes conflict in work tree' '\n \tpristine_detach initial &&\n \tcat <<-EOF >expected &&\n@@ -335,6 +375,52 @@ test_expect_success 'revert conflict, diff3 -m style' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success \\\n+\t'revert conflict, ensure commit.cleanup = scissors places scissors line properly' '\n+\tpristine_detach initial &&\n+\tgit config commit.cleanup scissors &&\n+\tcat >expected <<-EOF &&\n+\t\tRevert \"picked\"\n+\n+\t\tThis reverts commit OBJID.\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfoo\n+\t\tEOF\n+\n+\ttest_must_fail git revert picked &&\n+\n+\tsed \"s/$OID_REGEX/OBJID/\" .git/MERGE_MSG >actual &&\n+\ttest_i18ncmp expected actual\n+'\n+\n+test_expect_success \\\n+\t'revert conflict, ensure cleanup=scissors places scissors line properly' '\n+\tpristine_detach initial &&\n+\tgit config --unset commit.cleanup &&\n+\tcat >expected <<-EOF &&\n+\t\tRevert \"picked\"\n+\n+\t\tThis reverts commit OBJID.\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfoo\n+\t\tEOF\n+\n+\ttest_must_fail git revert --cleanup=scissors picked &&\n+\n+\tsed \"s/$OID_REGEX/OBJID/\" .git/MERGE_MSG >actual &&\n+\ttest_i18ncmp expected actual\n+'\n+\n test_expect_success 'failed cherry-pick does not forget -s' '\n \tpristine_detach initial &&\n \ttest_must_fail git cherry-pick -s picked &&\n-- \n2.21.0.370.g4fdb13b891\n\n"},{"id":"371118","messageId":"CAPig+cS5YsjxuLGNAtfFguQvy2p2bJECSsHksx356+WsO1ct5w@mail.gmail.com","threadId":"49830","inReplyTo":"b2b82954e7060a3f5d47f77e4743493ff4256bd6.1552275703.git.liu.denton@gmail.com","subject":"Re: [PATCH v7 5/8] merge: cleanup messages like commit","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-03-11T05:49:23Z","receivedAt":"2019-03-11T05:49:37Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Mar 10, 2019 at 11:42 PM Denton Liu <liu.denton@gmail.com> wrote:\n> This change allows git-merge messages to be cleaned up with the\n> commit.cleanup configuration or --cleanup option, just like how\n> git-commit does it.\n>\n> We also give git-pull the passthrough option of --cleanup so that it can\n> also take advantage of this change.\n>\n> Finally, add testing to ensure that messages are properly cleaned up.\n> Note that some newlines that were added to the commit message were\n> removed so that if a file were read via -F, it would be copied\n> faithfully.\n>\n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n> diff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh\n> @@ -47,4 +47,65 @@ test_expect_success 'merge --log appends to custom message' '\n> +test_expect_success 'cleanup commit messages (verbatim option)' '\n> +       git reset --hard c1 &&\n> +       git merge --cleanup=verbatim -F expect c2 &&\n> +       git cat-file -p HEAD |sed -e \"1,/^\\$/d\" >actual &&\n\nAn earlier patch in this series \"fixed\" a test with a Git command\nupstream of a pipe. Yet, this test adds such an instance. (Also,\nstyle: add space after '|'.)\n\n> +       test_cmp expect actual\n> +'\n> +\n> +test_expect_success 'cleanup commit messages (whitespace option)' '\n> +       git reset --hard c1 &&\n> +       { echo;echo \"# text\";echo; } >text &&\n\nStyle: add space after semicolon or use &&-chaining inside {...}.\n\nAlternately, less ugly:\n\n    test_write_lines \"\" \"# text\" \"\" >text &&\n\n(Or even a here-doc, though the leading and trailing blank lines make\nthe here-doc ugly.)\n\n> +       echo \"# text\" >expect &&\n> +       git merge --cleanup=whitespace -F text c2 &&\n> +       git cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n\nGit upstream pipe.\n\n> +       test_cmp expect actual\n> +\n> +'\n\nStyle: drop the blank line before the closing quote.\n\n> +test_expect_success 'cleanup merge messages (scissors option)' '\n> +       git reset --hard c1 &&\n> +       cat >text <<EOF &&\n\nThis here-doc probably ought to be using '-' and '\\', so:\n\n    cat >text <<-\\EOF &&\n\nand indent the here-doc body.\n\n> +# to be kept\n> +\n> +  # ------------------------ >8 ------------------------\n> +# to be kept, too\n> +# ------------------------ >8 ------------------------\n> +to be removed\n> +# ------------------------ >8 ------------------------\n> +to be removed, too\n> +EOF\n> +\n> +       cat >expect <<EOF &&\n\nDitto: <<-\\EOF and indent body.\n\n> +# to be kept\n> +\n> +  # ------------------------ >8 ------------------------\n> +# to be kept, too\n> +EOF\n> +       git merge --cleanup=scissors -e -F text c2 &&\n> +       git cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n\nGit upstream pipe.\n\n> +       test_cmp expect actual\n> +'\n> +\n> +test_expect_success 'cleanup commit messages (strip option)' '\n> +       git reset --hard c1 &&\n> +       { echo;echo \"# text\";echo sample;echo; } >text &&\n\ntest_write_lines \"\" \"# text\" sample \"\" >text &&\n\n> +       echo sample >expect &&\n> +       git merge --cleanup=strip -F text c2 &&\n> +       git cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n\nGit upstream pipe.\n\n> +       test_cmp expect actual\n> +\n> +'\n\nDrop blank line before closing quote.\n"},{"id":"371119","messageId":"CAPig+cTV8MeugunLLqS4cL-6c5X=xxXTRMV1EB8Nf3i-SapGfQ@mail.gmail.com","threadId":"49830","inReplyTo":"b6aca21639eb96b65f201f4a3526d8d4a4180c59.1552275703.git.liu.denton@gmail.com","subject":"Re: [PATCH v7 6/8] merge: add scissors line on merge conflict","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-03-11T05:55:12Z","receivedAt":"2019-03-11T05:55:26Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Mar 10, 2019 at 11:42 PM Denton Liu <liu.denton@gmail.com> wrote:\n> This fixes a bug where the scissors line is placed after the Conflicts:\n> section, in the case where a merge conflict occurs and\n> commit.cleanup = scissors.\n>\n> Next, if commit.cleanup = scissors is specified, don't produce a\n> scissors line in commit if one already exists in the MERGE_MSG file.\n>\n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n> diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\n> @@ -246,6 +246,52 @@ test_expect_success 'merge --squash c3 with c7' '\n> +test_expect_success 'merge c3 with c7 with commit.cleanup = scissors' '\n> +       git config commit.cleanup scissors &&\n> +       git reset --hard c3 &&\n> +       test_must_fail git merge c7 &&\n> +       cat result.9z >file &&\n> +       git commit --no-edit -a &&\n> +\n> +       cat >expect <<-EOF &&\n\nUse <<-\\EOF here.\n\n> +               Merge tag '\"'\"'c7'\"'\"'\n> +\n> +               # ------------------------ >8 ------------------------\n> +               # Do not modify or remove the line above.\n> +               # Everything below it will be ignored.\n> +               #\n> +               # Conflicts:\n> +               #       file\n> +               EOF\n\nStyle: here-doc body is normally indented to the same level as the\ncommand which starts the here-doc, so this body is indented too much.\n\nSame comments apply to other new test added by this patch.\n\n> +       git cat-file commit HEAD >tmp &&\n> +       sed -e '1,/^$/d' <tmp >actual &&\n> +       test_i18ncmp expect actual\n> +'\n"},{"id":"371120","messageId":"CAPig+cQGvmi-237s1Leb1NpOMA0683PkCG4HBCKn_+x5YDHnCQ@mail.gmail.com","threadId":"49830","inReplyTo":"ced1df0fc76c97c9896b6cbb5b4154532461716d.1552275703.git.liu.denton@gmail.com","subject":"Re: [PATCH v7 7/8] sequencer.c: define get_config_from_cleanup","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-03-11T06:19:00Z","receivedAt":"2019-03-11T06:19:15Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Mar 10, 2019 at 11:42 PM Denton Liu <liu.denton@gmail.com> wrote:\n> Define a function which allows us to get the string configuration value\n> of a enum commit_msg_cleanup_mode. This is done by refactoring\n> get_cleanup_mode such that it uses a lookup table to find the mappings\n> between string and enum and then using the same LUT in reverse to define\n> get_config_from_cleanup.\n\nAside from a missing 'static', the comments below are mostly style\nsuggestions to make the new code less noisy. The basic idea is to\nreduce the \"wordiness\" of the code so that the eye can glide over it\nmore easily, thus allowing the reader to grasp its meaning \"at a\nglance\", without necessarily having to read it attentively. You may or\nmay not consider the suggestions actionable.\n\n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n> diff --git a/sequencer.c b/sequencer.c\n> @@ -160,6 +160,22 @@ static GIT_PATH_FUNC(rebase_path_strategy_opts, \"rebase-merge/strategy_opts\")\n> +struct cleanup_config_mapping {\n> +    const char *config_value;\n> +    enum commit_msg_cleanup_mode editor_cleanup;\n> +    enum commit_msg_cleanup_mode no_editor_cleanup;\n> +};\n\nThese members are already inside a struct named\n\"cleanup_config_mapping\", so we can drop some of the wordiness from\nthe member names. For instance:\n\n    config --or-- value --or-- val\n    editor\n    no_editor\n\n> +/* note that we assume that cleanup_config_mapping[0] contains the default settings */\n> +struct cleanup_config_mapping cleanup_config_mappings[] = {\n> +       { \"default\", COMMIT_MSG_CLEANUP_ALL, COMMIT_MSG_CLEANUP_SPACE },\n> +       { \"verbatim\", COMMIT_MSG_CLEANUP_NONE, COMMIT_MSG_CLEANUP_NONE },\n> +       { \"whitespace\", COMMIT_MSG_CLEANUP_SPACE, COMMIT_MSG_CLEANUP_SPACE },\n> +       { \"strip\", COMMIT_MSG_CLEANUP_ALL, COMMIT_MSG_CLEANUP_ALL },\n> +       { \"scissors\", COMMIT_MSG_CLEANUP_SCISSORS, COMMIT_MSG_CLEANUP_SPACE },\n> +       { NULL, 0, 0 }\n> +};\n\nThis table should be 'static'.\n\n> @@ -504,26 +520,42 @@ static int fast_forward_to(struct repository *r,\n>  enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n>         int use_editor, int die_on_error)\n>  {\n> +       struct cleanup_config_mapping *default_mapping = &cleanup_config_mappings[0];\n> +       struct cleanup_config_mapping *current_mapping;\n\nWe can shorten these two variable names to \"def\" and \"p\",\nrespectively, without losing clarity; there are only two variables in\nthe function, so it's not difficult to remember what they are. More\nimportantly, the rest of the code becomes considerably shorter,\nallowing the eye to glide over it while easily taking in its meaning\nrather than having to spend time actively reading it.\n\n> +       if (!cleanup_arg) {\n> +               return use_editor ? default_mapping->editor_cleanup :\n> +                                   default_mapping->no_editor_cleanup;\n> +       }\n> +\n> +       for (current_mapping = cleanup_config_mappings; current_mapping->config_value; current_mapping++) {\n> +               if (!strcmp(cleanup_arg, current_mapping->config_value)) {\n> +                       return use_editor ? current_mapping->editor_cleanup :\n> +                                           current_mapping->no_editor_cleanup;\n> +               }\n> +       }\n\nFor instance, with the shorter names, the above loop (while also\ndropping unnecessary braces) becomes:\n\n    for (p = cleanup_config_mappings; p->val; p++)\n        if (!strcmp(cleanup_arg, p->val))\n            return use_editor ? p->editor : p->no_editor;\n\n> +       if (!die_on_error) {\n>                 warning(_(\"Invalid cleanup mode %s, falling back to default\"), cleanup_arg);\n> -               return use_editor ? COMMIT_MSG_CLEANUP_ALL :\n> -                                   COMMIT_MSG_CLEANUP_SPACE;\n> +               return use_editor ? default_mapping->editor_cleanup :\n> +                                   default_mapping->no_editor_cleanup;\n>         } else\n>                 die(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n>  }\n\nSame comments apply to other new code introduced by this patch.\n\n> +const char *get_config_from_cleanup(enum commit_msg_cleanup_mode cleanup_mode)\n> +{\n> +       struct cleanup_config_mapping *current_mapping;\n> +\n> +       for (current_mapping = &cleanup_config_mappings[1]; current_mapping->config_value; current_mapping++) {\n> +               if (cleanup_mode == current_mapping->editor_cleanup) {\n> +                       return current_mapping->config_value;\n> +               }\n> +       }\n> +\n> +       BUG(_(\"invalid cleanup_mode provided\"));\n\nDon't localize BUG() messages; they are intended only for developer\neyes, not end-users. So:\n\n    BUG(\"invalid cleanup_mode provided\");\n\n> +}\n> diff --git a/sequencer.h b/sequencer.h\n> @@ -118,6 +118,7 @@ void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);\n>  enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n>         int use_editor, int die_on_error);\n> +const char *get_config_from_cleanup(enum commit_msg_cleanup_mode cleanup_mode);\n\nA more intuitive name might be describe_cleanup_mode().\n"},{"id":"371123","messageId":"xmqqpnqxkj0g.fsf@gitster-ct.c.googlers.com","threadId":"49830","inReplyTo":"cover.1552275703.git.liu.denton@gmail.com","subject":"Re: [PATCH v7 0/8] Fix scissors bug during conflict","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-11T06:44:15Z","receivedAt":"2019-03-11T06:44:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Denton Liu <liu.denton@gmail.com> writes:\n\n> This is a complete replacement for dl/merge-cleanup-scissors-fix.\n\nThanks; will take a look and requeue.\n"},{"id":"371138","messageId":"2e10d78a-d92c-76c1-abc3-49ad1be55779@gmail.com","threadId":"49830","inReplyTo":"CAPig+cS5YsjxuLGNAtfFguQvy2p2bJECSsHksx356+WsO1ct5w@mail.gmail.com","subject":"Re: [PATCH v7 5/8] merge: cleanup messages like commit","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-03-11T10:14:46Z","receivedAt":"2019-03-11T10:14:52Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton/Eric\n\nOn 11/03/2019 05:49, Eric Sunshine wrote:\n> On Sun, Mar 10, 2019 at 11:42 PM Denton Liu <liu.denton@gmail.com> wrote:\n>> This change allows git-merge messages to be cleaned up with the\n>> commit.cleanup configuration or --cleanup option, just like how\n>> git-commit does it.\n>>\n>> We also give git-pull the passthrough option of --cleanup so that it can\n>> also take advantage of this change.\n>>\n>> Finally, add testing to ensure that messages are properly cleaned up.\n>> Note that some newlines that were added to the commit message were\n>> removed so that if a file were read via -F, it would be copied\n>> faithfully.\n>>\n>> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n>> ---\n>> diff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh\n>> @@ -47,4 +47,65 @@ test_expect_success 'merge --log appends to custom message' '\n>> +test_expect_success 'cleanup commit messages (verbatim option)' '\n>> +       git reset --hard c1 &&\n>> +       git merge --cleanup=verbatim -F expect c2 &&\n>> +       git cat-file -p HEAD |sed -e \"1,/^\\$/d\" >actual &&\n> \n> An earlier patch in this series \"fixed\" a test with a Git command\n> upstream of a pipe. Yet, this test adds such an instance.\n\nIs it worth worrying about? We're not trying to test cat-file here and \nif it does fail the test will fail as actual will be empty. You could use\ngit log --pretty=%B >actual\nwhich is clearer and saves forking sed.\n\nBest Wishes\n\nPhillip\n\n> (Also, style: add space after '|'.)\n> \n>> +       test_cmp expect actual\n>> +'\n>> +\n>> +test_expect_success 'cleanup commit messages (whitespace option)' '\n>> +       git reset --hard c1 &&\n>> +       { echo;echo \"# text\";echo; } >text &&\n> \n> Style: add space after semicolon or use &&-chaining inside {...}.\n> \n> Alternately, less ugly:\n> \n>      test_write_lines \"\" \"# text\" \"\" >text &&\n> \n> (Or even a here-doc, though the leading and trailing blank lines make\n> the here-doc ugly.)\n> \n>> +       echo \"# text\" >expect &&\n>> +       git merge --cleanup=whitespace -F text c2 &&\n>> +       git cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n> \n> Git upstream pipe.\n> \n>> +       test_cmp expect actual\n>> +\n>> +'\n> \n> Style: drop the blank line before the closing quote.\n> \n>> +test_expect_success 'cleanup merge messages (scissors option)' '\n>> +       git reset --hard c1 &&\n>> +       cat >text <<EOF &&\n> \n> This here-doc probably ought to be using '-' and '\\', so:\n> \n>      cat >text <<-\\EOF &&\n> \n> and indent the here-doc body.\n> \n>> +# to be kept\n>> +\n>> +  # ------------------------ >8 ------------------------\n>> +# to be kept, too\n>> +# ------------------------ >8 ------------------------\n>> +to be removed\n>> +# ------------------------ >8 ------------------------\n>> +to be removed, too\n>> +EOF\n>> +\n>> +       cat >expect <<EOF &&\n> \n> Ditto: <<-\\EOF and indent body.\n> \n>> +# to be kept\n>> +\n>> +  # ------------------------ >8 ------------------------\n>> +# to be kept, too\n>> +EOF\n>> +       git merge --cleanup=scissors -e -F text c2 &&\n>> +       git cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n> \n> Git upstream pipe.\n> \n>> +       test_cmp expect actual\n>> +'\n>> +\n>> +test_expect_success 'cleanup commit messages (strip option)' '\n>> +       git reset --hard c1 &&\n>> +       { echo;echo \"# text\";echo sample;echo; } >text &&\n> \n> test_write_lines \"\" \"# text\" sample \"\" >text &&\n> \n>> +       echo sample >expect &&\n>> +       git merge --cleanup=strip -F text c2 &&\n>> +       git cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n> \n> Git upstream pipe.\n> \n>> +       test_cmp expect actual\n>> +\n>> +'\n> \n> Drop blank line before closing quote.\n> \n"},{"id":"371164","messageId":"3b863432-e9b8-051a-a52e-efe4938f62d0@gmail.com","threadId":"49830","inReplyTo":"6d5ce3ffffe5ef6c23b12538650b2cdf86e4cac9.1552275703.git.liu.denton@gmail.com","subject":"Re: [PATCH v7 4/8] sequencer.c: remove duplicate code","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-03-11T16:45:45Z","receivedAt":"2019-03-11T16:45:51Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOne small comment below, this basically looks fine to me\n\nOn 11/03/2019 03:42, Denton Liu wrote:\n> Since we implemented get_cleanup_mode, we had some duplicate code in\n> git_sequencer_config which essentially performed the same operations.\n> Refactor git_sequencer_config to take advantage of the logic already in\n> get_cleanup_mode.\n> \n> Note that we had to introduce a separate argument to get_cleanup_mode\n> indicating whether to die or not on an invalid value. This is because if\n> we are parsing a config, we do not want to die but instead, we only want\n> to warn the user, whereas if we are parsing a command-line option, we\n> would like to actually die.\n> \n> Finally, this is almost a no-op refactor but not quite. Previously, in\n> the case that an invalid value is presented, default_msg_cleanup would\n> not be set. We change the behaviour so that default_msg_cleanup will now\n> take on the value as if \"default\" were provided as the cleanup_arg.\n> \n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>   builtin/commit.c |  2 +-\n>   sequencer.c      | 20 +++++++-------------\n>   sequencer.h      |  2 +-\n>   3 files changed, 9 insertions(+), 15 deletions(-)\n> \n> diff --git a/builtin/commit.c b/builtin/commit.c\n> index 0df15e4851..81e3bd21ca 100644\n> --- a/builtin/commit.c\n> +++ b/builtin/commit.c\n> @@ -1172,7 +1172,7 @@ static int parse_and_validate_options(int argc, const char *argv[],\n>   \t\tdie(_(\"Only one of --include/--only/--all/--interactive/--patch can be used.\"));\n>   \tif (argc == 0 && (also || (only && !amend && !allow_empty)))\n>   \t\tdie(_(\"No paths with --include/--only does not make sense.\"));\n> -\tcleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);\n> +\tcleanup_mode = get_cleanup_mode(cleanup_arg, use_editor, 1);\n>   \n>   \thandle_untracked_files_arg(s);\n>   \n> diff --git a/sequencer.c b/sequencer.c\n> index 224c823b43..612621f221 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -172,17 +172,7 @@ static int git_sequencer_config(const char *k, const char *v, void *cb)\n>   \t\tif (status)\n>   \t\t\treturn status;\n>   \n> -\t\tif (!strcmp(s, \"verbatim\"))\n> -\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;\n> -\t\telse if (!strcmp(s, \"whitespace\"))\n> -\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;\n> -\t\telse if (!strcmp(s, \"strip\"))\n> -\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;\n> -\t\telse if (!strcmp(s, \"scissors\"))\n> -\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;\n> -\t\telse\n> -\t\t\twarning(_(\"invalid commit message cleanup mode '%s'\"),\n> -\t\t\t\t  s);\n> +\t\topts->default_msg_cleanup = get_cleanup_mode(s, 0, 0);\n>   \n>   \t\tfree((char *)s);\n>   \t\treturn status;\n> @@ -512,7 +502,7 @@ static int fast_forward_to(struct repository *r,\n>   }\n>   \n>   enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n> -\tint use_editor)\n> +\tint use_editor, int die_on_error)\n>   {\n>   \tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n>   \t\treturn use_editor ? COMMIT_MSG_CLEANUP_ALL :\n> @@ -526,7 +516,11 @@ enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n>   \telse if (!strcmp(cleanup_arg, \"scissors\"))\n>   \t\treturn use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n>   \t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n> -\telse\n> +\telse if (!die_on_error) {\n> +\t\twarning(_(\"Invalid cleanup mode %s, falling back to default\"), cleanup_arg);\n\ngit generally starts error messages with a lower-case letter so I would \nchange the message in the previous patch to match that pattern rather \nthan changing this one.\n\n\nBest Wishes\n\nPhillip\n\n\n> +\t\treturn use_editor ? COMMIT_MSG_CLEANUP_ALL :\n> +\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n> +\t} else\n>   \t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n>   }\n>   \n> diff --git a/sequencer.h b/sequencer.h\n> index eb9bd97ef3..e7908f558e 100644\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -117,7 +117,7 @@ void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);\n>   \n>   void append_conflicts_hint(struct index_state *istate, struct strbuf *msgbuf);\n>   enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n> -\tint use_editor);\n> +\tint use_editor, int die_on_error);\n>   \n>   void cleanup_message(struct strbuf *msgbuf,\n>   \tenum commit_msg_cleanup_mode cleanup_mode, int verbose);\n> \n"},{"id":"371165","messageId":"c151ea74-ed9b-f33c-eb14-03243325eaf7@gmail.com","threadId":"49830","inReplyTo":"b2b82954e7060a3f5d47f77e4743493ff4256bd6.1552275703.git.liu.denton@gmail.com","subject":"Re: [PATCH v7 5/8] merge: cleanup messages like commit","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-03-11T16:58:23Z","receivedAt":"2019-03-11T16:58:29Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 11/03/2019 03:42, Denton Liu wrote:\n> This change allows git-merge messages to be cleaned up with the\n> commit.cleanup configuration or --cleanup option, just like how\n> git-commit does it.\n> \n> We also give git-pull the passthrough option of --cleanup so that it can\n> also take advantage of this change.\n> \n> Finally, add testing to ensure that messages are properly cleaned up.\n> Note that some newlines that were added to the commit message were\n> removed so that if a file were read via -F, it would be copied\n> faithfully.\n> \n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>   Documentation/merge-options.txt |  5 +++\n>   builtin/merge.c                 | 31 +++++++++++++----\n>   builtin/pull.c                  |  6 ++++\n>   t/t7604-merge-custom-message.sh | 61 +++++++++++++++++++++++++++++++++\n>   wt-status.c                     | 12 +++++--\n>   wt-status.h                     |  1 +\n>   6 files changed, 106 insertions(+), 10 deletions(-)\n> \n> diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\n> index 92a7d936c1..646100ea9a 100644\n> --- a/Documentation/merge-options.txt\n> +++ b/Documentation/merge-options.txt\n> @@ -32,6 +32,11 @@ they run `git merge`. To make it easier to adjust such scripts to the\n>   updated behaviour, the environment variable `GIT_MERGE_AUTOEDIT` can be\n>   set to `no` at the beginning of them.\n>   \n> +--cleanup=<mode>::\n> +\tThis option determines how the merge message will be cleaned up\n> +\tbefore commiting or being passed on. See linkgit:git-commit[1] for more\n> +\tdetails.\n> +\n>   --ff::\n>   \tWhen the merge resolves as a fast-forward, only update the branch\n>   \tpointer, without creating a merge commit.  This is the default\n> diff --git a/builtin/merge.c b/builtin/merge.c\n> index 5ce8946d39..4f5fcf5ce9 100644\n> --- a/builtin/merge.c\n> +++ b/builtin/merge.c\n> @@ -38,6 +38,7 @@\n>   #include \"tag.h\"\n>   #include \"alias.h\"\n>   #include \"commit-reach.h\"\n> +#include \"wt-status.h\"\n>   \n>   #define DEFAULT_TWOHEAD (1<<0)\n>   #define DEFAULT_OCTOPUS (1<<1)\n> @@ -98,6 +99,9 @@ enum ff_type {\n>   \n>   static enum ff_type fast_forward = FF_ALLOW;\n>   \n> +static const char *cleanup_arg;\n> +static enum commit_msg_cleanup_mode cleanup_mode;\n> +\n>   static int option_parse_message(const struct option *opt,\n>   \t\t\t\tconst char *arg, int unset)\n>   {\n> @@ -249,6 +253,7 @@ static struct option builtin_merge_options[] = {\n>   \t\tN_(\"perform a commit if the merge succeeds (default)\")),\n>   \tOPT_BOOL('e', \"edit\", &option_edit,\n>   \t\tN_(\"edit message before committing\")),\n> +\tOPT_STRING(0, \"cleanup\", &cleanup_arg, N_(\"default\"), N_(\"how to strip spaces and #comments from message\")),\n>   \tOPT_SET_INT(0, \"ff\", &fast_forward, N_(\"allow fast-forward (default)\"), FF_ALLOW),\n>   \tOPT_SET_INT_F(0, \"ff-only\", &fast_forward,\n>   \t\t      N_(\"abort if fast-forward is not possible\"),\n> @@ -612,6 +617,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n>   \t\treturn git_config_string(&pull_twohead, k, v);\n>   \telse if (!strcmp(k, \"pull.octopus\"))\n>   \t\treturn git_config_string(&pull_octopus, k, v);\n> +\telse if (!strcmp(k, \"commit.cleanup\"))\n> +\t\treturn git_config_string(&cleanup_arg, k, v);\n>   \telse if (!strcmp(k, \"merge.renormalize\"))\n>   \t\toption_renormalize = git_config_bool(k, v);\n>   \telse if (!strcmp(k, \"merge.ff\")) {\n> @@ -797,23 +804,32 @@ static void abort_commit(struct commit_list *remoteheads, const char *err_msg)\n>   \texit(1);\n>   }\n>   \n> +static const char comment_line_explanation[] =\n> +N_(\"Lines starting with '%c' will be ignored.\\n\");\n> +\n>   static const char merge_editor_comment[] =\n>   N_(\"Please enter a commit message to explain why this merge is necessary,\\n\"\n>      \"especially if it merges an updated upstream into a topic branch.\\n\"\n>      \"\\n\"\n> -   \"Lines starting with '%c' will be ignored, and an empty message aborts\\n\"\n> -   \"the commit.\\n\");\n> +   \"An empty message aborts the commit.\\n\");\n>   \n>   static void write_merge_heads(struct commit_list *);\n>   static void prepare_to_commit(struct commit_list *remoteheads)\n>   {\n>   \tstruct strbuf msg = STRBUF_INIT;\n>   \tstrbuf_addbuf(&msg, &merge_msg);\n> -\tstrbuf_addch(&msg, '\\n');\n>   \tif (squash)\n>   \t\tBUG(\"the control must not reach here under --squash\");\n> -\tif (0 < option_edit)\n> -\t\tstrbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);\n> +\tif (0 < option_edit) {\n> +\t\tstrbuf_addch(&msg, '\\n');\n> +\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n> +\t\t\twt_status_append_cut_line(&msg);\n> +\t\telse\n> +\t\t\tstrbuf_commented_addf(&msg, _(comment_line_explanation), comment_line_char);\n> +\n> +\t\tstrbuf_commented_addf(&msg, \"\\n\");\n> +\t\tstrbuf_commented_addf(&msg, _(merge_editor_comment));\n\nI think this has rearranged the message presented to the user so it now \nreads\n\nLines starting with '#' will be ignored.\nPlease enter a commit message to explain why this merge is necessary,\nespecially if it merges an updated upstream into a topic branch.\n\nAn empty message aborts the commit.\n\nTo me it read better before, it would be a little more work but I think \nit would be worth preserving the message (especially as this is the \nmessage people will see unless they specify --cleanup=scissors)\n\n> +\t}\n>   \tif (signoff)\n>   \t\tappend_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);\n>   \twrite_merge_heads(remoteheads);\n> @@ -832,7 +848,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)\n>   \t\tabort_commit(remoteheads, NULL);\n>   \n>   \tread_merge_msg(&msg);\n> -\tstrbuf_stripspace(&msg, 0 < option_edit);\n> +\tcleanup_message(&msg, cleanup_mode, 0);\n>   \tif (!msg.len)\n>   \t\tabort_commit(remoteheads, _(\"Empty commit message.\"));\n>   \tstrbuf_release(&merge_msg);\n> @@ -880,7 +896,6 @@ static int finish_automerge(struct commit *head,\n>   \tparents = remoteheads;\n>   \tif (!head_subsumed || fast_forward == FF_NO)\n>   \t\tcommit_list_insert(head, &parents);\n> -\tstrbuf_addch(&merge_msg, '\\n');\n>   \tprepare_to_commit(remoteheads);\n>   \tif (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,\n>   \t\t\t&result_commit, NULL, sign_commit))\n> @@ -1389,6 +1404,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n>   \tif (option_edit < 0)\n>   \t\toption_edit = default_edit_option();\n>   \n> +\tcleanup_mode = get_cleanup_mode(cleanup_arg, 0 < option_edit, 1);\n> +\n>   \tif (!use_strategies) {\n>   \t\tif (!remoteheads)\n>   \t\t\t; /* already up-to-date */\n> diff --git a/builtin/pull.c b/builtin/pull.c\n> index 33db889955..3ec7882375 100644\n> --- a/builtin/pull.c\n> +++ b/builtin/pull.c\n> @@ -101,6 +101,7 @@ static char *opt_signoff;\n>   static char *opt_squash;\n>   static char *opt_commit;\n>   static char *opt_edit;\n> +static char *opt_cleanup;\n>   static char *opt_ff;\n>   static char *opt_verify_signatures;\n>   static int opt_autostash = -1;\n> @@ -168,6 +169,9 @@ static struct option pull_options[] = {\n>   \tOPT_PASSTHRU(0, \"edit\", &opt_edit, NULL,\n>   \t\tN_(\"edit message before committing\"),\n>   \t\tPARSE_OPT_NOARG),\n> +\tOPT_PASSTHRU(0, \"cleanup\", &opt_cleanup, NULL,\n> +\t\tN_(\"how to strip spaces and #comments from message\"),\n> +\t\tPARSE_OPT_NOARG),\n\ncleanup needs to take an argument so PARSE_OPT_NOARG does not look \nright. Also I think it would be bettor from the user's point of view if \nthe value of the argument was checked by pull before it does any work \nrather otherwise if they pass in invalid value pull mostly runs and then \nmerge errors out at the end.\n\nBest Wishes\n\nPhillip\n\n\n>   \tOPT_PASSTHRU(0, \"ff\", &opt_ff, NULL,\n>   \t\tN_(\"allow fast-forward\"),\n>   \t\tPARSE_OPT_NOARG),\n> @@ -644,6 +648,8 @@ static int run_merge(void)\n>   \t\targv_array_push(&args, opt_commit);\n>   \tif (opt_edit)\n>   \t\targv_array_push(&args, opt_edit);\n> +\tif (opt_cleanup)\n> +\t\targv_array_push(&args, opt_cleanup);\n>   \tif (opt_ff)\n>   \t\targv_array_push(&args, opt_ff);\n>   \tif (opt_verify_signatures)\n> diff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh\n> index 89619cf446..3fcd58eb2c 100755\n> --- a/t/t7604-merge-custom-message.sh\n> +++ b/t/t7604-merge-custom-message.sh\n> @@ -47,4 +47,65 @@ test_expect_success 'merge --log appends to custom message' '\n>   \ttest_cmp exp.log actual\n>   '\n>   \n> +mesg_with_comment_and_newlines='\n> +# text\n> +\n> +'\n> +\n> +test_expect_success 'prepare file with comment line and trailing newlines'  '\n> +\tprintf \"%s\" \"$mesg_with_comment_and_newlines\" >expect\n> +'\n> +\n> +test_expect_success 'cleanup commit messages (verbatim option)' '\n> +\tgit reset --hard c1 &&\n> +\tgit merge --cleanup=verbatim -F expect c2 &&\n> +\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\" >actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n> +test_expect_success 'cleanup commit messages (whitespace option)' '\n> +\tgit reset --hard c1 &&\n> +\t{ echo;echo \"# text\";echo; } >text &&\n> +\techo \"# text\" >expect &&\n> +\tgit merge --cleanup=whitespace -F text c2 &&\n> +\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n> +\ttest_cmp expect actual\n> +\n> +'\n> +\n> +test_expect_success 'cleanup merge messages (scissors option)' '\n> +\tgit reset --hard c1 &&\n> +\tcat >text <<EOF &&\n> +\n> +# to be kept\n> +\n> +  # ------------------------ >8 ------------------------\n> +# to be kept, too\n> +# ------------------------ >8 ------------------------\n> +to be removed\n> +# ------------------------ >8 ------------------------\n> +to be removed, too\n> +EOF\n> +\n> +\tcat >expect <<EOF &&\n> +# to be kept\n> +\n> +  # ------------------------ >8 ------------------------\n> +# to be kept, too\n> +EOF\n> +\tgit merge --cleanup=scissors -e -F text c2 &&\n> +\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n> +test_expect_success 'cleanup commit messages (strip option)' '\n> +\tgit reset --hard c1 &&\n> +\t{ echo;echo \"# text\";echo sample;echo; } >text &&\n> +\techo sample >expect &&\n> +\tgit merge --cleanup=strip -F text c2 &&\n> +\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n> +\ttest_cmp expect actual\n> +\n> +'\n> +\n>   test_done\n> diff --git a/wt-status.c b/wt-status.c\n> index 445a36204a..b81fcd428d 100644\n> --- a/wt-status.c\n> +++ b/wt-status.c\n> @@ -1006,13 +1006,19 @@ size_t wt_status_locate_end(const char *s, size_t len)\n>   \treturn len;\n>   }\n>   \n> -void wt_status_add_cut_line(FILE *fp)\n> +void wt_status_append_cut_line(struct strbuf *buf)\n>   {\n>   \tconst char *explanation = _(\"Do not modify or remove the line above.\\nEverything below it will be ignored.\");\n> +\n> +\tstrbuf_commented_addf(buf, \"%s\", cut_line);\n> +\tstrbuf_add_commented_lines(buf, explanation, strlen(explanation));\n> +}\n> +\n> +void wt_status_add_cut_line(FILE *fp)\n> +{\n>   \tstruct strbuf buf = STRBUF_INIT;\n>   \n> -\tfprintf(fp, \"%c %s\", comment_line_char, cut_line);\n> -\tstrbuf_add_commented_lines(&buf, explanation, strlen(explanation));\n> +\twt_status_append_cut_line(&buf);\n>   \tfputs(buf.buf, fp);\n>   \tstrbuf_release(&buf);\n>   }\n> diff --git a/wt-status.h b/wt-status.h\n> index 3a95975032..64f1ddc9fd 100644\n> --- a/wt-status.h\n> +++ b/wt-status.h\n> @@ -129,6 +129,7 @@ struct wt_status {\n>   };\n>   \n>   size_t wt_status_locate_end(const char *s, size_t len);\n> +void wt_status_append_cut_line(struct strbuf *buf);\n>   void wt_status_add_cut_line(FILE *fp);\n>   void wt_status_prepare(struct repository *r, struct wt_status *s);\n>   void wt_status_print(struct wt_status *s);\n> \n"},{"id":"371166","messageId":"CAPig+cQB2g-ZnbZc5ipPKQo0nGFK_2okOP+hq0cdW8Eq5tddaQ@mail.gmail.com","threadId":"49830","inReplyTo":"2e10d78a-d92c-76c1-abc3-49ad1be55779@gmail.com","subject":"Re: [PATCH v7 5/8] merge: cleanup messages like commit","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-03-11T17:00:44Z","receivedAt":"2019-03-11T17:00:59Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Mar 11, 2019 at 6:14 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> On 11/03/2019 05:49, Eric Sunshine wrote:\n> > On Sun, Mar 10, 2019 at 11:42 PM Denton Liu <liu.denton@gmail.com> wrote:\n> >> +       git cat-file -p HEAD |sed -e \"1,/^\\$/d\" >actual &&\n> >\n> > An earlier patch in this series \"fixed\" a test with a Git command\n> > upstream of a pipe. Yet, this test adds such an instance.\n>\n> Is it worth worrying about? We're not trying to test cat-file here and\n> if it does fail the test will fail as actual will be empty.\n\ngit-cat-file could crash _after_ producing the expected output, in\nwhich case we wouldn't learn about the crash at all since the pipe\nwould swallow it. While it's true that we're not specifically testing\ngit-cat-file here, with all the recent effort toward moving away from\nhaving Git command upstream of a pipe, I don't think it makes sense to\nadd new cases. And it doesn't hurt to play it safe here: we might\nactually catch a crash in git-cat-file which isn't caught by other\nscripts which are genuinely testing that command.\n"},{"id":"371208","messageId":"xmqqzhq0j449.fsf@gitster-ct.c.googlers.com","threadId":"49830","inReplyTo":"6716d28a5187c44c1d90f5ce840c44441f62352c.1552275703.git.liu.denton@gmail.com","subject":"Re: [PATCH v7 1/8] t7600: clean up 'merge --squash c3 with c7' test","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-12T01:03:34Z","receivedAt":"2019-03-12T01:03:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Denton Liu <liu.denton@gmail.com> writes:\n\n> -\t{\n> -\t\tcat <<-EOF\n> +\tcat >expect <<-EOF &&\n>  \t\tSquashed commit of the following:\n>  \n>  \t\t$(git show -s c7)\n> @@ -242,8 +241,8 @@ test_expect_success 'merge --squash c3 with c7' '\n>  \t\t# Conflicts:\n>  \t\t#\tfile\n>  \t\tEOF\n> -\t} >expect &&\n\nAs you are dedenting the \"cat <<-EOF\", the body and closing EOF\nshould also be dedented one level, i.e.\n\n\tcat >expect <<-EOF &&\n\tSquashed commit of the following:\n\t...\n\t# Conflicts:\n\t#\tfile\n\tEOF\n\n> -\tgit cat-file commit HEAD | sed -e '1,/^$/d' >actual &&\n> +\tgit cat-file commit HEAD >tmp &&\n> +\tsed -e '1,/^$/d' <tmp >actual &&\n\nThe intermediary file may want a name better than 'tmp', if it is to\nbe left behind, but this will do for now.\n\n>  \ttest_cmp expect actual\n>  '\n\nThanks for the clean-up.\n"},{"id":"371219","messageId":"xmqqsgvsiqtu.fsf@gitster-ct.c.googlers.com","threadId":"49830","inReplyTo":"c151ea74-ed9b-f33c-eb14-03243325eaf7@gmail.com","subject":"Re: [PATCH v7 5/8] merge: cleanup messages like commit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-12T05:50:37Z","receivedAt":"2019-03-12T05:51:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n>> -\tif (0 < option_edit)\n>> -\t\tstrbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);\n>> +\tif (0 < option_edit) {\n>> +\t\tstrbuf_addch(&msg, '\\n');\n>> +\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n>> +\t\t\twt_status_append_cut_line(&msg);\n>> +\t\telse\n>> +\t\t\tstrbuf_commented_addf(&msg, _(comment_line_explanation), comment_line_char);\n>> +\n>> +\t\tstrbuf_commented_addf(&msg, \"\\n\");\n>> +\t\tstrbuf_commented_addf(&msg, _(merge_editor_comment));\n>\n> I think this has rearranged the message presented to the user so it\n> now reads\n>\n> Lines starting with '#' will be ignored.\n> Please enter a commit message to explain why this merge is necessary,\n> especially if it merges an updated upstream into a topic branch.\n>\n> An empty message aborts the commit.\n>\n> To me it read better before, it would be a little more work but I\n> think it would be worth preserving the message (especially as this is\n> the message people will see unless they specify --cleanup=scissors)\n\nThat may be subjective, but unless the new message is vastly and\nuncontroversially better (which I do not think it is, with your\nobjection), I agree that we should avoid churning the message.\n\n>> @@ -168,6 +169,9 @@ static struct option pull_options[] = {\n>>   \tOPT_PASSTHRU(0, \"edit\", &opt_edit, NULL,\n>>   \t\tN_(\"edit message before committing\"),\n>>   \t\tPARSE_OPT_NOARG),\n>> +\tOPT_PASSTHRU(0, \"cleanup\", &opt_cleanup, NULL,\n>> +\t\tN_(\"how to strip spaces and #comments from message\"),\n>> +\t\tPARSE_OPT_NOARG),\n>\n> cleanup needs to take an argument so PARSE_OPT_NOARG does not look\n> right. Also I think it would be bettor from the user's point of view\n> if the value of the argument was checked by pull before it does any\n> work rather otherwise if they pass in invalid value pull mostly runs\n> and then merge errors out at the end.\n\nBoth good points.\n"},{"id":"371220","messageId":"xmqqef7cip13.fsf@gitster-ct.c.googlers.com","threadId":"49830","inReplyTo":"ced1df0fc76c97c9896b6cbb5b4154532461716d.1552275703.git.liu.denton@gmail.com","subject":"Re: [PATCH v7 7/8] sequencer.c: define get_config_from_cleanup","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-12T06:29:28Z","receivedAt":"2019-03-12T06:29:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Denton Liu <liu.denton@gmail.com> writes:\n\n> +struct cleanup_config_mapping {\n> +    const char *config_value;\n> +    enum commit_msg_cleanup_mode editor_cleanup;\n> +    enum commit_msg_cleanup_mode no_editor_cleanup;\n> +};\n\nIs this code using 4-space indent?  Please don't.  Also, I found\nthat Eric's comment on naming gave a good suggestion.\n\nIs the cleanup_config_mapping[] array we see below supposed to be\nconstant, or does it allow further runtime configuration?  I am\nassuming the former (i.e. when the user says \"default\", then\neditor_cleanup will always become CLEANUP_ALL and no_editor_cleanup\nwill always become CLEANUP_SPACE), in which case, I wonder if we can\nbe more explicit about constness of the table.\n\n> +/* note that we assume that cleanup_config_mapping[0] contains the default settings */\n\nThat sounds as if it is bad to make that assumption.  Be more\npositive and direct to clearly tell future programmers what rule\nthey need to honor, e.g.\n\n\t/* the 0th element of this array must be the \"default\" */\n\n> +struct cleanup_config_mapping cleanup_config_mappings[] = {\n\nDo not give a plural name to an array.  Access to an element in a\narray \"type thing[]\" can be written thing[4] and can be more\nnaturally read as \"the fourth thing\" (you do not say \"the fourth\nthings\") that way.\n\nAn exception is when you very often pass around the array as a whole\nas one unit of datum across callchains.  \n\n> +\tstruct cleanup_config_mapping *default_mapping = &cleanup_config_mappings[0];\n> +\tstruct cleanup_config_mapping *current_mapping;\n> +\n> +\tif (!cleanup_arg) {\n> +\t\treturn use_editor ? default_mapping->editor_cleanup :\n> +\t\t\t\t    default_mapping->no_editor_cleanup;\n> +\t}\n\nNo need for extra {}.\n\n> +\n> +\tfor (current_mapping = cleanup_config_mappings; current_mapping->config_value; current_mapping++) {\n> +\t\tif (!strcmp(cleanup_arg, current_mapping->config_value)) {\n> +\t\t\treturn use_editor ? current_mapping->editor_cleanup :\n> +\t\t\t\t\t    current_mapping->no_editor_cleanup;\n> +\t\t}\n> +\t}\n\nDitto.  In addition, perhaps split the for (...) like so:\n\n\tfor (current_mapping = cleanup_config_mappings;\n             current_mapping->config_value;\n             current_mapping++)\n\t\tif (...)\n\t\t\treturn ...;\n\n> +const char *get_config_from_cleanup(enum commit_msg_cleanup_mode cleanup_mode)\n\nstatic???\n\nIs this really getting \"config\" from \"cleanup\"?  It rather smells\nbackwards i.e. grabbing the clean-up settings from the config system\nto me.\n\n> +{\n> +\tstruct cleanup_config_mapping *current_mapping;\n> +\n> +\tfor (current_mapping = &cleanup_config_mappings[1]; current_mapping->config_value; current_mapping++) {\n> +\t\tif (cleanup_mode == current_mapping->editor_cleanup) {\n> +\t\t\treturn current_mapping->config_value;\n> +\t\t}\n> +\t}\n> +\n> +\tBUG(_(\"invalid cleanup_mode provided\"));\n\nIs that a bug (i.e. programming error) or a bad configuration file?\nI think you meant the former, but then I do not think we want _()\naround the message.  Instead, however, we may want to show the\ncleanup_mode that was provided, possibly with the available values\nin the .editor_cleanup field of cleanup_config_mapping[] entries.\n"},{"id":"371239","messageId":"646a1d6a-5e40-5ff1-4702-a41addd4b315@gmail.com","threadId":"49830","inReplyTo":"5b30d939674542b2cfeeeef8341ab25c7d6c0543.1552275703.git.liu.denton@gmail.com","subject":"Re: [PATCH v7 8/8] cherry-pick/revert: add scissors line on merge conflict","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-03-12T11:11:13Z","receivedAt":"2019-03-12T11:11:20Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nI've got a couple of small comments, but this looks fine to me\n\nOn 11/03/2019 03:42, Denton Liu wrote:\n> Fix a bug where the scissors line is placed after the Conflicts:\n> section, in the case where a merge conflict occurs and\n> commit.cleanup = scissors.\n> \n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>   Documentation/git-cherry-pick.txt |  7 +++\n>   Documentation/git-revert.txt      |  7 +++\n>   builtin/merge.c                   |  9 +---\n>   builtin/rebase--interactive.c     |  2 +-\n>   builtin/revert.c                  |  5 ++\n>   sequencer.c                       | 22 +++++---\n>   sequencer.h                       |  3 +-\n>   t/t3507-cherry-pick-conflict.sh   | 86 +++++++++++++++++++++++++++++++\n>   8 files changed, 123 insertions(+), 18 deletions(-)\n> \n> diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt\n> index b8cfeec67e..b4ff8e136d 100644\n> --- a/Documentation/git-cherry-pick.txt\n> +++ b/Documentation/git-cherry-pick.txt\n> @@ -57,6 +57,13 @@ OPTIONS\n>   \tWith this option, 'git cherry-pick' will let you edit the commit\n>   \tmessage prior to committing.\n>   \n> +--cleanup=<mode>::\n> +\tThis option determines how the commit message will be cleaned up before\n> +\tbeing passed on. See linkgit:git-commit[1] for more details. In\n> +\taddition, if the '<mode>' is given a value of `scissors`, scissors will\n> +\tbe appended to MERGE_MSG before being passed on in the case of a\n> +\tconflict.\n\nI'm not sure what you mean by before being passed on.\n\n>   -x::\n>   \tWhen recording the commit, append a line that says\n>   \t\"(cherry picked from commit ...)\" to the original commit\n> diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt\n> index 837707a8fd..bd4ad395a9 100644\n> --- a/Documentation/git-revert.txt\n> +++ b/Documentation/git-revert.txt\n> @@ -66,6 +66,13 @@ more details.\n>   \tWith this option, 'git revert' will not start the commit\n>   \tmessage editor.\n>   \n> +--cleanup=<mode>::\n> +\tThis option determines how the commit message will be cleaned up before\n> +\tbeing passed on. See linkgit:git-commit[1] for more details. In\n> +\taddition, if the '<mode>' is given a value of `scissors`, scissors will\n> +\tbe appended to MERGE_MSG before being passed on in the case of a\n> +\tconflict.\n> +\n>   -n::\n>   --no-commit::\n>   \tUsually the command automatically creates some commits with\n> diff --git a/builtin/merge.c b/builtin/merge.c\n> index b620d4bfcb..671d6a19a4 100644\n> --- a/builtin/merge.c\n> +++ b/builtin/merge.c\n> @@ -923,14 +923,7 @@ static int suggest_conflicts(void)\n>   \t * Thus, we will get the cleanup mode which is returned when we _are_ using\n>   \t * an editor.\n>   \t */\n> -\tif (get_cleanup_mode(cleanup_arg, 1, 1) == COMMIT_MSG_CLEANUP_SCISSORS) {\n> -\t    fputc('\\n', fp);\n> -\t    wt_status_add_cut_line(fp);\n> -\t    /* comments out the newline from append_conflicts_hint */\n> -\t    fputc(comment_line_char, fp);\n> -\t}\n> -\n> -\tappend_conflicts_hint(&the_index, &msgbuf);\n> +\tappend_conflicts_hint(&the_index, &msgbuf, get_cleanup_mode(cleanup_arg, 1, 1));\n>   \tfputs(msgbuf.buf, fp);\n>   \tstrbuf_release(&msgbuf);\n>   \tfclose(fp);\n> diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\n> index 888390f911..cf2151b271 100644\n> --- a/builtin/rebase--interactive.c\n> +++ b/builtin/rebase--interactive.c\n> @@ -199,10 +199,10 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>   \t\tOPT_END()\n>   \t};\n>   \n> +\topts.action = REPLAY_INTERACTIVE_REBASE;\n>   \tsequencer_init_config(&opts);\n>   \tgit_config_get_bool(\"rebase.abbreviatecommands\", &abbreviate_commands);\n>   \n> -\topts.action = REPLAY_INTERACTIVE_REBASE;\n>   \topts.allow_ff = 1;\n>   \topts.allow_empty = 1;\n>   \n> diff --git a/builtin/revert.c b/builtin/revert.c\n> index a47b53ceaf..e8168e0214 100644\n> --- a/builtin/revert.c\n> +++ b/builtin/revert.c\n> @@ -96,11 +96,13 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)\n>   {\n>   \tconst char * const * usage_str = revert_or_cherry_pick_usage(opts);\n>   \tconst char *me = action_name(opts);\n> +\tconst char *cleanup_arg = NULL;\n>   \tint cmd = 0;\n>   \tstruct option base_options[] = {\n>   \t\tOPT_CMDMODE(0, \"quit\", &cmd, N_(\"end revert or cherry-pick sequence\"), 'q'),\n>   \t\tOPT_CMDMODE(0, \"continue\", &cmd, N_(\"resume revert or cherry-pick sequence\"), 'c'),\n>   \t\tOPT_CMDMODE(0, \"abort\", &cmd, N_(\"cancel revert or cherry-pick sequence\"), 'a'),\n> +\t\tOPT_STRING(0, \"cleanup\", &cleanup_arg, N_(\"default\"), N_(\"how to strip spaces and #comments from message\")),\n\nThis line is rather long. As you're adding --cleanup to several commands \nit might be an idea to add an OPT_CLEANUP macro to parse-options.h. That \nwould ensure the help is consistent across all the commands and save \nrepeating the option declaration.\n\nBest Wishes\n\nPhillip\n\n>   \t\tOPT_BOOL('n', \"no-commit\", &opts->no_commit, N_(\"don't automatically commit\")),\n>   \t\tOPT_BOOL('e', \"edit\", &opts->edit, N_(\"edit the commit message\")),\n>   \t\tOPT_NOOP_NOARG('r', NULL),\n> @@ -137,6 +139,9 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)\n>   \tif (opts->keep_redundant_commits)\n>   \t\topts->allow_empty = 1;\n>   \n> +\tif (cleanup_arg)\n> +\t\topts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1, 1);\n> +\n>   \t/* Check for incompatible command line arguments */\n>   \tif (cmd) {\n>   \t\tchar *this_operation;\n> diff --git a/sequencer.c b/sequencer.c\n> index 5d94e2c865..496554a474 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -176,6 +176,11 @@ struct cleanup_config_mapping cleanup_config_mappings[] = {\n>   \t{ NULL, 0, 0 }\n>   };\n>   \n> +static inline int is_rebase_i(const struct replay_opts *opts)\n> +{\n> +\treturn opts->action == REPLAY_INTERACTIVE_REBASE;\n> +}\n> +\n>   static int git_sequencer_config(const char *k, const char *v, void *cb)\n>   {\n>   \tstruct replay_opts *opts = cb;\n> @@ -188,7 +193,7 @@ static int git_sequencer_config(const char *k, const char *v, void *cb)\n>   \t\tif (status)\n>   \t\t\treturn status;\n>   \n> -\t\topts->default_msg_cleanup = get_cleanup_mode(s, 0, 0);\n> +\t\topts->default_msg_cleanup = get_cleanup_mode(s, !is_rebase_i(opts), 0);\n>   \n>   \t\tfree((char *)s);\n>   \t\treturn status;\n> @@ -212,11 +217,6 @@ void sequencer_init_config(struct replay_opts *opts)\n>   \tgit_config(git_sequencer_config, opts);\n>   }\n>   \n> -static inline int is_rebase_i(const struct replay_opts *opts)\n> -{\n> -\treturn opts->action == REPLAY_INTERACTIVE_REBASE;\n> -}\n> -\n>   static const char *get_dir(const struct replay_opts *opts)\n>   {\n>   \tif (is_rebase_i(opts))\n> @@ -557,10 +557,16 @@ const char *get_config_from_cleanup(enum commit_msg_cleanup_mode cleanup_mode)\n>   }\n>   \n>   void append_conflicts_hint(struct index_state *istate,\n> -\t\t\t   struct strbuf *msgbuf)\n> +\t\tstruct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode)\n>   {\n>   \tint i;\n>   \n> +\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {\n> +\t\tstrbuf_addch(msgbuf, '\\n');\n> +\t\twt_status_append_cut_line(msgbuf);\n> +\t\tstrbuf_addch(msgbuf, comment_line_char);\n> +\t}\n> +\n>   \tstrbuf_addch(msgbuf, '\\n');\n>   \tstrbuf_commented_addf(msgbuf, \"Conflicts:\\n\");\n>   \tfor (i = 0; i < istate->cache_nr;) {\n> @@ -628,7 +634,7 @@ static int do_recursive_merge(struct repository *r,\n>   \t\t\t_(action_name(opts)));\n>   \n>   \tif (!clean)\n> -\t\tappend_conflicts_hint(r->index, msgbuf);\n> +\t\tappend_conflicts_hint(r->index, msgbuf, opts->default_msg_cleanup);\n>   \n>   \treturn !clean;\n>   }\n> diff --git a/sequencer.h b/sequencer.h\n> index e3c1f44807..95ff9db508 100644\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -115,7 +115,8 @@ int rearrange_squash(struct repository *r);\n>    */\n>   void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);\n>   \n> -void append_conflicts_hint(struct index_state *istate, struct strbuf *msgbuf);\n> +void append_conflicts_hint(struct index_state *istate,\n> +\t\tstruct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode);\n>   enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n>   \tint use_editor, int die_on_error);\n>   const char *get_config_from_cleanup(enum commit_msg_cleanup_mode cleanup_mode);\n> diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh\n> index 74ff925526..c3894ca9d6 100755\n> --- a/t/t3507-cherry-pick-conflict.sh\n> +++ b/t/t3507-cherry-pick-conflict.sh\n> @@ -189,6 +189,46 @@ test_expect_success 'failed cherry-pick registers participants in index' '\n>   \ttest_cmp expected actual\n>   '\n>   \n> +test_expect_success \\\n> +\t'cherry-pick conflict, ensure commit.cleanup = scissors places scissors line properly' '\n> +\tpristine_detach initial &&\n> +\tgit config commit.cleanup scissors &&\n> +\tcat <<-EOF >expected &&\n> +\t\tpicked\n> +\n> +\t\t# ------------------------ >8 ------------------------\n> +\t\t# Do not modify or remove the line above.\n> +\t\t# Everything below it will be ignored.\n> +\t\t#\n> +\t\t# Conflicts:\n> +\t\t#\tfoo\n> +\t\tEOF\n> +\n> +\ttest_must_fail git cherry-pick picked &&\n> +\n> +\ttest_i18ncmp expected .git/MERGE_MSG\n> +'\n> +\n> +test_expect_success \\\n> +\t'cherry-pick conflict, ensure cleanup=scissors places scissors line properly' '\n> +\tpristine_detach initial &&\n> +\tgit config --unset commit.cleanup &&\n> +\tcat <<-EOF >expected &&\n> +\t\tpicked\n> +\n> +\t\t# ------------------------ >8 ------------------------\n> +\t\t# Do not modify or remove the line above.\n> +\t\t# Everything below it will be ignored.\n> +\t\t#\n> +\t\t# Conflicts:\n> +\t\t#\tfoo\n> +\t\tEOF\n> +\n> +\ttest_must_fail git cherry-pick --cleanup=scissors picked &&\n> +\n> +\ttest_i18ncmp expected .git/MERGE_MSG\n> +'\n> +\n>   test_expect_success 'failed cherry-pick describes conflict in work tree' '\n>   \tpristine_detach initial &&\n>   \tcat <<-EOF >expected &&\n> @@ -335,6 +375,52 @@ test_expect_success 'revert conflict, diff3 -m style' '\n>   \ttest_cmp expected actual\n>   '\n>   \n> +test_expect_success \\\n> +\t'revert conflict, ensure commit.cleanup = scissors places scissors line properly' '\n> +\tpristine_detach initial &&\n> +\tgit config commit.cleanup scissors &&\n> +\tcat >expected <<-EOF &&\n> +\t\tRevert \"picked\"\n> +\n> +\t\tThis reverts commit OBJID.\n> +\n> +\t\t# ------------------------ >8 ------------------------\n> +\t\t# Do not modify or remove the line above.\n> +\t\t# Everything below it will be ignored.\n> +\t\t#\n> +\t\t# Conflicts:\n> +\t\t#\tfoo\n> +\t\tEOF\n> +\n> +\ttest_must_fail git revert picked &&\n> +\n> +\tsed \"s/$OID_REGEX/OBJID/\" .git/MERGE_MSG >actual &&\n> +\ttest_i18ncmp expected actual\n> +'\n> +\n> +test_expect_success \\\n> +\t'revert conflict, ensure cleanup=scissors places scissors line properly' '\n> +\tpristine_detach initial &&\n> +\tgit config --unset commit.cleanup &&\n> +\tcat >expected <<-EOF &&\n> +\t\tRevert \"picked\"\n> +\n> +\t\tThis reverts commit OBJID.\n> +\n> +\t\t# ------------------------ >8 ------------------------\n> +\t\t# Do not modify or remove the line above.\n> +\t\t# Everything below it will be ignored.\n> +\t\t#\n> +\t\t# Conflicts:\n> +\t\t#\tfoo\n> +\t\tEOF\n> +\n> +\ttest_must_fail git revert --cleanup=scissors picked &&\n> +\n> +\tsed \"s/$OID_REGEX/OBJID/\" .git/MERGE_MSG >actual &&\n> +\ttest_i18ncmp expected actual\n> +'\n> +\n>   test_expect_success 'failed cherry-pick does not forget -s' '\n>   \tpristine_detach initial &&\n>   \ttest_must_fail git cherry-pick -s picked &&\n> \n"},{"id":"371681","messageId":"cover.1552817044.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1552275703.git.liu.denton@gmail.com","subject":"[PATCH v8 00/11] Fix scissors bug during conflict","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-17T10:15:50Z","receivedAt":"2019-03-17T10:15:56Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Sorry for taking so long to do a reroll, I've been pretty busy this week\nand I've only been able to find the time now.\n\nPrevious discussion on the cherry-pick/revert changes can be found here[1].\n\nChanges since revert/cherry-pick v3:\n\n* Rebased on top of latest master\n* Reordered and squashed patches\n* Added populate_opts_cb and save_opts to save default_msg_cleanup at Phillip's suggestion\n\nChanges since v7:\n\n* Cleaned up more tests according to Eric's advice (and added some more cleanup patches!)\n* Clarify meaning of \"passed on\" in documentation\n* Consolidate common options into OPT_CLEANUP macro in parse-options.h\n* Fix space indent to tabs in sequencer.c\n* Shorten variable names for readability\n* Make comment about 0th element in array being default more assertive\n* Remove unnecessary braces around if/for statements\n* Lowercase warning/error messages\n* Unmark BUG messages for translation\n* Rename get_config_from_cleanup -> describe_cleanup_mode\n* Squashed in Ramsay's static patch\n\n[1]: https://public-inbox.org/git/cover.1551867827.git.liu.denton@gmail.com/T/#u\n\n\nDenton Liu (11):\n  t7600: clean up style\n  t3507: cleanup space after redirection operators\n  t7604: refactor out Git commands upstream of pipe\n  t7502: clean up test style\n  commit: extract cleanup_mode functions to sequencer\n  parse-options.h: extract common --cleanup option\n  sequencer.c: remove duplicate code\n  merge: cleanup messages like commit\n  merge: add scissors line on merge conflict\n  sequencer.c: define describe_cleanup_mode\n  cherry-pick/revert: add scissors line on merge conflict\n\n Documentation/git-cherry-pick.txt |   7 ++\n Documentation/git-revert.txt      |   7 ++\n Documentation/merge-options.txt   |   7 ++\n builtin/commit.c                  |  47 +++++-------\n builtin/merge.c                   |  40 ++++++++--\n builtin/pull.c                    |   6 ++\n builtin/rebase--interactive.c     |   2 +-\n builtin/revert.c                  |   5 ++\n builtin/tag.c                     |   3 +-\n parse-options.h                   |   1 +\n sequencer.c                       |  97 +++++++++++++++++++-----\n sequencer.h                       |   9 ++-\n t/t3507-cherry-pick-conflict.sh   | 120 +++++++++++++++++++++++++-----\n t/t7502-commit-porcelain.sh       | 100 +++++++++++++++----------\n t/t7600-merge.sh                  |  70 ++++++++++++++---\n t/t7604-merge-custom-message.sh   |  75 ++++++++++++++++++-\n wt-status.c                       |  12 ++-\n wt-status.h                       |   1 +\n 18 files changed, 478 insertions(+), 131 deletions(-)\n\nInterdiff against v7:\ndiff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt\nindex b4ff8e136d..2601e1868e 100644\n--- a/Documentation/git-cherry-pick.txt\n+++ b/Documentation/git-cherry-pick.txt\n@@ -60,9 +60,9 @@ OPTIONS\n --cleanup=<mode>::\n \tThis option determines how the commit message will be cleaned up before\n \tbeing passed on. See linkgit:git-commit[1] for more details. In\n-\taddition, if the '<mode>' is given a value of `scissors`, scissors will\n-\tbe appended to MERGE_MSG before being passed on in the case of a\n-\tconflict.\n+\tparticular, if the '<mode>' is given a value of `scissors`, scissors\n+\twill be appended to `MERGE_MSG` before being passed on to the commit\n+\tmachinery in the case of a conflict.\n \n -x::\n \tWhen recording the commit, append a line that says\ndiff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt\nindex bd4ad395a9..cabeb9df2c 100644\n--- a/Documentation/git-revert.txt\n+++ b/Documentation/git-revert.txt\n@@ -69,9 +69,9 @@ more details.\n --cleanup=<mode>::\n \tThis option determines how the commit message will be cleaned up before\n \tbeing passed on. See linkgit:git-commit[1] for more details. In\n-\taddition, if the '<mode>' is given a value of `scissors`, scissors will\n-\tbe appended to MERGE_MSG before being passed on in the case of a\n-\tconflict.\n+\tparticular, if the '<mode>' is given a value of `scissors`, scissors\n+\twill be appended to `MERGE_MSG` before being passed on to the commit\n+\tmachinery in the case of a conflict.\n \n -n::\n --no-commit::\ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex c7b889d6f5..405e16c617 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -36,8 +36,8 @@ set to `no` at the beginning of them.\n \tThis option determines how the merge message will be cleaned up\n \tbefore commiting or being passed on. See linkgit:git-commit[1] for more\n \tdetails. In addition, if the '<mode>' is given a value of `scissors`,\n-\tscissors will be appended to MERGE_MSG before being passed on in the\n-\tcase of a merge conflict.\n+\tscissors will be appended to `MERGE_MSG` before being passed on to the\n+\tcommit machinery in the case of a merge conflict.\n \n --ff::\n \tWhen the merge resolves as a fast-forward, only update the branch\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex d8c4626a68..b9159c2e7b 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1483,7 +1483,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL('s', \"signoff\", &signoff, N_(\"add Signed-off-by:\")),\n \t\tOPT_FILENAME('t', \"template\", &template_file, N_(\"use specified template file\")),\n \t\tOPT_BOOL('e', \"edit\", &edit_flag, N_(\"force edit of commit\")),\n-\t\tOPT_STRING(0, \"cleanup\", &cleanup_arg, N_(\"default\"), N_(\"how to strip spaces and #comments from message\")),\n+\t\tOPT_CLEANUP(&cleanup_arg),\n \t\tOPT_BOOL(0, \"status\", &include_status, N_(\"include status in commit message template\")),\n \t\t{ OPTION_STRING, 'S', \"gpg-sign\", &sign_commit, N_(\"key-id\"),\n \t\t  N_(\"GPG sign commit\"), PARSE_OPT_OPTARG, NULL, (intptr_t) \"\" },\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 671d6a19a4..90c64346e9 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -253,7 +253,7 @@ static struct option builtin_merge_options[] = {\n \t\tN_(\"perform a commit if the merge succeeds (default)\")),\n \tOPT_BOOL('e', \"edit\", &option_edit,\n \t\tN_(\"edit message before committing\")),\n-\tOPT_STRING(0, \"cleanup\", &cleanup_arg, N_(\"default\"), N_(\"how to strip spaces and #comments from message\")),\n+\tOPT_CLEANUP(&cleanup_arg),\n \tOPT_SET_INT(0, \"ff\", &fast_forward, N_(\"allow fast-forward (default)\"), FF_ALLOW),\n \tOPT_SET_INT_F(0, \"ff-only\", &fast_forward,\n \t\t      N_(\"abort if fast-forward is not possible\"),\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 3ec7882375..292c1dac27 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -171,7 +171,7 @@ static struct option pull_options[] = {\n \t\tPARSE_OPT_NOARG),\n \tOPT_PASSTHRU(0, \"cleanup\", &opt_cleanup, NULL,\n \t\tN_(\"how to strip spaces and #comments from message\"),\n-\t\tPARSE_OPT_NOARG),\n+\t\t0),\n \tOPT_PASSTHRU(0, \"ff\", &opt_ff, NULL,\n \t\tN_(\"allow fast-forward\"),\n \t\tPARSE_OPT_NOARG),\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex e8168e0214..41e7392c24 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -102,7 +102,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)\n \t\tOPT_CMDMODE(0, \"quit\", &cmd, N_(\"end revert or cherry-pick sequence\"), 'q'),\n \t\tOPT_CMDMODE(0, \"continue\", &cmd, N_(\"resume revert or cherry-pick sequence\"), 'c'),\n \t\tOPT_CMDMODE(0, \"abort\", &cmd, N_(\"cancel revert or cherry-pick sequence\"), 'a'),\n-\t\tOPT_STRING(0, \"cleanup\", &cleanup_arg, N_(\"default\"), N_(\"how to strip spaces and #comments from message\")),\n+\t\tOPT_CLEANUP(&cleanup_arg),\n \t\tOPT_BOOL('n', \"no-commit\", &opts->no_commit, N_(\"don't automatically commit\")),\n \t\tOPT_BOOL('e', \"edit\", &opts->edit, N_(\"edit the commit message\")),\n \t\tOPT_NOOP_NOARG('r', NULL),\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 02f6bd1279..a3870fbdba 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -397,8 +397,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tOPT_FILENAME('F', \"file\", &msgfile, N_(\"read message from file\")),\n \t\tOPT_BOOL('e', \"edit\", &edit_flag, N_(\"force edit of tag message\")),\n \t\tOPT_BOOL('s', \"sign\", &opt.sign, N_(\"annotated and GPG-signed tag\")),\n-\t\tOPT_STRING(0, \"cleanup\", &cleanup_arg, N_(\"mode\"),\n-\t\t\tN_(\"how to strip spaces and #comments from message\")),\n+\t\tOPT_CLEANUP(&cleanup_arg),\n \t\tOPT_STRING('u', \"local-user\", &keyid, N_(\"key-id\"),\n \t\t\t\t\tN_(\"use another key to sign the tag\")),\n \t\tOPT__FORCE(&force, N_(\"replace the tag if exists\"), 0),\ndiff --git a/parse-options.h b/parse-options.h\nindex 7d83e2971d..85faaee390 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -303,5 +303,6 @@ int parse_opt_passthru_argv(const struct option *, const char *, int);\n #define OPT_NO_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH(\"no-contains\", v, h, PARSE_OPT_NONEG)\n #define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH(\"with\", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)\n #define OPT_WITHOUT(v, h) _OPT_CONTAINS_OR_WITH(\"without\", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)\n+#define OPT_CLEANUP(v) OPT_STRING(0, \"cleanup\", v, N_(\"mode\"), N_(\"how to strip spaces and #comments from message\"))\n \n #endif\ndiff --git a/sequencer.c b/sequencer.c\nindex 496554a474..ea4687c3da 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -161,13 +161,13 @@ static GIT_PATH_FUNC(rebase_path_allow_rerere_autoupdate, \"rebase-merge/allow_re\n static GIT_PATH_FUNC(rebase_path_reschedule_failed_exec, \"rebase-merge/reschedule-failed-exec\")\n \n struct cleanup_config_mapping {\n-    const char *config_value;\n-    enum commit_msg_cleanup_mode editor_cleanup;\n-    enum commit_msg_cleanup_mode no_editor_cleanup;\n+\tconst char *value;\n+\tenum commit_msg_cleanup_mode editor;\n+\tenum commit_msg_cleanup_mode no_editor;\n };\n \n-/* note that we assume that cleanup_config_mapping[0] contains the default settings */\n-struct cleanup_config_mapping cleanup_config_mappings[] = {\n+/* the 0th element of this array must be the \"default\" */\n+static struct cleanup_config_mapping cleanup_config_mapping[] = {\n \t{ \"default\", COMMIT_MSG_CLEANUP_ALL, COMMIT_MSG_CLEANUP_SPACE },\n \t{ \"verbatim\", COMMIT_MSG_CLEANUP_NONE, COMMIT_MSG_CLEANUP_NONE },\n \t{ \"whitespace\", COMMIT_MSG_CLEANUP_SPACE, COMMIT_MSG_CLEANUP_SPACE },\n@@ -520,40 +520,35 @@ static int fast_forward_to(struct repository *r,\n enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n \tint use_editor, int die_on_error)\n {\n-\tstruct cleanup_config_mapping *default_mapping = &cleanup_config_mappings[0];\n-\tstruct cleanup_config_mapping *current_mapping;\n+\tstruct cleanup_config_mapping *def = &cleanup_config_mapping[0];\n+\tstruct cleanup_config_mapping *p;\n \n-\tif (!cleanup_arg) {\n-\t\treturn use_editor ? default_mapping->editor_cleanup :\n-\t\t\t\t    default_mapping->no_editor_cleanup;\n-\t}\n+\tif (!cleanup_arg)\n+\t\treturn use_editor ? def->editor :\n+\t\t\t\t    def->no_editor;\n \n-\tfor (current_mapping = cleanup_config_mappings; current_mapping->config_value; current_mapping++) {\n-\t\tif (!strcmp(cleanup_arg, current_mapping->config_value)) {\n-\t\t\treturn use_editor ? current_mapping->editor_cleanup :\n-\t\t\t\t\t    current_mapping->no_editor_cleanup;\n-\t\t}\n-\t}\n+\tfor (p = cleanup_config_mapping; p->value; p++)\n+\t\tif (!strcmp(cleanup_arg, p->value))\n+\t\t\treturn use_editor ? p->editor :\n+\t\t\t\t\t    p->no_editor;\n \n \tif (!die_on_error) {\n-\t\twarning(_(\"Invalid cleanup mode %s, falling back to default\"), cleanup_arg);\n-\t\treturn use_editor ? default_mapping->editor_cleanup :\n-\t\t\t\t    default_mapping->no_editor_cleanup;\n+\t\twarning(_(\"invalid cleanup mode %s, falling back to default\"), cleanup_arg);\n+\t\treturn use_editor ? def->editor :\n+\t\t\t\t    def->no_editor;\n \t} else\n-\t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n+\t\tdie(_(\"invalid cleanup mode %s\"), cleanup_arg);\n }\n \n-const char *get_config_from_cleanup(enum commit_msg_cleanup_mode cleanup_mode)\n+static const char *describe_cleanup_mode(enum commit_msg_cleanup_mode cleanup_mode)\n {\n-\tstruct cleanup_config_mapping *current_mapping;\n+\tstruct cleanup_config_mapping *curr;\n \n-\tfor (current_mapping = &cleanup_config_mappings[1]; current_mapping->config_value; current_mapping++) {\n-\t\tif (cleanup_mode == current_mapping->editor_cleanup) {\n-\t\t\treturn current_mapping->config_value;\n-\t\t}\n-\t}\n+\tfor (curr = &cleanup_config_mapping[1]; curr->value; curr++)\n+\t\tif (cleanup_mode == curr->editor)\n+\t\t\treturn curr->value;\n \n-\tBUG(_(\"invalid cleanup_mode provided\"));\n+\tBUG(\"invalid cleanup_mode provided (%d)\", cleanup_mode);\n }\n \n void append_conflicts_hint(struct index_state *istate,\n@@ -2796,7 +2791,7 @@ static int save_opts(struct replay_opts *opts)\n \t\t\t\t\t\t     \"true\" : \"false\");\n \n \tres |= git_config_set_in_file_gently(opts_file, \"options.default-msg-cleanup\",\n-\t\t\t\t\t     get_config_from_cleanup(opts->default_msg_cleanup));\n+\t\t\t\t\t     describe_cleanup_mode(opts->default_msg_cleanup));\n \treturn res;\n }\n \ndiff --git a/sequencer.h b/sequencer.h\nindex 95ff9db508..830d8232f6 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -119,7 +119,6 @@ void append_conflicts_hint(struct index_state *istate,\n \t\tstruct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode);\n enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n \tint use_editor, int die_on_error);\n-const char *get_config_from_cleanup(enum commit_msg_cleanup_mode cleanup_mode);\n \n void cleanup_message(struct strbuf *msgbuf,\n \tenum commit_msg_cleanup_mode cleanup_mode, int verbose);\ndiff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh\nindex ca4a740da0..f035e4a507 100755\n--- a/t/t7502-commit-porcelain.sh\n+++ b/t/t7502-commit-porcelain.sh\n@@ -16,7 +16,9 @@ commit_msg_is () {\n # Arguments: [<prefix] [<commit message>] [<commit options>]\n check_summary_oneline() {\n \ttest_tick &&\n-\tgit commit ${3+\"$3\"} -m \"$2\" | head -1 > act &&\n+\tgit commit ${3+\"$3\"} -m \"$2\" >act &&\n+\thead -1 <act >tmp &&\n+\tmv tmp act &&\n \n \t# branch name\n \tSUMMARY_PREFIX=\"$(git name-rev --name-only HEAD)\" &&\n@@ -68,7 +70,7 @@ test_expect_success 'output summary format for merges' '\n \tgit checkout recursive-a &&\n \ttest_must_fail git merge recursive-b &&\n \t# resolve the conflict\n-\techo commit-a > file1 &&\n+\techo commit-a >file1 &&\n \tgit add file1 &&\n \tcheck_summary_oneline \"\" \"Merge\"\n '\n@@ -142,8 +144,8 @@ test_expect_success 'sign off' '\n \t>positive &&\n \tgit add positive &&\n \tgit commit -s -m \"thank you\" &&\n-\tactual=$(git cat-file commit HEAD | sed -ne \"s/Signed-off-by: //p\") &&\n-\texpected=$(git var GIT_COMMITTER_IDENT | sed -e \"s/>.*/>/\") &&\n+\tactual=$(git cat-file commit HEAD >tmp && sed -ne \"s/Signed-off-by: //p\" <tmp && rm tmp) &&\n+\texpected=$(git var GIT_COMMITTER_IDENT >tmp && sed -e \"s/>.*/>/\" <tmp && rm tmp) &&\n \ttest \"z$actual\" = \"z$expected\"\n \n '\n@@ -153,8 +155,8 @@ test_expect_success 'multiple -m' '\n \t>negative &&\n \tgit add negative &&\n \tgit commit -m \"one\" -m \"two\" -m \"three\" &&\n-\tactual=$(git cat-file commit HEAD | sed -e \"1,/^\\$/d\") &&\n-\texpected=$(echo one; echo; echo two; echo; echo three) &&\n+\tactual=$(git cat-file commit HEAD >tmp && sed -e \"1,/^\\$/d\" <tmp && rm tmp) &&\n+\texpected=$(test_write_lines \"one\" \"\" \"two\" \"\" \"three\") &&\n \ttest \"z$actual\" = \"z$expected\"\n \n '\n@@ -163,7 +165,9 @@ test_expect_success 'verbose' '\n \n \techo minus >negative &&\n \tgit add negative &&\n-\tgit status -v | sed -ne \"/^diff --git /p\" >actual &&\n+\tgit status -v >actual &&\n+\tsed -ne \"/^diff --git /p\" <actual >tmp &&\n+\tmv tmp actual &&\n \techo \"diff --git a/negative b/negative\" >expect &&\n \ttest_cmp expect actual\n \n@@ -189,7 +193,9 @@ test_expect_success 'cleanup commit messages (verbatim option,-t)' '\n \n \techo >>negative &&\n \tgit commit --cleanup=verbatim --no-status -t expect -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\" >actual &&\n+\tgit cat-file -p HEAD >actual &&\n+\tsed -e \"1,/^\\$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp expect actual\n \n '\n@@ -198,7 +204,9 @@ test_expect_success 'cleanup commit messages (verbatim option,-F)' '\n \n \techo >>negative &&\n \tgit commit --cleanup=verbatim -F expect -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >actual &&\n+\tsed -e \"1,/^\\$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp expect actual\n \n '\n@@ -207,7 +215,9 @@ test_expect_success 'cleanup commit messages (verbatim option,-m)' '\n \n \techo >>negative &&\n \tgit commit --cleanup=verbatim -m \"$mesg_with_comment_and_newlines\" -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >actual &&\n+\tsed -e \"1,/^\\$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp expect actual\n \n '\n@@ -215,10 +225,12 @@ test_expect_success 'cleanup commit messages (verbatim option,-m)' '\n test_expect_success 'cleanup commit messages (whitespace option,-F)' '\n \n \techo >>negative &&\n-\t{ echo;echo \"# text\";echo; } >text &&\n+\ttest_write_lines \"\" \"# text\" \"\" >text &&\n \techo \"# text\" >expect &&\n \tgit commit --cleanup=whitespace -F text -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >actual &&\n+\tsed -e \"1,/^\\$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp expect actual\n \n '\n@@ -226,48 +238,54 @@ test_expect_success 'cleanup commit messages (whitespace option,-F)' '\n test_expect_success 'cleanup commit messages (scissors option,-F,-e)' '\n \n \techo >>negative &&\n-\tcat >text <<EOF &&\n+\tcat >text <<-\\EOF &&\n \n-# to be kept\n+\t# to be kept\n \n-  # ------------------------ >8 ------------------------\n-# to be kept, too\n-# ------------------------ >8 ------------------------\n-to be removed\n-# ------------------------ >8 ------------------------\n-to be removed, too\n-EOF\n+\t  # ------------------------ >8 ------------------------\n+\t# to be kept, too\n+\t# ------------------------ >8 ------------------------\n+\tto be removed\n+\t# ------------------------ >8 ------------------------\n+\tto be removed, too\n+\tEOF\n \n-\tcat >expect <<EOF &&\n-# to be kept\n+\tcat >expect <<-\\EOF &&\n+\t# to be kept\n \n-  # ------------------------ >8 ------------------------\n-# to be kept, too\n-EOF\n+\t  # ------------------------ >8 ------------------------\n+\t# to be kept, too\n+\tEOF\n \tgit commit --cleanup=scissors -e -F text -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >actual &&\n+\tsed -e \"1,/^\\$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp expect actual\n '\n \n test_expect_success 'cleanup commit messages (scissors option,-F,-e, scissors on first line)' '\n \n \techo >>negative &&\n-\tcat >text <<EOF &&\n-# ------------------------ >8 ------------------------\n-to be removed\n-EOF\n+\tcat >text <<-\\EOF &&\n+\t# ------------------------ >8 ------------------------\n+\tto be removed\n+\tEOF\n \tgit commit --cleanup=scissors -e -F text -a --allow-empty-message &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >actual &&\n+\tsed -e \"1,/^\\$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_must_be_empty actual\n '\n \n test_expect_success 'cleanup commit messages (strip option,-F)' '\n \n \techo >>negative &&\n-\t{ echo;echo \"# text\";echo sample;echo; } >text &&\n+\ttest_write_lines \"\" \"# text\" \"sample\" \"\" >text &&\n \techo sample >expect &&\n \tgit commit --cleanup=strip -F text -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >actual &&\n+\tsed -e \"1,/^\\$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp expect actual\n \n '\n@@ -275,7 +293,7 @@ test_expect_success 'cleanup commit messages (strip option,-F)' '\n test_expect_success 'cleanup commit messages (strip option,-F,-e)' '\n \n \techo >>negative &&\n-\t{ echo;echo sample;echo; } >text &&\n+\ttest_write_lines \"\" \"sample\" \"\" >text &&\n \tgit commit -e -F text -a &&\n \thead -n 4 .git/COMMIT_EDITMSG >actual\n '\n@@ -387,7 +405,7 @@ test_expect_success AUTOIDENT 'message shows committer when it is automatic' '\n '\n \n write_script .git/FAKE_EDITOR <<EOF\n-echo editor started > \"$(pwd)/.git/result\"\n+echo editor started >\"$(pwd)/.git/result\"\n exit 0\n EOF\n \n@@ -455,7 +473,7 @@ EOF\n test_expect_success EXECKEEPSPID 'a SIGTERM should break locks' '\n \techo >>negative &&\n \t! \"$SHELL_PATH\" -c '\\''\n-\t  echo kill -TERM $$ >> .git/FAKE_EDITOR\n+\t  echo kill -TERM $$ >>.git/FAKE_EDITOR\n \t  GIT_EDITOR=.git/FAKE_EDITOR\n \t  export GIT_EDITOR\n \t  exec git commit -a'\\'' &&\n@@ -471,7 +489,9 @@ test_expect_success 'Hand committing of a redundant merge removes dups' '\n \ttest_must_fail git merge second master &&\n \tgit checkout master g &&\n \tEDITOR=: git commit -a &&\n-\tgit cat-file commit HEAD | sed -n -e \"s/^parent //p\" -e \"/^$/q\" >actual &&\n+\tgit cat-file commit HEAD >actual &&\n+\tsed -n -e \"s/^parent //p\" -e \"/^$/q\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp expect actual\n \n '\n@@ -480,7 +500,9 @@ test_expect_success 'A single-liner subject with a token plus colon is not a foo\n \n \tgit reset --hard &&\n \tgit commit -s -m \"hello: kitty\" --allow-empty &&\n-\tgit cat-file commit HEAD | sed -e \"1,/^$/d\" >actual &&\n+\tgit cat-file commit HEAD >actual &&\n+\tsed -e \"1,/^$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_line_count = 3 actual\n \n '\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex 2cb57e5878..ed11b08db2 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -234,15 +234,16 @@ test_expect_success 'merge --squash c3 with c7' '\n \tgit commit --no-edit -a &&\n \n \tcat >expect <<-EOF &&\n-\t\tSquashed commit of the following:\n+\tSquashed commit of the following:\n \n-\t\t$(git show -s c7)\n+\t$(git show -s c7)\n \n-\t\t# Conflicts:\n-\t\t#\tfile\n-\t\tEOF\n-\tgit cat-file commit HEAD >tmp &&\n-\tsed -e '1,/^$/d' <tmp >actual &&\n+\t# Conflicts:\n+\t#\tfile\n+\tEOF\n+\tgit cat-file commit HEAD >actual &&\n+\tsed -e '1,/^$/d' <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp expect actual\n '\n \n@@ -253,18 +254,19 @@ test_expect_success 'merge c3 with c7 with commit.cleanup = scissors' '\n \tcat result.9z >file &&\n \tgit commit --no-edit -a &&\n \n-\tcat >expect <<-EOF &&\n-\t\tMerge tag '\"'\"'c7'\"'\"'\n-\n-\t\t# ------------------------ >8 ------------------------\n-\t\t# Do not modify or remove the line above.\n-\t\t# Everything below it will be ignored.\n-\t\t#\n-\t\t# Conflicts:\n-\t\t#\tfile\n-\t\tEOF\n-\tgit cat-file commit HEAD >tmp &&\n-\tsed -e '1,/^$/d' <tmp >actual &&\n+\tcat >expect <<-\\EOF &&\n+\tMerge tag '\"'\"'c7'\"'\"'\n+\n+\t# ------------------------ >8 ------------------------\n+\t# Do not modify or remove the line above.\n+\t# Everything below it will be ignored.\n+\t#\n+\t# Conflicts:\n+\t#\tfile\n+\tEOF\n+\tgit cat-file commit HEAD >actual &&\n+\tsed -e '1,/^$/d' <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_i18ncmp expect actual\n '\n \n@@ -276,19 +278,20 @@ test_expect_success 'merge c3 with c7 with --squash commit.cleanup = scissors' '\n \tgit commit --no-edit -a &&\n \n \tcat >expect <<-EOF &&\n-\t\tSquashed commit of the following:\n-\n-\t\t$(git show -s c7)\n-\n-\t\t# ------------------------ >8 ------------------------\n-\t\t# Do not modify or remove the line above.\n-\t\t# Everything below it will be ignored.\n-\t\t#\n-\t\t# Conflicts:\n-\t\t#\tfile\n-\t\tEOF\n-\tgit cat-file commit HEAD >tmp &&\n-\tsed -e '1,/^$/d' <tmp >actual &&\n+\tSquashed commit of the following:\n+\n+\t$(git show -s c7)\n+\n+\t# ------------------------ >8 ------------------------\n+\t# Do not modify or remove the line above.\n+\t# Everything below it will be ignored.\n+\t#\n+\t# Conflicts:\n+\t#\tfile\n+\tEOF\n+\tgit cat-file commit HEAD >actual &&\n+\tsed -e '1,/^$/d' <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_i18ncmp expect actual\n '\n \n@@ -728,7 +731,7 @@ cat >editor <<\\EOF\n \tcat <\"$1\"\n ) >\"$1.tmp\" && mv \"$1.tmp\" \"$1\"\n # strip comments and blank lines from end of message\n-sed -e '/^#/d' < \"$1\" | sed -e :a -e '/^\\n*$/{$d;N;ba' -e '}' > expected\n+sed -e '/^#/d' < \"$1\" | sed -e :a -e '/^\\n*$/{$d;N;ba' -e '}' >expected\n EOF\n chmod 755 editor\n \n@@ -859,7 +862,7 @@ EOF\n test_expect_success EXECKEEPSPID 'killed merge can be completed with --continue' '\n \tgit reset --hard c0 &&\n \t! \"$SHELL_PATH\" -c '\\''\n-\t  echo kill -TERM $$ >> .git/FAKE_EDITOR\n+\t  echo kill -TERM $$ >>.git/FAKE_EDITOR\n \t  GIT_EDITOR=.git/FAKE_EDITOR\n \t  export GIT_EDITOR\n \t  exec git merge --no-ff --edit c1'\\'' &&\ndiff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh\nindex 3fcd58eb2c..c9685a318d 100755\n--- a/t/t7604-merge-custom-message.sh\n+++ b/t/t7604-merge-custom-message.sh\n@@ -36,14 +36,18 @@ test_expect_success 'setup' '\n test_expect_success 'merge c2 with a custom message' '\n \tgit reset --hard c1 &&\n \tgit merge -m \"$(cat exp.subject)\" c2 &&\n-\tgit cat-file commit HEAD | sed -e \"1,/^$/d\" >actual &&\n+\tgit cat-file commit HEAD >actual &&\n+\tsed -e \"1,/^$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp exp.subject actual\n '\n \n test_expect_success 'merge --log appends to custom message' '\n \tgit reset --hard c1 &&\n \tgit merge --log -m \"$(cat exp.subject)\" c2 &&\n-\tgit cat-file commit HEAD | sed -e \"1,/^$/d\" >actual &&\n+\tgit cat-file commit HEAD >actual &&\n+\tsed -e \"1,/^$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp exp.log actual\n '\n \n@@ -59,53 +63,59 @@ test_expect_success 'prepare file with comment line and trailing newlines'  '\n test_expect_success 'cleanup commit messages (verbatim option)' '\n \tgit reset --hard c1 &&\n \tgit merge --cleanup=verbatim -F expect c2 &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\" >actual &&\n+\tgit cat-file commit HEAD >actual &&\n+\tsed -e \"1,/^$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp expect actual\n '\n \n test_expect_success 'cleanup commit messages (whitespace option)' '\n \tgit reset --hard c1 &&\n-\t{ echo;echo \"# text\";echo; } >text &&\n+\ttest_write_lines \"\" \"# text\" \"\" >text &&\n \techo \"# text\" >expect &&\n \tgit merge --cleanup=whitespace -F text c2 &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file commit HEAD >actual &&\n+\tsed -e \"1,/^$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp expect actual\n-\n '\n \n test_expect_success 'cleanup merge messages (scissors option)' '\n \tgit reset --hard c1 &&\n-\tcat >text <<EOF &&\n+\tcat >text <<-\\EOF &&\n \n-# to be kept\n+\t# to be kept\n \n-  # ------------------------ >8 ------------------------\n-# to be kept, too\n-# ------------------------ >8 ------------------------\n-to be removed\n-# ------------------------ >8 ------------------------\n-to be removed, too\n-EOF\n+\t  # ------------------------ >8 ------------------------\n+\t# to be kept, too\n+\t# ------------------------ >8 ------------------------\n+\tto be removed\n+\t# ------------------------ >8 ------------------------\n+\tto be removed, too\n+\tEOF\n \n-\tcat >expect <<EOF &&\n-# to be kept\n+\tcat >expect <<-\\EOF &&\n+\t# to be kept\n \n-  # ------------------------ >8 ------------------------\n-# to be kept, too\n-EOF\n+\t  # ------------------------ >8 ------------------------\n+\t# to be kept, too\n+\tEOF\n \tgit merge --cleanup=scissors -e -F text c2 &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file commit HEAD >actual &&\n+\tsed -e \"1,/^$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp expect actual\n '\n \n test_expect_success 'cleanup commit messages (strip option)' '\n \tgit reset --hard c1 &&\n-\t{ echo;echo \"# text\";echo sample;echo; } >text &&\n+\ttest_write_lines \"\" \"# text\" \"sample\" \"\" >text &&\n \techo sample >expect &&\n \tgit merge --cleanup=strip -F text c2 &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file commit HEAD >actual &&\n+\tsed -e \"1,/^$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp expect actual\n-\n '\n \n test_done\n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"371682","messageId":"08426189b5a29b376581eb0172e52222ab22387a.1552817044.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1552817044.git.liu.denton@gmail.com","subject":"[PATCH v8 01/11] t7600: clean up style","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-17T10:15:53Z","receivedAt":"2019-03-17T10:15:59Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"This cleans up the 'merge --squash c3 with c7' test by removing some\nunnecessary braces and removing a pipe. Finally, unindent a here-doc for\nstyle.\n\nAlso, remove stray spaces after \"> \" for style.\n\nHelped-by: SZEDER Gábor <szeder.dev@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nReviewed-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t7600-merge.sh | 22 +++++++++++-----------\n 1 file changed, 11 insertions(+), 11 deletions(-)\n\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex 106148254d..a9620cd991 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -233,17 +233,17 @@ test_expect_success 'merge --squash c3 with c7' '\n \tcat result.9z >file &&\n \tgit commit --no-edit -a &&\n \n-\t{\n-\t\tcat <<-EOF\n-\t\tSquashed commit of the following:\n+\tcat >expect <<-EOF &&\n+\tSquashed commit of the following:\n \n-\t\t$(git show -s c7)\n+\t$(git show -s c7)\n \n-\t\t# Conflicts:\n-\t\t#\tfile\n-\t\tEOF\n-\t} >expect &&\n-\tgit cat-file commit HEAD | sed -e '1,/^$/d' >actual &&\n+\t# Conflicts:\n+\t#\tfile\n+\tEOF\n+\tgit cat-file commit HEAD >actual &&\n+\tsed -e '1,/^$/d' <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp expect actual\n '\n \n@@ -683,7 +683,7 @@ cat >editor <<\\EOF\n \tcat <\"$1\"\n ) >\"$1.tmp\" && mv \"$1.tmp\" \"$1\"\n # strip comments and blank lines from end of message\n-sed -e '/^#/d' < \"$1\" | sed -e :a -e '/^\\n*$/{$d;N;ba' -e '}' > expected\n+sed -e '/^#/d' < \"$1\" | sed -e :a -e '/^\\n*$/{$d;N;ba' -e '}' >expected\n EOF\n chmod 755 editor\n \n@@ -814,7 +814,7 @@ EOF\n test_expect_success EXECKEEPSPID 'killed merge can be completed with --continue' '\n \tgit reset --hard c0 &&\n \t! \"$SHELL_PATH\" -c '\\''\n-\t  echo kill -TERM $$ >> .git/FAKE_EDITOR\n+\t  echo kill -TERM $$ >>.git/FAKE_EDITOR\n \t  GIT_EDITOR=.git/FAKE_EDITOR\n \t  export GIT_EDITOR\n \t  exec git merge --no-ff --edit c1'\\'' &&\n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"371683","messageId":"8bd03165e4703d6a791a617838a82fd0cb4f7e02.1552817044.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1552817044.git.liu.denton@gmail.com","subject":"[PATCH v8 02/11] t3507: cleanup space after redirection operators","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-17T10:15:56Z","receivedAt":"2019-03-17T10:16:01Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Signed-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t3507-cherry-pick-conflict.sh | 34 ++++++++++++++++-----------------\n 1 file changed, 17 insertions(+), 17 deletions(-)\n\ndiff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh\nindex 0db166152a..74ff925526 100755\n--- a/t/t3507-cherry-pick-conflict.sh\n+++ b/t/t3507-cherry-pick-conflict.sh\n@@ -88,7 +88,7 @@ test_expect_success 'cherry-pick --no-commit does not set CHERRY_PICK_HEAD' '\n \n test_expect_success 'cherry-pick w/dirty tree does not set CHERRY_PICK_HEAD' '\n \tpristine_detach initial &&\n-\techo foo > foo &&\n+\techo foo >foo &&\n \ttest_must_fail git cherry-pick base &&\n \ttest_must_fail git rev-parse --verify CHERRY_PICK_HEAD\n '\n@@ -96,7 +96,7 @@ test_expect_success 'cherry-pick w/dirty tree does not set CHERRY_PICK_HEAD' '\n test_expect_success \\\n \t'cherry-pick --strategy=resolve w/dirty tree does not set CHERRY_PICK_HEAD' '\n \tpristine_detach initial &&\n-\techo foo > foo &&\n+\techo foo >foo &&\n \ttest_must_fail git cherry-pick --strategy=resolve base &&\n \ttest_must_fail git rev-parse --verify CHERRY_PICK_HEAD\n '\n@@ -175,23 +175,23 @@ test_expect_success 'failed cherry-pick registers participants in index' '\n \t\tgit ls-files --stage foo &&\n \t\tgit checkout picked -- foo &&\n \t\tgit ls-files --stage foo\n-\t} > stages &&\n+\t} >stages &&\n \tsed \"\n \t\t1 s/ 0\t/ 1\t/\n \t\t2 s/ 0\t/ 2\t/\n \t\t3 s/ 0\t/ 3\t/\n-\t\" < stages > expected &&\n+\t\" <stages >expected &&\n \tgit read-tree -u --reset HEAD &&\n \n \ttest_must_fail git cherry-pick picked &&\n-\tgit ls-files --stage --unmerged > actual &&\n+\tgit ls-files --stage --unmerged >actual &&\n \n \ttest_cmp expected actual\n '\n \n test_expect_success 'failed cherry-pick describes conflict in work tree' '\n \tpristine_detach initial &&\n-\tcat <<-EOF > expected &&\n+\tcat <<-EOF >expected &&\n \t<<<<<<< HEAD\n \ta\n \t=======\n@@ -201,14 +201,14 @@ test_expect_success 'failed cherry-pick describes conflict in work tree' '\n \n \ttest_must_fail git cherry-pick picked &&\n \n-\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo > actual &&\n+\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo >actual &&\n \ttest_cmp expected actual\n '\n \n test_expect_success 'diff3 -m style' '\n \tpristine_detach initial &&\n \tgit config merge.conflictstyle diff3 &&\n-\tcat <<-EOF > expected &&\n+\tcat <<-EOF >expected &&\n \t<<<<<<< HEAD\n \ta\n \t||||||| parent of objid picked\n@@ -220,14 +220,14 @@ test_expect_success 'diff3 -m style' '\n \n \ttest_must_fail git cherry-pick picked &&\n \n-\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo > actual &&\n+\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo >actual &&\n \ttest_cmp expected actual\n '\n \n test_expect_success 'revert also handles conflicts sanely' '\n \tgit config --unset merge.conflictstyle &&\n \tpristine_detach initial &&\n-\tcat <<-EOF > expected &&\n+\tcat <<-EOF >expected &&\n \t<<<<<<< HEAD\n \ta\n \t=======\n@@ -241,24 +241,24 @@ test_expect_success 'revert also handles conflicts sanely' '\n \t\tgit ls-files --stage foo &&\n \t\tgit checkout base -- foo &&\n \t\tgit ls-files --stage foo\n-\t} > stages &&\n+\t} >stages &&\n \tsed \"\n \t\t1 s/ 0\t/ 1\t/\n \t\t2 s/ 0\t/ 2\t/\n \t\t3 s/ 0\t/ 3\t/\n-\t\" < stages > expected-stages &&\n+\t\" <stages >expected-stages &&\n \tgit read-tree -u --reset HEAD &&\n \n \thead=$(git rev-parse HEAD) &&\n \ttest_must_fail git revert picked &&\n \tnewhead=$(git rev-parse HEAD) &&\n-\tgit ls-files --stage --unmerged > actual-stages &&\n+\tgit ls-files --stage --unmerged >actual-stages &&\n \n \ttest \"$head\" = \"$newhead\" &&\n \ttest_must_fail git update-index --refresh -q &&\n \ttest_must_fail git diff-index --exit-code HEAD &&\n \ttest_cmp expected-stages actual-stages &&\n-\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo > actual &&\n+\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -284,7 +284,7 @@ test_expect_success 'revert --no-commit sets REVERT_HEAD' '\n \n test_expect_success 'revert w/dirty tree does not set REVERT_HEAD' '\n \tpristine_detach base &&\n-\techo foo > foo &&\n+\techo foo >foo &&\n \ttest_must_fail git revert base &&\n \ttest_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&\n \ttest_must_fail git rev-parse --verify REVERT_HEAD\n@@ -319,7 +319,7 @@ test_expect_success 'failed commit does not clear REVERT_HEAD' '\n test_expect_success 'revert conflict, diff3 -m style' '\n \tpristine_detach initial &&\n \tgit config merge.conflictstyle diff3 &&\n-\tcat <<-EOF > expected &&\n+\tcat <<-EOF >expected &&\n \t<<<<<<< HEAD\n \ta\n \t||||||| objid picked\n@@ -331,7 +331,7 @@ test_expect_success 'revert conflict, diff3 -m style' '\n \n \ttest_must_fail git revert picked &&\n \n-\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo > actual &&\n+\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo >actual &&\n \ttest_cmp expected actual\n '\n \n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"371684","messageId":"9db7453b4e2b7221a039aa18509589e5c4f662b3.1552817044.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1552817044.git.liu.denton@gmail.com","subject":"[PATCH v8 03/11] t7604: refactor out Git commands upstream of pipe","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-17T10:15:59Z","receivedAt":"2019-03-17T10:16:04Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Before, we had some Git commands which were upstream of the pipe. This\nmeant that if it produced an error, it would've gone unnoticed. Refactor\nto place Git commands on their own.\n\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t7604-merge-custom-message.sh | 8 ++++++--\n 1 file changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh\nindex 89619cf446..b045fdb413 100755\n--- a/t/t7604-merge-custom-message.sh\n+++ b/t/t7604-merge-custom-message.sh\n@@ -36,14 +36,18 @@ test_expect_success 'setup' '\n test_expect_success 'merge c2 with a custom message' '\n \tgit reset --hard c1 &&\n \tgit merge -m \"$(cat exp.subject)\" c2 &&\n-\tgit cat-file commit HEAD | sed -e \"1,/^$/d\" >actual &&\n+\tgit cat-file commit HEAD >actual &&\n+\tsed -e \"1,/^$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp exp.subject actual\n '\n \n test_expect_success 'merge --log appends to custom message' '\n \tgit reset --hard c1 &&\n \tgit merge --log -m \"$(cat exp.subject)\" c2 &&\n-\tgit cat-file commit HEAD | sed -e \"1,/^$/d\" >actual &&\n+\tgit cat-file commit HEAD >actual &&\n+\tsed -e \"1,/^$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp exp.log actual\n '\n \n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"371685","messageId":"0759de616eb57825852a2a1b5e27f6e884d68fc2.1552817044.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1552817044.git.liu.denton@gmail.com","subject":"[PATCH v8 04/11] t7502: clean up test style","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-17T10:16:02Z","receivedAt":"2019-03-17T10:16:07Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Refactor out git commands that were upstream of a pipe. Also, as a style\ncleanup, remove spaces after \"> \". Next, indent here-docs. Finally,\nconvert echo chains to use the test_write_lines function.\n\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t7502-commit-porcelain.sh | 100 ++++++++++++++++++++++--------------\n 1 file changed, 61 insertions(+), 39 deletions(-)\n\ndiff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh\nindex ca4a740da0..f035e4a507 100755\n--- a/t/t7502-commit-porcelain.sh\n+++ b/t/t7502-commit-porcelain.sh\n@@ -16,7 +16,9 @@ commit_msg_is () {\n # Arguments: [<prefix] [<commit message>] [<commit options>]\n check_summary_oneline() {\n \ttest_tick &&\n-\tgit commit ${3+\"$3\"} -m \"$2\" | head -1 > act &&\n+\tgit commit ${3+\"$3\"} -m \"$2\" >act &&\n+\thead -1 <act >tmp &&\n+\tmv tmp act &&\n \n \t# branch name\n \tSUMMARY_PREFIX=\"$(git name-rev --name-only HEAD)\" &&\n@@ -68,7 +70,7 @@ test_expect_success 'output summary format for merges' '\n \tgit checkout recursive-a &&\n \ttest_must_fail git merge recursive-b &&\n \t# resolve the conflict\n-\techo commit-a > file1 &&\n+\techo commit-a >file1 &&\n \tgit add file1 &&\n \tcheck_summary_oneline \"\" \"Merge\"\n '\n@@ -142,8 +144,8 @@ test_expect_success 'sign off' '\n \t>positive &&\n \tgit add positive &&\n \tgit commit -s -m \"thank you\" &&\n-\tactual=$(git cat-file commit HEAD | sed -ne \"s/Signed-off-by: //p\") &&\n-\texpected=$(git var GIT_COMMITTER_IDENT | sed -e \"s/>.*/>/\") &&\n+\tactual=$(git cat-file commit HEAD >tmp && sed -ne \"s/Signed-off-by: //p\" <tmp && rm tmp) &&\n+\texpected=$(git var GIT_COMMITTER_IDENT >tmp && sed -e \"s/>.*/>/\" <tmp && rm tmp) &&\n \ttest \"z$actual\" = \"z$expected\"\n \n '\n@@ -153,8 +155,8 @@ test_expect_success 'multiple -m' '\n \t>negative &&\n \tgit add negative &&\n \tgit commit -m \"one\" -m \"two\" -m \"three\" &&\n-\tactual=$(git cat-file commit HEAD | sed -e \"1,/^\\$/d\") &&\n-\texpected=$(echo one; echo; echo two; echo; echo three) &&\n+\tactual=$(git cat-file commit HEAD >tmp && sed -e \"1,/^\\$/d\" <tmp && rm tmp) &&\n+\texpected=$(test_write_lines \"one\" \"\" \"two\" \"\" \"three\") &&\n \ttest \"z$actual\" = \"z$expected\"\n \n '\n@@ -163,7 +165,9 @@ test_expect_success 'verbose' '\n \n \techo minus >negative &&\n \tgit add negative &&\n-\tgit status -v | sed -ne \"/^diff --git /p\" >actual &&\n+\tgit status -v >actual &&\n+\tsed -ne \"/^diff --git /p\" <actual >tmp &&\n+\tmv tmp actual &&\n \techo \"diff --git a/negative b/negative\" >expect &&\n \ttest_cmp expect actual\n \n@@ -189,7 +193,9 @@ test_expect_success 'cleanup commit messages (verbatim option,-t)' '\n \n \techo >>negative &&\n \tgit commit --cleanup=verbatim --no-status -t expect -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\" >actual &&\n+\tgit cat-file -p HEAD >actual &&\n+\tsed -e \"1,/^\\$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp expect actual\n \n '\n@@ -198,7 +204,9 @@ test_expect_success 'cleanup commit messages (verbatim option,-F)' '\n \n \techo >>negative &&\n \tgit commit --cleanup=verbatim -F expect -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >actual &&\n+\tsed -e \"1,/^\\$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp expect actual\n \n '\n@@ -207,7 +215,9 @@ test_expect_success 'cleanup commit messages (verbatim option,-m)' '\n \n \techo >>negative &&\n \tgit commit --cleanup=verbatim -m \"$mesg_with_comment_and_newlines\" -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >actual &&\n+\tsed -e \"1,/^\\$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp expect actual\n \n '\n@@ -215,10 +225,12 @@ test_expect_success 'cleanup commit messages (verbatim option,-m)' '\n test_expect_success 'cleanup commit messages (whitespace option,-F)' '\n \n \techo >>negative &&\n-\t{ echo;echo \"# text\";echo; } >text &&\n+\ttest_write_lines \"\" \"# text\" \"\" >text &&\n \techo \"# text\" >expect &&\n \tgit commit --cleanup=whitespace -F text -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >actual &&\n+\tsed -e \"1,/^\\$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp expect actual\n \n '\n@@ -226,48 +238,54 @@ test_expect_success 'cleanup commit messages (whitespace option,-F)' '\n test_expect_success 'cleanup commit messages (scissors option,-F,-e)' '\n \n \techo >>negative &&\n-\tcat >text <<EOF &&\n+\tcat >text <<-\\EOF &&\n \n-# to be kept\n+\t# to be kept\n \n-  # ------------------------ >8 ------------------------\n-# to be kept, too\n-# ------------------------ >8 ------------------------\n-to be removed\n-# ------------------------ >8 ------------------------\n-to be removed, too\n-EOF\n+\t  # ------------------------ >8 ------------------------\n+\t# to be kept, too\n+\t# ------------------------ >8 ------------------------\n+\tto be removed\n+\t# ------------------------ >8 ------------------------\n+\tto be removed, too\n+\tEOF\n \n-\tcat >expect <<EOF &&\n-# to be kept\n+\tcat >expect <<-\\EOF &&\n+\t# to be kept\n \n-  # ------------------------ >8 ------------------------\n-# to be kept, too\n-EOF\n+\t  # ------------------------ >8 ------------------------\n+\t# to be kept, too\n+\tEOF\n \tgit commit --cleanup=scissors -e -F text -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >actual &&\n+\tsed -e \"1,/^\\$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp expect actual\n '\n \n test_expect_success 'cleanup commit messages (scissors option,-F,-e, scissors on first line)' '\n \n \techo >>negative &&\n-\tcat >text <<EOF &&\n-# ------------------------ >8 ------------------------\n-to be removed\n-EOF\n+\tcat >text <<-\\EOF &&\n+\t# ------------------------ >8 ------------------------\n+\tto be removed\n+\tEOF\n \tgit commit --cleanup=scissors -e -F text -a --allow-empty-message &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >actual &&\n+\tsed -e \"1,/^\\$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_must_be_empty actual\n '\n \n test_expect_success 'cleanup commit messages (strip option,-F)' '\n \n \techo >>negative &&\n-\t{ echo;echo \"# text\";echo sample;echo; } >text &&\n+\ttest_write_lines \"\" \"# text\" \"sample\" \"\" >text &&\n \techo sample >expect &&\n \tgit commit --cleanup=strip -F text -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >actual &&\n+\tsed -e \"1,/^\\$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp expect actual\n \n '\n@@ -275,7 +293,7 @@ test_expect_success 'cleanup commit messages (strip option,-F)' '\n test_expect_success 'cleanup commit messages (strip option,-F,-e)' '\n \n \techo >>negative &&\n-\t{ echo;echo sample;echo; } >text &&\n+\ttest_write_lines \"\" \"sample\" \"\" >text &&\n \tgit commit -e -F text -a &&\n \thead -n 4 .git/COMMIT_EDITMSG >actual\n '\n@@ -387,7 +405,7 @@ test_expect_success AUTOIDENT 'message shows committer when it is automatic' '\n '\n \n write_script .git/FAKE_EDITOR <<EOF\n-echo editor started > \"$(pwd)/.git/result\"\n+echo editor started >\"$(pwd)/.git/result\"\n exit 0\n EOF\n \n@@ -455,7 +473,7 @@ EOF\n test_expect_success EXECKEEPSPID 'a SIGTERM should break locks' '\n \techo >>negative &&\n \t! \"$SHELL_PATH\" -c '\\''\n-\t  echo kill -TERM $$ >> .git/FAKE_EDITOR\n+\t  echo kill -TERM $$ >>.git/FAKE_EDITOR\n \t  GIT_EDITOR=.git/FAKE_EDITOR\n \t  export GIT_EDITOR\n \t  exec git commit -a'\\'' &&\n@@ -471,7 +489,9 @@ test_expect_success 'Hand committing of a redundant merge removes dups' '\n \ttest_must_fail git merge second master &&\n \tgit checkout master g &&\n \tEDITOR=: git commit -a &&\n-\tgit cat-file commit HEAD | sed -n -e \"s/^parent //p\" -e \"/^$/q\" >actual &&\n+\tgit cat-file commit HEAD >actual &&\n+\tsed -n -e \"s/^parent //p\" -e \"/^$/q\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_cmp expect actual\n \n '\n@@ -480,7 +500,9 @@ test_expect_success 'A single-liner subject with a token plus colon is not a foo\n \n \tgit reset --hard &&\n \tgit commit -s -m \"hello: kitty\" --allow-empty &&\n-\tgit cat-file commit HEAD | sed -e \"1,/^$/d\" >actual &&\n+\tgit cat-file commit HEAD >actual &&\n+\tsed -e \"1,/^$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n \ttest_line_count = 3 actual\n \n '\n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"371686","messageId":"d2e4db44426dbe3980dbe2cbc9afa0b724818e2b.1552817044.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1552817044.git.liu.denton@gmail.com","subject":"[PATCH v8 05/11] commit: extract cleanup_mode functions to sequencer","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-17T10:16:05Z","receivedAt":"2019-03-17T10:16:10Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Signed-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/commit.c | 25 ++-----------------------\n sequencer.c      | 29 +++++++++++++++++++++++++++++\n sequencer.h      |  6 ++++++\n 3 files changed, 37 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex f17537474a..0df15e4851 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1172,24 +1172,7 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t\tdie(_(\"Only one of --include/--only/--all/--interactive/--patch can be used.\"));\n \tif (argc == 0 && (also || (only && !amend && !allow_empty)))\n \t\tdie(_(\"No paths with --include/--only does not make sense.\"));\n-\tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n-\t\tcleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_ALL :\n-\t\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n-\telse if (!strcmp(cleanup_arg, \"verbatim\"))\n-\t\tcleanup_mode = COMMIT_MSG_CLEANUP_NONE;\n-\telse if (!strcmp(cleanup_arg, \"whitespace\"))\n-\t\tcleanup_mode = COMMIT_MSG_CLEANUP_SPACE;\n-\telse if (!strcmp(cleanup_arg, \"strip\"))\n-\t\tcleanup_mode = COMMIT_MSG_CLEANUP_ALL;\n-\telse if (!strcmp(cleanup_arg, \"scissors\"))\n-\t\tcleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n-\t\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n-\t/*\n-\t * Please update _git_commit() in git-completion.bash when you\n-\t * add new options.\n-\t */\n-\telse\n-\t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n+\tcleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);\n \n \thandle_untracked_files_arg(s);\n \n@@ -1626,11 +1609,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"could not read commit message: %s\"), strerror(saved_errno));\n \t}\n \n-\tif (verbose || /* Truncate the message just before the diff, if any. */\n-\t    cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n-\t\tstrbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));\n-\tif (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)\n-\t\tstrbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);\n+\tcleanup_message(&sb, cleanup_mode, verbose);\n \n \tif (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {\n \t\trollback_index_files();\ndiff --git a/sequencer.c b/sequencer.c\nindex 95dda23eee..224c823b43 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -511,6 +511,25 @@ static int fast_forward_to(struct repository *r,\n \treturn 0;\n }\n \n+enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n+\tint use_editor)\n+{\n+\tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n+\t\treturn use_editor ? COMMIT_MSG_CLEANUP_ALL :\n+\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n+\telse if (!strcmp(cleanup_arg, \"verbatim\"))\n+\t\treturn COMMIT_MSG_CLEANUP_NONE;\n+\telse if (!strcmp(cleanup_arg, \"whitespace\"))\n+\t\treturn COMMIT_MSG_CLEANUP_SPACE;\n+\telse if (!strcmp(cleanup_arg, \"strip\"))\n+\t\treturn COMMIT_MSG_CLEANUP_ALL;\n+\telse if (!strcmp(cleanup_arg, \"scissors\"))\n+\t\treturn use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n+\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n+\telse\n+\t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n+}\n+\n void append_conflicts_hint(struct index_state *istate,\n \t\t\t   struct strbuf *msgbuf)\n {\n@@ -1013,6 +1032,16 @@ static int rest_is_empty(const struct strbuf *sb, int start)\n \treturn 1;\n }\n \n+void cleanup_message(struct strbuf *msgbuf,\n+\tenum commit_msg_cleanup_mode cleanup_mode, int verbose)\n+{\n+\tif (verbose || /* Truncate the message just before the diff, if any. */\n+\t    cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n+\t\tstrbuf_setlen(msgbuf, wt_status_locate_end(msgbuf->buf, msgbuf->len));\n+\tif (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)\n+\t\tstrbuf_stripspace(msgbuf, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);\n+}\n+\n /*\n  * Find out if the message in the strbuf contains only whitespace and\n  * Signed-off-by lines.\ndiff --git a/sequencer.h b/sequencer.h\nindex 4d505b3590..eb9bd97ef3 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -116,6 +116,12 @@ int rearrange_squash(struct repository *r);\n void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);\n \n void append_conflicts_hint(struct index_state *istate, struct strbuf *msgbuf);\n+enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n+\tint use_editor);\n+\n+void cleanup_message(struct strbuf *msgbuf,\n+\tenum commit_msg_cleanup_mode cleanup_mode, int verbose);\n+\n int message_is_empty(const struct strbuf *sb,\n \t\t     enum commit_msg_cleanup_mode cleanup_mode);\n int template_untouched(const struct strbuf *sb, const char *template_file,\n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"371687","messageId":"19cdc7528c155cd887299a7c24b3bd5ce05a67a6.1552817044.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1552817044.git.liu.denton@gmail.com","subject":"[PATCH v8 06/11] parse-options.h: extract common --cleanup option","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-17T10:16:07Z","receivedAt":"2019-03-17T10:16:13Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"The --cleanup option is commonly used. Extract it so that its definition\nis not repeated.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/commit.c | 2 +-\n builtin/tag.c    | 3 +--\n parse-options.h  | 1 +\n 3 files changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 0df15e4851..8277da8474 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1473,7 +1473,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL('s', \"signoff\", &signoff, N_(\"add Signed-off-by:\")),\n \t\tOPT_FILENAME('t', \"template\", &template_file, N_(\"use specified template file\")),\n \t\tOPT_BOOL('e', \"edit\", &edit_flag, N_(\"force edit of commit\")),\n-\t\tOPT_STRING(0, \"cleanup\", &cleanup_arg, N_(\"default\"), N_(\"how to strip spaces and #comments from message\")),\n+\t\tOPT_CLEANUP(&cleanup_arg),\n \t\tOPT_BOOL(0, \"status\", &include_status, N_(\"include status in commit message template\")),\n \t\t{ OPTION_STRING, 'S', \"gpg-sign\", &sign_commit, N_(\"key-id\"),\n \t\t  N_(\"GPG sign commit\"), PARSE_OPT_OPTARG, NULL, (intptr_t) \"\" },\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 02f6bd1279..a3870fbdba 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -397,8 +397,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tOPT_FILENAME('F', \"file\", &msgfile, N_(\"read message from file\")),\n \t\tOPT_BOOL('e', \"edit\", &edit_flag, N_(\"force edit of tag message\")),\n \t\tOPT_BOOL('s', \"sign\", &opt.sign, N_(\"annotated and GPG-signed tag\")),\n-\t\tOPT_STRING(0, \"cleanup\", &cleanup_arg, N_(\"mode\"),\n-\t\t\tN_(\"how to strip spaces and #comments from message\")),\n+\t\tOPT_CLEANUP(&cleanup_arg),\n \t\tOPT_STRING('u', \"local-user\", &keyid, N_(\"key-id\"),\n \t\t\t\t\tN_(\"use another key to sign the tag\")),\n \t\tOPT__FORCE(&force, N_(\"replace the tag if exists\"), 0),\ndiff --git a/parse-options.h b/parse-options.h\nindex 7d83e2971d..85faaee390 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -303,5 +303,6 @@ int parse_opt_passthru_argv(const struct option *, const char *, int);\n #define OPT_NO_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH(\"no-contains\", v, h, PARSE_OPT_NONEG)\n #define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH(\"with\", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)\n #define OPT_WITHOUT(v, h) _OPT_CONTAINS_OR_WITH(\"without\", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)\n+#define OPT_CLEANUP(v) OPT_STRING(0, \"cleanup\", v, N_(\"mode\"), N_(\"how to strip spaces and #comments from message\"))\n \n #endif\n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"371688","messageId":"5cab2c3ed2085b7c706682d3d9878b6bf807d2f7.1552817044.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1552817044.git.liu.denton@gmail.com","subject":"[PATCH v8 07/11] sequencer.c: remove duplicate code","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-17T10:16:10Z","receivedAt":"2019-03-17T10:16:16Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Since we implemented get_cleanup_mode, we had some duplicate code in\ngit_sequencer_config which essentially performed the same operations.\nRefactor git_sequencer_config to take advantage of the logic already in\nget_cleanup_mode.\n\nNote that we had to introduce a separate argument to get_cleanup_mode\nindicating whether to die or not on an invalid value. This is because if\nwe are parsing a config, we do not want to die but instead, we only want\nto warn the user, whereas if we are parsing a command-line option, we\nwould like to actually die.\n\nFinally, this is almost a no-op refactor but not quite. Previously, in\nthe case that an invalid value is presented, default_msg_cleanup would\nnot be set. We change the behaviour so that default_msg_cleanup will now\ntake on the value as if \"default\" were provided as the cleanup_arg.\nAlso, we lowercase some user-facing strings.\n\nReviewed-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/commit.c |  2 +-\n sequencer.c      | 22 ++++++++--------------\n sequencer.h      |  2 +-\n 3 files changed, 10 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 8277da8474..ba1e6027ba 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1172,7 +1172,7 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t\tdie(_(\"Only one of --include/--only/--all/--interactive/--patch can be used.\"));\n \tif (argc == 0 && (also || (only && !amend && !allow_empty)))\n \t\tdie(_(\"No paths with --include/--only does not make sense.\"));\n-\tcleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);\n+\tcleanup_mode = get_cleanup_mode(cleanup_arg, use_editor, 1);\n \n \thandle_untracked_files_arg(s);\n \ndiff --git a/sequencer.c b/sequencer.c\nindex 224c823b43..2cbfb848dd 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -172,17 +172,7 @@ static int git_sequencer_config(const char *k, const char *v, void *cb)\n \t\tif (status)\n \t\t\treturn status;\n \n-\t\tif (!strcmp(s, \"verbatim\"))\n-\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;\n-\t\telse if (!strcmp(s, \"whitespace\"))\n-\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;\n-\t\telse if (!strcmp(s, \"strip\"))\n-\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;\n-\t\telse if (!strcmp(s, \"scissors\"))\n-\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;\n-\t\telse\n-\t\t\twarning(_(\"invalid commit message cleanup mode '%s'\"),\n-\t\t\t\t  s);\n+\t\topts->default_msg_cleanup = get_cleanup_mode(s, 0, 0);\n \n \t\tfree((char *)s);\n \t\treturn status;\n@@ -512,7 +502,7 @@ static int fast_forward_to(struct repository *r,\n }\n \n enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n-\tint use_editor)\n+\tint use_editor, int die_on_error)\n {\n \tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n \t\treturn use_editor ? COMMIT_MSG_CLEANUP_ALL :\n@@ -526,8 +516,12 @@ enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n \telse if (!strcmp(cleanup_arg, \"scissors\"))\n \t\treturn use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n \t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n-\telse\n-\t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n+\telse if (!die_on_error) {\n+\t\twarning(_(\"invalid cleanup mode %s, falling back to default\"), cleanup_arg);\n+\t\treturn use_editor ? COMMIT_MSG_CLEANUP_ALL :\n+\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n+\t} else\n+\t\tdie(_(\"invalid cleanup mode %s\"), cleanup_arg);\n }\n \n void append_conflicts_hint(struct index_state *istate,\ndiff --git a/sequencer.h b/sequencer.h\nindex eb9bd97ef3..e7908f558e 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -117,7 +117,7 @@ void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);\n \n void append_conflicts_hint(struct index_state *istate, struct strbuf *msgbuf);\n enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n-\tint use_editor);\n+\tint use_editor, int die_on_error);\n \n void cleanup_message(struct strbuf *msgbuf,\n \tenum commit_msg_cleanup_mode cleanup_mode, int verbose);\n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"371689","messageId":"cd5c764fbf2c7aa6fb09a08a93c171580a41940d.1552817044.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1552817044.git.liu.denton@gmail.com","subject":"[PATCH v8 08/11] merge: cleanup messages like commit","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-17T10:16:13Z","receivedAt":"2019-03-17T10:16:19Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"This change allows git-merge messages to be cleaned up with the\ncommit.cleanup configuration or --cleanup option, just like how\ngit-commit does it.\n\nWe also give git-pull the passthrough option of --cleanup so that it can\nalso take advantage of this change.\n\nFinally, add testing to ensure that messages are properly cleaned up.\nNote that some newlines that were added to the commit message were\nremoved so that if a file were read via -F, it would be copied\nfaithfully.\n\nReviewed-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nReviewed-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n\nPhillip Wood wrote:\n> cleanup needs to take an argument so PARSE_OPT_NOARG does not look \n> right. Also I think it would be bettor from the user's point of view if \n> the value of the argument was checked by pull before it does any work \n> rather otherwise if they pass in invalid value pull mostly runs and then \n> merge errors out at the end.\n\nI opted not to do a check on the validity of the value of the\n--cleanup-mode argument because the strategy options that existed before\nalso didn't verify the validity of their values. In the future, it might\nbe a good idea to check the values of both cleanup-mode and the\nstrategy options but for now, I think we can leave it as it is.\n\n Documentation/merge-options.txt |  5 +++\n builtin/merge.c                 | 31 +++++++++++----\n builtin/pull.c                  |  6 +++\n t/t7604-merge-custom-message.sh | 67 +++++++++++++++++++++++++++++++++\n wt-status.c                     | 12 ++++--\n wt-status.h                     |  1 +\n 6 files changed, 112 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 92a7d936c1..646100ea9a 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -32,6 +32,11 @@ they run `git merge`. To make it easier to adjust such scripts to the\n updated behaviour, the environment variable `GIT_MERGE_AUTOEDIT` can be\n set to `no` at the beginning of them.\n \n+--cleanup=<mode>::\n+\tThis option determines how the merge message will be cleaned up\n+\tbefore commiting or being passed on. See linkgit:git-commit[1] for more\n+\tdetails.\n+\n --ff::\n \tWhen the merge resolves as a fast-forward, only update the branch\n \tpointer, without creating a merge commit.  This is the default\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 5ce8946d39..7be03a2610 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -38,6 +38,7 @@\n #include \"tag.h\"\n #include \"alias.h\"\n #include \"commit-reach.h\"\n+#include \"wt-status.h\"\n \n #define DEFAULT_TWOHEAD (1<<0)\n #define DEFAULT_OCTOPUS (1<<1)\n@@ -98,6 +99,9 @@ enum ff_type {\n \n static enum ff_type fast_forward = FF_ALLOW;\n \n+static const char *cleanup_arg;\n+static enum commit_msg_cleanup_mode cleanup_mode;\n+\n static int option_parse_message(const struct option *opt,\n \t\t\t\tconst char *arg, int unset)\n {\n@@ -249,6 +253,7 @@ static struct option builtin_merge_options[] = {\n \t\tN_(\"perform a commit if the merge succeeds (default)\")),\n \tOPT_BOOL('e', \"edit\", &option_edit,\n \t\tN_(\"edit message before committing\")),\n+\tOPT_CLEANUP(&cleanup_arg),\n \tOPT_SET_INT(0, \"ff\", &fast_forward, N_(\"allow fast-forward (default)\"), FF_ALLOW),\n \tOPT_SET_INT_F(0, \"ff-only\", &fast_forward,\n \t\t      N_(\"abort if fast-forward is not possible\"),\n@@ -612,6 +617,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n \t\treturn git_config_string(&pull_twohead, k, v);\n \telse if (!strcmp(k, \"pull.octopus\"))\n \t\treturn git_config_string(&pull_octopus, k, v);\n+\telse if (!strcmp(k, \"commit.cleanup\"))\n+\t\treturn git_config_string(&cleanup_arg, k, v);\n \telse if (!strcmp(k, \"merge.renormalize\"))\n \t\toption_renormalize = git_config_bool(k, v);\n \telse if (!strcmp(k, \"merge.ff\")) {\n@@ -797,23 +804,32 @@ static void abort_commit(struct commit_list *remoteheads, const char *err_msg)\n \texit(1);\n }\n \n+static const char comment_line_explanation[] =\n+N_(\"Lines starting with '%c' will be ignored.\\n\");\n+\n static const char merge_editor_comment[] =\n N_(\"Please enter a commit message to explain why this merge is necessary,\\n\"\n    \"especially if it merges an updated upstream into a topic branch.\\n\"\n    \"\\n\"\n-   \"Lines starting with '%c' will be ignored, and an empty message aborts\\n\"\n-   \"the commit.\\n\");\n+   \"An empty message aborts the commit.\\n\");\n \n static void write_merge_heads(struct commit_list *);\n static void prepare_to_commit(struct commit_list *remoteheads)\n {\n \tstruct strbuf msg = STRBUF_INIT;\n \tstrbuf_addbuf(&msg, &merge_msg);\n-\tstrbuf_addch(&msg, '\\n');\n \tif (squash)\n \t\tBUG(\"the control must not reach here under --squash\");\n-\tif (0 < option_edit)\n-\t\tstrbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);\n+\tif (0 < option_edit) {\n+\t\tstrbuf_addch(&msg, '\\n');\n+\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n+\t\t\twt_status_append_cut_line(&msg);\n+\t\telse\n+\t\t\tstrbuf_commented_addf(&msg, _(comment_line_explanation), comment_line_char);\n+\n+\t\tstrbuf_commented_addf(&msg, \"\\n\");\n+\t\tstrbuf_commented_addf(&msg, _(merge_editor_comment));\n+\t}\n \tif (signoff)\n \t\tappend_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);\n \twrite_merge_heads(remoteheads);\n@@ -832,7 +848,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)\n \t\tabort_commit(remoteheads, NULL);\n \n \tread_merge_msg(&msg);\n-\tstrbuf_stripspace(&msg, 0 < option_edit);\n+\tcleanup_message(&msg, cleanup_mode, 0);\n \tif (!msg.len)\n \t\tabort_commit(remoteheads, _(\"Empty commit message.\"));\n \tstrbuf_release(&merge_msg);\n@@ -880,7 +896,6 @@ static int finish_automerge(struct commit *head,\n \tparents = remoteheads;\n \tif (!head_subsumed || fast_forward == FF_NO)\n \t\tcommit_list_insert(head, &parents);\n-\tstrbuf_addch(&merge_msg, '\\n');\n \tprepare_to_commit(remoteheads);\n \tif (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,\n \t\t\t&result_commit, NULL, sign_commit))\n@@ -1389,6 +1404,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \tif (option_edit < 0)\n \t\toption_edit = default_edit_option();\n \n+\tcleanup_mode = get_cleanup_mode(cleanup_arg, 0 < option_edit, 1);\n+\n \tif (!use_strategies) {\n \t\tif (!remoteheads)\n \t\t\t; /* already up-to-date */\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 33db889955..292c1dac27 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -101,6 +101,7 @@ static char *opt_signoff;\n static char *opt_squash;\n static char *opt_commit;\n static char *opt_edit;\n+static char *opt_cleanup;\n static char *opt_ff;\n static char *opt_verify_signatures;\n static int opt_autostash = -1;\n@@ -168,6 +169,9 @@ static struct option pull_options[] = {\n \tOPT_PASSTHRU(0, \"edit\", &opt_edit, NULL,\n \t\tN_(\"edit message before committing\"),\n \t\tPARSE_OPT_NOARG),\n+\tOPT_PASSTHRU(0, \"cleanup\", &opt_cleanup, NULL,\n+\t\tN_(\"how to strip spaces and #comments from message\"),\n+\t\t0),\n \tOPT_PASSTHRU(0, \"ff\", &opt_ff, NULL,\n \t\tN_(\"allow fast-forward\"),\n \t\tPARSE_OPT_NOARG),\n@@ -644,6 +648,8 @@ static int run_merge(void)\n \t\targv_array_push(&args, opt_commit);\n \tif (opt_edit)\n \t\targv_array_push(&args, opt_edit);\n+\tif (opt_cleanup)\n+\t\targv_array_push(&args, opt_cleanup);\n \tif (opt_ff)\n \t\targv_array_push(&args, opt_ff);\n \tif (opt_verify_signatures)\ndiff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh\nindex b045fdb413..c9685a318d 100755\n--- a/t/t7604-merge-custom-message.sh\n+++ b/t/t7604-merge-custom-message.sh\n@@ -51,4 +51,71 @@ test_expect_success 'merge --log appends to custom message' '\n \ttest_cmp exp.log actual\n '\n \n+mesg_with_comment_and_newlines='\n+# text\n+\n+'\n+\n+test_expect_success 'prepare file with comment line and trailing newlines'  '\n+\tprintf \"%s\" \"$mesg_with_comment_and_newlines\" >expect\n+'\n+\n+test_expect_success 'cleanup commit messages (verbatim option)' '\n+\tgit reset --hard c1 &&\n+\tgit merge --cleanup=verbatim -F expect c2 &&\n+\tgit cat-file commit HEAD >actual &&\n+\tsed -e \"1,/^$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'cleanup commit messages (whitespace option)' '\n+\tgit reset --hard c1 &&\n+\ttest_write_lines \"\" \"# text\" \"\" >text &&\n+\techo \"# text\" >expect &&\n+\tgit merge --cleanup=whitespace -F text c2 &&\n+\tgit cat-file commit HEAD >actual &&\n+\tsed -e \"1,/^$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'cleanup merge messages (scissors option)' '\n+\tgit reset --hard c1 &&\n+\tcat >text <<-\\EOF &&\n+\n+\t# to be kept\n+\n+\t  # ------------------------ >8 ------------------------\n+\t# to be kept, too\n+\t# ------------------------ >8 ------------------------\n+\tto be removed\n+\t# ------------------------ >8 ------------------------\n+\tto be removed, too\n+\tEOF\n+\n+\tcat >expect <<-\\EOF &&\n+\t# to be kept\n+\n+\t  # ------------------------ >8 ------------------------\n+\t# to be kept, too\n+\tEOF\n+\tgit merge --cleanup=scissors -e -F text c2 &&\n+\tgit cat-file commit HEAD >actual &&\n+\tsed -e \"1,/^$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'cleanup commit messages (strip option)' '\n+\tgit reset --hard c1 &&\n+\ttest_write_lines \"\" \"# text\" \"sample\" \"\" >text &&\n+\techo sample >expect &&\n+\tgit merge --cleanup=strip -F text c2 &&\n+\tgit cat-file commit HEAD >actual &&\n+\tsed -e \"1,/^$/d\" <actual >tmp &&\n+\tmv tmp actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\ndiff --git a/wt-status.c b/wt-status.c\nindex 445a36204a..b81fcd428d 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1006,13 +1006,19 @@ size_t wt_status_locate_end(const char *s, size_t len)\n \treturn len;\n }\n \n-void wt_status_add_cut_line(FILE *fp)\n+void wt_status_append_cut_line(struct strbuf *buf)\n {\n \tconst char *explanation = _(\"Do not modify or remove the line above.\\nEverything below it will be ignored.\");\n+\n+\tstrbuf_commented_addf(buf, \"%s\", cut_line);\n+\tstrbuf_add_commented_lines(buf, explanation, strlen(explanation));\n+}\n+\n+void wt_status_add_cut_line(FILE *fp)\n+{\n \tstruct strbuf buf = STRBUF_INIT;\n \n-\tfprintf(fp, \"%c %s\", comment_line_char, cut_line);\n-\tstrbuf_add_commented_lines(&buf, explanation, strlen(explanation));\n+\twt_status_append_cut_line(&buf);\n \tfputs(buf.buf, fp);\n \tstrbuf_release(&buf);\n }\ndiff --git a/wt-status.h b/wt-status.h\nindex 3a95975032..64f1ddc9fd 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -129,6 +129,7 @@ struct wt_status {\n };\n \n size_t wt_status_locate_end(const char *s, size_t len);\n+void wt_status_append_cut_line(struct strbuf *buf);\n void wt_status_add_cut_line(FILE *fp);\n void wt_status_prepare(struct repository *r, struct wt_status *s);\n void wt_status_print(struct wt_status *s);\n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"371690","messageId":"41e399af28495e0fdec87992526daf23bafef272.1552817044.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1552817044.git.liu.denton@gmail.com","subject":"[PATCH v8 09/11] merge: add scissors line on merge conflict","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-17T10:16:16Z","receivedAt":"2019-03-17T10:16:21Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"This fixes a bug where the scissors line is placed after the Conflicts:\nsection, in the case where a merge conflict occurs and\ncommit.cleanup = scissors.\n\nNext, if commit.cleanup = scissors is specified, don't produce a\nscissors line in commit if one already exists in the MERGE_MSG file.\n\nReviewed-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/merge-options.txt |  4 ++-\n builtin/commit.c                | 20 ++++++++++----\n builtin/merge.c                 | 14 ++++++++++\n t/t7600-merge.sh                | 48 +++++++++++++++++++++++++++++++++\n 4 files changed, 80 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 646100ea9a..405e16c617 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -35,7 +35,9 @@ set to `no` at the beginning of them.\n --cleanup=<mode>::\n \tThis option determines how the merge message will be cleaned up\n \tbefore commiting or being passed on. See linkgit:git-commit[1] for more\n-\tdetails.\n+\tdetails. In addition, if the '<mode>' is given a value of `scissors`,\n+\tscissors will be appended to `MERGE_MSG` before being passed on to the\n+\tcommit machinery in the case of a merge conflict.\n \n --ff::\n \tWhen the merge resolves as a fast-forward, only update the branch\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex ba1e6027ba..b9159c2e7b 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -668,6 +668,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \tconst char *hook_arg2 = NULL;\n \tint clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);\n \tint old_display_comment_prefix;\n+\tint merge_contains_scissors = 0;\n \n \t/* This checks and barfs if author is badly specified */\n \tdetermine_author_info(author_ident);\n@@ -728,6 +729,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\tstrbuf_addbuf(&sb, &message);\n \t\thook_arg1 = \"message\";\n \t} else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {\n+\t\tsize_t merge_msg_start;\n+\n \t\t/*\n \t\t * prepend SQUASH_MSG here if it exists and a\n \t\t * \"merge --squash\" was originally performed\n@@ -738,8 +741,14 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\thook_arg1 = \"squash\";\n \t\t} else\n \t\t\thook_arg1 = \"merge\";\n+\n+\t\tmerge_msg_start = sb.len;\n \t\tif (strbuf_read_file(&sb, git_path_merge_msg(the_repository), 0) < 0)\n \t\t\tdie_errno(_(\"could not read MERGE_MSG\"));\n+\n+\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\t    wt_status_locate_end(sb.buf + merge_msg_start, sb.len - merge_msg_start) < sb.len - merge_msg_start)\n+\t\t\tmerge_contains_scissors = 1;\n \t} else if (!stat(git_path_squash_msg(the_repository), &statbuf)) {\n \t\tif (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)\n \t\t\tdie_errno(_(\"could not read SQUASH_MSG\"));\n@@ -807,7 +816,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\tstruct ident_split ci, ai;\n \n \t\tif (whence != FROM_COMMIT) {\n-\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n+\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\t\t\t!merge_contains_scissors)\n \t\t\t\twt_status_add_cut_line(s->fp);\n \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL,\n \t\t\t    whence == FROM_MERGE\n@@ -832,10 +842,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\nwith '%c' will be ignored, and an empty\"\n \t\t\t\t  \" message aborts the commit.\\n\"), comment_line_char);\n-\t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n-\t\t\t whence == FROM_COMMIT)\n-\t\t\twt_status_add_cut_line(s->fp);\n-\t\telse /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n+\t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {\n+\t\t\tif (whence == FROM_COMMIT && !merge_contains_scissors)\n+\t\t\t\twt_status_add_cut_line(s->fp);\n+\t\t} else /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n \t\t\tstatus_printf(s, GIT_COLOR_NORMAL,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\n\"\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 7be03a2610..86c54bff76 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -916,6 +916,20 @@ static int suggest_conflicts(void)\n \tfilename = git_path_merge_msg(the_repository);\n \tfp = xfopen(filename, \"a\");\n \n+\t/*\n+\t * We can't use cleanup_mode because if we're not using the editor,\n+\t * get_cleanup_mode will return COMMIT_MSG_CLEANUP_SPACE instead, even\n+\t * though the message is meant to be processed later by git-commit.\n+\t * Thus, we will get the cleanup mode which is returned when we _are_ using\n+\t * an editor.\n+\t */\n+\tif (get_cleanup_mode(cleanup_arg, 1, 1) == COMMIT_MSG_CLEANUP_SCISSORS) {\n+\t    fputc('\\n', fp);\n+\t    wt_status_add_cut_line(fp);\n+\t    /* comments out the newline from append_conflicts_hint */\n+\t    fputc(comment_line_char, fp);\n+\t}\n+\n \tappend_conflicts_hint(&the_index, &msgbuf);\n \tfputs(msgbuf.buf, fp);\n \tstrbuf_release(&msgbuf);\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex a9620cd991..ed11b08db2 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -247,6 +247,54 @@ test_expect_success 'merge --squash c3 with c7' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'merge c3 with c7 with commit.cleanup = scissors' '\n+\tgit config commit.cleanup scissors &&\n+\tgit reset --hard c3 &&\n+\ttest_must_fail git merge c7 &&\n+\tcat result.9z >file &&\n+\tgit commit --no-edit -a &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tMerge tag '\"'\"'c7'\"'\"'\n+\n+\t# ------------------------ >8 ------------------------\n+\t# Do not modify or remove the line above.\n+\t# Everything below it will be ignored.\n+\t#\n+\t# Conflicts:\n+\t#\tfile\n+\tEOF\n+\tgit cat-file commit HEAD >actual &&\n+\tsed -e '1,/^$/d' <actual >tmp &&\n+\tmv tmp actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n+test_expect_success 'merge c3 with c7 with --squash commit.cleanup = scissors' '\n+\tgit config commit.cleanup scissors &&\n+\tgit reset --hard c3 &&\n+\ttest_must_fail git merge --squash c7 &&\n+\tcat result.9z >file &&\n+\tgit commit --no-edit -a &&\n+\n+\tcat >expect <<-EOF &&\n+\tSquashed commit of the following:\n+\n+\t$(git show -s c7)\n+\n+\t# ------------------------ >8 ------------------------\n+\t# Do not modify or remove the line above.\n+\t# Everything below it will be ignored.\n+\t#\n+\t# Conflicts:\n+\t#\tfile\n+\tEOF\n+\tgit cat-file commit HEAD >actual &&\n+\tsed -e '1,/^$/d' <actual >tmp &&\n+\tmv tmp actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n test_debug 'git log --graph --decorate --oneline --all'\n \n test_expect_success 'merge c1 with c2 and c3' '\n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"371691","messageId":"4edca9db0b2a32621a7622b5b709db23b2512d1e.1552817044.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1552817044.git.liu.denton@gmail.com","subject":"[PATCH v8 10/11] sequencer.c: define describe_cleanup_mode","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-17T10:16:19Z","receivedAt":"2019-03-17T10:16:24Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Define a function which allows us to get the string configuration value\nof a enum commit_msg_cleanup_mode. This is done by refactoring\nget_cleanup_mode such that it uses a lookup table to find the mappings\nbetween string and enum and then using the same LUT in reverse to define\ndescribe_cleanup_mode.\n\nReviewed-by: Eric Sunshine <sunshine@sunshineco.com>\nReviewed-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Ramsay Jones <ramsay@ramsayjones.plus.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 62 ++++++++++++++++++++++++++++++++++++++++-------------\n 1 file changed, 47 insertions(+), 15 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 2cbfb848dd..f782e8bab2 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -160,6 +160,22 @@ static GIT_PATH_FUNC(rebase_path_strategy_opts, \"rebase-merge/strategy_opts\")\n static GIT_PATH_FUNC(rebase_path_allow_rerere_autoupdate, \"rebase-merge/allow_rerere_autoupdate\")\n static GIT_PATH_FUNC(rebase_path_reschedule_failed_exec, \"rebase-merge/reschedule-failed-exec\")\n \n+struct cleanup_config_mapping {\n+\tconst char *value;\n+\tenum commit_msg_cleanup_mode editor;\n+\tenum commit_msg_cleanup_mode no_editor;\n+};\n+\n+/* the 0th element of this array must be the \"default\" */\n+static struct cleanup_config_mapping cleanup_config_mapping[] = {\n+\t{ \"default\", COMMIT_MSG_CLEANUP_ALL, COMMIT_MSG_CLEANUP_SPACE },\n+\t{ \"verbatim\", COMMIT_MSG_CLEANUP_NONE, COMMIT_MSG_CLEANUP_NONE },\n+\t{ \"whitespace\", COMMIT_MSG_CLEANUP_SPACE, COMMIT_MSG_CLEANUP_SPACE },\n+\t{ \"strip\", COMMIT_MSG_CLEANUP_ALL, COMMIT_MSG_CLEANUP_ALL },\n+\t{ \"scissors\", COMMIT_MSG_CLEANUP_SCISSORS, COMMIT_MSG_CLEANUP_SPACE },\n+\t{ NULL, 0, 0 }\n+};\n+\n static int git_sequencer_config(const char *k, const char *v, void *cb)\n {\n \tstruct replay_opts *opts = cb;\n@@ -504,26 +520,37 @@ static int fast_forward_to(struct repository *r,\n enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n \tint use_editor, int die_on_error)\n {\n-\tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n-\t\treturn use_editor ? COMMIT_MSG_CLEANUP_ALL :\n-\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n-\telse if (!strcmp(cleanup_arg, \"verbatim\"))\n-\t\treturn COMMIT_MSG_CLEANUP_NONE;\n-\telse if (!strcmp(cleanup_arg, \"whitespace\"))\n-\t\treturn COMMIT_MSG_CLEANUP_SPACE;\n-\telse if (!strcmp(cleanup_arg, \"strip\"))\n-\t\treturn COMMIT_MSG_CLEANUP_ALL;\n-\telse if (!strcmp(cleanup_arg, \"scissors\"))\n-\t\treturn use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n-\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n-\telse if (!die_on_error) {\n+\tstruct cleanup_config_mapping *def = &cleanup_config_mapping[0];\n+\tstruct cleanup_config_mapping *p;\n+\n+\tif (!cleanup_arg)\n+\t\treturn use_editor ? def->editor :\n+\t\t\t\t    def->no_editor;\n+\n+\tfor (p = cleanup_config_mapping; p->value; p++)\n+\t\tif (!strcmp(cleanup_arg, p->value))\n+\t\t\treturn use_editor ? p->editor :\n+\t\t\t\t\t    p->no_editor;\n+\n+\tif (!die_on_error) {\n \t\twarning(_(\"invalid cleanup mode %s, falling back to default\"), cleanup_arg);\n-\t\treturn use_editor ? COMMIT_MSG_CLEANUP_ALL :\n-\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n+\t\treturn use_editor ? def->editor :\n+\t\t\t\t    def->no_editor;\n \t} else\n \t\tdie(_(\"invalid cleanup mode %s\"), cleanup_arg);\n }\n \n+static const char *describe_cleanup_mode(enum commit_msg_cleanup_mode cleanup_mode)\n+{\n+\tstruct cleanup_config_mapping *curr;\n+\n+\tfor (curr = &cleanup_config_mapping[1]; curr->value; curr++)\n+\t\tif (cleanup_mode == curr->editor)\n+\t\t\treturn curr->value;\n+\n+\tBUG(\"invalid cleanup_mode provided (%d)\", cleanup_mode);\n+}\n+\n void append_conflicts_hint(struct index_state *istate,\n \t\t\t   struct strbuf *msgbuf)\n {\n@@ -2350,6 +2377,8 @@ static int populate_opts_cb(const char *key, const char *value, void *data)\n \t\topts->allow_rerere_auto =\n \t\t\tgit_config_bool_or_int(key, value, &error_flag) ?\n \t\t\t\tRERERE_AUTOUPDATE : RERERE_NOAUTOUPDATE;\n+\telse if (!strcmp(key, \"options.default-msg-cleanup\"))\n+\t\topts->default_msg_cleanup = get_cleanup_mode(value, 1, 1);\n \telse\n \t\treturn error(_(\"invalid key: %s\"), key);\n \n@@ -2754,6 +2783,9 @@ static int save_opts(struct replay_opts *opts)\n \t\tres |= git_config_set_in_file_gently(opts_file, \"options.allow-rerere-auto\",\n \t\t\t\t\t\t     opts->allow_rerere_auto == RERERE_AUTOUPDATE ?\n \t\t\t\t\t\t     \"true\" : \"false\");\n+\n+\tres |= git_config_set_in_file_gently(opts_file, \"options.default-msg-cleanup\",\n+\t\t\t\t\t     describe_cleanup_mode(opts->default_msg_cleanup));\n \treturn res;\n }\n \n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"371692","messageId":"5685ff80b582ab1d6daf4f2d0fcb93344b9069f5.1552817044.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1552817044.git.liu.denton@gmail.com","subject":"[PATCH v8 11/11] cherry-pick/revert: add scissors line on merge conflict","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-17T10:16:22Z","receivedAt":"2019-03-17T10:16:27Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Fix a bug where the scissors line is placed after the Conflicts:\nsection, in the case where a merge conflict occurs and\ncommit.cleanup = scissors.\n\nReviewed-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/git-cherry-pick.txt |  7 +++\n Documentation/git-revert.txt      |  7 +++\n builtin/merge.c                   |  9 +---\n builtin/rebase--interactive.c     |  2 +-\n builtin/revert.c                  |  5 ++\n sequencer.c                       | 22 +++++---\n sequencer.h                       |  3 +-\n t/t3507-cherry-pick-conflict.sh   | 86 +++++++++++++++++++++++++++++++\n 8 files changed, 123 insertions(+), 18 deletions(-)\n\ndiff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt\nindex b8cfeec67e..2601e1868e 100644\n--- a/Documentation/git-cherry-pick.txt\n+++ b/Documentation/git-cherry-pick.txt\n@@ -57,6 +57,13 @@ OPTIONS\n \tWith this option, 'git cherry-pick' will let you edit the commit\n \tmessage prior to committing.\n \n+--cleanup=<mode>::\n+\tThis option determines how the commit message will be cleaned up before\n+\tbeing passed on. See linkgit:git-commit[1] for more details. In\n+\tparticular, if the '<mode>' is given a value of `scissors`, scissors\n+\twill be appended to `MERGE_MSG` before being passed on to the commit\n+\tmachinery in the case of a conflict.\n+\n -x::\n \tWhen recording the commit, append a line that says\n \t\"(cherry picked from commit ...)\" to the original commit\ndiff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt\nindex 837707a8fd..cabeb9df2c 100644\n--- a/Documentation/git-revert.txt\n+++ b/Documentation/git-revert.txt\n@@ -66,6 +66,13 @@ more details.\n \tWith this option, 'git revert' will not start the commit\n \tmessage editor.\n \n+--cleanup=<mode>::\n+\tThis option determines how the commit message will be cleaned up before\n+\tbeing passed on. See linkgit:git-commit[1] for more details. In\n+\tparticular, if the '<mode>' is given a value of `scissors`, scissors\n+\twill be appended to `MERGE_MSG` before being passed on to the commit\n+\tmachinery in the case of a conflict.\n+\n -n::\n --no-commit::\n \tUsually the command automatically creates some commits with\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 86c54bff76..90c64346e9 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -923,14 +923,7 @@ static int suggest_conflicts(void)\n \t * Thus, we will get the cleanup mode which is returned when we _are_ using\n \t * an editor.\n \t */\n-\tif (get_cleanup_mode(cleanup_arg, 1, 1) == COMMIT_MSG_CLEANUP_SCISSORS) {\n-\t    fputc('\\n', fp);\n-\t    wt_status_add_cut_line(fp);\n-\t    /* comments out the newline from append_conflicts_hint */\n-\t    fputc(comment_line_char, fp);\n-\t}\n-\n-\tappend_conflicts_hint(&the_index, &msgbuf);\n+\tappend_conflicts_hint(&the_index, &msgbuf, get_cleanup_mode(cleanup_arg, 1, 1));\n \tfputs(msgbuf.buf, fp);\n \tstrbuf_release(&msgbuf);\n \tfclose(fp);\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 888390f911..cf2151b271 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -199,10 +199,10 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n+\topts.action = REPLAY_INTERACTIVE_REBASE;\n \tsequencer_init_config(&opts);\n \tgit_config_get_bool(\"rebase.abbreviatecommands\", &abbreviate_commands);\n \n-\topts.action = REPLAY_INTERACTIVE_REBASE;\n \topts.allow_ff = 1;\n \topts.allow_empty = 1;\n \ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex a47b53ceaf..41e7392c24 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -96,11 +96,13 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)\n {\n \tconst char * const * usage_str = revert_or_cherry_pick_usage(opts);\n \tconst char *me = action_name(opts);\n+\tconst char *cleanup_arg = NULL;\n \tint cmd = 0;\n \tstruct option base_options[] = {\n \t\tOPT_CMDMODE(0, \"quit\", &cmd, N_(\"end revert or cherry-pick sequence\"), 'q'),\n \t\tOPT_CMDMODE(0, \"continue\", &cmd, N_(\"resume revert or cherry-pick sequence\"), 'c'),\n \t\tOPT_CMDMODE(0, \"abort\", &cmd, N_(\"cancel revert or cherry-pick sequence\"), 'a'),\n+\t\tOPT_CLEANUP(&cleanup_arg),\n \t\tOPT_BOOL('n', \"no-commit\", &opts->no_commit, N_(\"don't automatically commit\")),\n \t\tOPT_BOOL('e', \"edit\", &opts->edit, N_(\"edit the commit message\")),\n \t\tOPT_NOOP_NOARG('r', NULL),\n@@ -137,6 +139,9 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)\n \tif (opts->keep_redundant_commits)\n \t\topts->allow_empty = 1;\n \n+\tif (cleanup_arg)\n+\t\topts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1, 1);\n+\n \t/* Check for incompatible command line arguments */\n \tif (cmd) {\n \t\tchar *this_operation;\ndiff --git a/sequencer.c b/sequencer.c\nindex f782e8bab2..ea4687c3da 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -176,6 +176,11 @@ static struct cleanup_config_mapping cleanup_config_mapping[] = {\n \t{ NULL, 0, 0 }\n };\n \n+static inline int is_rebase_i(const struct replay_opts *opts)\n+{\n+\treturn opts->action == REPLAY_INTERACTIVE_REBASE;\n+}\n+\n static int git_sequencer_config(const char *k, const char *v, void *cb)\n {\n \tstruct replay_opts *opts = cb;\n@@ -188,7 +193,7 @@ static int git_sequencer_config(const char *k, const char *v, void *cb)\n \t\tif (status)\n \t\t\treturn status;\n \n-\t\topts->default_msg_cleanup = get_cleanup_mode(s, 0, 0);\n+\t\topts->default_msg_cleanup = get_cleanup_mode(s, !is_rebase_i(opts), 0);\n \n \t\tfree((char *)s);\n \t\treturn status;\n@@ -212,11 +217,6 @@ void sequencer_init_config(struct replay_opts *opts)\n \tgit_config(git_sequencer_config, opts);\n }\n \n-static inline int is_rebase_i(const struct replay_opts *opts)\n-{\n-\treturn opts->action == REPLAY_INTERACTIVE_REBASE;\n-}\n-\n static const char *get_dir(const struct replay_opts *opts)\n {\n \tif (is_rebase_i(opts))\n@@ -552,10 +552,16 @@ static const char *describe_cleanup_mode(enum commit_msg_cleanup_mode cleanup_mo\n }\n \n void append_conflicts_hint(struct index_state *istate,\n-\t\t\t   struct strbuf *msgbuf)\n+\t\tstruct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode)\n {\n \tint i;\n \n+\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {\n+\t\tstrbuf_addch(msgbuf, '\\n');\n+\t\twt_status_append_cut_line(msgbuf);\n+\t\tstrbuf_addch(msgbuf, comment_line_char);\n+\t}\n+\n \tstrbuf_addch(msgbuf, '\\n');\n \tstrbuf_commented_addf(msgbuf, \"Conflicts:\\n\");\n \tfor (i = 0; i < istate->cache_nr;) {\n@@ -623,7 +629,7 @@ static int do_recursive_merge(struct repository *r,\n \t\t\t_(action_name(opts)));\n \n \tif (!clean)\n-\t\tappend_conflicts_hint(r->index, msgbuf);\n+\t\tappend_conflicts_hint(r->index, msgbuf, opts->default_msg_cleanup);\n \n \treturn !clean;\n }\ndiff --git a/sequencer.h b/sequencer.h\nindex e7908f558e..830d8232f6 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -115,7 +115,8 @@ int rearrange_squash(struct repository *r);\n  */\n void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);\n \n-void append_conflicts_hint(struct index_state *istate, struct strbuf *msgbuf);\n+void append_conflicts_hint(struct index_state *istate,\n+\t\tstruct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode);\n enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n \tint use_editor, int die_on_error);\n \ndiff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh\nindex 74ff925526..c3894ca9d6 100755\n--- a/t/t3507-cherry-pick-conflict.sh\n+++ b/t/t3507-cherry-pick-conflict.sh\n@@ -189,6 +189,46 @@ test_expect_success 'failed cherry-pick registers participants in index' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success \\\n+\t'cherry-pick conflict, ensure commit.cleanup = scissors places scissors line properly' '\n+\tpristine_detach initial &&\n+\tgit config commit.cleanup scissors &&\n+\tcat <<-EOF >expected &&\n+\t\tpicked\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfoo\n+\t\tEOF\n+\n+\ttest_must_fail git cherry-pick picked &&\n+\n+\ttest_i18ncmp expected .git/MERGE_MSG\n+'\n+\n+test_expect_success \\\n+\t'cherry-pick conflict, ensure cleanup=scissors places scissors line properly' '\n+\tpristine_detach initial &&\n+\tgit config --unset commit.cleanup &&\n+\tcat <<-EOF >expected &&\n+\t\tpicked\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfoo\n+\t\tEOF\n+\n+\ttest_must_fail git cherry-pick --cleanup=scissors picked &&\n+\n+\ttest_i18ncmp expected .git/MERGE_MSG\n+'\n+\n test_expect_success 'failed cherry-pick describes conflict in work tree' '\n \tpristine_detach initial &&\n \tcat <<-EOF >expected &&\n@@ -335,6 +375,52 @@ test_expect_success 'revert conflict, diff3 -m style' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success \\\n+\t'revert conflict, ensure commit.cleanup = scissors places scissors line properly' '\n+\tpristine_detach initial &&\n+\tgit config commit.cleanup scissors &&\n+\tcat >expected <<-EOF &&\n+\t\tRevert \"picked\"\n+\n+\t\tThis reverts commit OBJID.\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfoo\n+\t\tEOF\n+\n+\ttest_must_fail git revert picked &&\n+\n+\tsed \"s/$OID_REGEX/OBJID/\" .git/MERGE_MSG >actual &&\n+\ttest_i18ncmp expected actual\n+'\n+\n+test_expect_success \\\n+\t'revert conflict, ensure cleanup=scissors places scissors line properly' '\n+\tpristine_detach initial &&\n+\tgit config --unset commit.cleanup &&\n+\tcat >expected <<-EOF &&\n+\t\tRevert \"picked\"\n+\n+\t\tThis reverts commit OBJID.\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfoo\n+\t\tEOF\n+\n+\ttest_must_fail git revert --cleanup=scissors picked &&\n+\n+\tsed \"s/$OID_REGEX/OBJID/\" .git/MERGE_MSG >actual &&\n+\ttest_i18ncmp expected actual\n+'\n+\n test_expect_success 'failed cherry-pick does not forget -s' '\n \tpristine_detach initial &&\n \ttest_must_fail git cherry-pick -s picked &&\n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"371722","messageId":"20190317130539.GA23160@szeder.dev","threadId":"49830","inReplyTo":"cover.1552817044.git.liu.denton@gmail.com","subject":"Re: [PATCH v8 00/11] Fix scissors bug during conflict","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2019-03-17T13:05:39Z","receivedAt":"2019-03-17T13:05:49Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Sun, Mar 17, 2019 at 03:15:50AM -0700, Denton Liu wrote:\n> Sorry for taking so long to do a reroll, I've been pretty busy this week\n> and I've only been able to find the time now.\n\nNo problem, and thank you for sticking with it!\n\n\n> diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh\n> index ca4a740da0..f035e4a507 100755\n> --- a/t/t7502-commit-porcelain.sh\n> +++ b/t/t7502-commit-porcelain.sh\n> @@ -16,7 +16,9 @@ commit_msg_is () {\n>  # Arguments: [<prefix] [<commit message>] [<commit options>]\n>  check_summary_oneline() {\n>  \ttest_tick &&\n> -\tgit commit ${3+\"$3\"} -m \"$2\" | head -1 > act &&\n> +\tgit commit ${3+\"$3\"} -m \"$2\" >act &&\n> +\thead -1 <act >tmp &&\n> +\tmv tmp act &&\n\nHere you could swap the order of when you write to 'act' and 'tmp',\nand thus make the 'mv' unnecessary, like this:\n\n  git commit [...] >tmp &&\n  head -1 act >tmp &&\n  [...rest of the test...]\n\nNote also that 'head' (or 'sed' in later tests) can open its input\nfile on its own, there's no need to redirect its standard input.\n\nThis is a recurring pattern in patches 1, 4, 8, and 9.\n\n> @@ -142,8 +144,8 @@ test_expect_success 'sign off' '\n>  \t>positive &&\n>  \tgit add positive &&\n>  \tgit commit -s -m \"thank you\" &&\n> -\tactual=$(git cat-file commit HEAD | sed -ne \"s/Signed-off-by: //p\") &&\n> -\texpected=$(git var GIT_COMMITTER_IDENT | sed -e \"s/>.*/>/\") &&\n> +\tactual=$(git cat-file commit HEAD >tmp && sed -ne \"s/Signed-off-by: //p\" <tmp && rm tmp) &&\n> +\texpected=$(git var GIT_COMMITTER_IDENT >tmp && sed -e \"s/>.*/>/\" <tmp && rm tmp) &&\n>  \ttest \"z$actual\" = \"z$expected\"\n\nMay I ask you to go one step further in restructuring this and the\nfollowing tests? :)  Instead of using 'test' to compare the contents\nof the $actual and $expected variables, use 'test_cmp' to compare the\n'actual' and 'expected' files, something like:\n\n  git cat-file commit HEAD >tmp &&\n  sed -ne \"s/Signed-off-by: //p\" tmp >actual &&\n  git var GIT_COMMITTER_IDENT >tmp &&\n  sed -e \"s/>.*/>/\" >expected &&\n  test_cmp expected actual\n\n\n \n"},{"id":"371767","messageId":"20190318030220.GA14098@archbookpro.localdomain","threadId":"49830","inReplyTo":"20190317130539.GA23160@szeder.dev","subject":"Re: [PATCH v8 00/11] Fix scissors bug during conflict","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-18T03:02:20Z","receivedAt":"2019-03-18T03:02:28Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi Szeder,\n\nOn Sun, Mar 17, 2019 at 02:05:39PM +0100, SZEDER Gábor wrote:\n> On Sun, Mar 17, 2019 at 03:15:50AM -0700, Denton Liu wrote:\n> > Sorry for taking so long to do a reroll, I've been pretty busy this week\n> > and I've only been able to find the time now.\n> \n> No problem, and thank you for sticking with it!\n> \n> \n> > diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh\n> > index ca4a740da0..f035e4a507 100755\n> > --- a/t/t7502-commit-porcelain.sh\n> > +++ b/t/t7502-commit-porcelain.sh\n> > @@ -16,7 +16,9 @@ commit_msg_is () {\n> >  # Arguments: [<prefix] [<commit message>] [<commit options>]\n> >  check_summary_oneline() {\n> >  \ttest_tick &&\n> > -\tgit commit ${3+\"$3\"} -m \"$2\" | head -1 > act &&\n> > +\tgit commit ${3+\"$3\"} -m \"$2\" >act &&\n> > +\thead -1 <act >tmp &&\n> > +\tmv tmp act &&\n> \n> Here you could swap the order of when you write to 'act' and 'tmp',\n> and thus make the 'mv' unnecessary, like this:\n> \n>   git commit [...] >tmp &&\n>   head -1 act >tmp &&\n>   [...rest of the test...]\n> \n> Note also that 'head' (or 'sed' in later tests) can open its input\n> file on its own, there's no need to redirect its standard input.\n> \n> This is a recurring pattern in patches 1, 4, 8, and 9.\n> \n\nThe reason I did it this way was because earlier, Junio said:\n> > -     git cat-file commit HEAD | sed -e '1,/^$/d' >actual &&\n> > +     git cat-file commit HEAD >tmp &&\n> > +     sed -e '1,/^$/d' <tmp >actual &&\n> \n> The intermediary file may want a name better than 'tmp', if it is to\n> be left behind, but this will do for now.\n\nSo I opted to write the tests in a way where a tmp file won't be\nproduced. This pattern was shamelessly stolen from \n'set up mod-256 conflict scenario' in t7600 where it does the following:\n\n\t# 256 near-identical stanzas...\n\tfor i in $(test_seq 1 256); do\n\t\tfor j in 1 2 3 4 5; do\n\t\t\techo $i-$j\n\t\tdone\n\tdone >file &&\n\tgit add file &&\n\tgit commit -m base &&\n\n\t# one side changes the first line of each to \"master\"\n\tsed s/-1/-master/ <file >tmp &&\n\tmv tmp file &&\n\tgit commit -am master &&\n\nGood point about the heads and seds, though. I completely forgot that\nthey accept a file argument.\n\n> > @@ -142,8 +144,8 @@ test_expect_success 'sign off' '\n> >  \t>positive &&\n> >  \tgit add positive &&\n> >  \tgit commit -s -m \"thank you\" &&\n> > -\tactual=$(git cat-file commit HEAD | sed -ne \"s/Signed-off-by: //p\") &&\n> > -\texpected=$(git var GIT_COMMITTER_IDENT | sed -e \"s/>.*/>/\") &&\n> > +\tactual=$(git cat-file commit HEAD >tmp && sed -ne \"s/Signed-off-by: //p\" <tmp && rm tmp) &&\n> > +\texpected=$(git var GIT_COMMITTER_IDENT >tmp && sed -e \"s/>.*/>/\" <tmp && rm tmp) &&\n> >  \ttest \"z$actual\" = \"z$expected\"\n> \n> May I ask you to go one step further in restructuring this and the\n> following tests? :)  Instead of using 'test' to compare the contents\n> of the $actual and $expected variables, use 'test_cmp' to compare the\n> 'actual' and 'expected' files, something like:\n> \n>   git cat-file commit HEAD >tmp &&\n>   sed -ne \"s/Signed-off-by: //p\" tmp >actual &&\n>   git var GIT_COMMITTER_IDENT >tmp &&\n>   sed -e \"s/>.*/>/\" >expected &&\n>   test_cmp expected actual\n\nWill do.\n\nThanks,\n\nDenton\n"},{"id":"371782","messageId":"xmqqa7hs3d0z.fsf@gitster-ct.c.googlers.com","threadId":"49830","inReplyTo":"20190317130539.GA23160@szeder.dev","subject":"Re: [PATCH v8 00/11] Fix scissors bug during conflict","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-18T06:35:56Z","receivedAt":"2019-03-18T06:36:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"SZEDER Gábor <szeder.dev@gmail.com> writes:\n\n>> +\tgit commit ${3+\"$3\"} -m \"$2\" >act &&\n>> +\thead -1 <act >tmp &&\n>> +\tmv tmp act &&\n>\n> Here you could swap the order of when you write to 'act' and 'tmp',\n> and thus make the 'mv' unnecessary, like this:\n>\n>   git commit [...] >tmp &&\n>   head -1 act >tmp &&\n>   [...rest of the test...]\n>\n> Note also that 'head' (or 'sed' in later tests) can open its input\n> file on its own, there's no need to redirect its standard input.\n\nYup, that is better with a slight fix\n\n\tgit testing-output-from-this-command ... >tmp &&\n\thead -n 1 tmp >actual &&\n\nto actually read from the output kept in the temporary file.\n\nEspecially I think bare -<num> is a BSDism that has been\nremoved from POSIX some time ago.\n"},{"id":"371792","messageId":"20190318080306.GA28784@archbookpro.localdomain","threadId":"49830","inReplyTo":"xmqqa7hs3d0z.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v8 00/11] Fix scissors bug during conflict","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-18T08:03:06Z","receivedAt":"2019-03-18T08:03:12Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi Junio,\n\nOn Mon, Mar 18, 2019 at 03:35:56PM +0900, Junio C Hamano wrote:\n> SZEDER Gábor <szeder.dev@gmail.com> writes:\n> \n> >> +\tgit commit ${3+\"$3\"} -m \"$2\" >act &&\n> >> +\thead -1 <act >tmp &&\n> >> +\tmv tmp act &&\n> >\n> > Here you could swap the order of when you write to 'act' and 'tmp',\n> > and thus make the 'mv' unnecessary, like this:\n> >\n> >   git commit [...] >tmp &&\n> >   head -1 act >tmp &&\n> >   [...rest of the test...]\n> >\n> > Note also that 'head' (or 'sed' in later tests) can open its input\n> > file on its own, there's no need to redirect its standard input.\n> \n> Yup, that is better with a slight fix\n> \n> \tgit testing-output-from-this-command ... >tmp &&\n> \thead -n 1 tmp >actual &&\n> \n> to actually read from the output kept in the temporary file.\n> \n> Especially I think bare -<num> is a BSDism that has been\n> removed from POSIX some time ago.\n\nYou mentioned in an earlier email that,\n\n> The intermediary file may want a name better than 'tmp', if it is to\n> be left behind, but this will do for now.\n\nso I wrote it in a way temporary files wouldn't be produced, similar to\nhow 'set up mod-256 conflict scenario' in t7600 does it.\n\nWould it be better to leave the temporary files behind like how v7 of\nthe patches did it or should keep the mv?\n\nThanks,\n\nDenton\n"},{"id":"371795","messageId":"xmqqlg1czj18.fsf@gitster-ct.c.googlers.com","threadId":"49830","inReplyTo":"20190318080306.GA28784@archbookpro.localdomain","subject":"Re: [PATCH v8 00/11] Fix scissors bug during conflict","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-03-18T08:25:07Z","receivedAt":"2019-03-18T08:25:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Denton Liu <liu.denton@gmail.com> writes:\n\n>> > Here you could swap the order of when you write to 'act' and 'tmp',\n>> > and thus make the 'mv' unnecessary, like this:\n>> >\n>> >   git commit [...] >tmp &&\n>> >   head -1 act >tmp &&\n>> >   [...rest of the test...]\n>> >\n>> > Note also that 'head' (or 'sed' in later tests) can open its input\n>> > file on its own, there's no need to redirect its standard input.\n>> \n>> Yup, that is better with a slight fix\n>> \n>> \tgit testing-output-from-this-command ... >tmp &&\n>> \thead -n 1 tmp >actual &&\n>> \n>> to actually read from the output kept in the temporary file.\n>> \n>> Especially I think bare -<num> is a BSDism that has been\n>> removed from POSIX some time ago.\n>\n> You mentioned in an earlier email that,\n>\n>> The intermediary file may want a name better than 'tmp', if it is to\n>> be left behind, but this will do for now.\n>\n> so I wrote it in a way temporary files wouldn't be produced, similar to\n> how 'set up mod-256 conflict scenario' in t7600 does it.\n\nI didn't mean to say that the name of the final file should be\ngarbage, though ;-) I was hinting, by mentioning \"if it is to be\nleft behind\", to remove it when you are done with the temporary.\n\nIf we were to give more meaningful name, then perhaps just get rid\nof the name \"tmp\", like so.\n\n\techo what is expected >expect &&\n\tgit testing-output-from-this-command ... >output.raw &&\n\tsed-or-something-to-extract-what-matters output.raw >actual &&\n\ttest_cmp expect actual\n\nwhich is also good (and when the test fails, looking at the trash\ndirectory and finding the raw output named *.raw may be easier from\nthe output of \"ls trash*\" while debugging).\n\n"},{"id":"371838","messageId":"CAPig+cRmz2C7mAzc7Z=ZStAxd3qDSmbC4sbwyLGKqvkf2yzVPA@mail.gmail.com","threadId":"49830","inReplyTo":"4edca9db0b2a32621a7622b5b709db23b2512d1e.1552817044.git.liu.denton@gmail.com","subject":"Re: [PATCH v8 10/11] sequencer.c: define describe_cleanup_mode","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-03-18T20:04:22Z","receivedAt":"2019-03-18T20:04:37Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Mar 17, 2019 at 6:16 AM Denton Liu <liu.denton@gmail.com> wrote:\n> Define a function which allows us to get the string configuration value\n> of a enum commit_msg_cleanup_mode. This is done by refactoring\n> get_cleanup_mode such that it uses a lookup table to find the mappings\n> between string and enum and then using the same LUT in reverse to define\n> describe_cleanup_mode.\n>\n> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com>\n> Reviewed-by: Junio C Hamano <gitster@pobox.com>\n\nThese two Reviewed-by: lines should be dropped for a couple reasons.\n\nFirst, neither Junio nor I reviewed _this_ version of the patch.\n\nSecond, a Reviewed-by: is given explicitly (not taken). When a\nreviewer has thoroughly read and understood a patch and considers it\nproblem-free, he or she may say explicitly \"Reviewed-by: <me>\",\nstating satisfaction that the patch seems worthy of inclusion in the\nproject. If he sees fit, Junio may then pick up that Reviewed-by: at\nthe time he queues the patch in his tree.\n\n> Signed-off-by: Ramsay Jones <ramsay@ramsayjones.plus.com>\n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n"},{"id":"371840","messageId":"20190318203007.GA14875@dev-l","threadId":"49830","inReplyTo":"CAPig+cRmz2C7mAzc7Z=ZStAxd3qDSmbC4sbwyLGKqvkf2yzVPA@mail.gmail.com","subject":"Re: [PATCH v8 10/11] sequencer.c: define describe_cleanup_mode","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-18T20:30:07Z","receivedAt":"2019-03-18T20:30:13Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"On Mon, Mar 18, 2019 at 04:04:22PM -0400, Eric Sunshine wrote:\n> On Sun, Mar 17, 2019 at 6:16 AM Denton Liu <liu.denton@gmail.com> wrote:\n> > Define a function which allows us to get the string configuration value\n> > of a enum commit_msg_cleanup_mode. This is done by refactoring\n> > get_cleanup_mode such that it uses a lookup table to find the mappings\n> > between string and enum and then using the same LUT in reverse to define\n> > describe_cleanup_mode.\n> >\n> > Reviewed-by: Eric Sunshine <sunshine@sunshineco.com>\n> > Reviewed-by: Junio C Hamano <gitster@pobox.com>\n> \n> These two Reviewed-by: lines should be dropped for a couple reasons.\n> \n> First, neither Junio nor I reviewed _this_ version of the patch.\n> \n> Second, a Reviewed-by: is given explicitly (not taken). When a\n> reviewer has thoroughly read and understood a patch and considers it\n> problem-free, he or she may say explicitly \"Reviewed-by: <me>\",\n> stating satisfaction that the patch seems worthy of inclusion in the\n> project. If he sees fit, Junio may then pick up that Reviewed-by: at\n> the time he queues the patch in his tree.\n\nMy mistake, I misunderstood the purpose of the Reviewed-by tag.\n\nI was using it to give credit for changes that I incorporated in\nresponse to a review. In addition, it was helpful for bookkeeping who\nsuggested what where.\n\nWould a Helped-by tag be more appropriate in this situation?\n\nThanks,\n\nDenton\n\n> \n> > Signed-off-by: Ramsay Jones <ramsay@ramsayjones.plus.com>\n> > Signed-off-by: Denton Liu <liu.denton@gmail.com>\n"},{"id":"371841","messageId":"CAPig+cSAVLh7eahoqitC0tdYdDa6j4=iBZMiNw3ab-ysR=i_Cw@mail.gmail.com","threadId":"49830","inReplyTo":"20190318203007.GA14875@dev-l","subject":"Re: [PATCH v8 10/11] sequencer.c: define describe_cleanup_mode","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2019-03-18T20:32:06Z","receivedAt":"2019-03-18T20:32:20Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Mar 18, 2019 at 4:30 PM Denton Liu <liu.denton@gmail.com> wrote:\n> On Mon, Mar 18, 2019 at 04:04:22PM -0400, Eric Sunshine wrote:\n> > These two Reviewed-by: lines should be dropped for a couple reasons.\n> >\n> I was using it to give credit for changes that I incorporated in\n> response to a review. In addition, it was helpful for bookkeeping who\n> suggested what where.\n>\n> Would a Helped-by tag be more appropriate in this situation?\n\nUsing Helped-by: would be quite appropriate.\n"},{"id":"371866","messageId":"5b97cc72-a318-bd70-3a8d-4fd48537b2b6@ramsayjones.plus.com","threadId":"49830","inReplyTo":"CAPig+cRmz2C7mAzc7Z=ZStAxd3qDSmbC4sbwyLGKqvkf2yzVPA@mail.gmail.com","subject":"Re: [PATCH v8 10/11] sequencer.c: define describe_cleanup_mode","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsayjones.plus.com","sentAt":"2019-03-19T00:55:00Z","receivedAt":"2019-03-19T00:55:14Z","isPatch":true,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"\n\nOn 18/03/2019 20:04, Eric Sunshine wrote:\n> On Sun, Mar 17, 2019 at 6:16 AM Denton Liu <liu.denton@gmail.com> wrote:\n>> Define a function which allows us to get the string configuration value\n>> of a enum commit_msg_cleanup_mode. This is done by refactoring\n>> get_cleanup_mode such that it uses a lookup table to find the mappings\n>> between string and enum and then using the same LUT in reverse to define\n>> describe_cleanup_mode.\n>>\n>> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com>\n>> Reviewed-by: Junio C Hamano <gitster@pobox.com>\n> \n> These two Reviewed-by: lines should be dropped for a couple reasons.\n> \n> First, neither Junio nor I reviewed _this_ version of the patch.\n> \n> Second, a Reviewed-by: is given explicitly (not taken). When a\n> reviewer has thoroughly read and understood a patch and considers it\n> problem-free, he or she may say explicitly \"Reviewed-by: <me>\",\n> stating satisfaction that the patch seems worthy of inclusion in the\n> project. If he sees fit, Junio may then pick up that Reviewed-by: at\n> the time he queues the patch in his tree.\n> \n>> Signed-off-by: Ramsay Jones <ramsay@ramsayjones.plus.com>\n\nI was similarly surprised to see this SOB line as well. There was\nnothing copyright-able in the 'squash patch' I sent, so I don't\nthink this is warranted. (A 'Helped-by:' at _most_, I would think).\n;-)\n\nATB,\nRamsay Jones\n"},{"id":"371899","messageId":"304e99ef-553c-f538-f1d8-aaa0937da62f@gmail.com","threadId":"49830","inReplyTo":"cd5c764fbf2c7aa6fb09a08a93c171580a41940d.1552817044.git.liu.denton@gmail.com","subject":"Re: [PATCH v8 08/11] merge: cleanup messages like commit","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-03-19T11:13:37Z","receivedAt":"2019-03-19T11:13:44Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 17/03/2019 10:16, Denton Liu wrote:\n> This change allows git-merge messages to be cleaned up with the\n> commit.cleanup configuration or --cleanup option, just like how\n> git-commit does it.\n> \n> We also give git-pull the passthrough option of --cleanup so that it can\n> also take advantage of this change.\n> \n> Finally, add testing to ensure that messages are properly cleaned up.\n> Note that some newlines that were added to the commit message were\n> removed so that if a file were read via -F, it would be copied\n> faithfully.\n> \n> Reviewed-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nI'd echo Eric's comments about Reviewed-by tags.\n\n> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com>\n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n> \n> Phillip Wood wrote:\n>> cleanup needs to take an argument so PARSE_OPT_NOARG does not look\n>> right. Also I think it would be bettor from the user's point of view if\n>> the value of the argument was checked by pull before it does any work\n>> rather otherwise if they pass in invalid value pull mostly runs and then\n>> merge errors out at the end.\n> \n> I opted not to do a check on the validity of the value of the\n> --cleanup-mode argument because the strategy options that existed before\n> also didn't verify the validity of their values. In the future, it might\n> be a good idea to check the values of both cleanup-mode and the\n> strategy options but for now, I think we can leave it as it is.\n\nWith --strategy-option the valid values depend on the --strategy option \nwhich may invoke an external command so in general there is no way to \ncheck the values are valid (it could do for the strategies we know \nabout). With --cleanup we know the valid values and have a function that \ncan check for them so I think it would be worth doing.\n\n>   Documentation/merge-options.txt |  5 +++\n>   builtin/merge.c                 | 31 +++++++++++----\n>   builtin/pull.c                  |  6 +++\n>   t/t7604-merge-custom-message.sh | 67 +++++++++++++++++++++++++++++++++\n>   wt-status.c                     | 12 ++++--\n>   wt-status.h                     |  1 +\n>   6 files changed, 112 insertions(+), 10 deletions(-)\n> \n> diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\n> index 92a7d936c1..646100ea9a 100644\n> --- a/Documentation/merge-options.txt\n> +++ b/Documentation/merge-options.txt\n> @@ -32,6 +32,11 @@ they run `git merge`. To make it easier to adjust such scripts to the\n>   updated behaviour, the environment variable `GIT_MERGE_AUTOEDIT` can be\n>   set to `no` at the beginning of them.\n>   \n> +--cleanup=<mode>::\n> +\tThis option determines how the merge message will be cleaned up\n> +\tbefore commiting or being passed on. See linkgit:git-commit[1] for more\n\nAs I commented before I don't understand 'passed on'\n\n> +\tdetails.\n> +\n>   --ff::\n>   \tWhen the merge resolves as a fast-forward, only update the branch\n>   \tpointer, without creating a merge commit.  This is the default\n> diff --git a/builtin/merge.c b/builtin/merge.c\n> index 5ce8946d39..7be03a2610 100644\n> --- a/builtin/merge.c\n> +++ b/builtin/merge.c\n> @@ -38,6 +38,7 @@\n>   #include \"tag.h\"\n>   #include \"alias.h\"\n>   #include \"commit-reach.h\"\n> +#include \"wt-status.h\"\n>   \n>   #define DEFAULT_TWOHEAD (1<<0)\n>   #define DEFAULT_OCTOPUS (1<<1)\n> @@ -98,6 +99,9 @@ enum ff_type {\n>   \n>   static enum ff_type fast_forward = FF_ALLOW;\n>   \n> +static const char *cleanup_arg;\n> +static enum commit_msg_cleanup_mode cleanup_mode;\n> +\n>   static int option_parse_message(const struct option *opt,\n>   \t\t\t\tconst char *arg, int unset)\n>   {\n> @@ -249,6 +253,7 @@ static struct option builtin_merge_options[] = {\n>   \t\tN_(\"perform a commit if the merge succeeds (default)\")),\n>   \tOPT_BOOL('e', \"edit\", &option_edit,\n>   \t\tN_(\"edit message before committing\")),\n> +\tOPT_CLEANUP(&cleanup_arg),\n>   \tOPT_SET_INT(0, \"ff\", &fast_forward, N_(\"allow fast-forward (default)\"), FF_ALLOW),\n>   \tOPT_SET_INT_F(0, \"ff-only\", &fast_forward,\n>   \t\t      N_(\"abort if fast-forward is not possible\"),\n> @@ -612,6 +617,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n>   \t\treturn git_config_string(&pull_twohead, k, v);\n>   \telse if (!strcmp(k, \"pull.octopus\"))\n>   \t\treturn git_config_string(&pull_octopus, k, v);\n> +\telse if (!strcmp(k, \"commit.cleanup\"))\n> +\t\treturn git_config_string(&cleanup_arg, k, v);\n>   \telse if (!strcmp(k, \"merge.renormalize\"))\n>   \t\toption_renormalize = git_config_bool(k, v);\n>   \telse if (!strcmp(k, \"merge.ff\")) {\n> @@ -797,23 +804,32 @@ static void abort_commit(struct commit_list *remoteheads, const char *err_msg)\n>   \texit(1);\n>   }\n>   \n> +static const char comment_line_explanation[] =\n> +N_(\"Lines starting with '%c' will be ignored.\\n\");\n> +\n>   static const char merge_editor_comment[] =\n>   N_(\"Please enter a commit message to explain why this merge is necessary,\\n\"\n>      \"especially if it merges an updated upstream into a topic branch.\\n\"\n>      \"\\n\"\n> -   \"Lines starting with '%c' will be ignored, and an empty message aborts\\n\"\n> -   \"the commit.\\n\");\n> +   \"An empty message aborts the commit.\\n\");\n>   \n>   static void write_merge_heads(struct commit_list *);\n>   static void prepare_to_commit(struct commit_list *remoteheads)\n>   {\n>   \tstruct strbuf msg = STRBUF_INIT;\n>   \tstrbuf_addbuf(&msg, &merge_msg);\n> -\tstrbuf_addch(&msg, '\\n');\n>   \tif (squash)\n>   \t\tBUG(\"the control must not reach here under --squash\");\n> -\tif (0 < option_edit)\n> -\t\tstrbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);\n> +\tif (0 < option_edit) {\n> +\t\tstrbuf_addch(&msg, '\\n');\n> +\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n> +\t\t\twt_status_append_cut_line(&msg);\n> +\t\telse\n> +\t\t\tstrbuf_commented_addf(&msg, _(comment_line_explanation), comment_line_char);\n> +\n> +\t\tstrbuf_commented_addf(&msg, \"\\n\");\n> +\t\tstrbuf_commented_addf(&msg, _(merge_editor_comment));\n> +\t}\n\nThis still changes the wording of the message cf \nhttps://public-inbox.org/git/cover.1552275703.git.liu.denton@gmail.com/T/#m09cb1a05eb3bffb47ee9f25572904a7279efa362\n\nBest Wishes\n\nPhillip\n\n>   \tif (signoff)\n>   \t\tappend_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);\n>   \twrite_merge_heads(remoteheads);\n> @@ -832,7 +848,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)\n>   \t\tabort_commit(remoteheads, NULL);\n>   \n>   \tread_merge_msg(&msg);\n> -\tstrbuf_stripspace(&msg, 0 < option_edit);\n> +\tcleanup_message(&msg, cleanup_mode, 0);\n>   \tif (!msg.len)\n>   \t\tabort_commit(remoteheads, _(\"Empty commit message.\"));\n>   \tstrbuf_release(&merge_msg);\n> @@ -880,7 +896,6 @@ static int finish_automerge(struct commit *head,\n>   \tparents = remoteheads;\n>   \tif (!head_subsumed || fast_forward == FF_NO)\n>   \t\tcommit_list_insert(head, &parents);\n> -\tstrbuf_addch(&merge_msg, '\\n');\n>   \tprepare_to_commit(remoteheads);\n>   \tif (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,\n>   \t\t\t&result_commit, NULL, sign_commit))\n> @@ -1389,6 +1404,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n>   \tif (option_edit < 0)\n>   \t\toption_edit = default_edit_option();\n>   \n> +\tcleanup_mode = get_cleanup_mode(cleanup_arg, 0 < option_edit, 1);\n> +\n>   \tif (!use_strategies) {\n>   \t\tif (!remoteheads)\n>   \t\t\t; /* already up-to-date */\n> diff --git a/builtin/pull.c b/builtin/pull.c\n> index 33db889955..292c1dac27 100644\n> --- a/builtin/pull.c\n> +++ b/builtin/pull.c\n> @@ -101,6 +101,7 @@ static char *opt_signoff;\n>   static char *opt_squash;\n>   static char *opt_commit;\n>   static char *opt_edit;\n> +static char *opt_cleanup;\n>   static char *opt_ff;\n>   static char *opt_verify_signatures;\n>   static int opt_autostash = -1;\n> @@ -168,6 +169,9 @@ static struct option pull_options[] = {\n>   \tOPT_PASSTHRU(0, \"edit\", &opt_edit, NULL,\n>   \t\tN_(\"edit message before committing\"),\n>   \t\tPARSE_OPT_NOARG),\n> +\tOPT_PASSTHRU(0, \"cleanup\", &opt_cleanup, NULL,\n> +\t\tN_(\"how to strip spaces and #comments from message\"),\n> +\t\t0),\n>   \tOPT_PASSTHRU(0, \"ff\", &opt_ff, NULL,\n>   \t\tN_(\"allow fast-forward\"),\n>   \t\tPARSE_OPT_NOARG),\n> @@ -644,6 +648,8 @@ static int run_merge(void)\n>   \t\targv_array_push(&args, opt_commit);\n>   \tif (opt_edit)\n>   \t\targv_array_push(&args, opt_edit);\n> +\tif (opt_cleanup)\n> +\t\targv_array_push(&args, opt_cleanup);\n>   \tif (opt_ff)\n>   \t\targv_array_push(&args, opt_ff);\n>   \tif (opt_verify_signatures)\n> diff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh\n> index b045fdb413..c9685a318d 100755\n> --- a/t/t7604-merge-custom-message.sh\n> +++ b/t/t7604-merge-custom-message.sh\n> @@ -51,4 +51,71 @@ test_expect_success 'merge --log appends to custom message' '\n>   \ttest_cmp exp.log actual\n>   '\n>   \n> +mesg_with_comment_and_newlines='\n> +# text\n> +\n> +'\n> +\n> +test_expect_success 'prepare file with comment line and trailing newlines'  '\n> +\tprintf \"%s\" \"$mesg_with_comment_and_newlines\" >expect\n> +'\n> +\n> +test_expect_success 'cleanup commit messages (verbatim option)' '\n> +\tgit reset --hard c1 &&\n> +\tgit merge --cleanup=verbatim -F expect c2 &&\n> +\tgit cat-file commit HEAD >actual &&\n> +\tsed -e \"1,/^$/d\" <actual >tmp &&\n> +\tmv tmp actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n> +test_expect_success 'cleanup commit messages (whitespace option)' '\n> +\tgit reset --hard c1 &&\n> +\ttest_write_lines \"\" \"# text\" \"\" >text &&\n> +\techo \"# text\" >expect &&\n> +\tgit merge --cleanup=whitespace -F text c2 &&\n> +\tgit cat-file commit HEAD >actual &&\n> +\tsed -e \"1,/^$/d\" <actual >tmp &&\n> +\tmv tmp actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n> +test_expect_success 'cleanup merge messages (scissors option)' '\n> +\tgit reset --hard c1 &&\n> +\tcat >text <<-\\EOF &&\n> +\n> +\t# to be kept\n> +\n> +\t  # ------------------------ >8 ------------------------\n> +\t# to be kept, too\n> +\t# ------------------------ >8 ------------------------\n> +\tto be removed\n> +\t# ------------------------ >8 ------------------------\n> +\tto be removed, too\n> +\tEOF\n> +\n> +\tcat >expect <<-\\EOF &&\n> +\t# to be kept\n> +\n> +\t  # ------------------------ >8 ------------------------\n> +\t# to be kept, too\n> +\tEOF\n> +\tgit merge --cleanup=scissors -e -F text c2 &&\n> +\tgit cat-file commit HEAD >actual &&\n> +\tsed -e \"1,/^$/d\" <actual >tmp &&\n> +\tmv tmp actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n> +test_expect_success 'cleanup commit messages (strip option)' '\n> +\tgit reset --hard c1 &&\n> +\ttest_write_lines \"\" \"# text\" \"sample\" \"\" >text &&\n> +\techo sample >expect &&\n> +\tgit merge --cleanup=strip -F text c2 &&\n> +\tgit cat-file commit HEAD >actual &&\n> +\tsed -e \"1,/^$/d\" <actual >tmp &&\n> +\tmv tmp actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n>   test_done\n> diff --git a/wt-status.c b/wt-status.c\n> index 445a36204a..b81fcd428d 100644\n> --- a/wt-status.c\n> +++ b/wt-status.c\n> @@ -1006,13 +1006,19 @@ size_t wt_status_locate_end(const char *s, size_t len)\n>   \treturn len;\n>   }\n>   \n> -void wt_status_add_cut_line(FILE *fp)\n> +void wt_status_append_cut_line(struct strbuf *buf)\n>   {\n>   \tconst char *explanation = _(\"Do not modify or remove the line above.\\nEverything below it will be ignored.\");\n> +\n> +\tstrbuf_commented_addf(buf, \"%s\", cut_line);\n> +\tstrbuf_add_commented_lines(buf, explanation, strlen(explanation));\n> +}\n> +\n> +void wt_status_add_cut_line(FILE *fp)\n> +{\n>   \tstruct strbuf buf = STRBUF_INIT;\n>   \n> -\tfprintf(fp, \"%c %s\", comment_line_char, cut_line);\n> -\tstrbuf_add_commented_lines(&buf, explanation, strlen(explanation));\n> +\twt_status_append_cut_line(&buf);\n>   \tfputs(buf.buf, fp);\n>   \tstrbuf_release(&buf);\n>   }\n> diff --git a/wt-status.h b/wt-status.h\n> index 3a95975032..64f1ddc9fd 100644\n> --- a/wt-status.h\n> +++ b/wt-status.h\n> @@ -129,6 +129,7 @@ struct wt_status {\n>   };\n>   \n>   size_t wt_status_locate_end(const char *s, size_t len);\n> +void wt_status_append_cut_line(struct strbuf *buf);\n>   void wt_status_add_cut_line(FILE *fp);\n>   void wt_status_prepare(struct repository *r, struct wt_status *s);\n>   void wt_status_print(struct wt_status *s);\n> \n"},{"id":"371955","messageId":"20190320063215.GA9432@archbookpro.localdomain","threadId":"49830","inReplyTo":"304e99ef-553c-f538-f1d8-aaa0937da62f@gmail.com","subject":"Re: [PATCH v8 08/11] merge: cleanup messages like commit","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-20T06:32:15Z","receivedAt":"2019-03-20T06:32:23Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi Phillip,\n\nOn Tue, Mar 19, 2019 at 11:13:37AM +0000, Phillip Wood wrote:\n> Hi Denton\n> \n> On 17/03/2019 10:16, Denton Liu wrote:\n> > This change allows git-merge messages to be cleaned up with the\n> > commit.cleanup configuration or --cleanup option, just like how\n> > git-commit does it.\n> > \n> > We also give git-pull the passthrough option of --cleanup so that it can\n> > also take advantage of this change.\n> > \n> > Finally, add testing to ensure that messages are properly cleaned up.\n> > Note that some newlines that were added to the commit message were\n> > removed so that if a file were read via -F, it would be copied\n> > faithfully.\n> > \n> > Reviewed-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> \n> I'd echo Eric's comments about Reviewed-by tags.\n\nWill do.\n\n> \n> > Reviewed-by: Eric Sunshine <sunshine@sunshineco.com>\n> > Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> > ---\n> > \n> > Phillip Wood wrote:\n> > > cleanup needs to take an argument so PARSE_OPT_NOARG does not look\n> > > right. Also I think it would be bettor from the user's point of view if\n> > > the value of the argument was checked by pull before it does any work\n> > > rather otherwise if they pass in invalid value pull mostly runs and then\n> > > merge errors out at the end.\n> > \n> > I opted not to do a check on the validity of the value of the\n> > --cleanup-mode argument because the strategy options that existed before\n> > also didn't verify the validity of their values. In the future, it might\n> > be a good idea to check the values of both cleanup-mode and the\n> > strategy options but for now, I think we can leave it as it is.\n> \n> With --strategy-option the valid values depend on the --strategy option\n> which may invoke an external command so in general there is no way to check\n> the values are valid (it could do for the strategies we know about). With\n> --cleanup we know the valid values and have a function that can check for\n> them so I think it would be worth doing.\n\nMakes sense, I'll do it (and throw in a test case for free ;) )\n\n> \n> >   Documentation/merge-options.txt |  5 +++\n> >   builtin/merge.c                 | 31 +++++++++++----\n> >   builtin/pull.c                  |  6 +++\n> >   t/t7604-merge-custom-message.sh | 67 +++++++++++++++++++++++++++++++++\n> >   wt-status.c                     | 12 ++++--\n> >   wt-status.h                     |  1 +\n> >   6 files changed, 112 insertions(+), 10 deletions(-)\n> > \n> > diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\n> > index 92a7d936c1..646100ea9a 100644\n> > --- a/Documentation/merge-options.txt\n> > +++ b/Documentation/merge-options.txt\n> > @@ -32,6 +32,11 @@ they run `git merge`. To make it easier to adjust such scripts to the\n> >   updated behaviour, the environment variable `GIT_MERGE_AUTOEDIT` can be\n> >   set to `no` at the beginning of them.\n> > +--cleanup=<mode>::\n> > +\tThis option determines how the merge message will be cleaned up\n> > +\tbefore commiting or being passed on. See linkgit:git-commit[1] for more\n> \n> As I commented before I don't understand 'passed on'\n\nI changed it in git-revert.txt and git-cherry-pick.txt but I forgot to\nchange it here. What do you think about this:\n\n\t--cleanup=<mode>::\n\t\tThis option determines how the merge message will be cleaned up\n\t\tbefore commiting or being passed on to the commit machinery. See\n\t\tlinkgit:git-commit[1] for more details.\n\n> \n> > +\tdetails.\n> > +\n> >   --ff::\n> >   \tWhen the merge resolves as a fast-forward, only update the branch\n> >   \tpointer, without creating a merge commit.  This is the default\n> > diff --git a/builtin/merge.c b/builtin/merge.c\n> > index 5ce8946d39..7be03a2610 100644\n> > --- a/builtin/merge.c\n> > +++ b/builtin/merge.c\n> > @@ -38,6 +38,7 @@\n> >   #include \"tag.h\"\n> >   #include \"alias.h\"\n> >   #include \"commit-reach.h\"\n> > +#include \"wt-status.h\"\n> >   #define DEFAULT_TWOHEAD (1<<0)\n> >   #define DEFAULT_OCTOPUS (1<<1)\n> > @@ -98,6 +99,9 @@ enum ff_type {\n> >   static enum ff_type fast_forward = FF_ALLOW;\n> > +static const char *cleanup_arg;\n> > +static enum commit_msg_cleanup_mode cleanup_mode;\n> > +\n> >   static int option_parse_message(const struct option *opt,\n> >   \t\t\t\tconst char *arg, int unset)\n> >   {\n> > @@ -249,6 +253,7 @@ static struct option builtin_merge_options[] = {\n> >   \t\tN_(\"perform a commit if the merge succeeds (default)\")),\n> >   \tOPT_BOOL('e', \"edit\", &option_edit,\n> >   \t\tN_(\"edit message before committing\")),\n> > +\tOPT_CLEANUP(&cleanup_arg),\n> >   \tOPT_SET_INT(0, \"ff\", &fast_forward, N_(\"allow fast-forward (default)\"), FF_ALLOW),\n> >   \tOPT_SET_INT_F(0, \"ff-only\", &fast_forward,\n> >   \t\t      N_(\"abort if fast-forward is not possible\"),\n> > @@ -612,6 +617,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n> >   \t\treturn git_config_string(&pull_twohead, k, v);\n> >   \telse if (!strcmp(k, \"pull.octopus\"))\n> >   \t\treturn git_config_string(&pull_octopus, k, v);\n> > +\telse if (!strcmp(k, \"commit.cleanup\"))\n> > +\t\treturn git_config_string(&cleanup_arg, k, v);\n> >   \telse if (!strcmp(k, \"merge.renormalize\"))\n> >   \t\toption_renormalize = git_config_bool(k, v);\n> >   \telse if (!strcmp(k, \"merge.ff\")) {\n> > @@ -797,23 +804,32 @@ static void abort_commit(struct commit_list *remoteheads, const char *err_msg)\n> >   \texit(1);\n> >   }\n> > +static const char comment_line_explanation[] =\n> > +N_(\"Lines starting with '%c' will be ignored.\\n\");\n> > +\n> >   static const char merge_editor_comment[] =\n> >   N_(\"Please enter a commit message to explain why this merge is necessary,\\n\"\n> >      \"especially if it merges an updated upstream into a topic branch.\\n\"\n> >      \"\\n\"\n> > -   \"Lines starting with '%c' will be ignored, and an empty message aborts\\n\"\n> > -   \"the commit.\\n\");\n> > +   \"An empty message aborts the commit.\\n\");\n> >   static void write_merge_heads(struct commit_list *);\n> >   static void prepare_to_commit(struct commit_list *remoteheads)\n> >   {\n> >   \tstruct strbuf msg = STRBUF_INIT;\n> >   \tstrbuf_addbuf(&msg, &merge_msg);\n> > -\tstrbuf_addch(&msg, '\\n');\n> >   \tif (squash)\n> >   \t\tBUG(\"the control must not reach here under --squash\");\n> > -\tif (0 < option_edit)\n> > -\t\tstrbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);\n> > +\tif (0 < option_edit) {\n> > +\t\tstrbuf_addch(&msg, '\\n');\n> > +\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n> > +\t\t\twt_status_append_cut_line(&msg);\n> > +\t\telse\n> > +\t\t\tstrbuf_commented_addf(&msg, _(comment_line_explanation), comment_line_char);\n> > +\n> > +\t\tstrbuf_commented_addf(&msg, \"\\n\");\n> > +\t\tstrbuf_commented_addf(&msg, _(merge_editor_comment));\n> > +\t}\n> \n> This still changes the wording of the message cf https://public-inbox.org/git/cover.1552275703.git.liu.denton@gmail.com/T/#m09cb1a05eb3bffb47ee9f25572904a7279efa362\n\nWill do.\n\nThanks again for reviewing carefully!\n\n> \n> Best Wishes\n> \n> Phillip\n> \n> >   \tif (signoff)\n> >   \t\tappend_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);\n> >   \twrite_merge_heads(remoteheads);\n> > @@ -832,7 +848,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)\n> >   \t\tabort_commit(remoteheads, NULL);\n> >   \tread_merge_msg(&msg);\n> > -\tstrbuf_stripspace(&msg, 0 < option_edit);\n> > +\tcleanup_message(&msg, cleanup_mode, 0);\n> >   \tif (!msg.len)\n> >   \t\tabort_commit(remoteheads, _(\"Empty commit message.\"));\n> >   \tstrbuf_release(&merge_msg);\n> > @@ -880,7 +896,6 @@ static int finish_automerge(struct commit *head,\n> >   \tparents = remoteheads;\n> >   \tif (!head_subsumed || fast_forward == FF_NO)\n> >   \t\tcommit_list_insert(head, &parents);\n> > -\tstrbuf_addch(&merge_msg, '\\n');\n> >   \tprepare_to_commit(remoteheads);\n> >   \tif (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,\n> >   \t\t\t&result_commit, NULL, sign_commit))\n> > @@ -1389,6 +1404,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n> >   \tif (option_edit < 0)\n> >   \t\toption_edit = default_edit_option();\n> > +\tcleanup_mode = get_cleanup_mode(cleanup_arg, 0 < option_edit, 1);\n> > +\n> >   \tif (!use_strategies) {\n> >   \t\tif (!remoteheads)\n> >   \t\t\t; /* already up-to-date */\n> > diff --git a/builtin/pull.c b/builtin/pull.c\n> > index 33db889955..292c1dac27 100644\n> > --- a/builtin/pull.c\n> > +++ b/builtin/pull.c\n> > @@ -101,6 +101,7 @@ static char *opt_signoff;\n> >   static char *opt_squash;\n> >   static char *opt_commit;\n> >   static char *opt_edit;\n> > +static char *opt_cleanup;\n> >   static char *opt_ff;\n> >   static char *opt_verify_signatures;\n> >   static int opt_autostash = -1;\n> > @@ -168,6 +169,9 @@ static struct option pull_options[] = {\n> >   \tOPT_PASSTHRU(0, \"edit\", &opt_edit, NULL,\n> >   \t\tN_(\"edit message before committing\"),\n> >   \t\tPARSE_OPT_NOARG),\n> > +\tOPT_PASSTHRU(0, \"cleanup\", &opt_cleanup, NULL,\n> > +\t\tN_(\"how to strip spaces and #comments from message\"),\n> > +\t\t0),\n> >   \tOPT_PASSTHRU(0, \"ff\", &opt_ff, NULL,\n> >   \t\tN_(\"allow fast-forward\"),\n> >   \t\tPARSE_OPT_NOARG),\n> > @@ -644,6 +648,8 @@ static int run_merge(void)\n> >   \t\targv_array_push(&args, opt_commit);\n> >   \tif (opt_edit)\n> >   \t\targv_array_push(&args, opt_edit);\n> > +\tif (opt_cleanup)\n> > +\t\targv_array_push(&args, opt_cleanup);\n> >   \tif (opt_ff)\n> >   \t\targv_array_push(&args, opt_ff);\n> >   \tif (opt_verify_signatures)\n> > diff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh\n> > index b045fdb413..c9685a318d 100755\n> > --- a/t/t7604-merge-custom-message.sh\n> > +++ b/t/t7604-merge-custom-message.sh\n> > @@ -51,4 +51,71 @@ test_expect_success 'merge --log appends to custom message' '\n> >   \ttest_cmp exp.log actual\n> >   '\n> > +mesg_with_comment_and_newlines='\n> > +# text\n> > +\n> > +'\n> > +\n> > +test_expect_success 'prepare file with comment line and trailing newlines'  '\n> > +\tprintf \"%s\" \"$mesg_with_comment_and_newlines\" >expect\n> > +'\n> > +\n> > +test_expect_success 'cleanup commit messages (verbatim option)' '\n> > +\tgit reset --hard c1 &&\n> > +\tgit merge --cleanup=verbatim -F expect c2 &&\n> > +\tgit cat-file commit HEAD >actual &&\n> > +\tsed -e \"1,/^$/d\" <actual >tmp &&\n> > +\tmv tmp actual &&\n> > +\ttest_cmp expect actual\n> > +'\n> > +\n> > +test_expect_success 'cleanup commit messages (whitespace option)' '\n> > +\tgit reset --hard c1 &&\n> > +\ttest_write_lines \"\" \"# text\" \"\" >text &&\n> > +\techo \"# text\" >expect &&\n> > +\tgit merge --cleanup=whitespace -F text c2 &&\n> > +\tgit cat-file commit HEAD >actual &&\n> > +\tsed -e \"1,/^$/d\" <actual >tmp &&\n> > +\tmv tmp actual &&\n> > +\ttest_cmp expect actual\n> > +'\n> > +\n> > +test_expect_success 'cleanup merge messages (scissors option)' '\n> > +\tgit reset --hard c1 &&\n> > +\tcat >text <<-\\EOF &&\n> > +\n> > +\t# to be kept\n> > +\n> > +\t  # ------------------------ >8 ------------------------\n> > +\t# to be kept, too\n> > +\t# ------------------------ >8 ------------------------\n> > +\tto be removed\n> > +\t# ------------------------ >8 ------------------------\n> > +\tto be removed, too\n> > +\tEOF\n> > +\n> > +\tcat >expect <<-\\EOF &&\n> > +\t# to be kept\n> > +\n> > +\t  # ------------------------ >8 ------------------------\n> > +\t# to be kept, too\n> > +\tEOF\n> > +\tgit merge --cleanup=scissors -e -F text c2 &&\n> > +\tgit cat-file commit HEAD >actual &&\n> > +\tsed -e \"1,/^$/d\" <actual >tmp &&\n> > +\tmv tmp actual &&\n> > +\ttest_cmp expect actual\n> > +'\n> > +\n> > +test_expect_success 'cleanup commit messages (strip option)' '\n> > +\tgit reset --hard c1 &&\n> > +\ttest_write_lines \"\" \"# text\" \"sample\" \"\" >text &&\n> > +\techo sample >expect &&\n> > +\tgit merge --cleanup=strip -F text c2 &&\n> > +\tgit cat-file commit HEAD >actual &&\n> > +\tsed -e \"1,/^$/d\" <actual >tmp &&\n> > +\tmv tmp actual &&\n> > +\ttest_cmp expect actual\n> > +'\n> > +\n> >   test_done\n> > diff --git a/wt-status.c b/wt-status.c\n> > index 445a36204a..b81fcd428d 100644\n> > --- a/wt-status.c\n> > +++ b/wt-status.c\n> > @@ -1006,13 +1006,19 @@ size_t wt_status_locate_end(const char *s, size_t len)\n> >   \treturn len;\n> >   }\n> > -void wt_status_add_cut_line(FILE *fp)\n> > +void wt_status_append_cut_line(struct strbuf *buf)\n> >   {\n> >   \tconst char *explanation = _(\"Do not modify or remove the line above.\\nEverything below it will be ignored.\");\n> > +\n> > +\tstrbuf_commented_addf(buf, \"%s\", cut_line);\n> > +\tstrbuf_add_commented_lines(buf, explanation, strlen(explanation));\n> > +}\n> > +\n> > +void wt_status_add_cut_line(FILE *fp)\n> > +{\n> >   \tstruct strbuf buf = STRBUF_INIT;\n> > -\tfprintf(fp, \"%c %s\", comment_line_char, cut_line);\n> > -\tstrbuf_add_commented_lines(&buf, explanation, strlen(explanation));\n> > +\twt_status_append_cut_line(&buf);\n> >   \tfputs(buf.buf, fp);\n> >   \tstrbuf_release(&buf);\n> >   }\n> > diff --git a/wt-status.h b/wt-status.h\n> > index 3a95975032..64f1ddc9fd 100644\n> > --- a/wt-status.h\n> > +++ b/wt-status.h\n> > @@ -129,6 +129,7 @@ struct wt_status {\n> >   };\n> >   size_t wt_status_locate_end(const char *s, size_t len);\n> > +void wt_status_append_cut_line(struct strbuf *buf);\n> >   void wt_status_add_cut_line(FILE *fp);\n> >   void wt_status_prepare(struct repository *r, struct wt_status *s);\n> >   void wt_status_print(struct wt_status *s);\n> > \n"},{"id":"372060","messageId":"cover.1553150827.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1552817044.git.liu.denton@gmail.com","subject":"[PATCH v9 00/11] Fix scissors bug during conflict","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-21T06:53:34Z","receivedAt":"2019-03-21T06:53:41Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Thanks again for the reviews, everyone.\n\nWe're slowly making it there ;)\n\n\n\nPrevious discussion on the cherry-pick/revert changes can be found here[1].\n\nChanges since revert/cherry-pick v3:\n\n* Rebased on top of latest master\n* Reordered and squashed patches\n* Added populate_opts_cb and save_opts to save default_msg_cleanup at Phillip's suggestion\n\nChanges since v7:\n\n* Cleaned up more tests according to Eric's advice (and added some more cleanup patches!)\n* Clarify meaning of \"passed on\" in documentation\n* Consolidate common options into OPT_CLEANUP macro in parse-options.h\n* Fix space indent to tabs in sequencer.c\n* Shorten variable names for readability\n* Make comment about 0th element in array being default more assertive\n* Remove unnecessary braces around if/for statements\n* Lowercase warning/error messages\n* Unmark BUG messages for translation\n* Rename get_config_from_cleanup -> describe_cleanup_mode\n* Change PARSE_OPT_NOARG to 0 for --cleanup in git-pull\n* Squashed in Ramsay's static patch\n\nChanges since v8:\n\n* More test cleanup\n* Changed Reviewed-by: to Helped-by: in commit messages\n* Check validity of --cleanup arg in git-pull\n* Revised documentation to make \"passed on\" more clear (I hope!)\n* Restore merge message to original text in the case of non-scissors\n\n[1]: https://public-inbox.org/git/cover.1551867827.git.liu.denton@gmail.com/T/#u\n\n\nDenton Liu (11):\n  t7600: clean up style\n  t3507: clean up style\n  t7604: clean up style\n  t7502: clean up style\n  commit: extract cleanup_mode functions to sequencer\n  parse-options.h: extract common --cleanup option\n  sequencer.c: remove duplicate code\n  merge: cleanup messages like commit\n  merge: add scissors line on merge conflict\n  sequencer.c: define describe_cleanup_mode\n  cherry-pick/revert: add scissors line on merge conflict\n\n Documentation/git-cherry-pick.txt |   7 ++\n Documentation/git-revert.txt      |   7 ++\n Documentation/merge-options.txt   |   7 ++\n builtin/commit.c                  |  47 +++++------\n builtin/merge.c                   |  44 ++++++++--\n builtin/pull.c                    |  23 ++++++\n builtin/rebase--interactive.c     |   2 +-\n builtin/revert.c                  |   5 ++\n builtin/tag.c                     |   3 +-\n parse-options.h                   |   1 +\n sequencer.c                       |  97 +++++++++++++++++-----\n sequencer.h                       |   9 ++-\n t/t3507-cherry-pick-conflict.sh   | 128 +++++++++++++++++++++++++-----\n t/t5521-pull-options.sh           |   8 ++\n t/t7502-commit-porcelain.sh       |  93 ++++++++++++----------\n t/t7600-merge.sh                  |  73 +++++++++++++----\n t/t7604-merge-custom-message.sh   |  75 +++++++++++++++--\n wt-status.c                       |  12 ++-\n wt-status.h                       |   1 +\n 19 files changed, 500 insertions(+), 142 deletions(-)\n\nInterdiff against v8:\ndiff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt\nindex 2601e1868e..cbb663a843 100644\n--- a/Documentation/git-cherry-pick.txt\n+++ b/Documentation/git-cherry-pick.txt\n@@ -59,10 +59,10 @@ OPTIONS\n \n --cleanup=<mode>::\n \tThis option determines how the commit message will be cleaned up before\n-\tbeing passed on. See linkgit:git-commit[1] for more details. In\n-\tparticular, if the '<mode>' is given a value of `scissors`, scissors\n-\twill be appended to `MERGE_MSG` before being passed on to the commit\n-\tmachinery in the case of a conflict.\n+\tbeing passed on to the commit machinery. See linkgit:git-commit[1] for more\n+\tdetails. In particular, if the '<mode>' is given a value of `scissors`,\n+\tscissors will be appended to `MERGE_MSG` before being passed on in the case\n+\tof a conflict.\n \n -x::\n \tWhen recording the commit, append a line that says\ndiff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt\nindex cabeb9df2c..7fd254df92 100644\n--- a/Documentation/git-revert.txt\n+++ b/Documentation/git-revert.txt\n@@ -68,10 +68,10 @@ more details.\n \n --cleanup=<mode>::\n \tThis option determines how the commit message will be cleaned up before\n-\tbeing passed on. See linkgit:git-commit[1] for more details. In\n-\tparticular, if the '<mode>' is given a value of `scissors`, scissors\n-\twill be appended to `MERGE_MSG` before being passed on to the commit\n-\tmachinery in the case of a conflict.\n+\tbeing passed on to the commit machinery. See linkgit:git-commit[1] for more\n+\tdetails. In particular, if the '<mode>' is given a value of `scissors`,\n+\tscissors will be appended to `MERGE_MSG` before being passed on in the case\n+\tof a conflict.\n \n -n::\n --no-commit::\ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 405e16c617..61876dbc33 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -33,11 +33,11 @@ updated behaviour, the environment variable `GIT_MERGE_AUTOEDIT` can be\n set to `no` at the beginning of them.\n \n --cleanup=<mode>::\n-\tThis option determines how the merge message will be cleaned up\n-\tbefore commiting or being passed on. See linkgit:git-commit[1] for more\n-\tdetails. In addition, if the '<mode>' is given a value of `scissors`,\n-\tscissors will be appended to `MERGE_MSG` before being passed on to the\n-\tcommit machinery in the case of a merge conflict.\n+\tThis option determines how the merge message will be cleaned up before\n+\tcommiting. See linkgit:git-commit[1] for more details. In addition, if\n+\tthe '<mode>' is given a value of `scissors`, scissors will be appended\n+\tto `MERGE_MSG` before being passed on to the commit machinery in the\n+\tcase of a merge conflict.\n \n --ff::\n \tWhen the merge resolves as a fast-forward, only update the branch\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 90c64346e9..235cd3b2e1 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -804,14 +804,17 @@ static void abort_commit(struct commit_list *remoteheads, const char *err_msg)\n \texit(1);\n }\n \n-static const char comment_line_explanation[] =\n-N_(\"Lines starting with '%c' will be ignored.\\n\");\n-\n static const char merge_editor_comment[] =\n N_(\"Please enter a commit message to explain why this merge is necessary,\\n\"\n    \"especially if it merges an updated upstream into a topic branch.\\n\"\n-   \"\\n\"\n-   \"An empty message aborts the commit.\\n\");\n+   \"\\n\");\n+\n+static const char scissors_editor_comment[] =\n+N_(\"An empty message aborts the commit.\\n\");\n+\n+static const char no_scissors_editor_comment[] =\n+N_(\"Lines starting with '%c' will be ignored, and an empty message aborts\\n\"\n+   \"the commit.\\n\");\n \n static void write_merge_heads(struct commit_list *);\n static void prepare_to_commit(struct commit_list *remoteheads)\n@@ -824,11 +827,12 @@ static void prepare_to_commit(struct commit_list *remoteheads)\n \t\tstrbuf_addch(&msg, '\\n');\n \t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n \t\t\twt_status_append_cut_line(&msg);\n-\t\telse\n-\t\t\tstrbuf_commented_addf(&msg, _(comment_line_explanation), comment_line_char);\n \n \t\tstrbuf_commented_addf(&msg, \"\\n\");\n \t\tstrbuf_commented_addf(&msg, _(merge_editor_comment));\n+\t\tstrbuf_commented_addf(&msg, _(cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS ?\n+\t\t\tscissors_editor_comment :\n+\t\t\tno_scissors_editor_comment), comment_line_char);\n \t}\n \tif (signoff)\n \t\tappend_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 292c1dac27..55ebb5808e 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -24,6 +24,7 @@\n #include \"lockfile.h\"\n #include \"wt-status.h\"\n #include \"commit-reach.h\"\n+#include \"sequencer.h\"\n \n enum rebase_type {\n \tREBASE_INVALID = -1,\n@@ -881,6 +882,22 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);\n \n+\tif (opt_cleanup) {\n+\t\tconst char *prefix = \"--cleanup=\";\n+\t\tconst char *cleanup_arg;\n+\n+\t\tif (strncmp(opt_cleanup, prefix, strlen(prefix)))\n+\t\t\tBUG(\"expecting prefix %s, argument is %s\", prefix, opt_cleanup);\n+\n+\t\tcleanup_arg = &opt_cleanup[strlen(prefix)];\n+\n+\t\t/*\n+\t\t * this only checks the validity of cleanup_arg; we don't need\n+\t\t * a valid value for use_editor\n+\t\t */\n+\t\tget_cleanup_mode(cleanup_arg, 0, 1);\n+\t}\n+\n \tparse_repo_refspecs(argc, argv, &repo, &refspecs);\n \n \tif (!opt_ff)\ndiff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh\nindex c3894ca9d6..a584b11c98 100755\n--- a/t/t3507-cherry-pick-conflict.sh\n+++ b/t/t3507-cherry-pick-conflict.sh\n@@ -180,7 +180,7 @@ test_expect_success 'failed cherry-pick registers participants in index' '\n \t\t1 s/ 0\t/ 1\t/\n \t\t2 s/ 0\t/ 2\t/\n \t\t3 s/ 0\t/ 3\t/\n-\t\" <stages >expected &&\n+\t\" stages >expected &&\n \tgit read-tree -u --reset HEAD &&\n \n \ttest_must_fail git cherry-pick picked &&\n@@ -286,7 +286,7 @@ test_expect_success 'revert also handles conflicts sanely' '\n \t\t1 s/ 0\t/ 1\t/\n \t\t2 s/ 0\t/ 2\t/\n \t\t3 s/ 0\t/ 3\t/\n-\t\" <stages >expected-stages &&\n+\t\" stages >expected-stages &&\n \tgit read-tree -u --reset HEAD &&\n \n \thead=$(git rev-parse HEAD) &&\n@@ -431,7 +431,7 @@ test_expect_success 'commit after failed cherry-pick does not add duplicated -s'\n \tpristine_detach initial &&\n \ttest_must_fail git cherry-pick -s picked-signed &&\n \tgit commit -a -s &&\n-\ttest $(git show -s |grep -c \"Signed-off-by\") = 1\n+\ttest $(git show -s >tmp && grep -c \"Signed-off-by\" tmp && rm tmp) = 1\n '\n \n test_expect_success 'commit after failed cherry-pick adds -s at the right place' '\n@@ -445,7 +445,7 @@ test_expect_success 'commit after failed cherry-pick adds -s at the right place'\n \tSigned-off-by: C O Mitter <committer@example.com>\n \t# Conflicts:\n \tEOF\n-\tgrep -e \"^# Conflicts:\" -e '^Signed-off-by' <.git/COMMIT_EDITMSG >actual &&\n+\tgrep -e \"^# Conflicts:\" -e '^Signed-off-by' .git/COMMIT_EDITMSG >actual &&\n \ttest_cmp expect actual &&\n \n \tcat <<-\\EOF >expected &&\n@@ -464,7 +464,7 @@ test_expect_success 'commit --amend -s places the sign-off at the right place' '\n \n \t# emulate old-style conflicts block\n \tmv .git/MERGE_MSG .git/MERGE_MSG+ &&\n-\tsed -e \"/^# Conflicts:/,\\$s/^# *//\" <.git/MERGE_MSG+ >.git/MERGE_MSG &&\n+\tsed -e \"/^# Conflicts:/,\\$s/^# *//\" .git/MERGE_MSG+ >.git/MERGE_MSG &&\n \n \tgit commit -a &&\n \tgit commit --amend -s &&\n@@ -474,7 +474,7 @@ test_expect_success 'commit --amend -s places the sign-off at the right place' '\n \tSigned-off-by: C O Mitter <committer@example.com>\n \tConflicts:\n \tEOF\n-\tgrep -e \"^Conflicts:\" -e '^Signed-off-by' <.git/COMMIT_EDITMSG >actual &&\n+\tgrep -e \"^Conflicts:\" -e '^Signed-off-by' .git/COMMIT_EDITMSG >actual &&\n \ttest_cmp expect actual\n '\n \ndiff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh\nindex c19d8dbc9d..ccde8ba491 100755\n--- a/t/t5521-pull-options.sh\n+++ b/t/t5521-pull-options.sh\n@@ -77,6 +77,14 @@ test_expect_success 'git pull -q -v' '\n \ttest_must_be_empty out &&\n \ttest -s err)\n '\n+test_expect_success 'git pull --cleanup errors early on invalid argument' '\n+\tmkdir clonedcleanup &&\n+\t(cd clonedcleanup && git init &&\n+\ttest_must_fail git pull --cleanup invalid \"../parent\" >out 2>err &&\n+\ttest_must_be_empty out &&\n+\ttest -s err)\n+'\n+\n \n test_expect_success 'git pull --force' '\n \tmkdir clonedoldstyle &&\ndiff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh\nindex f035e4a507..5733d9cd34 100755\n--- a/t/t7502-commit-porcelain.sh\n+++ b/t/t7502-commit-porcelain.sh\n@@ -16,9 +16,8 @@ commit_msg_is () {\n # Arguments: [<prefix] [<commit message>] [<commit options>]\n check_summary_oneline() {\n \ttest_tick &&\n-\tgit commit ${3+\"$3\"} -m \"$2\" >act &&\n-\thead -1 <act >tmp &&\n-\tmv tmp act &&\n+\tgit commit ${3+\"$3\"} -m \"$2\" >raw &&\n+\thead -n 1 raw >act &&\n \n \t# branch name\n \tSUMMARY_PREFIX=\"$(git name-rev --name-only HEAD)\" &&\n@@ -144,9 +143,11 @@ test_expect_success 'sign off' '\n \t>positive &&\n \tgit add positive &&\n \tgit commit -s -m \"thank you\" &&\n-\tactual=$(git cat-file commit HEAD >tmp && sed -ne \"s/Signed-off-by: //p\" <tmp && rm tmp) &&\n-\texpected=$(git var GIT_COMMITTER_IDENT >tmp && sed -e \"s/>.*/>/\" <tmp && rm tmp) &&\n-\ttest \"z$actual\" = \"z$expected\"\n+\tgit cat-file commit HEAD >commit.msg &&\n+\tsed -ne \"s/Signed-off-by: //p\" commit.msg >actual &&\n+\tgit var GIT_COMMITTER_IDENT >ident &&\n+\tsed -e \"s/>.*/>/\" ident >expected &&\n+\ttest_cmp expected actual\n \n '\n \n@@ -155,7 +156,7 @@ test_expect_success 'multiple -m' '\n \t>negative &&\n \tgit add negative &&\n \tgit commit -m \"one\" -m \"two\" -m \"three\" &&\n-\tactual=$(git cat-file commit HEAD >tmp && sed -e \"1,/^\\$/d\" <tmp && rm tmp) &&\n+\tactual=$(git cat-file commit HEAD >tmp && sed -e \"1,/^\\$/d\" tmp && rm tmp) &&\n \texpected=$(test_write_lines \"one\" \"\" \"two\" \"\" \"three\") &&\n \ttest \"z$actual\" = \"z$expected\"\n \n@@ -165,9 +166,8 @@ test_expect_success 'verbose' '\n \n \techo minus >negative &&\n \tgit add negative &&\n-\tgit status -v >actual &&\n-\tsed -ne \"/^diff --git /p\" <actual >tmp &&\n-\tmv tmp actual &&\n+\tgit status -v >raw &&\n+\tsed -ne \"/^diff --git /p\" raw >actual &&\n \techo \"diff --git a/negative b/negative\" >expect &&\n \ttest_cmp expect actual\n \n@@ -193,9 +193,8 @@ test_expect_success 'cleanup commit messages (verbatim option,-t)' '\n \n \techo >>negative &&\n \tgit commit --cleanup=verbatim --no-status -t expect -a &&\n-\tgit cat-file -p HEAD >actual &&\n-\tsed -e \"1,/^\\$/d\" <actual >tmp &&\n-\tmv tmp actual &&\n+\tgit cat-file -p HEAD >raw &&\n+\tsed -e \"1,/^\\$/d\" raw >actual &&\n \ttest_cmp expect actual\n \n '\n@@ -204,9 +203,8 @@ test_expect_success 'cleanup commit messages (verbatim option,-F)' '\n \n \techo >>negative &&\n \tgit commit --cleanup=verbatim -F expect -a &&\n-\tgit cat-file -p HEAD >actual &&\n-\tsed -e \"1,/^\\$/d\" <actual >tmp &&\n-\tmv tmp actual &&\n+\tgit cat-file -p HEAD >raw &&\n+\tsed -e \"1,/^\\$/d\" raw >actual &&\n \ttest_cmp expect actual\n \n '\n@@ -215,9 +213,8 @@ test_expect_success 'cleanup commit messages (verbatim option,-m)' '\n \n \techo >>negative &&\n \tgit commit --cleanup=verbatim -m \"$mesg_with_comment_and_newlines\" -a &&\n-\tgit cat-file -p HEAD >actual &&\n-\tsed -e \"1,/^\\$/d\" <actual >tmp &&\n-\tmv tmp actual &&\n+\tgit cat-file -p HEAD >raw &&\n+\tsed -e \"1,/^\\$/d\" raw >actual &&\n \ttest_cmp expect actual\n \n '\n@@ -228,9 +225,8 @@ test_expect_success 'cleanup commit messages (whitespace option,-F)' '\n \ttest_write_lines \"\" \"# text\" \"\" >text &&\n \techo \"# text\" >expect &&\n \tgit commit --cleanup=whitespace -F text -a &&\n-\tgit cat-file -p HEAD >actual &&\n-\tsed -e \"1,/^\\$/d\" <actual >tmp &&\n-\tmv tmp actual &&\n+\tgit cat-file -p HEAD >raw &&\n+\tsed -e \"1,/^\\$/d\" raw >actual &&\n \ttest_cmp expect actual\n \n '\n@@ -257,9 +253,8 @@ test_expect_success 'cleanup commit messages (scissors option,-F,-e)' '\n \t# to be kept, too\n \tEOF\n \tgit commit --cleanup=scissors -e -F text -a &&\n-\tgit cat-file -p HEAD >actual &&\n-\tsed -e \"1,/^\\$/d\" <actual >tmp &&\n-\tmv tmp actual &&\n+\tgit cat-file -p HEAD >raw &&\n+\tsed -e \"1,/^\\$/d\" raw >actual &&\n \ttest_cmp expect actual\n '\n \n@@ -271,9 +266,8 @@ test_expect_success 'cleanup commit messages (scissors option,-F,-e, scissors on\n \tto be removed\n \tEOF\n \tgit commit --cleanup=scissors -e -F text -a --allow-empty-message &&\n-\tgit cat-file -p HEAD >actual &&\n-\tsed -e \"1,/^\\$/d\" <actual >tmp &&\n-\tmv tmp actual &&\n+\tgit cat-file -p HEAD >raw &&\n+\tsed -e \"1,/^\\$/d\" raw >actual &&\n \ttest_must_be_empty actual\n '\n \n@@ -283,9 +277,8 @@ test_expect_success 'cleanup commit messages (strip option,-F)' '\n \ttest_write_lines \"\" \"# text\" \"sample\" \"\" >text &&\n \techo sample >expect &&\n \tgit commit --cleanup=strip -F text -a &&\n-\tgit cat-file -p HEAD >actual &&\n-\tsed -e \"1,/^\\$/d\" <actual >tmp &&\n-\tmv tmp actual &&\n+\tgit cat-file -p HEAD >raw &&\n+\tsed -e \"1,/^\\$/d\" raw >actual &&\n \ttest_cmp expect actual\n \n '\n@@ -489,9 +482,8 @@ test_expect_success 'Hand committing of a redundant merge removes dups' '\n \ttest_must_fail git merge second master &&\n \tgit checkout master g &&\n \tEDITOR=: git commit -a &&\n-\tgit cat-file commit HEAD >actual &&\n-\tsed -n -e \"s/^parent //p\" -e \"/^$/q\" <actual >tmp &&\n-\tmv tmp actual &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -n -e \"s/^parent //p\" -e \"/^$/q\" raw >actual &&\n \ttest_cmp expect actual\n \n '\n@@ -500,9 +492,8 @@ test_expect_success 'A single-liner subject with a token plus colon is not a foo\n \n \tgit reset --hard &&\n \tgit commit -s -m \"hello: kitty\" --allow-empty &&\n-\tgit cat-file commit HEAD >actual &&\n-\tsed -e \"1,/^$/d\" <actual >tmp &&\n-\tmv tmp actual &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e \"1,/^$/d\" raw >actual &&\n \ttest_line_count = 3 actual\n \n '\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex ed11b08db2..7f9c68cbe7 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -241,9 +241,8 @@ test_expect_success 'merge --squash c3 with c7' '\n \t# Conflicts:\n \t#\tfile\n \tEOF\n-\tgit cat-file commit HEAD >actual &&\n-\tsed -e '1,/^$/d' <actual >tmp &&\n-\tmv tmp actual &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e '1,/^$/d' raw >actual &&\n \ttest_cmp expect actual\n '\n \n@@ -264,9 +263,8 @@ test_expect_success 'merge c3 with c7 with commit.cleanup = scissors' '\n \t# Conflicts:\n \t#\tfile\n \tEOF\n-\tgit cat-file commit HEAD >actual &&\n-\tsed -e '1,/^$/d' <actual >tmp &&\n-\tmv tmp actual &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e '1,/^$/d' raw >actual &&\n \ttest_i18ncmp expect actual\n '\n \n@@ -289,9 +287,8 @@ test_expect_success 'merge c3 with c7 with --squash commit.cleanup = scissors' '\n \t# Conflicts:\n \t#\tfile\n \tEOF\n-\tgit cat-file commit HEAD >actual &&\n-\tsed -e '1,/^$/d' <actual >tmp &&\n-\tmv tmp actual &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e '1,/^$/d' raw >actual &&\n \ttest_i18ncmp expect actual\n '\n \n@@ -728,10 +725,10 @@ cat >editor <<\\EOF\n (\n \techo \"Merge work done on the side branch c1\"\n \techo\n-\tcat <\"$1\"\n+\tcat \"$1\"\n ) >\"$1.tmp\" && mv \"$1.tmp\" \"$1\"\n # strip comments and blank lines from end of message\n-sed -e '/^#/d' < \"$1\" | sed -e :a -e '/^\\n*$/{$d;N;ba' -e '}' >expected\n+sed -e '/^#/d' \"$1\" | sed -e :a -e '/^\\n*$/{$d;N;ba' -e '}' >expected\n EOF\n chmod 755 editor\n \n@@ -816,14 +813,14 @@ test_expect_success 'set up mod-256 conflict scenario' '\n \tgit commit -m base &&\n \n \t# one side changes the first line of each to \"master\"\n-\tsed s/-1/-master/ <file >tmp &&\n+\tsed s/-1/-master/ file >tmp &&\n \tmv tmp file &&\n \tgit commit -am master &&\n \n \t# and the other to \"side\"; merging the two will\n \t# yield 256 separate conflicts\n \tgit checkout -b side HEAD^ &&\n-\tsed s/-1/-side/ <file >tmp &&\n+\tsed s/-1/-side/ file >tmp &&\n \tmv tmp file &&\n \tgit commit -am side\n '\ndiff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh\nindex c9685a318d..cd4f9607dc 100755\n--- a/t/t7604-merge-custom-message.sh\n+++ b/t/t7604-merge-custom-message.sh\n@@ -16,16 +16,16 @@ create_merge_msgs() {\n }\n \n test_expect_success 'setup' '\n-\techo c0 > c0.c &&\n+\techo c0 >c0.c &&\n \tgit add c0.c &&\n \tgit commit -m c0 &&\n \tgit tag c0 &&\n-\techo c1 > c1.c &&\n+\techo c1 >c1.c &&\n \tgit add c1.c &&\n \tgit commit -m c1 &&\n \tgit tag c1 &&\n \tgit reset --hard c0 &&\n-\techo c2 > c2.c &&\n+\techo c2 >c2.c &&\n \tgit add c2.c &&\n \tgit commit -m c2 &&\n \tgit tag c2 &&\n@@ -36,18 +36,16 @@ test_expect_success 'setup' '\n test_expect_success 'merge c2 with a custom message' '\n \tgit reset --hard c1 &&\n \tgit merge -m \"$(cat exp.subject)\" c2 &&\n-\tgit cat-file commit HEAD >actual &&\n-\tsed -e \"1,/^$/d\" <actual >tmp &&\n-\tmv tmp actual &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e \"1,/^$/d\" raw >actual &&\n \ttest_cmp exp.subject actual\n '\n \n test_expect_success 'merge --log appends to custom message' '\n \tgit reset --hard c1 &&\n \tgit merge --log -m \"$(cat exp.subject)\" c2 &&\n-\tgit cat-file commit HEAD >actual &&\n-\tsed -e \"1,/^$/d\" <actual >tmp &&\n-\tmv tmp actual &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e \"1,/^$/d\" raw >actual &&\n \ttest_cmp exp.log actual\n '\n \n@@ -63,9 +61,8 @@ test_expect_success 'prepare file with comment line and trailing newlines'  '\n test_expect_success 'cleanup commit messages (verbatim option)' '\n \tgit reset --hard c1 &&\n \tgit merge --cleanup=verbatim -F expect c2 &&\n-\tgit cat-file commit HEAD >actual &&\n-\tsed -e \"1,/^$/d\" <actual >tmp &&\n-\tmv tmp actual &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e \"1,/^$/d\" raw >actual &&\n \ttest_cmp expect actual\n '\n \n@@ -74,9 +71,8 @@ test_expect_success 'cleanup commit messages (whitespace option)' '\n \ttest_write_lines \"\" \"# text\" \"\" >text &&\n \techo \"# text\" >expect &&\n \tgit merge --cleanup=whitespace -F text c2 &&\n-\tgit cat-file commit HEAD >actual &&\n-\tsed -e \"1,/^$/d\" <actual >tmp &&\n-\tmv tmp actual &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e \"1,/^$/d\" raw >actual &&\n \ttest_cmp expect actual\n '\n \n@@ -101,9 +97,8 @@ test_expect_success 'cleanup merge messages (scissors option)' '\n \t# to be kept, too\n \tEOF\n \tgit merge --cleanup=scissors -e -F text c2 &&\n-\tgit cat-file commit HEAD >actual &&\n-\tsed -e \"1,/^$/d\" <actual >tmp &&\n-\tmv tmp actual &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e \"1,/^$/d\" raw >actual &&\n \ttest_cmp expect actual\n '\n \n@@ -112,9 +107,8 @@ test_expect_success 'cleanup commit messages (strip option)' '\n \ttest_write_lines \"\" \"# text\" \"sample\" \"\" >text &&\n \techo sample >expect &&\n \tgit merge --cleanup=strip -F text c2 &&\n-\tgit cat-file commit HEAD >actual &&\n-\tsed -e \"1,/^$/d\" <actual >tmp &&\n-\tmv tmp actual &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e \"1,/^$/d\" raw >actual &&\n \ttest_cmp expect actual\n '\n \n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"372061","messageId":"3274886c16141fe1b5c228e02d75f623f79a0539.1553150827.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1553150827.git.liu.denton@gmail.com","subject":"[PATCH v9 01/11] t7600: clean up style","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-21T06:53:37Z","receivedAt":"2019-03-21T06:53:46Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Clean up the 'merge --squash c3 with c7' test by removing some\nunnecessary braces and removing a pipe.\n\nAlso, generally cleanup style by unindenting a here-doc, removing stray\nspaces after a redirection operator and allowing sed to open its own\ninput instead of redirecting input from the shell.\n\nHelped-by: SZEDER Gábor <szeder.dev@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t7600-merge.sh | 27 +++++++++++++--------------\n 1 file changed, 13 insertions(+), 14 deletions(-)\n\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex 106148254d..2f4c2801fb 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -233,17 +233,16 @@ test_expect_success 'merge --squash c3 with c7' '\n \tcat result.9z >file &&\n \tgit commit --no-edit -a &&\n \n-\t{\n-\t\tcat <<-EOF\n-\t\tSquashed commit of the following:\n+\tcat >expect <<-EOF &&\n+\tSquashed commit of the following:\n \n-\t\t$(git show -s c7)\n+\t$(git show -s c7)\n \n-\t\t# Conflicts:\n-\t\t#\tfile\n-\t\tEOF\n-\t} >expect &&\n-\tgit cat-file commit HEAD | sed -e '1,/^$/d' >actual &&\n+\t# Conflicts:\n+\t#\tfile\n+\tEOF\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e '1,/^$/d' raw >actual &&\n \ttest_cmp expect actual\n '\n \n@@ -680,10 +679,10 @@ cat >editor <<\\EOF\n (\n \techo \"Merge work done on the side branch c1\"\n \techo\n-\tcat <\"$1\"\n+\tcat \"$1\"\n ) >\"$1.tmp\" && mv \"$1.tmp\" \"$1\"\n # strip comments and blank lines from end of message\n-sed -e '/^#/d' < \"$1\" | sed -e :a -e '/^\\n*$/{$d;N;ba' -e '}' > expected\n+sed -e '/^#/d' \"$1\" | sed -e :a -e '/^\\n*$/{$d;N;ba' -e '}' >expected\n EOF\n chmod 755 editor\n \n@@ -768,14 +767,14 @@ test_expect_success 'set up mod-256 conflict scenario' '\n \tgit commit -m base &&\n \n \t# one side changes the first line of each to \"master\"\n-\tsed s/-1/-master/ <file >tmp &&\n+\tsed s/-1/-master/ file >tmp &&\n \tmv tmp file &&\n \tgit commit -am master &&\n \n \t# and the other to \"side\"; merging the two will\n \t# yield 256 separate conflicts\n \tgit checkout -b side HEAD^ &&\n-\tsed s/-1/-side/ <file >tmp &&\n+\tsed s/-1/-side/ file >tmp &&\n \tmv tmp file &&\n \tgit commit -am side\n '\n@@ -814,7 +813,7 @@ EOF\n test_expect_success EXECKEEPSPID 'killed merge can be completed with --continue' '\n \tgit reset --hard c0 &&\n \t! \"$SHELL_PATH\" -c '\\''\n-\t  echo kill -TERM $$ >> .git/FAKE_EDITOR\n+\t  echo kill -TERM $$ >>.git/FAKE_EDITOR\n \t  GIT_EDITOR=.git/FAKE_EDITOR\n \t  export GIT_EDITOR\n \t  exec git merge --no-ff --edit c1'\\'' &&\n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"372062","messageId":"7d8d39b93002d1416eaf730f3e8262360c925c42.1553150827.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1553150827.git.liu.denton@gmail.com","subject":"[PATCH v9 02/11] t3507: clean up style","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-21T06:53:40Z","receivedAt":"2019-03-21T06:53:46Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Remove space after redirection operators for style. Also, remove a git\ncommand which was upstream of a pipe. Finally, let grep and sed open\ntheir own input instead of letting the shell redirect the input.\n\nHelped-by: SZEDER Gábor <szeder.dev@gmail.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t3507-cherry-pick-conflict.sh | 42 ++++++++++++++++-----------------\n 1 file changed, 21 insertions(+), 21 deletions(-)\n\ndiff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh\nindex 0db166152a..777fb23d18 100755\n--- a/t/t3507-cherry-pick-conflict.sh\n+++ b/t/t3507-cherry-pick-conflict.sh\n@@ -88,7 +88,7 @@ test_expect_success 'cherry-pick --no-commit does not set CHERRY_PICK_HEAD' '\n \n test_expect_success 'cherry-pick w/dirty tree does not set CHERRY_PICK_HEAD' '\n \tpristine_detach initial &&\n-\techo foo > foo &&\n+\techo foo >foo &&\n \ttest_must_fail git cherry-pick base &&\n \ttest_must_fail git rev-parse --verify CHERRY_PICK_HEAD\n '\n@@ -96,7 +96,7 @@ test_expect_success 'cherry-pick w/dirty tree does not set CHERRY_PICK_HEAD' '\n test_expect_success \\\n \t'cherry-pick --strategy=resolve w/dirty tree does not set CHERRY_PICK_HEAD' '\n \tpristine_detach initial &&\n-\techo foo > foo &&\n+\techo foo >foo &&\n \ttest_must_fail git cherry-pick --strategy=resolve base &&\n \ttest_must_fail git rev-parse --verify CHERRY_PICK_HEAD\n '\n@@ -175,23 +175,23 @@ test_expect_success 'failed cherry-pick registers participants in index' '\n \t\tgit ls-files --stage foo &&\n \t\tgit checkout picked -- foo &&\n \t\tgit ls-files --stage foo\n-\t} > stages &&\n+\t} >stages &&\n \tsed \"\n \t\t1 s/ 0\t/ 1\t/\n \t\t2 s/ 0\t/ 2\t/\n \t\t3 s/ 0\t/ 3\t/\n-\t\" < stages > expected &&\n+\t\" stages >expected &&\n \tgit read-tree -u --reset HEAD &&\n \n \ttest_must_fail git cherry-pick picked &&\n-\tgit ls-files --stage --unmerged > actual &&\n+\tgit ls-files --stage --unmerged >actual &&\n \n \ttest_cmp expected actual\n '\n \n test_expect_success 'failed cherry-pick describes conflict in work tree' '\n \tpristine_detach initial &&\n-\tcat <<-EOF > expected &&\n+\tcat <<-EOF >expected &&\n \t<<<<<<< HEAD\n \ta\n \t=======\n@@ -201,14 +201,14 @@ test_expect_success 'failed cherry-pick describes conflict in work tree' '\n \n \ttest_must_fail git cherry-pick picked &&\n \n-\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo > actual &&\n+\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo >actual &&\n \ttest_cmp expected actual\n '\n \n test_expect_success 'diff3 -m style' '\n \tpristine_detach initial &&\n \tgit config merge.conflictstyle diff3 &&\n-\tcat <<-EOF > expected &&\n+\tcat <<-EOF >expected &&\n \t<<<<<<< HEAD\n \ta\n \t||||||| parent of objid picked\n@@ -220,14 +220,14 @@ test_expect_success 'diff3 -m style' '\n \n \ttest_must_fail git cherry-pick picked &&\n \n-\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo > actual &&\n+\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo >actual &&\n \ttest_cmp expected actual\n '\n \n test_expect_success 'revert also handles conflicts sanely' '\n \tgit config --unset merge.conflictstyle &&\n \tpristine_detach initial &&\n-\tcat <<-EOF > expected &&\n+\tcat <<-EOF >expected &&\n \t<<<<<<< HEAD\n \ta\n \t=======\n@@ -241,24 +241,24 @@ test_expect_success 'revert also handles conflicts sanely' '\n \t\tgit ls-files --stage foo &&\n \t\tgit checkout base -- foo &&\n \t\tgit ls-files --stage foo\n-\t} > stages &&\n+\t} >stages &&\n \tsed \"\n \t\t1 s/ 0\t/ 1\t/\n \t\t2 s/ 0\t/ 2\t/\n \t\t3 s/ 0\t/ 3\t/\n-\t\" < stages > expected-stages &&\n+\t\" stages >expected-stages &&\n \tgit read-tree -u --reset HEAD &&\n \n \thead=$(git rev-parse HEAD) &&\n \ttest_must_fail git revert picked &&\n \tnewhead=$(git rev-parse HEAD) &&\n-\tgit ls-files --stage --unmerged > actual-stages &&\n+\tgit ls-files --stage --unmerged >actual-stages &&\n \n \ttest \"$head\" = \"$newhead\" &&\n \ttest_must_fail git update-index --refresh -q &&\n \ttest_must_fail git diff-index --exit-code HEAD &&\n \ttest_cmp expected-stages actual-stages &&\n-\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo > actual &&\n+\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -284,7 +284,7 @@ test_expect_success 'revert --no-commit sets REVERT_HEAD' '\n \n test_expect_success 'revert w/dirty tree does not set REVERT_HEAD' '\n \tpristine_detach base &&\n-\techo foo > foo &&\n+\techo foo >foo &&\n \ttest_must_fail git revert base &&\n \ttest_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&\n \ttest_must_fail git rev-parse --verify REVERT_HEAD\n@@ -319,7 +319,7 @@ test_expect_success 'failed commit does not clear REVERT_HEAD' '\n test_expect_success 'revert conflict, diff3 -m style' '\n \tpristine_detach initial &&\n \tgit config merge.conflictstyle diff3 &&\n-\tcat <<-EOF > expected &&\n+\tcat <<-EOF >expected &&\n \t<<<<<<< HEAD\n \ta\n \t||||||| objid picked\n@@ -331,7 +331,7 @@ test_expect_success 'revert conflict, diff3 -m style' '\n \n \ttest_must_fail git revert picked &&\n \n-\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo > actual &&\n+\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -345,7 +345,7 @@ test_expect_success 'commit after failed cherry-pick does not add duplicated -s'\n \tpristine_detach initial &&\n \ttest_must_fail git cherry-pick -s picked-signed &&\n \tgit commit -a -s &&\n-\ttest $(git show -s |grep -c \"Signed-off-by\") = 1\n+\ttest $(git show -s >tmp && grep -c \"Signed-off-by\" tmp && rm tmp) = 1\n '\n \n test_expect_success 'commit after failed cherry-pick adds -s at the right place' '\n@@ -359,7 +359,7 @@ test_expect_success 'commit after failed cherry-pick adds -s at the right place'\n \tSigned-off-by: C O Mitter <committer@example.com>\n \t# Conflicts:\n \tEOF\n-\tgrep -e \"^# Conflicts:\" -e '^Signed-off-by' <.git/COMMIT_EDITMSG >actual &&\n+\tgrep -e \"^# Conflicts:\" -e '^Signed-off-by' .git/COMMIT_EDITMSG >actual &&\n \ttest_cmp expect actual &&\n \n \tcat <<-\\EOF >expected &&\n@@ -378,7 +378,7 @@ test_expect_success 'commit --amend -s places the sign-off at the right place' '\n \n \t# emulate old-style conflicts block\n \tmv .git/MERGE_MSG .git/MERGE_MSG+ &&\n-\tsed -e \"/^# Conflicts:/,\\$s/^# *//\" <.git/MERGE_MSG+ >.git/MERGE_MSG &&\n+\tsed -e \"/^# Conflicts:/,\\$s/^# *//\" .git/MERGE_MSG+ >.git/MERGE_MSG &&\n \n \tgit commit -a &&\n \tgit commit --amend -s &&\n@@ -388,7 +388,7 @@ test_expect_success 'commit --amend -s places the sign-off at the right place' '\n \tSigned-off-by: C O Mitter <committer@example.com>\n \tConflicts:\n \tEOF\n-\tgrep -e \"^Conflicts:\" -e '^Signed-off-by' <.git/COMMIT_EDITMSG >actual &&\n+\tgrep -e \"^Conflicts:\" -e '^Signed-off-by' .git/COMMIT_EDITMSG >actual &&\n \ttest_cmp expect actual\n '\n \n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"372063","messageId":"61d1c7c907500662a8918e8175b2b96282406fef.1553150827.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1553150827.git.liu.denton@gmail.com","subject":"[PATCH v9 03/11] t7604: clean up style","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-21T06:53:43Z","receivedAt":"2019-03-21T06:53:51Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Before, we had some Git commands which were upstream of the pipe. This\nmeant that if it produced an error, it would've gone unnoticed. Refactor\nto place Git commands on their own.\n\nAlso, while we're at it, remove spaces after redirection operators.\n\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nHelped-by: SZEDER Gábor <szeder.dev@gmail.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t7604-merge-custom-message.sh | 12 +++++++-----\n 1 file changed, 7 insertions(+), 5 deletions(-)\n\ndiff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh\nindex 89619cf446..aba1279132 100755\n--- a/t/t7604-merge-custom-message.sh\n+++ b/t/t7604-merge-custom-message.sh\n@@ -16,16 +16,16 @@ create_merge_msgs() {\n }\n \n test_expect_success 'setup' '\n-\techo c0 > c0.c &&\n+\techo c0 >c0.c &&\n \tgit add c0.c &&\n \tgit commit -m c0 &&\n \tgit tag c0 &&\n-\techo c1 > c1.c &&\n+\techo c1 >c1.c &&\n \tgit add c1.c &&\n \tgit commit -m c1 &&\n \tgit tag c1 &&\n \tgit reset --hard c0 &&\n-\techo c2 > c2.c &&\n+\techo c2 >c2.c &&\n \tgit add c2.c &&\n \tgit commit -m c2 &&\n \tgit tag c2 &&\n@@ -36,14 +36,16 @@ test_expect_success 'setup' '\n test_expect_success 'merge c2 with a custom message' '\n \tgit reset --hard c1 &&\n \tgit merge -m \"$(cat exp.subject)\" c2 &&\n-\tgit cat-file commit HEAD | sed -e \"1,/^$/d\" >actual &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e \"1,/^$/d\" raw >actual &&\n \ttest_cmp exp.subject actual\n '\n \n test_expect_success 'merge --log appends to custom message' '\n \tgit reset --hard c1 &&\n \tgit merge --log -m \"$(cat exp.subject)\" c2 &&\n-\tgit cat-file commit HEAD | sed -e \"1,/^$/d\" >actual &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e \"1,/^$/d\" raw >actual &&\n \ttest_cmp exp.log actual\n '\n \n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"372064","messageId":"845a32295467706d9ce029ba1282851577fa51ac.1553150827.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1553150827.git.liu.denton@gmail.com","subject":"[PATCH v9 04/11] t7502: clean up style","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-21T06:53:46Z","receivedAt":"2019-03-21T06:53:51Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Refactor out Git commands that were upstream of a pipe. Remove spaces\nafter \"> \". Indent here-docs appropriately. Convert echo chains to use\nthe test_write_lines function. Refactor 'sign off' test to use test_cmp\ninstead of comparing variables.\n\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nHelped-by: SZEDER Gábor <szeder.dev@gmail.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t7502-commit-porcelain.sh | 93 +++++++++++++++++++++----------------\n 1 file changed, 53 insertions(+), 40 deletions(-)\n\ndiff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh\nindex ca4a740da0..5733d9cd34 100755\n--- a/t/t7502-commit-porcelain.sh\n+++ b/t/t7502-commit-porcelain.sh\n@@ -16,7 +16,8 @@ commit_msg_is () {\n # Arguments: [<prefix] [<commit message>] [<commit options>]\n check_summary_oneline() {\n \ttest_tick &&\n-\tgit commit ${3+\"$3\"} -m \"$2\" | head -1 > act &&\n+\tgit commit ${3+\"$3\"} -m \"$2\" >raw &&\n+\thead -n 1 raw >act &&\n \n \t# branch name\n \tSUMMARY_PREFIX=\"$(git name-rev --name-only HEAD)\" &&\n@@ -68,7 +69,7 @@ test_expect_success 'output summary format for merges' '\n \tgit checkout recursive-a &&\n \ttest_must_fail git merge recursive-b &&\n \t# resolve the conflict\n-\techo commit-a > file1 &&\n+\techo commit-a >file1 &&\n \tgit add file1 &&\n \tcheck_summary_oneline \"\" \"Merge\"\n '\n@@ -142,9 +143,11 @@ test_expect_success 'sign off' '\n \t>positive &&\n \tgit add positive &&\n \tgit commit -s -m \"thank you\" &&\n-\tactual=$(git cat-file commit HEAD | sed -ne \"s/Signed-off-by: //p\") &&\n-\texpected=$(git var GIT_COMMITTER_IDENT | sed -e \"s/>.*/>/\") &&\n-\ttest \"z$actual\" = \"z$expected\"\n+\tgit cat-file commit HEAD >commit.msg &&\n+\tsed -ne \"s/Signed-off-by: //p\" commit.msg >actual &&\n+\tgit var GIT_COMMITTER_IDENT >ident &&\n+\tsed -e \"s/>.*/>/\" ident >expected &&\n+\ttest_cmp expected actual\n \n '\n \n@@ -153,8 +156,8 @@ test_expect_success 'multiple -m' '\n \t>negative &&\n \tgit add negative &&\n \tgit commit -m \"one\" -m \"two\" -m \"three\" &&\n-\tactual=$(git cat-file commit HEAD | sed -e \"1,/^\\$/d\") &&\n-\texpected=$(echo one; echo; echo two; echo; echo three) &&\n+\tactual=$(git cat-file commit HEAD >tmp && sed -e \"1,/^\\$/d\" tmp && rm tmp) &&\n+\texpected=$(test_write_lines \"one\" \"\" \"two\" \"\" \"three\") &&\n \ttest \"z$actual\" = \"z$expected\"\n \n '\n@@ -163,7 +166,8 @@ test_expect_success 'verbose' '\n \n \techo minus >negative &&\n \tgit add negative &&\n-\tgit status -v | sed -ne \"/^diff --git /p\" >actual &&\n+\tgit status -v >raw &&\n+\tsed -ne \"/^diff --git /p\" raw >actual &&\n \techo \"diff --git a/negative b/negative\" >expect &&\n \ttest_cmp expect actual\n \n@@ -189,7 +193,8 @@ test_expect_success 'cleanup commit messages (verbatim option,-t)' '\n \n \techo >>negative &&\n \tgit commit --cleanup=verbatim --no-status -t expect -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\" >actual &&\n+\tgit cat-file -p HEAD >raw &&\n+\tsed -e \"1,/^\\$/d\" raw >actual &&\n \ttest_cmp expect actual\n \n '\n@@ -198,7 +203,8 @@ test_expect_success 'cleanup commit messages (verbatim option,-F)' '\n \n \techo >>negative &&\n \tgit commit --cleanup=verbatim -F expect -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >raw &&\n+\tsed -e \"1,/^\\$/d\" raw >actual &&\n \ttest_cmp expect actual\n \n '\n@@ -207,7 +213,8 @@ test_expect_success 'cleanup commit messages (verbatim option,-m)' '\n \n \techo >>negative &&\n \tgit commit --cleanup=verbatim -m \"$mesg_with_comment_and_newlines\" -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >raw &&\n+\tsed -e \"1,/^\\$/d\" raw >actual &&\n \ttest_cmp expect actual\n \n '\n@@ -215,10 +222,11 @@ test_expect_success 'cleanup commit messages (verbatim option,-m)' '\n test_expect_success 'cleanup commit messages (whitespace option,-F)' '\n \n \techo >>negative &&\n-\t{ echo;echo \"# text\";echo; } >text &&\n+\ttest_write_lines \"\" \"# text\" \"\" >text &&\n \techo \"# text\" >expect &&\n \tgit commit --cleanup=whitespace -F text -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >raw &&\n+\tsed -e \"1,/^\\$/d\" raw >actual &&\n \ttest_cmp expect actual\n \n '\n@@ -226,48 +234,51 @@ test_expect_success 'cleanup commit messages (whitespace option,-F)' '\n test_expect_success 'cleanup commit messages (scissors option,-F,-e)' '\n \n \techo >>negative &&\n-\tcat >text <<EOF &&\n+\tcat >text <<-\\EOF &&\n \n-# to be kept\n+\t# to be kept\n \n-  # ------------------------ >8 ------------------------\n-# to be kept, too\n-# ------------------------ >8 ------------------------\n-to be removed\n-# ------------------------ >8 ------------------------\n-to be removed, too\n-EOF\n+\t  # ------------------------ >8 ------------------------\n+\t# to be kept, too\n+\t# ------------------------ >8 ------------------------\n+\tto be removed\n+\t# ------------------------ >8 ------------------------\n+\tto be removed, too\n+\tEOF\n \n-\tcat >expect <<EOF &&\n-# to be kept\n+\tcat >expect <<-\\EOF &&\n+\t# to be kept\n \n-  # ------------------------ >8 ------------------------\n-# to be kept, too\n-EOF\n+\t  # ------------------------ >8 ------------------------\n+\t# to be kept, too\n+\tEOF\n \tgit commit --cleanup=scissors -e -F text -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >raw &&\n+\tsed -e \"1,/^\\$/d\" raw >actual &&\n \ttest_cmp expect actual\n '\n \n test_expect_success 'cleanup commit messages (scissors option,-F,-e, scissors on first line)' '\n \n \techo >>negative &&\n-\tcat >text <<EOF &&\n-# ------------------------ >8 ------------------------\n-to be removed\n-EOF\n+\tcat >text <<-\\EOF &&\n+\t# ------------------------ >8 ------------------------\n+\tto be removed\n+\tEOF\n \tgit commit --cleanup=scissors -e -F text -a --allow-empty-message &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >raw &&\n+\tsed -e \"1,/^\\$/d\" raw >actual &&\n \ttest_must_be_empty actual\n '\n \n test_expect_success 'cleanup commit messages (strip option,-F)' '\n \n \techo >>negative &&\n-\t{ echo;echo \"# text\";echo sample;echo; } >text &&\n+\ttest_write_lines \"\" \"# text\" \"sample\" \"\" >text &&\n \techo sample >expect &&\n \tgit commit --cleanup=strip -F text -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >raw &&\n+\tsed -e \"1,/^\\$/d\" raw >actual &&\n \ttest_cmp expect actual\n \n '\n@@ -275,7 +286,7 @@ test_expect_success 'cleanup commit messages (strip option,-F)' '\n test_expect_success 'cleanup commit messages (strip option,-F,-e)' '\n \n \techo >>negative &&\n-\t{ echo;echo sample;echo; } >text &&\n+\ttest_write_lines \"\" \"sample\" \"\" >text &&\n \tgit commit -e -F text -a &&\n \thead -n 4 .git/COMMIT_EDITMSG >actual\n '\n@@ -387,7 +398,7 @@ test_expect_success AUTOIDENT 'message shows committer when it is automatic' '\n '\n \n write_script .git/FAKE_EDITOR <<EOF\n-echo editor started > \"$(pwd)/.git/result\"\n+echo editor started >\"$(pwd)/.git/result\"\n exit 0\n EOF\n \n@@ -455,7 +466,7 @@ EOF\n test_expect_success EXECKEEPSPID 'a SIGTERM should break locks' '\n \techo >>negative &&\n \t! \"$SHELL_PATH\" -c '\\''\n-\t  echo kill -TERM $$ >> .git/FAKE_EDITOR\n+\t  echo kill -TERM $$ >>.git/FAKE_EDITOR\n \t  GIT_EDITOR=.git/FAKE_EDITOR\n \t  export GIT_EDITOR\n \t  exec git commit -a'\\'' &&\n@@ -471,7 +482,8 @@ test_expect_success 'Hand committing of a redundant merge removes dups' '\n \ttest_must_fail git merge second master &&\n \tgit checkout master g &&\n \tEDITOR=: git commit -a &&\n-\tgit cat-file commit HEAD | sed -n -e \"s/^parent //p\" -e \"/^$/q\" >actual &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -n -e \"s/^parent //p\" -e \"/^$/q\" raw >actual &&\n \ttest_cmp expect actual\n \n '\n@@ -480,7 +492,8 @@ test_expect_success 'A single-liner subject with a token plus colon is not a foo\n \n \tgit reset --hard &&\n \tgit commit -s -m \"hello: kitty\" --allow-empty &&\n-\tgit cat-file commit HEAD | sed -e \"1,/^$/d\" >actual &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e \"1,/^$/d\" raw >actual &&\n \ttest_line_count = 3 actual\n \n '\n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"372065","messageId":"6f20c20939fb946af18f5e0af794305242ca0ef2.1553150827.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1553150827.git.liu.denton@gmail.com","subject":"[PATCH v9 05/11] commit: extract cleanup_mode functions to sequencer","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-21T06:53:48Z","receivedAt":"2019-03-21T06:53:54Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Signed-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/commit.c | 25 ++-----------------------\n sequencer.c      | 29 +++++++++++++++++++++++++++++\n sequencer.h      |  6 ++++++\n 3 files changed, 37 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex f17537474a..0df15e4851 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1172,24 +1172,7 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t\tdie(_(\"Only one of --include/--only/--all/--interactive/--patch can be used.\"));\n \tif (argc == 0 && (also || (only && !amend && !allow_empty)))\n \t\tdie(_(\"No paths with --include/--only does not make sense.\"));\n-\tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n-\t\tcleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_ALL :\n-\t\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n-\telse if (!strcmp(cleanup_arg, \"verbatim\"))\n-\t\tcleanup_mode = COMMIT_MSG_CLEANUP_NONE;\n-\telse if (!strcmp(cleanup_arg, \"whitespace\"))\n-\t\tcleanup_mode = COMMIT_MSG_CLEANUP_SPACE;\n-\telse if (!strcmp(cleanup_arg, \"strip\"))\n-\t\tcleanup_mode = COMMIT_MSG_CLEANUP_ALL;\n-\telse if (!strcmp(cleanup_arg, \"scissors\"))\n-\t\tcleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n-\t\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n-\t/*\n-\t * Please update _git_commit() in git-completion.bash when you\n-\t * add new options.\n-\t */\n-\telse\n-\t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n+\tcleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);\n \n \thandle_untracked_files_arg(s);\n \n@@ -1626,11 +1609,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"could not read commit message: %s\"), strerror(saved_errno));\n \t}\n \n-\tif (verbose || /* Truncate the message just before the diff, if any. */\n-\t    cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n-\t\tstrbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));\n-\tif (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)\n-\t\tstrbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);\n+\tcleanup_message(&sb, cleanup_mode, verbose);\n \n \tif (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {\n \t\trollback_index_files();\ndiff --git a/sequencer.c b/sequencer.c\nindex 95dda23eee..224c823b43 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -511,6 +511,25 @@ static int fast_forward_to(struct repository *r,\n \treturn 0;\n }\n \n+enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n+\tint use_editor)\n+{\n+\tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n+\t\treturn use_editor ? COMMIT_MSG_CLEANUP_ALL :\n+\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n+\telse if (!strcmp(cleanup_arg, \"verbatim\"))\n+\t\treturn COMMIT_MSG_CLEANUP_NONE;\n+\telse if (!strcmp(cleanup_arg, \"whitespace\"))\n+\t\treturn COMMIT_MSG_CLEANUP_SPACE;\n+\telse if (!strcmp(cleanup_arg, \"strip\"))\n+\t\treturn COMMIT_MSG_CLEANUP_ALL;\n+\telse if (!strcmp(cleanup_arg, \"scissors\"))\n+\t\treturn use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n+\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n+\telse\n+\t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n+}\n+\n void append_conflicts_hint(struct index_state *istate,\n \t\t\t   struct strbuf *msgbuf)\n {\n@@ -1013,6 +1032,16 @@ static int rest_is_empty(const struct strbuf *sb, int start)\n \treturn 1;\n }\n \n+void cleanup_message(struct strbuf *msgbuf,\n+\tenum commit_msg_cleanup_mode cleanup_mode, int verbose)\n+{\n+\tif (verbose || /* Truncate the message just before the diff, if any. */\n+\t    cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n+\t\tstrbuf_setlen(msgbuf, wt_status_locate_end(msgbuf->buf, msgbuf->len));\n+\tif (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)\n+\t\tstrbuf_stripspace(msgbuf, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);\n+}\n+\n /*\n  * Find out if the message in the strbuf contains only whitespace and\n  * Signed-off-by lines.\ndiff --git a/sequencer.h b/sequencer.h\nindex 4d505b3590..eb9bd97ef3 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -116,6 +116,12 @@ int rearrange_squash(struct repository *r);\n void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);\n \n void append_conflicts_hint(struct index_state *istate, struct strbuf *msgbuf);\n+enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n+\tint use_editor);\n+\n+void cleanup_message(struct strbuf *msgbuf,\n+\tenum commit_msg_cleanup_mode cleanup_mode, int verbose);\n+\n int message_is_empty(const struct strbuf *sb,\n \t\t     enum commit_msg_cleanup_mode cleanup_mode);\n int template_untouched(const struct strbuf *sb, const char *template_file,\n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"372066","messageId":"2c966ae31840cff8f58e64d203fdddbf9ad57f87.1553150827.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1553150827.git.liu.denton@gmail.com","subject":"[PATCH v9 06/11] parse-options.h: extract common --cleanup option","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-21T06:53:51Z","receivedAt":"2019-03-21T06:53:57Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"The --cleanup option is commonly used. Extract it so that its definition\nis not repeated.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/commit.c | 2 +-\n builtin/tag.c    | 3 +--\n parse-options.h  | 1 +\n 3 files changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 0df15e4851..8277da8474 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1473,7 +1473,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL('s', \"signoff\", &signoff, N_(\"add Signed-off-by:\")),\n \t\tOPT_FILENAME('t', \"template\", &template_file, N_(\"use specified template file\")),\n \t\tOPT_BOOL('e', \"edit\", &edit_flag, N_(\"force edit of commit\")),\n-\t\tOPT_STRING(0, \"cleanup\", &cleanup_arg, N_(\"default\"), N_(\"how to strip spaces and #comments from message\")),\n+\t\tOPT_CLEANUP(&cleanup_arg),\n \t\tOPT_BOOL(0, \"status\", &include_status, N_(\"include status in commit message template\")),\n \t\t{ OPTION_STRING, 'S', \"gpg-sign\", &sign_commit, N_(\"key-id\"),\n \t\t  N_(\"GPG sign commit\"), PARSE_OPT_OPTARG, NULL, (intptr_t) \"\" },\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 02f6bd1279..a3870fbdba 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -397,8 +397,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tOPT_FILENAME('F', \"file\", &msgfile, N_(\"read message from file\")),\n \t\tOPT_BOOL('e', \"edit\", &edit_flag, N_(\"force edit of tag message\")),\n \t\tOPT_BOOL('s', \"sign\", &opt.sign, N_(\"annotated and GPG-signed tag\")),\n-\t\tOPT_STRING(0, \"cleanup\", &cleanup_arg, N_(\"mode\"),\n-\t\t\tN_(\"how to strip spaces and #comments from message\")),\n+\t\tOPT_CLEANUP(&cleanup_arg),\n \t\tOPT_STRING('u', \"local-user\", &keyid, N_(\"key-id\"),\n \t\t\t\t\tN_(\"use another key to sign the tag\")),\n \t\tOPT__FORCE(&force, N_(\"replace the tag if exists\"), 0),\ndiff --git a/parse-options.h b/parse-options.h\nindex 7d83e2971d..85faaee390 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -303,5 +303,6 @@ int parse_opt_passthru_argv(const struct option *, const char *, int);\n #define OPT_NO_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH(\"no-contains\", v, h, PARSE_OPT_NONEG)\n #define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH(\"with\", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)\n #define OPT_WITHOUT(v, h) _OPT_CONTAINS_OR_WITH(\"without\", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)\n+#define OPT_CLEANUP(v) OPT_STRING(0, \"cleanup\", v, N_(\"mode\"), N_(\"how to strip spaces and #comments from message\"))\n \n #endif\n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"372067","messageId":"492ba73797ac66cc48da2b44cdae2152b41aa481.1553150827.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1553150827.git.liu.denton@gmail.com","subject":"[PATCH v9 07/11] sequencer.c: remove duplicate code","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-21T06:53:54Z","receivedAt":"2019-03-21T06:53:59Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Since we implemented get_cleanup_mode, we had some duplicate code in\ngit_sequencer_config which essentially performed the same operations.\nRefactor git_sequencer_config to take advantage of the logic already in\nget_cleanup_mode.\n\nNote that we had to introduce a separate argument to get_cleanup_mode\nindicating whether to die or not on an invalid value. This is because if\nwe are parsing a config, we do not want to die but instead, we only want\nto warn the user, whereas if we are parsing a command-line option, we\nwould like to actually die.\n\nFinally, this is almost a no-op refactor but not quite. Previously, in\nthe case that an invalid value is presented, default_msg_cleanup would\nnot be set. We change the behaviour so that default_msg_cleanup will now\ntake on the value as if \"default\" were provided as the cleanup_arg.\nAlso, we lowercase some user-facing strings.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/commit.c |  2 +-\n sequencer.c      | 22 ++++++++--------------\n sequencer.h      |  2 +-\n 3 files changed, 10 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 8277da8474..ba1e6027ba 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1172,7 +1172,7 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t\tdie(_(\"Only one of --include/--only/--all/--interactive/--patch can be used.\"));\n \tif (argc == 0 && (also || (only && !amend && !allow_empty)))\n \t\tdie(_(\"No paths with --include/--only does not make sense.\"));\n-\tcleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);\n+\tcleanup_mode = get_cleanup_mode(cleanup_arg, use_editor, 1);\n \n \thandle_untracked_files_arg(s);\n \ndiff --git a/sequencer.c b/sequencer.c\nindex 224c823b43..2cbfb848dd 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -172,17 +172,7 @@ static int git_sequencer_config(const char *k, const char *v, void *cb)\n \t\tif (status)\n \t\t\treturn status;\n \n-\t\tif (!strcmp(s, \"verbatim\"))\n-\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;\n-\t\telse if (!strcmp(s, \"whitespace\"))\n-\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;\n-\t\telse if (!strcmp(s, \"strip\"))\n-\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;\n-\t\telse if (!strcmp(s, \"scissors\"))\n-\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;\n-\t\telse\n-\t\t\twarning(_(\"invalid commit message cleanup mode '%s'\"),\n-\t\t\t\t  s);\n+\t\topts->default_msg_cleanup = get_cleanup_mode(s, 0, 0);\n \n \t\tfree((char *)s);\n \t\treturn status;\n@@ -512,7 +502,7 @@ static int fast_forward_to(struct repository *r,\n }\n \n enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n-\tint use_editor)\n+\tint use_editor, int die_on_error)\n {\n \tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n \t\treturn use_editor ? COMMIT_MSG_CLEANUP_ALL :\n@@ -526,8 +516,12 @@ enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n \telse if (!strcmp(cleanup_arg, \"scissors\"))\n \t\treturn use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n \t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n-\telse\n-\t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n+\telse if (!die_on_error) {\n+\t\twarning(_(\"invalid cleanup mode %s, falling back to default\"), cleanup_arg);\n+\t\treturn use_editor ? COMMIT_MSG_CLEANUP_ALL :\n+\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n+\t} else\n+\t\tdie(_(\"invalid cleanup mode %s\"), cleanup_arg);\n }\n \n void append_conflicts_hint(struct index_state *istate,\ndiff --git a/sequencer.h b/sequencer.h\nindex eb9bd97ef3..e7908f558e 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -117,7 +117,7 @@ void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);\n \n void append_conflicts_hint(struct index_state *istate, struct strbuf *msgbuf);\n enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n-\tint use_editor);\n+\tint use_editor, int die_on_error);\n \n void cleanup_message(struct strbuf *msgbuf,\n \tenum commit_msg_cleanup_mode cleanup_mode, int verbose);\n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"372068","messageId":"dc43289d0503fd3b691dc2635f9a6dc062af9c3d.1553150827.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1553150827.git.liu.denton@gmail.com","subject":"[PATCH v9 08/11] merge: cleanup messages like commit","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-21T06:53:57Z","receivedAt":"2019-03-21T06:54:02Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"This change allows git-merge messages to be cleaned up with the\ncommit.cleanup configuration or --cleanup option, just like how\ngit-commit does it.\n\nWe also give git-pull the passthrough option of --cleanup so that it can\nalso take advantage of this change.\n\nFinally, add testing to ensure that messages are properly cleaned up.\nNote that some newlines that were added to the commit message were\nremoved so that if a file were read via -F, it would be copied\nfaithfully.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/merge-options.txt |  4 +++\n builtin/merge.c                 | 35 ++++++++++++++----\n builtin/pull.c                  | 23 ++++++++++++\n t/t5521-pull-options.sh         |  8 +++++\n t/t7604-merge-custom-message.sh | 63 +++++++++++++++++++++++++++++++++\n wt-status.c                     | 12 +++++--\n wt-status.h                     |  1 +\n 7 files changed, 136 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 92a7d936c1..bcf0b3e49c 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -32,6 +32,10 @@ they run `git merge`. To make it easier to adjust such scripts to the\n updated behaviour, the environment variable `GIT_MERGE_AUTOEDIT` can be\n set to `no` at the beginning of them.\n \n+--cleanup=<mode>::\n+\tThis option determines how the merge message will be cleaned up\n+\tbefore commiting. See linkgit:git-commit[1] for more details.\n+\n --ff::\n \tWhen the merge resolves as a fast-forward, only update the branch\n \tpointer, without creating a merge commit.  This is the default\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 5ce8946d39..f5783a901d 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -38,6 +38,7 @@\n #include \"tag.h\"\n #include \"alias.h\"\n #include \"commit-reach.h\"\n+#include \"wt-status.h\"\n \n #define DEFAULT_TWOHEAD (1<<0)\n #define DEFAULT_OCTOPUS (1<<1)\n@@ -98,6 +99,9 @@ enum ff_type {\n \n static enum ff_type fast_forward = FF_ALLOW;\n \n+static const char *cleanup_arg;\n+static enum commit_msg_cleanup_mode cleanup_mode;\n+\n static int option_parse_message(const struct option *opt,\n \t\t\t\tconst char *arg, int unset)\n {\n@@ -249,6 +253,7 @@ static struct option builtin_merge_options[] = {\n \t\tN_(\"perform a commit if the merge succeeds (default)\")),\n \tOPT_BOOL('e', \"edit\", &option_edit,\n \t\tN_(\"edit message before committing\")),\n+\tOPT_CLEANUP(&cleanup_arg),\n \tOPT_SET_INT(0, \"ff\", &fast_forward, N_(\"allow fast-forward (default)\"), FF_ALLOW),\n \tOPT_SET_INT_F(0, \"ff-only\", &fast_forward,\n \t\t      N_(\"abort if fast-forward is not possible\"),\n@@ -612,6 +617,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n \t\treturn git_config_string(&pull_twohead, k, v);\n \telse if (!strcmp(k, \"pull.octopus\"))\n \t\treturn git_config_string(&pull_octopus, k, v);\n+\telse if (!strcmp(k, \"commit.cleanup\"))\n+\t\treturn git_config_string(&cleanup_arg, k, v);\n \telse if (!strcmp(k, \"merge.renormalize\"))\n \t\toption_renormalize = git_config_bool(k, v);\n \telse if (!strcmp(k, \"merge.ff\")) {\n@@ -800,8 +807,13 @@ static void abort_commit(struct commit_list *remoteheads, const char *err_msg)\n static const char merge_editor_comment[] =\n N_(\"Please enter a commit message to explain why this merge is necessary,\\n\"\n    \"especially if it merges an updated upstream into a topic branch.\\n\"\n-   \"\\n\"\n-   \"Lines starting with '%c' will be ignored, and an empty message aborts\\n\"\n+   \"\\n\");\n+\n+static const char scissors_editor_comment[] =\n+N_(\"An empty message aborts the commit.\\n\");\n+\n+static const char no_scissors_editor_comment[] =\n+N_(\"Lines starting with '%c' will be ignored, and an empty message aborts\\n\"\n    \"the commit.\\n\");\n \n static void write_merge_heads(struct commit_list *);\n@@ -809,11 +821,19 @@ static void prepare_to_commit(struct commit_list *remoteheads)\n {\n \tstruct strbuf msg = STRBUF_INIT;\n \tstrbuf_addbuf(&msg, &merge_msg);\n-\tstrbuf_addch(&msg, '\\n');\n \tif (squash)\n \t\tBUG(\"the control must not reach here under --squash\");\n-\tif (0 < option_edit)\n-\t\tstrbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);\n+\tif (0 < option_edit) {\n+\t\tstrbuf_addch(&msg, '\\n');\n+\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n+\t\t\twt_status_append_cut_line(&msg);\n+\n+\t\tstrbuf_commented_addf(&msg, \"\\n\");\n+\t\tstrbuf_commented_addf(&msg, _(merge_editor_comment));\n+\t\tstrbuf_commented_addf(&msg, _(cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS ?\n+\t\t\tscissors_editor_comment :\n+\t\t\tno_scissors_editor_comment), comment_line_char);\n+\t}\n \tif (signoff)\n \t\tappend_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);\n \twrite_merge_heads(remoteheads);\n@@ -832,7 +852,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)\n \t\tabort_commit(remoteheads, NULL);\n \n \tread_merge_msg(&msg);\n-\tstrbuf_stripspace(&msg, 0 < option_edit);\n+\tcleanup_message(&msg, cleanup_mode, 0);\n \tif (!msg.len)\n \t\tabort_commit(remoteheads, _(\"Empty commit message.\"));\n \tstrbuf_release(&merge_msg);\n@@ -880,7 +900,6 @@ static int finish_automerge(struct commit *head,\n \tparents = remoteheads;\n \tif (!head_subsumed || fast_forward == FF_NO)\n \t\tcommit_list_insert(head, &parents);\n-\tstrbuf_addch(&merge_msg, '\\n');\n \tprepare_to_commit(remoteheads);\n \tif (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,\n \t\t\t&result_commit, NULL, sign_commit))\n@@ -1389,6 +1408,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \tif (option_edit < 0)\n \t\toption_edit = default_edit_option();\n \n+\tcleanup_mode = get_cleanup_mode(cleanup_arg, 0 < option_edit, 1);\n+\n \tif (!use_strategies) {\n \t\tif (!remoteheads)\n \t\t\t; /* already up-to-date */\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 33db889955..55ebb5808e 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -24,6 +24,7 @@\n #include \"lockfile.h\"\n #include \"wt-status.h\"\n #include \"commit-reach.h\"\n+#include \"sequencer.h\"\n \n enum rebase_type {\n \tREBASE_INVALID = -1,\n@@ -101,6 +102,7 @@ static char *opt_signoff;\n static char *opt_squash;\n static char *opt_commit;\n static char *opt_edit;\n+static char *opt_cleanup;\n static char *opt_ff;\n static char *opt_verify_signatures;\n static int opt_autostash = -1;\n@@ -168,6 +170,9 @@ static struct option pull_options[] = {\n \tOPT_PASSTHRU(0, \"edit\", &opt_edit, NULL,\n \t\tN_(\"edit message before committing\"),\n \t\tPARSE_OPT_NOARG),\n+\tOPT_PASSTHRU(0, \"cleanup\", &opt_cleanup, NULL,\n+\t\tN_(\"how to strip spaces and #comments from message\"),\n+\t\t0),\n \tOPT_PASSTHRU(0, \"ff\", &opt_ff, NULL,\n \t\tN_(\"allow fast-forward\"),\n \t\tPARSE_OPT_NOARG),\n@@ -644,6 +649,8 @@ static int run_merge(void)\n \t\targv_array_push(&args, opt_commit);\n \tif (opt_edit)\n \t\targv_array_push(&args, opt_edit);\n+\tif (opt_cleanup)\n+\t\targv_array_push(&args, opt_cleanup);\n \tif (opt_ff)\n \t\targv_array_push(&args, opt_ff);\n \tif (opt_verify_signatures)\n@@ -875,6 +882,22 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);\n \n+\tif (opt_cleanup) {\n+\t\tconst char *prefix = \"--cleanup=\";\n+\t\tconst char *cleanup_arg;\n+\n+\t\tif (strncmp(opt_cleanup, prefix, strlen(prefix)))\n+\t\t\tBUG(\"expecting prefix %s, argument is %s\", prefix, opt_cleanup);\n+\n+\t\tcleanup_arg = &opt_cleanup[strlen(prefix)];\n+\n+\t\t/*\n+\t\t * this only checks the validity of cleanup_arg; we don't need\n+\t\t * a valid value for use_editor\n+\t\t */\n+\t\tget_cleanup_mode(cleanup_arg, 0, 1);\n+\t}\n+\n \tparse_repo_refspecs(argc, argv, &repo, &refspecs);\n \n \tif (!opt_ff)\ndiff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh\nindex c19d8dbc9d..ccde8ba491 100755\n--- a/t/t5521-pull-options.sh\n+++ b/t/t5521-pull-options.sh\n@@ -77,6 +77,14 @@ test_expect_success 'git pull -q -v' '\n \ttest_must_be_empty out &&\n \ttest -s err)\n '\n+test_expect_success 'git pull --cleanup errors early on invalid argument' '\n+\tmkdir clonedcleanup &&\n+\t(cd clonedcleanup && git init &&\n+\ttest_must_fail git pull --cleanup invalid \"../parent\" >out 2>err &&\n+\ttest_must_be_empty out &&\n+\ttest -s err)\n+'\n+\n \n test_expect_success 'git pull --force' '\n \tmkdir clonedoldstyle &&\ndiff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh\nindex aba1279132..cd4f9607dc 100755\n--- a/t/t7604-merge-custom-message.sh\n+++ b/t/t7604-merge-custom-message.sh\n@@ -49,4 +49,67 @@ test_expect_success 'merge --log appends to custom message' '\n \ttest_cmp exp.log actual\n '\n \n+mesg_with_comment_and_newlines='\n+# text\n+\n+'\n+\n+test_expect_success 'prepare file with comment line and trailing newlines'  '\n+\tprintf \"%s\" \"$mesg_with_comment_and_newlines\" >expect\n+'\n+\n+test_expect_success 'cleanup commit messages (verbatim option)' '\n+\tgit reset --hard c1 &&\n+\tgit merge --cleanup=verbatim -F expect c2 &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e \"1,/^$/d\" raw >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'cleanup commit messages (whitespace option)' '\n+\tgit reset --hard c1 &&\n+\ttest_write_lines \"\" \"# text\" \"\" >text &&\n+\techo \"# text\" >expect &&\n+\tgit merge --cleanup=whitespace -F text c2 &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e \"1,/^$/d\" raw >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'cleanup merge messages (scissors option)' '\n+\tgit reset --hard c1 &&\n+\tcat >text <<-\\EOF &&\n+\n+\t# to be kept\n+\n+\t  # ------------------------ >8 ------------------------\n+\t# to be kept, too\n+\t# ------------------------ >8 ------------------------\n+\tto be removed\n+\t# ------------------------ >8 ------------------------\n+\tto be removed, too\n+\tEOF\n+\n+\tcat >expect <<-\\EOF &&\n+\t# to be kept\n+\n+\t  # ------------------------ >8 ------------------------\n+\t# to be kept, too\n+\tEOF\n+\tgit merge --cleanup=scissors -e -F text c2 &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e \"1,/^$/d\" raw >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'cleanup commit messages (strip option)' '\n+\tgit reset --hard c1 &&\n+\ttest_write_lines \"\" \"# text\" \"sample\" \"\" >text &&\n+\techo sample >expect &&\n+\tgit merge --cleanup=strip -F text c2 &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e \"1,/^$/d\" raw >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\ndiff --git a/wt-status.c b/wt-status.c\nindex 445a36204a..b81fcd428d 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1006,13 +1006,19 @@ size_t wt_status_locate_end(const char *s, size_t len)\n \treturn len;\n }\n \n-void wt_status_add_cut_line(FILE *fp)\n+void wt_status_append_cut_line(struct strbuf *buf)\n {\n \tconst char *explanation = _(\"Do not modify or remove the line above.\\nEverything below it will be ignored.\");\n+\n+\tstrbuf_commented_addf(buf, \"%s\", cut_line);\n+\tstrbuf_add_commented_lines(buf, explanation, strlen(explanation));\n+}\n+\n+void wt_status_add_cut_line(FILE *fp)\n+{\n \tstruct strbuf buf = STRBUF_INIT;\n \n-\tfprintf(fp, \"%c %s\", comment_line_char, cut_line);\n-\tstrbuf_add_commented_lines(&buf, explanation, strlen(explanation));\n+\twt_status_append_cut_line(&buf);\n \tfputs(buf.buf, fp);\n \tstrbuf_release(&buf);\n }\ndiff --git a/wt-status.h b/wt-status.h\nindex 3a95975032..64f1ddc9fd 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -129,6 +129,7 @@ struct wt_status {\n };\n \n size_t wt_status_locate_end(const char *s, size_t len);\n+void wt_status_append_cut_line(struct strbuf *buf);\n void wt_status_add_cut_line(FILE *fp);\n void wt_status_prepare(struct repository *r, struct wt_status *s);\n void wt_status_print(struct wt_status *s);\n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"372069","messageId":"ebaeeb85fc05560e255d5fb2bc1b1566346906fd.1553150827.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1553150827.git.liu.denton@gmail.com","subject":"[PATCH v9 09/11] merge: add scissors line on merge conflict","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-21T06:53:59Z","receivedAt":"2019-03-21T06:54:05Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"This fixes a bug where the scissors line is placed after the Conflicts:\nsection, in the case where a merge conflict occurs and\ncommit.cleanup = scissors.\n\nNext, if commit.cleanup = scissors is specified, don't produce a\nscissors line in commit if one already exists in the MERGE_MSG file.\n\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/merge-options.txt |  7 +++--\n builtin/commit.c                | 20 ++++++++++----\n builtin/merge.c                 | 14 ++++++++++\n t/t7600-merge.sh                | 46 +++++++++++++++++++++++++++++++++\n 4 files changed, 80 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex bcf0b3e49c..61876dbc33 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -33,8 +33,11 @@ updated behaviour, the environment variable `GIT_MERGE_AUTOEDIT` can be\n set to `no` at the beginning of them.\n \n --cleanup=<mode>::\n-\tThis option determines how the merge message will be cleaned up\n-\tbefore commiting. See linkgit:git-commit[1] for more details.\n+\tThis option determines how the merge message will be cleaned up before\n+\tcommiting. See linkgit:git-commit[1] for more details. In addition, if\n+\tthe '<mode>' is given a value of `scissors`, scissors will be appended\n+\tto `MERGE_MSG` before being passed on to the commit machinery in the\n+\tcase of a merge conflict.\n \n --ff::\n \tWhen the merge resolves as a fast-forward, only update the branch\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex ba1e6027ba..b9159c2e7b 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -668,6 +668,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \tconst char *hook_arg2 = NULL;\n \tint clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);\n \tint old_display_comment_prefix;\n+\tint merge_contains_scissors = 0;\n \n \t/* This checks and barfs if author is badly specified */\n \tdetermine_author_info(author_ident);\n@@ -728,6 +729,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\tstrbuf_addbuf(&sb, &message);\n \t\thook_arg1 = \"message\";\n \t} else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {\n+\t\tsize_t merge_msg_start;\n+\n \t\t/*\n \t\t * prepend SQUASH_MSG here if it exists and a\n \t\t * \"merge --squash\" was originally performed\n@@ -738,8 +741,14 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\thook_arg1 = \"squash\";\n \t\t} else\n \t\t\thook_arg1 = \"merge\";\n+\n+\t\tmerge_msg_start = sb.len;\n \t\tif (strbuf_read_file(&sb, git_path_merge_msg(the_repository), 0) < 0)\n \t\t\tdie_errno(_(\"could not read MERGE_MSG\"));\n+\n+\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\t    wt_status_locate_end(sb.buf + merge_msg_start, sb.len - merge_msg_start) < sb.len - merge_msg_start)\n+\t\t\tmerge_contains_scissors = 1;\n \t} else if (!stat(git_path_squash_msg(the_repository), &statbuf)) {\n \t\tif (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)\n \t\t\tdie_errno(_(\"could not read SQUASH_MSG\"));\n@@ -807,7 +816,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\tstruct ident_split ci, ai;\n \n \t\tif (whence != FROM_COMMIT) {\n-\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n+\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\t\t\t!merge_contains_scissors)\n \t\t\t\twt_status_add_cut_line(s->fp);\n \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL,\n \t\t\t    whence == FROM_MERGE\n@@ -832,10 +842,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\nwith '%c' will be ignored, and an empty\"\n \t\t\t\t  \" message aborts the commit.\\n\"), comment_line_char);\n-\t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n-\t\t\t whence == FROM_COMMIT)\n-\t\t\twt_status_add_cut_line(s->fp);\n-\t\telse /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n+\t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {\n+\t\t\tif (whence == FROM_COMMIT && !merge_contains_scissors)\n+\t\t\t\twt_status_add_cut_line(s->fp);\n+\t\t} else /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n \t\t\tstatus_printf(s, GIT_COLOR_NORMAL,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\n\"\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex f5783a901d..148554f08e 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -920,6 +920,20 @@ static int suggest_conflicts(void)\n \tfilename = git_path_merge_msg(the_repository);\n \tfp = xfopen(filename, \"a\");\n \n+\t/*\n+\t * We can't use cleanup_mode because if we're not using the editor,\n+\t * get_cleanup_mode will return COMMIT_MSG_CLEANUP_SPACE instead, even\n+\t * though the message is meant to be processed later by git-commit.\n+\t * Thus, we will get the cleanup mode which is returned when we _are_ using\n+\t * an editor.\n+\t */\n+\tif (get_cleanup_mode(cleanup_arg, 1, 1) == COMMIT_MSG_CLEANUP_SCISSORS) {\n+\t    fputc('\\n', fp);\n+\t    wt_status_add_cut_line(fp);\n+\t    /* comments out the newline from append_conflicts_hint */\n+\t    fputc(comment_line_char, fp);\n+\t}\n+\n \tappend_conflicts_hint(&the_index, &msgbuf);\n \tfputs(msgbuf.buf, fp);\n \tstrbuf_release(&msgbuf);\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex 2f4c2801fb..7f9c68cbe7 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -246,6 +246,52 @@ test_expect_success 'merge --squash c3 with c7' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'merge c3 with c7 with commit.cleanup = scissors' '\n+\tgit config commit.cleanup scissors &&\n+\tgit reset --hard c3 &&\n+\ttest_must_fail git merge c7 &&\n+\tcat result.9z >file &&\n+\tgit commit --no-edit -a &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tMerge tag '\"'\"'c7'\"'\"'\n+\n+\t# ------------------------ >8 ------------------------\n+\t# Do not modify or remove the line above.\n+\t# Everything below it will be ignored.\n+\t#\n+\t# Conflicts:\n+\t#\tfile\n+\tEOF\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e '1,/^$/d' raw >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n+test_expect_success 'merge c3 with c7 with --squash commit.cleanup = scissors' '\n+\tgit config commit.cleanup scissors &&\n+\tgit reset --hard c3 &&\n+\ttest_must_fail git merge --squash c7 &&\n+\tcat result.9z >file &&\n+\tgit commit --no-edit -a &&\n+\n+\tcat >expect <<-EOF &&\n+\tSquashed commit of the following:\n+\n+\t$(git show -s c7)\n+\n+\t# ------------------------ >8 ------------------------\n+\t# Do not modify or remove the line above.\n+\t# Everything below it will be ignored.\n+\t#\n+\t# Conflicts:\n+\t#\tfile\n+\tEOF\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e '1,/^$/d' raw >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n test_debug 'git log --graph --decorate --oneline --all'\n \n test_expect_success 'merge c1 with c2 and c3' '\n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"372070","messageId":"5927064b68c3894df69ecfb2c7224acc46f786d4.1553150827.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1553150827.git.liu.denton@gmail.com","subject":"[PATCH v9 10/11] sequencer.c: define describe_cleanup_mode","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-21T06:54:02Z","receivedAt":"2019-03-21T06:54:08Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Define a function which allows us to get the string configuration value\nof a enum commit_msg_cleanup_mode. This is done by refactoring\nget_cleanup_mode such that it uses a lookup table to find the mappings\nbetween string and enum and then using the same LUT in reverse to define\ndescribe_cleanup_mode.\n\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Ramsay Jones <ramsay@ramsayjones.plus.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 62 ++++++++++++++++++++++++++++++++++++++++-------------\n 1 file changed, 47 insertions(+), 15 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 2cbfb848dd..f782e8bab2 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -160,6 +160,22 @@ static GIT_PATH_FUNC(rebase_path_strategy_opts, \"rebase-merge/strategy_opts\")\n static GIT_PATH_FUNC(rebase_path_allow_rerere_autoupdate, \"rebase-merge/allow_rerere_autoupdate\")\n static GIT_PATH_FUNC(rebase_path_reschedule_failed_exec, \"rebase-merge/reschedule-failed-exec\")\n \n+struct cleanup_config_mapping {\n+\tconst char *value;\n+\tenum commit_msg_cleanup_mode editor;\n+\tenum commit_msg_cleanup_mode no_editor;\n+};\n+\n+/* the 0th element of this array must be the \"default\" */\n+static struct cleanup_config_mapping cleanup_config_mapping[] = {\n+\t{ \"default\", COMMIT_MSG_CLEANUP_ALL, COMMIT_MSG_CLEANUP_SPACE },\n+\t{ \"verbatim\", COMMIT_MSG_CLEANUP_NONE, COMMIT_MSG_CLEANUP_NONE },\n+\t{ \"whitespace\", COMMIT_MSG_CLEANUP_SPACE, COMMIT_MSG_CLEANUP_SPACE },\n+\t{ \"strip\", COMMIT_MSG_CLEANUP_ALL, COMMIT_MSG_CLEANUP_ALL },\n+\t{ \"scissors\", COMMIT_MSG_CLEANUP_SCISSORS, COMMIT_MSG_CLEANUP_SPACE },\n+\t{ NULL, 0, 0 }\n+};\n+\n static int git_sequencer_config(const char *k, const char *v, void *cb)\n {\n \tstruct replay_opts *opts = cb;\n@@ -504,26 +520,37 @@ static int fast_forward_to(struct repository *r,\n enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n \tint use_editor, int die_on_error)\n {\n-\tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n-\t\treturn use_editor ? COMMIT_MSG_CLEANUP_ALL :\n-\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n-\telse if (!strcmp(cleanup_arg, \"verbatim\"))\n-\t\treturn COMMIT_MSG_CLEANUP_NONE;\n-\telse if (!strcmp(cleanup_arg, \"whitespace\"))\n-\t\treturn COMMIT_MSG_CLEANUP_SPACE;\n-\telse if (!strcmp(cleanup_arg, \"strip\"))\n-\t\treturn COMMIT_MSG_CLEANUP_ALL;\n-\telse if (!strcmp(cleanup_arg, \"scissors\"))\n-\t\treturn use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n-\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n-\telse if (!die_on_error) {\n+\tstruct cleanup_config_mapping *def = &cleanup_config_mapping[0];\n+\tstruct cleanup_config_mapping *p;\n+\n+\tif (!cleanup_arg)\n+\t\treturn use_editor ? def->editor :\n+\t\t\t\t    def->no_editor;\n+\n+\tfor (p = cleanup_config_mapping; p->value; p++)\n+\t\tif (!strcmp(cleanup_arg, p->value))\n+\t\t\treturn use_editor ? p->editor :\n+\t\t\t\t\t    p->no_editor;\n+\n+\tif (!die_on_error) {\n \t\twarning(_(\"invalid cleanup mode %s, falling back to default\"), cleanup_arg);\n-\t\treturn use_editor ? COMMIT_MSG_CLEANUP_ALL :\n-\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n+\t\treturn use_editor ? def->editor :\n+\t\t\t\t    def->no_editor;\n \t} else\n \t\tdie(_(\"invalid cleanup mode %s\"), cleanup_arg);\n }\n \n+static const char *describe_cleanup_mode(enum commit_msg_cleanup_mode cleanup_mode)\n+{\n+\tstruct cleanup_config_mapping *curr;\n+\n+\tfor (curr = &cleanup_config_mapping[1]; curr->value; curr++)\n+\t\tif (cleanup_mode == curr->editor)\n+\t\t\treturn curr->value;\n+\n+\tBUG(\"invalid cleanup_mode provided (%d)\", cleanup_mode);\n+}\n+\n void append_conflicts_hint(struct index_state *istate,\n \t\t\t   struct strbuf *msgbuf)\n {\n@@ -2350,6 +2377,8 @@ static int populate_opts_cb(const char *key, const char *value, void *data)\n \t\topts->allow_rerere_auto =\n \t\t\tgit_config_bool_or_int(key, value, &error_flag) ?\n \t\t\t\tRERERE_AUTOUPDATE : RERERE_NOAUTOUPDATE;\n+\telse if (!strcmp(key, \"options.default-msg-cleanup\"))\n+\t\topts->default_msg_cleanup = get_cleanup_mode(value, 1, 1);\n \telse\n \t\treturn error(_(\"invalid key: %s\"), key);\n \n@@ -2754,6 +2783,9 @@ static int save_opts(struct replay_opts *opts)\n \t\tres |= git_config_set_in_file_gently(opts_file, \"options.allow-rerere-auto\",\n \t\t\t\t\t\t     opts->allow_rerere_auto == RERERE_AUTOUPDATE ?\n \t\t\t\t\t\t     \"true\" : \"false\");\n+\n+\tres |= git_config_set_in_file_gently(opts_file, \"options.default-msg-cleanup\",\n+\t\t\t\t\t     describe_cleanup_mode(opts->default_msg_cleanup));\n \treturn res;\n }\n \n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"372072","messageId":"dd3eef7cb3269af2729f2b358255898bc80d301c.1553150827.git.liu.denton@gmail.com","threadId":"49830","inReplyTo":"cover.1553150827.git.liu.denton@gmail.com","subject":"[PATCH v9 11/11] cherry-pick/revert: add scissors line on merge conflict","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-03-21T06:54:05Z","receivedAt":"2019-03-21T06:54:11Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Fix a bug where the scissors line is placed after the Conflicts:\nsection, in the case where a merge conflict occurs and\ncommit.cleanup = scissors.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/git-cherry-pick.txt |  7 +++\n Documentation/git-revert.txt      |  7 +++\n builtin/merge.c                   |  9 +---\n builtin/rebase--interactive.c     |  2 +-\n builtin/revert.c                  |  5 ++\n sequencer.c                       | 22 +++++---\n sequencer.h                       |  3 +-\n t/t3507-cherry-pick-conflict.sh   | 86 +++++++++++++++++++++++++++++++\n 8 files changed, 123 insertions(+), 18 deletions(-)\n\ndiff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt\nindex b8cfeec67e..cbb663a843 100644\n--- a/Documentation/git-cherry-pick.txt\n+++ b/Documentation/git-cherry-pick.txt\n@@ -57,6 +57,13 @@ OPTIONS\n \tWith this option, 'git cherry-pick' will let you edit the commit\n \tmessage prior to committing.\n \n+--cleanup=<mode>::\n+\tThis option determines how the commit message will be cleaned up before\n+\tbeing passed on to the commit machinery. See linkgit:git-commit[1] for more\n+\tdetails. In particular, if the '<mode>' is given a value of `scissors`,\n+\tscissors will be appended to `MERGE_MSG` before being passed on in the case\n+\tof a conflict.\n+\n -x::\n \tWhen recording the commit, append a line that says\n \t\"(cherry picked from commit ...)\" to the original commit\ndiff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt\nindex 837707a8fd..7fd254df92 100644\n--- a/Documentation/git-revert.txt\n+++ b/Documentation/git-revert.txt\n@@ -66,6 +66,13 @@ more details.\n \tWith this option, 'git revert' will not start the commit\n \tmessage editor.\n \n+--cleanup=<mode>::\n+\tThis option determines how the commit message will be cleaned up before\n+\tbeing passed on to the commit machinery. See linkgit:git-commit[1] for more\n+\tdetails. In particular, if the '<mode>' is given a value of `scissors`,\n+\tscissors will be appended to `MERGE_MSG` before being passed on in the case\n+\tof a conflict.\n+\n -n::\n --no-commit::\n \tUsually the command automatically creates some commits with\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 148554f08e..235cd3b2e1 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -927,14 +927,7 @@ static int suggest_conflicts(void)\n \t * Thus, we will get the cleanup mode which is returned when we _are_ using\n \t * an editor.\n \t */\n-\tif (get_cleanup_mode(cleanup_arg, 1, 1) == COMMIT_MSG_CLEANUP_SCISSORS) {\n-\t    fputc('\\n', fp);\n-\t    wt_status_add_cut_line(fp);\n-\t    /* comments out the newline from append_conflicts_hint */\n-\t    fputc(comment_line_char, fp);\n-\t}\n-\n-\tappend_conflicts_hint(&the_index, &msgbuf);\n+\tappend_conflicts_hint(&the_index, &msgbuf, get_cleanup_mode(cleanup_arg, 1, 1));\n \tfputs(msgbuf.buf, fp);\n \tstrbuf_release(&msgbuf);\n \tfclose(fp);\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 888390f911..cf2151b271 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -199,10 +199,10 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n+\topts.action = REPLAY_INTERACTIVE_REBASE;\n \tsequencer_init_config(&opts);\n \tgit_config_get_bool(\"rebase.abbreviatecommands\", &abbreviate_commands);\n \n-\topts.action = REPLAY_INTERACTIVE_REBASE;\n \topts.allow_ff = 1;\n \topts.allow_empty = 1;\n \ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex a47b53ceaf..41e7392c24 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -96,11 +96,13 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)\n {\n \tconst char * const * usage_str = revert_or_cherry_pick_usage(opts);\n \tconst char *me = action_name(opts);\n+\tconst char *cleanup_arg = NULL;\n \tint cmd = 0;\n \tstruct option base_options[] = {\n \t\tOPT_CMDMODE(0, \"quit\", &cmd, N_(\"end revert or cherry-pick sequence\"), 'q'),\n \t\tOPT_CMDMODE(0, \"continue\", &cmd, N_(\"resume revert or cherry-pick sequence\"), 'c'),\n \t\tOPT_CMDMODE(0, \"abort\", &cmd, N_(\"cancel revert or cherry-pick sequence\"), 'a'),\n+\t\tOPT_CLEANUP(&cleanup_arg),\n \t\tOPT_BOOL('n', \"no-commit\", &opts->no_commit, N_(\"don't automatically commit\")),\n \t\tOPT_BOOL('e', \"edit\", &opts->edit, N_(\"edit the commit message\")),\n \t\tOPT_NOOP_NOARG('r', NULL),\n@@ -137,6 +139,9 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)\n \tif (opts->keep_redundant_commits)\n \t\topts->allow_empty = 1;\n \n+\tif (cleanup_arg)\n+\t\topts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1, 1);\n+\n \t/* Check for incompatible command line arguments */\n \tif (cmd) {\n \t\tchar *this_operation;\ndiff --git a/sequencer.c b/sequencer.c\nindex f782e8bab2..ea4687c3da 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -176,6 +176,11 @@ static struct cleanup_config_mapping cleanup_config_mapping[] = {\n \t{ NULL, 0, 0 }\n };\n \n+static inline int is_rebase_i(const struct replay_opts *opts)\n+{\n+\treturn opts->action == REPLAY_INTERACTIVE_REBASE;\n+}\n+\n static int git_sequencer_config(const char *k, const char *v, void *cb)\n {\n \tstruct replay_opts *opts = cb;\n@@ -188,7 +193,7 @@ static int git_sequencer_config(const char *k, const char *v, void *cb)\n \t\tif (status)\n \t\t\treturn status;\n \n-\t\topts->default_msg_cleanup = get_cleanup_mode(s, 0, 0);\n+\t\topts->default_msg_cleanup = get_cleanup_mode(s, !is_rebase_i(opts), 0);\n \n \t\tfree((char *)s);\n \t\treturn status;\n@@ -212,11 +217,6 @@ void sequencer_init_config(struct replay_opts *opts)\n \tgit_config(git_sequencer_config, opts);\n }\n \n-static inline int is_rebase_i(const struct replay_opts *opts)\n-{\n-\treturn opts->action == REPLAY_INTERACTIVE_REBASE;\n-}\n-\n static const char *get_dir(const struct replay_opts *opts)\n {\n \tif (is_rebase_i(opts))\n@@ -552,10 +552,16 @@ static const char *describe_cleanup_mode(enum commit_msg_cleanup_mode cleanup_mo\n }\n \n void append_conflicts_hint(struct index_state *istate,\n-\t\t\t   struct strbuf *msgbuf)\n+\t\tstruct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode)\n {\n \tint i;\n \n+\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {\n+\t\tstrbuf_addch(msgbuf, '\\n');\n+\t\twt_status_append_cut_line(msgbuf);\n+\t\tstrbuf_addch(msgbuf, comment_line_char);\n+\t}\n+\n \tstrbuf_addch(msgbuf, '\\n');\n \tstrbuf_commented_addf(msgbuf, \"Conflicts:\\n\");\n \tfor (i = 0; i < istate->cache_nr;) {\n@@ -623,7 +629,7 @@ static int do_recursive_merge(struct repository *r,\n \t\t\t_(action_name(opts)));\n \n \tif (!clean)\n-\t\tappend_conflicts_hint(r->index, msgbuf);\n+\t\tappend_conflicts_hint(r->index, msgbuf, opts->default_msg_cleanup);\n \n \treturn !clean;\n }\ndiff --git a/sequencer.h b/sequencer.h\nindex e7908f558e..830d8232f6 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -115,7 +115,8 @@ int rearrange_squash(struct repository *r);\n  */\n void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);\n \n-void append_conflicts_hint(struct index_state *istate, struct strbuf *msgbuf);\n+void append_conflicts_hint(struct index_state *istate,\n+\t\tstruct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode);\n enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n \tint use_editor, int die_on_error);\n \ndiff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh\nindex 777fb23d18..a584b11c98 100755\n--- a/t/t3507-cherry-pick-conflict.sh\n+++ b/t/t3507-cherry-pick-conflict.sh\n@@ -189,6 +189,46 @@ test_expect_success 'failed cherry-pick registers participants in index' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success \\\n+\t'cherry-pick conflict, ensure commit.cleanup = scissors places scissors line properly' '\n+\tpristine_detach initial &&\n+\tgit config commit.cleanup scissors &&\n+\tcat <<-EOF >expected &&\n+\t\tpicked\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfoo\n+\t\tEOF\n+\n+\ttest_must_fail git cherry-pick picked &&\n+\n+\ttest_i18ncmp expected .git/MERGE_MSG\n+'\n+\n+test_expect_success \\\n+\t'cherry-pick conflict, ensure cleanup=scissors places scissors line properly' '\n+\tpristine_detach initial &&\n+\tgit config --unset commit.cleanup &&\n+\tcat <<-EOF >expected &&\n+\t\tpicked\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfoo\n+\t\tEOF\n+\n+\ttest_must_fail git cherry-pick --cleanup=scissors picked &&\n+\n+\ttest_i18ncmp expected .git/MERGE_MSG\n+'\n+\n test_expect_success 'failed cherry-pick describes conflict in work tree' '\n \tpristine_detach initial &&\n \tcat <<-EOF >expected &&\n@@ -335,6 +375,52 @@ test_expect_success 'revert conflict, diff3 -m style' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success \\\n+\t'revert conflict, ensure commit.cleanup = scissors places scissors line properly' '\n+\tpristine_detach initial &&\n+\tgit config commit.cleanup scissors &&\n+\tcat >expected <<-EOF &&\n+\t\tRevert \"picked\"\n+\n+\t\tThis reverts commit OBJID.\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfoo\n+\t\tEOF\n+\n+\ttest_must_fail git revert picked &&\n+\n+\tsed \"s/$OID_REGEX/OBJID/\" .git/MERGE_MSG >actual &&\n+\ttest_i18ncmp expected actual\n+'\n+\n+test_expect_success \\\n+\t'revert conflict, ensure cleanup=scissors places scissors line properly' '\n+\tpristine_detach initial &&\n+\tgit config --unset commit.cleanup &&\n+\tcat >expected <<-EOF &&\n+\t\tRevert \"picked\"\n+\n+\t\tThis reverts commit OBJID.\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfoo\n+\t\tEOF\n+\n+\ttest_must_fail git revert --cleanup=scissors picked &&\n+\n+\tsed \"s/$OID_REGEX/OBJID/\" .git/MERGE_MSG >actual &&\n+\ttest_i18ncmp expected actual\n+'\n+\n test_expect_success 'failed cherry-pick does not forget -s' '\n \tpristine_detach initial &&\n \ttest_must_fail git cherry-pick -s picked &&\n-- \n2.21.0.512.g57bf1b23e1\n\n"},{"id":"372511","messageId":"69e3e5b7-58ec-4507-bc5c-c162393368c4@gmail.com","threadId":"49830","inReplyTo":"492ba73797ac66cc48da2b44cdae2152b41aa481.1553150827.git.liu.denton@gmail.com","subject":"Re: [PATCH v9 07/11] sequencer.c: remove duplicate code","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-03-26T10:44:25Z","receivedAt":"2019-03-26T10:44:31Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"\n\nOn 21/03/2019 06:53, Denton Liu wrote:\n> Since we implemented get_cleanup_mode, we had some duplicate code in\n> git_sequencer_config which essentially performed the same operations.\n> Refactor git_sequencer_config to take advantage of the logic already in\n> get_cleanup_mode.\n> \n> Note that we had to introduce a separate argument to get_cleanup_mode\n> indicating whether to die or not on an invalid value. This is because if\n> we are parsing a config, we do not want to die but instead, we only want\n> to warn the user, whereas if we are parsing a command-line option, we\n> would like to actually die.\n> \n> Finally, this is almost a no-op refactor but not quite. Previously, in\n> the case that an invalid value is presented, default_msg_cleanup would\n> not be set. \n\nLooking more closely at the code that's not correct, we set a default \nvalue before in sequencer_init_config() which unfortunately means my \nprevious concerns about needing different values for rebase were not \ncorrect.\n\n> We change the behaviour so that default_msg_cleanup will now\n> take on the value as if \"default\" were provided as the cleanup_arg.\n\nThat is not the default value we want, if the user provides an invalid \nvalue in the config we're better off using --verbatim which is what the \ncurrent code does.\n\nWhile looking at all this I noticed that in run_git_commit() we set up \nthe cleanup option for to use with\n\n\tif ((flags & CLEANUP_MSG))\n\t\targv_array_push(&cmd.args, \"--cleanup=strip\");\n\tif ((flags & EDIT_MSG))\n\t\targv_array_push(&cmd.args, \"-e\");\n\telse if (!(flags & CLEANUP_MSG) &&\n\t\t !opts->signoff && !opts->record_origin &&\n\t\t git_config_get_value(\"commit.cleanup\", &value))\n\t\targv_array_push(&cmd.args, \"--cleanup=verbatim\");\n\nSo we use --strip when editing (we always set CLEANUP_MSG when EDIT_MSG \nis set) otherwise if opts->signoff or opts->record_origin is set we use \ncommit's default cleanup which is --whitespace as we're not editing \nunless the user has set something else. In all other cases we use \n--verbatim unless the user has something set in their config.\n\nWith your changes we need to change \ngit_config_get_value(\"commit.cleanup\", &value) to something that checks \nto see if --cleanup was given on the command line as well. I think the \nbest way is to add an opts->explict_cleanup flag which will also help \nwith a bug I introduced when I wrote try_to_commit().\n\nIn try_to_commit() we do\n\n\tcleanup = (flags & CLEANUP_MSG) ? COMMIT_MSG_CLEANUP_ALL :\n\t\t\t\t\t  opts->default_msg_cleanup;\n\nWhich does not special case the opts->signoff or opts->record_origin \ncases (opts->default_msg_cleanup will be --verbatim unless the user has \nset something else) I'm planning to send in a patch for that soonish, it \nwill involve adding opts->explicit_cleanup which will interact badly \nwith this series as it is, I'll try and do some fixups to address that.\n\nI have a general question about this series. If I run 'merge \n--cleanup=scissors' or 'cherry-pick --cleanup=scissors' and there are \nconflicts what happens when if I run 'commit --no-edit' to commit the \nconflict resolution - does it know which cleanup mode to use?\n\nBest Wishes\n\nPhillip\n\n\n> Also, we lowercase some user-facing strings.\n> \n> Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>   builtin/commit.c |  2 +-\n>   sequencer.c      | 22 ++++++++--------------\n>   sequencer.h      |  2 +-\n>   3 files changed, 10 insertions(+), 16 deletions(-)\n> \n> diff --git a/builtin/commit.c b/builtin/commit.c\n> index 8277da8474..ba1e6027ba 100644\n> --- a/builtin/commit.c\n> +++ b/builtin/commit.c\n> @@ -1172,7 +1172,7 @@ static int parse_and_validate_options(int argc, const char *argv[],\n>   \t\tdie(_(\"Only one of --include/--only/--all/--interactive/--patch can be used.\"));\n>   \tif (argc == 0 && (also || (only && !amend && !allow_empty)))\n>   \t\tdie(_(\"No paths with --include/--only does not make sense.\"));\n> -\tcleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);\n> +\tcleanup_mode = get_cleanup_mode(cleanup_arg, use_editor, 1);\n>   \n>   \thandle_untracked_files_arg(s);\n>   \n> diff --git a/sequencer.c b/sequencer.c\n> index 224c823b43..2cbfb848dd 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -172,17 +172,7 @@ static int git_sequencer_config(const char *k, const char *v, void *cb)\n>   \t\tif (status)\n>   \t\t\treturn status;\n>   \n> -\t\tif (!strcmp(s, \"verbatim\"))\n> -\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;\n> -\t\telse if (!strcmp(s, \"whitespace\"))\n> -\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;\n> -\t\telse if (!strcmp(s, \"strip\"))\n> -\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;\n> -\t\telse if (!strcmp(s, \"scissors\"))\n> -\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;\n> -\t\telse\n> -\t\t\twarning(_(\"invalid commit message cleanup mode '%s'\"),\n> -\t\t\t\t  s);\n> +\t\topts->default_msg_cleanup = get_cleanup_mode(s, 0, 0);\n>   \n>   \t\tfree((char *)s);\n>   \t\treturn status;\n> @@ -512,7 +502,7 @@ static int fast_forward_to(struct repository *r,\n>   }\n>   \n>   enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n> -\tint use_editor)\n> +\tint use_editor, int die_on_error)\n>   {\n>   \tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n>   \t\treturn use_editor ? COMMIT_MSG_CLEANUP_ALL :\n> @@ -526,8 +516,12 @@ enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n>   \telse if (!strcmp(cleanup_arg, \"scissors\"))\n>   \t\treturn use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n>   \t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n> -\telse\n> -\t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n> +\telse if (!die_on_error) {\n> +\t\twarning(_(\"invalid cleanup mode %s, falling back to default\"), cleanup_arg);\n> +\t\treturn use_editor ? COMMIT_MSG_CLEANUP_ALL :\n> +\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n> +\t} else\n> +\t\tdie(_(\"invalid cleanup mode %s\"), cleanup_arg);\n>   }\n>   \n>   void append_conflicts_hint(struct index_state *istate,\n> diff --git a/sequencer.h b/sequencer.h\n> index eb9bd97ef3..e7908f558e 100644\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -117,7 +117,7 @@ void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);\n>   \n>   void append_conflicts_hint(struct index_state *istate, struct strbuf *msgbuf);\n>   enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n> -\tint use_editor);\n> +\tint use_editor, int die_on_error);\n>   \n>   void cleanup_message(struct strbuf *msgbuf,\n>   \tenum commit_msg_cleanup_mode cleanup_mode, int verbose);\n> \n"},{"id":"374018","messageId":"20190417102330.24434-2-phillip.wood123@gmail.com","threadId":"49830","inReplyTo":"20190417102330.24434-1-phillip.wood123@gmail.com","subject":"[PATCH v10 01/10] t7600: clean up style","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-04-17T10:23:21Z","receivedAt":"2019-04-17T10:24:57Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Denton Liu <liu.denton@gmail.com>\n\nClean up the 'merge --squash c3 with c7' test by removing some\nunnecessary braces and removing a pipe.\n\nAlso, generally cleanup style by unindenting a here-doc, removing stray\nspaces after a redirection operator and allowing sed to open its own\ninput instead of redirecting input from the shell.\n\nHelped-by: SZEDER Gábor <szeder.dev@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t7600-merge.sh | 27 +++++++++++++--------------\n 1 file changed, 13 insertions(+), 14 deletions(-)\n\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex 106148254d..2f4c2801fb 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -233,17 +233,16 @@ test_expect_success 'merge --squash c3 with c7' '\n \tcat result.9z >file &&\n \tgit commit --no-edit -a &&\n \n-\t{\n-\t\tcat <<-EOF\n-\t\tSquashed commit of the following:\n+\tcat >expect <<-EOF &&\n+\tSquashed commit of the following:\n \n-\t\t$(git show -s c7)\n+\t$(git show -s c7)\n \n-\t\t# Conflicts:\n-\t\t#\tfile\n-\t\tEOF\n-\t} >expect &&\n-\tgit cat-file commit HEAD | sed -e '1,/^$/d' >actual &&\n+\t# Conflicts:\n+\t#\tfile\n+\tEOF\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e '1,/^$/d' raw >actual &&\n \ttest_cmp expect actual\n '\n \n@@ -680,10 +679,10 @@ cat >editor <<\\EOF\n (\n \techo \"Merge work done on the side branch c1\"\n \techo\n-\tcat <\"$1\"\n+\tcat \"$1\"\n ) >\"$1.tmp\" && mv \"$1.tmp\" \"$1\"\n # strip comments and blank lines from end of message\n-sed -e '/^#/d' < \"$1\" | sed -e :a -e '/^\\n*$/{$d;N;ba' -e '}' > expected\n+sed -e '/^#/d' \"$1\" | sed -e :a -e '/^\\n*$/{$d;N;ba' -e '}' >expected\n EOF\n chmod 755 editor\n \n@@ -768,14 +767,14 @@ test_expect_success 'set up mod-256 conflict scenario' '\n \tgit commit -m base &&\n \n \t# one side changes the first line of each to \"master\"\n-\tsed s/-1/-master/ <file >tmp &&\n+\tsed s/-1/-master/ file >tmp &&\n \tmv tmp file &&\n \tgit commit -am master &&\n \n \t# and the other to \"side\"; merging the two will\n \t# yield 256 separate conflicts\n \tgit checkout -b side HEAD^ &&\n-\tsed s/-1/-side/ <file >tmp &&\n+\tsed s/-1/-side/ file >tmp &&\n \tmv tmp file &&\n \tgit commit -am side\n '\n@@ -814,7 +813,7 @@ EOF\n test_expect_success EXECKEEPSPID 'killed merge can be completed with --continue' '\n \tgit reset --hard c0 &&\n \t! \"$SHELL_PATH\" -c '\\''\n-\t  echo kill -TERM $$ >> .git/FAKE_EDITOR\n+\t  echo kill -TERM $$ >>.git/FAKE_EDITOR\n \t  GIT_EDITOR=.git/FAKE_EDITOR\n \t  export GIT_EDITOR\n \t  exec git merge --no-ff --edit c1'\\'' &&\n-- \n2.21.0\n\n"},{"id":"374019","messageId":"20190417102330.24434-1-phillip.wood123@gmail.com","threadId":"49830","inReplyTo":"cover.1553150827.git.liu.denton@gmail.com","subject":"[PATCH v10 00/10] Fix scissors bug during conflict","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-04-17T10:23:20Z","receivedAt":"2019-04-17T10:24:58Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nWith Denton's blessing I've rebased his patches on top of my patch to\nfix the message cleanup with cherry-pick --signoff and -x [1]. I've\nalso refactored a couple of the patches and cleaned up the coding\nstyle in places. The refactoring aims to simplify the code that deals\nwith saving and restoring the cleanup option for cherry-pick/revert\nand improve the --cleanup option handling/parsing in builtin/merge.c\nand builtin/pull.c. I've dropped patch 7 as it didn't sit well with\nthe new opts->explicit_cleanup added by [1]. I think it is easier just\nto keep the existing code in the sequencer for parsing the cleanup\noption rather trying to create a single function that does two\nslightly different things.\n\nApologies to those of you that received some of these patches\nyesterday when I was sending them to Denton, I didn't realize that\nsend-email \"helpfully\" cc'd people in the *-by: footers.\n\nBest Wishes\n\nPhillip\n\n[1] https://public-inbox.org/git/20190329110842.30604-1-phillip.wood123@gmail.com/\n\nDenton Liu (10):\n  t7600: clean up style\n  t3507: clean up style\n  t7604: clean up style\n  t7502: clean up style\n  commit: extract cleanup_mode functions to sequencer\n  parse-options.h: extract common --cleanup option\n  merge: cleanup messages like commit\n  merge: add scissors line on merge conflict\n  sequencer.c: save and restore cleanup mode\n  cherry-pick/revert: add scissors line on merge conflict\n\n Documentation/git-cherry-pick.txt |   7 ++\n Documentation/git-revert.txt      |   7 ++\n Documentation/merge-options.txt   |   7 ++\n builtin/commit.c                  |  49 +++++-------\n builtin/merge.c                   |  51 +++++++++---\n builtin/pull.c                    |  12 +++\n builtin/revert.c                  |   7 ++\n builtin/tag.c                     |   3 +-\n parse-options.h                   |   1 +\n sequencer.c                       |  73 +++++++++++++++--\n sequencer.h                       |   9 ++-\n t/t3507-cherry-pick-conflict.sh   | 128 +++++++++++++++++++++++++-----\n t/t5521-pull-options.sh           |   8 ++\n t/t7502-commit-porcelain.sh       |  93 ++++++++++++----------\n t/t7600-merge.sh                  |  73 +++++++++++++----\n t/t7604-merge-custom-message.sh   |  75 +++++++++++++++--\n wt-status.c                       |  12 ++-\n wt-status.h                       |   1 +\n 18 files changed, 484 insertions(+), 132 deletions(-)\n\nRange-diff to pu\n 1:  32caff17f2 !  1:  e660dff207 t7600: clean up style\n    @@ -13,7 +13,6 @@\n         Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n         Helped-by: Junio C Hamano <gitster@pobox.com>\n         Signed-off-by: Denton Liu <liu.denton@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n     \n      diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\n      --- a/t/t7600-merge.sh\n 2:  9af8efc5c2 !  2:  86ae21aff9 t3507: clean up style\n    @@ -8,7 +8,6 @@\n     \n         Helped-by: SZEDER Gábor <szeder.dev@gmail.com>\n         Signed-off-by: Denton Liu <liu.denton@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n     \n      diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh\n      --- a/t/t3507-cherry-pick-conflict.sh\n 3:  7d4e0b5f24 !  3:  6f3e0fcd3b t7604: clean up style\n    @@ -11,7 +11,6 @@\n         Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n         Helped-by: SZEDER Gábor <szeder.dev@gmail.com>\n         Signed-off-by: Denton Liu <liu.denton@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n     \n      diff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh\n      --- a/t/t7604-merge-custom-message.sh\n 4:  6743bfce4b !  4:  fa41595854 t7502: clean up style\n    @@ -10,7 +10,6 @@\n         Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n         Helped-by: SZEDER Gábor <szeder.dev@gmail.com>\n         Signed-off-by: Denton Liu <liu.denton@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n     \n      diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh\n      --- a/t/t7502-commit-porcelain.sh\n 5:  9e699bcc45 !  5:  3e87ab3176 commit: extract cleanup_mode functions to sequencer\n    @@ -3,7 +3,6 @@\n         commit: extract cleanup_mode functions to sequencer\n     \n         Signed-off-by: Denton Liu <liu.denton@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n     \n      diff --git a/builtin/commit.c b/builtin/commit.c\n      --- a/builtin/commit.c\n 6:  4753ea288a !  6:  d042d82f4f parse-options.h: extract common --cleanup option\n    @@ -7,7 +7,6 @@\n     \n         Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n         Signed-off-by: Denton Liu <liu.denton@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n     \n      diff --git a/builtin/commit.c b/builtin/commit.c\n      --- a/builtin/commit.c\n 7:  f2feeefc50 <  -:  ---------- sequencer.c: remove duplicate code\n 8:  b4ad22cdb6 !  7:  c4821d47fc merge: cleanup messages like commit\n    @@ -6,18 +6,17 @@\n         commit.cleanup configuration or --cleanup option, just like how\n         git-commit does it.\n     \n    -    We also give git-pull the passthrough option of --cleanup so that it can\n    -    also take advantage of this change.\n    +    We also give git-pull the option of --cleanup so that it can also take\n    +    advantage of this change.\n     \n         Finally, add testing to ensure that messages are properly cleaned up.\n         Note that some newlines that were added to the commit message were\n         removed so that if a file were read via -F, it would be copied\n         faithfully.\n     \n    -    Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n         Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n    +    Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n         Signed-off-by: Denton Liu <liu.denton@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n     \n      diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\n      --- a/Documentation/merge-options.txt\n    @@ -99,10 +98,10 @@\n     -\t\tstrbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);\n     +\tif (0 < option_edit) {\n     +\t\tstrbuf_addch(&msg, '\\n');\n    -+\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n    ++\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {\n     +\t\t\twt_status_append_cut_line(&msg);\n    -+\n    -+\t\tstrbuf_commented_addf(&msg, \"\\n\");\n    ++\t\t\tstrbuf_commented_addf(&msg, \"\\n\");\n    ++\t\t}\n     +\t\tstrbuf_commented_addf(&msg, _(merge_editor_comment));\n     +\t\tstrbuf_commented_addf(&msg, _(cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS ?\n     +\t\t\tscissors_editor_comment :\n    @@ -129,11 +128,24 @@\n      \tif (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,\n      \t\t\t&result_commit, NULL, sign_commit))\n     @@\n    - \tif (option_edit < 0)\n    - \t\toption_edit = default_edit_option();\n    + \t}\n    + \tresolve_undo_clear();\n      \n    -+\tcleanup_mode = get_cleanup_mode(cleanup_arg, 0 < option_edit, 1);\n    ++\tif (option_edit < 0)\n    ++\t\toption_edit = default_edit_option();\n     +\n    ++\tcleanup_mode = get_cleanup_mode(cleanup_arg, 0 < option_edit);\n    ++\n    + \tif (verbosity < 0)\n    + \t\tshow_diffstat = 0;\n    + \n    +@@\n    + \t\t\tfast_forward = FF_NO;\n    + \t}\n    + \n    +-\tif (option_edit < 0)\n    +-\t\toption_edit = default_edit_option();\n    +-\n      \tif (!use_strategies) {\n      \t\tif (!remoteheads)\n      \t\t\t; /* already up-to-date */\n    @@ -153,48 +165,37 @@\n      static char *opt_squash;\n      static char *opt_commit;\n      static char *opt_edit;\n    -+static char *opt_cleanup;\n    ++static char *cleanup_arg;\n      static char *opt_ff;\n      static char *opt_verify_signatures;\n      static int opt_autostash = -1;\n     @@\n      \tOPT_PASSTHRU(0, \"edit\", &opt_edit, NULL,\n      \t\tN_(\"edit message before committing\"),\n      \t\tPARSE_OPT_NOARG),\n    -+\tOPT_PASSTHRU(0, \"cleanup\", &opt_cleanup, NULL,\n    -+\t\tN_(\"how to strip spaces and #comments from message\"),\n    -+\t\t0),\n    ++\tOPT_CLEANUP(&cleanup_arg),\n      \tOPT_PASSTHRU(0, \"ff\", &opt_ff, NULL,\n      \t\tN_(\"allow fast-forward\"),\n      \t\tPARSE_OPT_NOARG),\n     @@\n      \t\targv_array_push(&args, opt_commit);\n      \tif (opt_edit)\n      \t\targv_array_push(&args, opt_edit);\n    -+\tif (opt_cleanup)\n    -+\t\targv_array_push(&args, opt_cleanup);\n    ++\tif (cleanup_arg)\n    ++\t\targv_array_pushf(&args, \"--cleanup=%s\", cleanup_arg);\n      \tif (opt_ff)\n      \t\targv_array_push(&args, opt_ff);\n      \tif (opt_verify_signatures)\n     @@\n      \n      \targc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);\n      \n    -+\tif (opt_cleanup) {\n    -+\t\tconst char *prefix = \"--cleanup=\";\n    -+\t\tconst char *cleanup_arg;\n    -+\n    -+\t\tif (strncmp(opt_cleanup, prefix, strlen(prefix)))\n    -+\t\t\tBUG(\"expecting prefix %s, argument is %s\", prefix, opt_cleanup);\n    -+\n    -+\t\tcleanup_arg = &opt_cleanup[strlen(prefix)];\n    -+\n    ++\tif (cleanup_arg)\n     +\t\t/*\n     +\t\t * this only checks the validity of cleanup_arg; we don't need\n     +\t\t * a valid value for use_editor\n     +\t\t */\n    -+\t\tget_cleanup_mode(cleanup_arg, 0, 1);\n    -+\t}\n    ++\t\tget_cleanup_mode(cleanup_arg, 0);\n     +\n      \tparse_repo_refspecs(argc, argv, &repo, &refspecs);\n      \n 9:  7bd6d4370f !  8:  4ad58797e4 merge: add scissors line on merge conflict\n    @@ -11,7 +11,6 @@\n     \n         Helped-by: Eric Sunshine <sunshine@sunshineco.com>\n         Signed-off-by: Denton Liu <liu.denton@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n     \n      diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\n      --- a/Documentation/merge-options.txt\n    @@ -61,7 +60,9 @@\n      \t\t\tdie_errno(_(\"could not read MERGE_MSG\"));\n     +\n     +\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n    -+\t\t    wt_status_locate_end(sb.buf + merge_msg_start, sb.len - merge_msg_start) < sb.len - merge_msg_start)\n    ++\t\t    wt_status_locate_end(sb.buf + merge_msg_start,\n    ++\t\t\t\t\t sb.len - merge_msg_start) <\n    ++\t\t\t\tsb.len - merge_msg_start)\n     +\t\t\tmerge_contains_scissors = 1;\n      \t} else if (!stat(git_path_squash_msg(the_repository), &statbuf)) {\n      \t\tif (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)\n    @@ -103,10 +104,10 @@\n     +\t * We can't use cleanup_mode because if we're not using the editor,\n     +\t * get_cleanup_mode will return COMMIT_MSG_CLEANUP_SPACE instead, even\n     +\t * though the message is meant to be processed later by git-commit.\n    -+\t * Thus, we will get the cleanup mode which is returned when we _are_ using\n    -+\t * an editor.\n    ++\t * Thus, we will get the cleanup mode which is returned when we _are_\n    ++\t * using an editor.\n     +\t */\n    -+\tif (get_cleanup_mode(cleanup_arg, 1, 1) == COMMIT_MSG_CLEANUP_SCISSORS) {\n    ++\tif (get_cleanup_mode(cleanup_arg, 1) == COMMIT_MSG_CLEANUP_SCISSORS) {\n     +\t    fputc('\\n', fp);\n     +\t    wt_status_add_cut_line(fp);\n     +\t    /* comments out the newline from append_conflicts_hint */\n10:  8805d809d4 <  -:  ---------- sequencer.c: define describe_cleanup_mode\n -:  ---------- >  9:  b3136d4fae sequencer.c: save and restore cleanup mode\n11:  b86bb747c9 ! 10:  582f842b9e cherry-pick/revert: add scissors line on merge conflict\n    @@ -8,7 +8,6 @@\n     \n         Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n         Signed-off-by: Denton Liu <liu.denton@gmail.com>\n    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>\n     \n      diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt\n      --- a/Documentation/git-cherry-pick.txt\n    @@ -50,38 +49,23 @@\n      --- a/builtin/merge.c\n      +++ b/builtin/merge.c\n     @@\n    - \t * Thus, we will get the cleanup mode which is returned when we _are_ using\n    - \t * an editor.\n    + \t * Thus, we will get the cleanup mode which is returned when we _are_\n    + \t * using an editor.\n      \t */\n    --\tif (get_cleanup_mode(cleanup_arg, 1, 1) == COMMIT_MSG_CLEANUP_SCISSORS) {\n    +-\tif (get_cleanup_mode(cleanup_arg, 1) == COMMIT_MSG_CLEANUP_SCISSORS) {\n     -\t    fputc('\\n', fp);\n     -\t    wt_status_add_cut_line(fp);\n     -\t    /* comments out the newline from append_conflicts_hint */\n     -\t    fputc(comment_line_char, fp);\n     -\t}\n     -\n     -\tappend_conflicts_hint(&the_index, &msgbuf);\n    -+\tappend_conflicts_hint(&the_index, &msgbuf, get_cleanup_mode(cleanup_arg, 1, 1));\n    ++\tappend_conflicts_hint(&the_index, &msgbuf,\n    ++\t\t\t      get_cleanup_mode(cleanup_arg, 1));\n      \tfputs(msgbuf.buf, fp);\n      \tstrbuf_release(&msgbuf);\n      \tfclose(fp);\n     \n    - diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\n    - --- a/builtin/rebase--interactive.c\n    - +++ b/builtin/rebase--interactive.c\n    -@@\n    - \t\tOPT_END()\n    - \t};\n    - \n    -+\topts.action = REPLAY_INTERACTIVE_REBASE;\n    - \tsequencer_init_config(&opts);\n    - \tgit_config_get_bool(\"rebase.abbreviatecommands\", &abbreviate_commands);\n    - \n    --\topts.action = REPLAY_INTERACTIVE_REBASE;\n    - \topts.allow_ff = 1;\n    - \topts.allow_empty = 1;\n    - \n    -\n      diff --git a/builtin/revert.c b/builtin/revert.c\n      --- a/builtin/revert.c\n      +++ b/builtin/revert.c\n    @@ -103,8 +87,10 @@\n      \tif (opts->keep_redundant_commits)\n      \t\topts->allow_empty = 1;\n      \n    -+\tif (cleanup_arg)\n    -+\t\topts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1, 1);\n    ++\tif (cleanup_arg) {\n    ++\t\topts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);\n    ++\t\topts->explicit_cleanup = 1;\n    ++\t}\n     +\n      \t/* Check for incompatible command line arguments */\n      \tif (cmd) {\n    @@ -114,44 +100,20 @@\n      --- a/sequencer.c\n      +++ b/sequencer.c\n     @@\n    - \t{ NULL, 0, 0 }\n    - };\n    - \n    -+static inline int is_rebase_i(const struct replay_opts *opts)\n    -+{\n    -+\treturn opts->action == REPLAY_INTERACTIVE_REBASE;\n    -+}\n    -+\n    - static int git_sequencer_config(const char *k, const char *v, void *cb)\n    - {\n    - \tstruct replay_opts *opts = cb;\n    -@@\n    - \t\tif (status)\n    - \t\t\treturn status;\n    - \n    --\t\topts->default_msg_cleanup = get_cleanup_mode(s, 0, 0);\n    -+\t\topts->default_msg_cleanup = get_cleanup_mode(s, !is_rebase_i(opts), 0);\n    - \n    - \t\tfree((char *)s);\n    - \t\treturn status;\n    -@@\n    - \tgit_config(git_sequencer_config, opts);\n    - }\n    - \n    --static inline int is_rebase_i(const struct replay_opts *opts)\n    --{\n    --\treturn opts->action == REPLAY_INTERACTIVE_REBASE;\n    --}\n    --\n    - static const char *get_dir(const struct replay_opts *opts)\n    - {\n    - \tif (is_rebase_i(opts))\n    + \t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;\n    + \t\t\topts->explicit_cleanup = 1;\n    + \t\t} else if (!strcmp(s, \"scissors\")) {\n    +-\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;\n    ++\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SCISSORS;\n    + \t\t\topts->explicit_cleanup = 1;\n    + \t\t} else {\n    + \t\t\twarning(_(\"invalid commit message cleanup mode '%s'\"),\n     @@\n      }\n      \n      void append_conflicts_hint(struct index_state *istate,\n     -\t\t\t   struct strbuf *msgbuf)\n    -+\t\tstruct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode)\n    ++\tstruct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode)\n      {\n      \tint i;\n      \n    @@ -169,10 +131,28 @@\n      \n      \tif (!clean)\n     -\t\tappend_conflicts_hint(r->index, msgbuf);\n    -+\t\tappend_conflicts_hint(r->index, msgbuf, opts->default_msg_cleanup);\n    ++\t\tappend_conflicts_hint(r->index, msgbuf,\n    ++\t\t\t\t      opts->default_msg_cleanup);\n      \n      \treturn !clean;\n      }\n    +@@\n    + \t\t\t  unsigned int flags)\n    + {\n    + \tstruct child_process cmd = CHILD_PROCESS_INIT;\n    +-\tconst char *value;\n    + \n    + \tif ((flags & CREATE_ROOT_COMMIT) && !(flags & AMEND_MSG)) {\n    + \t\tstruct strbuf msg = STRBUF_INIT, script = STRBUF_INIT;\n    +@@\n    + \t\targv_array_push(&cmd.args, \"-e\");\n    + \telse if (!(flags & CLEANUP_MSG) &&\n    + \t\t !opts->signoff && !opts->record_origin &&\n    +-\t\t git_config_get_value(\"commit.cleanup\", &value))\n    ++\t\t !opts->explicit_cleanup)\n    + \t\targv_array_push(&cmd.args, \"--cleanup=verbatim\");\n    + \n    + \tif ((flags & ALLOW_EMPTY))\n     \n      diff --git a/sequencer.h b/sequencer.h\n      --- a/sequencer.h\n    @@ -185,7 +165,7 @@\n     +void append_conflicts_hint(struct index_state *istate,\n     +\t\tstruct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode);\n      enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n    - \tint use_editor, int die_on_error);\n    + \tint use_editor);\n      \n     \n      diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh\n\n\n-- \n2.21.0\n\n"},{"id":"374020","messageId":"20190417102330.24434-5-phillip.wood123@gmail.com","threadId":"49830","inReplyTo":"20190417102330.24434-1-phillip.wood123@gmail.com","subject":"[PATCH v10 04/10] t7502: clean up style","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-04-17T10:23:24Z","receivedAt":"2019-04-17T10:25:01Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Denton Liu <liu.denton@gmail.com>\n\nRefactor out Git commands that were upstream of a pipe. Remove spaces\nafter \"> \". Indent here-docs appropriately. Convert echo chains to use\nthe test_write_lines function. Refactor 'sign off' test to use test_cmp\ninstead of comparing variables.\n\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nHelped-by: SZEDER Gábor <szeder.dev@gmail.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t7502-commit-porcelain.sh | 93 +++++++++++++++++++++----------------\n 1 file changed, 53 insertions(+), 40 deletions(-)\n\ndiff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh\nindex ca4a740da0..5733d9cd34 100755\n--- a/t/t7502-commit-porcelain.sh\n+++ b/t/t7502-commit-porcelain.sh\n@@ -16,7 +16,8 @@ commit_msg_is () {\n # Arguments: [<prefix] [<commit message>] [<commit options>]\n check_summary_oneline() {\n \ttest_tick &&\n-\tgit commit ${3+\"$3\"} -m \"$2\" | head -1 > act &&\n+\tgit commit ${3+\"$3\"} -m \"$2\" >raw &&\n+\thead -n 1 raw >act &&\n \n \t# branch name\n \tSUMMARY_PREFIX=\"$(git name-rev --name-only HEAD)\" &&\n@@ -68,7 +69,7 @@ test_expect_success 'output summary format for merges' '\n \tgit checkout recursive-a &&\n \ttest_must_fail git merge recursive-b &&\n \t# resolve the conflict\n-\techo commit-a > file1 &&\n+\techo commit-a >file1 &&\n \tgit add file1 &&\n \tcheck_summary_oneline \"\" \"Merge\"\n '\n@@ -142,9 +143,11 @@ test_expect_success 'sign off' '\n \t>positive &&\n \tgit add positive &&\n \tgit commit -s -m \"thank you\" &&\n-\tactual=$(git cat-file commit HEAD | sed -ne \"s/Signed-off-by: //p\") &&\n-\texpected=$(git var GIT_COMMITTER_IDENT | sed -e \"s/>.*/>/\") &&\n-\ttest \"z$actual\" = \"z$expected\"\n+\tgit cat-file commit HEAD >commit.msg &&\n+\tsed -ne \"s/Signed-off-by: //p\" commit.msg >actual &&\n+\tgit var GIT_COMMITTER_IDENT >ident &&\n+\tsed -e \"s/>.*/>/\" ident >expected &&\n+\ttest_cmp expected actual\n \n '\n \n@@ -153,8 +156,8 @@ test_expect_success 'multiple -m' '\n \t>negative &&\n \tgit add negative &&\n \tgit commit -m \"one\" -m \"two\" -m \"three\" &&\n-\tactual=$(git cat-file commit HEAD | sed -e \"1,/^\\$/d\") &&\n-\texpected=$(echo one; echo; echo two; echo; echo three) &&\n+\tactual=$(git cat-file commit HEAD >tmp && sed -e \"1,/^\\$/d\" tmp && rm tmp) &&\n+\texpected=$(test_write_lines \"one\" \"\" \"two\" \"\" \"three\") &&\n \ttest \"z$actual\" = \"z$expected\"\n \n '\n@@ -163,7 +166,8 @@ test_expect_success 'verbose' '\n \n \techo minus >negative &&\n \tgit add negative &&\n-\tgit status -v | sed -ne \"/^diff --git /p\" >actual &&\n+\tgit status -v >raw &&\n+\tsed -ne \"/^diff --git /p\" raw >actual &&\n \techo \"diff --git a/negative b/negative\" >expect &&\n \ttest_cmp expect actual\n \n@@ -189,7 +193,8 @@ test_expect_success 'cleanup commit messages (verbatim option,-t)' '\n \n \techo >>negative &&\n \tgit commit --cleanup=verbatim --no-status -t expect -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\" >actual &&\n+\tgit cat-file -p HEAD >raw &&\n+\tsed -e \"1,/^\\$/d\" raw >actual &&\n \ttest_cmp expect actual\n \n '\n@@ -198,7 +203,8 @@ test_expect_success 'cleanup commit messages (verbatim option,-F)' '\n \n \techo >>negative &&\n \tgit commit --cleanup=verbatim -F expect -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >raw &&\n+\tsed -e \"1,/^\\$/d\" raw >actual &&\n \ttest_cmp expect actual\n \n '\n@@ -207,75 +213,80 @@ test_expect_success 'cleanup commit messages (verbatim option,-m)' '\n \n \techo >>negative &&\n \tgit commit --cleanup=verbatim -m \"$mesg_with_comment_and_newlines\" -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >raw &&\n+\tsed -e \"1,/^\\$/d\" raw >actual &&\n \ttest_cmp expect actual\n \n '\n \n test_expect_success 'cleanup commit messages (whitespace option,-F)' '\n \n \techo >>negative &&\n-\t{ echo;echo \"# text\";echo; } >text &&\n+\ttest_write_lines \"\" \"# text\" \"\" >text &&\n \techo \"# text\" >expect &&\n \tgit commit --cleanup=whitespace -F text -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >raw &&\n+\tsed -e \"1,/^\\$/d\" raw >actual &&\n \ttest_cmp expect actual\n \n '\n \n test_expect_success 'cleanup commit messages (scissors option,-F,-e)' '\n \n \techo >>negative &&\n-\tcat >text <<EOF &&\n+\tcat >text <<-\\EOF &&\n \n-# to be kept\n+\t# to be kept\n \n-  # ------------------------ >8 ------------------------\n-# to be kept, too\n-# ------------------------ >8 ------------------------\n-to be removed\n-# ------------------------ >8 ------------------------\n-to be removed, too\n-EOF\n+\t  # ------------------------ >8 ------------------------\n+\t# to be kept, too\n+\t# ------------------------ >8 ------------------------\n+\tto be removed\n+\t# ------------------------ >8 ------------------------\n+\tto be removed, too\n+\tEOF\n \n-\tcat >expect <<EOF &&\n-# to be kept\n+\tcat >expect <<-\\EOF &&\n+\t# to be kept\n \n-  # ------------------------ >8 ------------------------\n-# to be kept, too\n-EOF\n+\t  # ------------------------ >8 ------------------------\n+\t# to be kept, too\n+\tEOF\n \tgit commit --cleanup=scissors -e -F text -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >raw &&\n+\tsed -e \"1,/^\\$/d\" raw >actual &&\n \ttest_cmp expect actual\n '\n \n test_expect_success 'cleanup commit messages (scissors option,-F,-e, scissors on first line)' '\n \n \techo >>negative &&\n-\tcat >text <<EOF &&\n-# ------------------------ >8 ------------------------\n-to be removed\n-EOF\n+\tcat >text <<-\\EOF &&\n+\t# ------------------------ >8 ------------------------\n+\tto be removed\n+\tEOF\n \tgit commit --cleanup=scissors -e -F text -a --allow-empty-message &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >raw &&\n+\tsed -e \"1,/^\\$/d\" raw >actual &&\n \ttest_must_be_empty actual\n '\n \n test_expect_success 'cleanup commit messages (strip option,-F)' '\n \n \techo >>negative &&\n-\t{ echo;echo \"# text\";echo sample;echo; } >text &&\n+\ttest_write_lines \"\" \"# text\" \"sample\" \"\" >text &&\n \techo sample >expect &&\n \tgit commit --cleanup=strip -F text -a &&\n-\tgit cat-file -p HEAD |sed -e \"1,/^\\$/d\">actual &&\n+\tgit cat-file -p HEAD >raw &&\n+\tsed -e \"1,/^\\$/d\" raw >actual &&\n \ttest_cmp expect actual\n \n '\n \n test_expect_success 'cleanup commit messages (strip option,-F,-e)' '\n \n \techo >>negative &&\n-\t{ echo;echo sample;echo; } >text &&\n+\ttest_write_lines \"\" \"sample\" \"\" >text &&\n \tgit commit -e -F text -a &&\n \thead -n 4 .git/COMMIT_EDITMSG >actual\n '\n@@ -387,7 +398,7 @@ test_expect_success AUTOIDENT 'message shows committer when it is automatic' '\n '\n \n write_script .git/FAKE_EDITOR <<EOF\n-echo editor started > \"$(pwd)/.git/result\"\n+echo editor started >\"$(pwd)/.git/result\"\n exit 0\n EOF\n \n@@ -455,7 +466,7 @@ EOF\n test_expect_success EXECKEEPSPID 'a SIGTERM should break locks' '\n \techo >>negative &&\n \t! \"$SHELL_PATH\" -c '\\''\n-\t  echo kill -TERM $$ >> .git/FAKE_EDITOR\n+\t  echo kill -TERM $$ >>.git/FAKE_EDITOR\n \t  GIT_EDITOR=.git/FAKE_EDITOR\n \t  export GIT_EDITOR\n \t  exec git commit -a'\\'' &&\n@@ -471,7 +482,8 @@ test_expect_success 'Hand committing of a redundant merge removes dups' '\n \ttest_must_fail git merge second master &&\n \tgit checkout master g &&\n \tEDITOR=: git commit -a &&\n-\tgit cat-file commit HEAD | sed -n -e \"s/^parent //p\" -e \"/^$/q\" >actual &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -n -e \"s/^parent //p\" -e \"/^$/q\" raw >actual &&\n \ttest_cmp expect actual\n \n '\n@@ -480,7 +492,8 @@ test_expect_success 'A single-liner subject with a token plus colon is not a foo\n \n \tgit reset --hard &&\n \tgit commit -s -m \"hello: kitty\" --allow-empty &&\n-\tgit cat-file commit HEAD | sed -e \"1,/^$/d\" >actual &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e \"1,/^$/d\" raw >actual &&\n \ttest_line_count = 3 actual\n \n '\n-- \n2.21.0\n\n"},{"id":"374021","messageId":"20190417102330.24434-4-phillip.wood123@gmail.com","threadId":"49830","inReplyTo":"20190417102330.24434-1-phillip.wood123@gmail.com","subject":"[PATCH v10 03/10] t7604: clean up style","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-04-17T10:23:23Z","receivedAt":"2019-04-17T10:25:02Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Denton Liu <liu.denton@gmail.com>\n\nBefore, we had some Git commands which were upstream of the pipe. This\nmeant that if it produced an error, it would've gone unnoticed. Refactor\nto place Git commands on their own.\n\nAlso, while we're at it, remove spaces after redirection operators.\n\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nHelped-by: SZEDER Gábor <szeder.dev@gmail.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t7604-merge-custom-message.sh | 12 +++++++-----\n 1 file changed, 7 insertions(+), 5 deletions(-)\n\ndiff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh\nindex 89619cf446..aba1279132 100755\n--- a/t/t7604-merge-custom-message.sh\n+++ b/t/t7604-merge-custom-message.sh\n@@ -16,16 +16,16 @@ create_merge_msgs() {\n }\n \n test_expect_success 'setup' '\n-\techo c0 > c0.c &&\n+\techo c0 >c0.c &&\n \tgit add c0.c &&\n \tgit commit -m c0 &&\n \tgit tag c0 &&\n-\techo c1 > c1.c &&\n+\techo c1 >c1.c &&\n \tgit add c1.c &&\n \tgit commit -m c1 &&\n \tgit tag c1 &&\n \tgit reset --hard c0 &&\n-\techo c2 > c2.c &&\n+\techo c2 >c2.c &&\n \tgit add c2.c &&\n \tgit commit -m c2 &&\n \tgit tag c2 &&\n@@ -36,14 +36,16 @@ test_expect_success 'setup' '\n test_expect_success 'merge c2 with a custom message' '\n \tgit reset --hard c1 &&\n \tgit merge -m \"$(cat exp.subject)\" c2 &&\n-\tgit cat-file commit HEAD | sed -e \"1,/^$/d\" >actual &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e \"1,/^$/d\" raw >actual &&\n \ttest_cmp exp.subject actual\n '\n \n test_expect_success 'merge --log appends to custom message' '\n \tgit reset --hard c1 &&\n \tgit merge --log -m \"$(cat exp.subject)\" c2 &&\n-\tgit cat-file commit HEAD | sed -e \"1,/^$/d\" >actual &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e \"1,/^$/d\" raw >actual &&\n \ttest_cmp exp.log actual\n '\n \n-- \n2.21.0\n\n"},{"id":"374022","messageId":"20190417102330.24434-3-phillip.wood123@gmail.com","threadId":"49830","inReplyTo":"20190417102330.24434-1-phillip.wood123@gmail.com","subject":"[PATCH v10 02/10] t3507: clean up style","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-04-17T10:23:22Z","receivedAt":"2019-04-17T10:25:03Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Denton Liu <liu.denton@gmail.com>\n\nRemove space after redirection operators for style. Also, remove a git\ncommand which was upstream of a pipe. Finally, let grep and sed open\ntheir own input instead of letting the shell redirect the input.\n\nHelped-by: SZEDER Gábor <szeder.dev@gmail.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t3507-cherry-pick-conflict.sh | 42 ++++++++++++++++-----------------\n 1 file changed, 21 insertions(+), 21 deletions(-)\n\ndiff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh\nindex 0db166152a..777fb23d18 100755\n--- a/t/t3507-cherry-pick-conflict.sh\n+++ b/t/t3507-cherry-pick-conflict.sh\n@@ -88,15 +88,15 @@ test_expect_success 'cherry-pick --no-commit does not set CHERRY_PICK_HEAD' '\n \n test_expect_success 'cherry-pick w/dirty tree does not set CHERRY_PICK_HEAD' '\n \tpristine_detach initial &&\n-\techo foo > foo &&\n+\techo foo >foo &&\n \ttest_must_fail git cherry-pick base &&\n \ttest_must_fail git rev-parse --verify CHERRY_PICK_HEAD\n '\n \n test_expect_success \\\n \t'cherry-pick --strategy=resolve w/dirty tree does not set CHERRY_PICK_HEAD' '\n \tpristine_detach initial &&\n-\techo foo > foo &&\n+\techo foo >foo &&\n \ttest_must_fail git cherry-pick --strategy=resolve base &&\n \ttest_must_fail git rev-parse --verify CHERRY_PICK_HEAD\n '\n@@ -175,23 +175,23 @@ test_expect_success 'failed cherry-pick registers participants in index' '\n \t\tgit ls-files --stage foo &&\n \t\tgit checkout picked -- foo &&\n \t\tgit ls-files --stage foo\n-\t} > stages &&\n+\t} >stages &&\n \tsed \"\n \t\t1 s/ 0\t/ 1\t/\n \t\t2 s/ 0\t/ 2\t/\n \t\t3 s/ 0\t/ 3\t/\n-\t\" < stages > expected &&\n+\t\" stages >expected &&\n \tgit read-tree -u --reset HEAD &&\n \n \ttest_must_fail git cherry-pick picked &&\n-\tgit ls-files --stage --unmerged > actual &&\n+\tgit ls-files --stage --unmerged >actual &&\n \n \ttest_cmp expected actual\n '\n \n test_expect_success 'failed cherry-pick describes conflict in work tree' '\n \tpristine_detach initial &&\n-\tcat <<-EOF > expected &&\n+\tcat <<-EOF >expected &&\n \t<<<<<<< HEAD\n \ta\n \t=======\n@@ -201,14 +201,14 @@ test_expect_success 'failed cherry-pick describes conflict in work tree' '\n \n \ttest_must_fail git cherry-pick picked &&\n \n-\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo > actual &&\n+\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo >actual &&\n \ttest_cmp expected actual\n '\n \n test_expect_success 'diff3 -m style' '\n \tpristine_detach initial &&\n \tgit config merge.conflictstyle diff3 &&\n-\tcat <<-EOF > expected &&\n+\tcat <<-EOF >expected &&\n \t<<<<<<< HEAD\n \ta\n \t||||||| parent of objid picked\n@@ -220,14 +220,14 @@ test_expect_success 'diff3 -m style' '\n \n \ttest_must_fail git cherry-pick picked &&\n \n-\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo > actual &&\n+\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo >actual &&\n \ttest_cmp expected actual\n '\n \n test_expect_success 'revert also handles conflicts sanely' '\n \tgit config --unset merge.conflictstyle &&\n \tpristine_detach initial &&\n-\tcat <<-EOF > expected &&\n+\tcat <<-EOF >expected &&\n \t<<<<<<< HEAD\n \ta\n \t=======\n@@ -241,24 +241,24 @@ test_expect_success 'revert also handles conflicts sanely' '\n \t\tgit ls-files --stage foo &&\n \t\tgit checkout base -- foo &&\n \t\tgit ls-files --stage foo\n-\t} > stages &&\n+\t} >stages &&\n \tsed \"\n \t\t1 s/ 0\t/ 1\t/\n \t\t2 s/ 0\t/ 2\t/\n \t\t3 s/ 0\t/ 3\t/\n-\t\" < stages > expected-stages &&\n+\t\" stages >expected-stages &&\n \tgit read-tree -u --reset HEAD &&\n \n \thead=$(git rev-parse HEAD) &&\n \ttest_must_fail git revert picked &&\n \tnewhead=$(git rev-parse HEAD) &&\n-\tgit ls-files --stage --unmerged > actual-stages &&\n+\tgit ls-files --stage --unmerged >actual-stages &&\n \n \ttest \"$head\" = \"$newhead\" &&\n \ttest_must_fail git update-index --refresh -q &&\n \ttest_must_fail git diff-index --exit-code HEAD &&\n \ttest_cmp expected-stages actual-stages &&\n-\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo > actual &&\n+\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -284,7 +284,7 @@ test_expect_success 'revert --no-commit sets REVERT_HEAD' '\n \n test_expect_success 'revert w/dirty tree does not set REVERT_HEAD' '\n \tpristine_detach base &&\n-\techo foo > foo &&\n+\techo foo >foo &&\n \ttest_must_fail git revert base &&\n \ttest_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&\n \ttest_must_fail git rev-parse --verify REVERT_HEAD\n@@ -319,7 +319,7 @@ test_expect_success 'failed commit does not clear REVERT_HEAD' '\n test_expect_success 'revert conflict, diff3 -m style' '\n \tpristine_detach initial &&\n \tgit config merge.conflictstyle diff3 &&\n-\tcat <<-EOF > expected &&\n+\tcat <<-EOF >expected &&\n \t<<<<<<< HEAD\n \ta\n \t||||||| objid picked\n@@ -331,7 +331,7 @@ test_expect_success 'revert conflict, diff3 -m style' '\n \n \ttest_must_fail git revert picked &&\n \n-\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo > actual &&\n+\tsed \"s/[a-f0-9]*\\.\\.\\./objid/\" foo >actual &&\n \ttest_cmp expected actual\n '\n \n@@ -345,7 +345,7 @@ test_expect_success 'commit after failed cherry-pick does not add duplicated -s'\n \tpristine_detach initial &&\n \ttest_must_fail git cherry-pick -s picked-signed &&\n \tgit commit -a -s &&\n-\ttest $(git show -s |grep -c \"Signed-off-by\") = 1\n+\ttest $(git show -s >tmp && grep -c \"Signed-off-by\" tmp && rm tmp) = 1\n '\n \n test_expect_success 'commit after failed cherry-pick adds -s at the right place' '\n@@ -359,7 +359,7 @@ test_expect_success 'commit after failed cherry-pick adds -s at the right place'\n \tSigned-off-by: C O Mitter <committer@example.com>\n \t# Conflicts:\n \tEOF\n-\tgrep -e \"^# Conflicts:\" -e '^Signed-off-by' <.git/COMMIT_EDITMSG >actual &&\n+\tgrep -e \"^# Conflicts:\" -e '^Signed-off-by' .git/COMMIT_EDITMSG >actual &&\n \ttest_cmp expect actual &&\n \n \tcat <<-\\EOF >expected &&\n@@ -378,7 +378,7 @@ test_expect_success 'commit --amend -s places the sign-off at the right place' '\n \n \t# emulate old-style conflicts block\n \tmv .git/MERGE_MSG .git/MERGE_MSG+ &&\n-\tsed -e \"/^# Conflicts:/,\\$s/^# *//\" <.git/MERGE_MSG+ >.git/MERGE_MSG &&\n+\tsed -e \"/^# Conflicts:/,\\$s/^# *//\" .git/MERGE_MSG+ >.git/MERGE_MSG &&\n \n \tgit commit -a &&\n \tgit commit --amend -s &&\n@@ -388,7 +388,7 @@ test_expect_success 'commit --amend -s places the sign-off at the right place' '\n \tSigned-off-by: C O Mitter <committer@example.com>\n \tConflicts:\n \tEOF\n-\tgrep -e \"^Conflicts:\" -e '^Signed-off-by' <.git/COMMIT_EDITMSG >actual &&\n+\tgrep -e \"^Conflicts:\" -e '^Signed-off-by' .git/COMMIT_EDITMSG >actual &&\n \ttest_cmp expect actual\n '\n \n-- \n2.21.0\n\n"},{"id":"374023","messageId":"20190417102330.24434-6-phillip.wood123@gmail.com","threadId":"49830","inReplyTo":"20190417102330.24434-1-phillip.wood123@gmail.com","subject":"[PATCH v10 05/10] commit: extract cleanup_mode functions to sequencer","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-04-17T10:23:25Z","receivedAt":"2019-04-17T10:25:07Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Denton Liu <liu.denton@gmail.com>\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/commit.c | 25 ++-----------------------\n sequencer.c      | 29 +++++++++++++++++++++++++++++\n sequencer.h      |  6 ++++++\n 3 files changed, 37 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex f17537474a..0df15e4851 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1172,24 +1172,7 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t\tdie(_(\"Only one of --include/--only/--all/--interactive/--patch can be used.\"));\n \tif (argc == 0 && (also || (only && !amend && !allow_empty)))\n \t\tdie(_(\"No paths with --include/--only does not make sense.\"));\n-\tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n-\t\tcleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_ALL :\n-\t\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n-\telse if (!strcmp(cleanup_arg, \"verbatim\"))\n-\t\tcleanup_mode = COMMIT_MSG_CLEANUP_NONE;\n-\telse if (!strcmp(cleanup_arg, \"whitespace\"))\n-\t\tcleanup_mode = COMMIT_MSG_CLEANUP_SPACE;\n-\telse if (!strcmp(cleanup_arg, \"strip\"))\n-\t\tcleanup_mode = COMMIT_MSG_CLEANUP_ALL;\n-\telse if (!strcmp(cleanup_arg, \"scissors\"))\n-\t\tcleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n-\t\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n-\t/*\n-\t * Please update _git_commit() in git-completion.bash when you\n-\t * add new options.\n-\t */\n-\telse\n-\t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n+\tcleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);\n \n \thandle_untracked_files_arg(s);\n \n@@ -1626,11 +1609,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"could not read commit message: %s\"), strerror(saved_errno));\n \t}\n \n-\tif (verbose || /* Truncate the message just before the diff, if any. */\n-\t    cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n-\t\tstrbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));\n-\tif (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)\n-\t\tstrbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);\n+\tcleanup_message(&sb, cleanup_mode, verbose);\n \n \tif (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {\n \t\trollback_index_files();\ndiff --git a/sequencer.c b/sequencer.c\nindex 87079ece5d..b049951c34 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -516,6 +516,25 @@ static int fast_forward_to(struct repository *r,\n \treturn 0;\n }\n \n+enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n+\tint use_editor)\n+{\n+\tif (!cleanup_arg || !strcmp(cleanup_arg, \"default\"))\n+\t\treturn use_editor ? COMMIT_MSG_CLEANUP_ALL :\n+\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n+\telse if (!strcmp(cleanup_arg, \"verbatim\"))\n+\t\treturn COMMIT_MSG_CLEANUP_NONE;\n+\telse if (!strcmp(cleanup_arg, \"whitespace\"))\n+\t\treturn COMMIT_MSG_CLEANUP_SPACE;\n+\telse if (!strcmp(cleanup_arg, \"strip\"))\n+\t\treturn COMMIT_MSG_CLEANUP_ALL;\n+\telse if (!strcmp(cleanup_arg, \"scissors\"))\n+\t\treturn use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :\n+\t\t\t\t    COMMIT_MSG_CLEANUP_SPACE;\n+\telse\n+\t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n+}\n+\n void append_conflicts_hint(struct index_state *istate,\n \t\t\t   struct strbuf *msgbuf)\n {\n@@ -1018,6 +1037,16 @@ static int rest_is_empty(const struct strbuf *sb, int start)\n \treturn 1;\n }\n \n+void cleanup_message(struct strbuf *msgbuf,\n+\tenum commit_msg_cleanup_mode cleanup_mode, int verbose)\n+{\n+\tif (verbose || /* Truncate the message just before the diff, if any. */\n+\t    cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n+\t\tstrbuf_setlen(msgbuf, wt_status_locate_end(msgbuf->buf, msgbuf->len));\n+\tif (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)\n+\t\tstrbuf_stripspace(msgbuf, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);\n+}\n+\n /*\n  * Find out if the message in the strbuf contains only whitespace and\n  * Signed-off-by lines.\ndiff --git a/sequencer.h b/sequencer.h\nindex 82bc7a48d5..8295c8406f 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -117,6 +117,12 @@ int rearrange_squash(struct repository *r);\n void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);\n \n void append_conflicts_hint(struct index_state *istate, struct strbuf *msgbuf);\n+enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n+\tint use_editor);\n+\n+void cleanup_message(struct strbuf *msgbuf,\n+\tenum commit_msg_cleanup_mode cleanup_mode, int verbose);\n+\n int message_is_empty(const struct strbuf *sb,\n \t\t     enum commit_msg_cleanup_mode cleanup_mode);\n int template_untouched(const struct strbuf *sb, const char *template_file,\n-- \n2.21.0\n\n"},{"id":"374024","messageId":"20190417102330.24434-7-phillip.wood123@gmail.com","threadId":"49830","inReplyTo":"20190417102330.24434-1-phillip.wood123@gmail.com","subject":"[PATCH v10 06/10] parse-options.h: extract common --cleanup option","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-04-17T10:23:26Z","receivedAt":"2019-04-17T10:25:08Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Denton Liu <liu.denton@gmail.com>\n\nThe --cleanup option is commonly used. Extract it so that its definition\nis not repeated.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/commit.c | 2 +-\n builtin/tag.c    | 3 +--\n parse-options.h  | 1 +\n 3 files changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 0df15e4851..8277da8474 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1473,7 +1473,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL('s', \"signoff\", &signoff, N_(\"add Signed-off-by:\")),\n \t\tOPT_FILENAME('t', \"template\", &template_file, N_(\"use specified template file\")),\n \t\tOPT_BOOL('e', \"edit\", &edit_flag, N_(\"force edit of commit\")),\n-\t\tOPT_STRING(0, \"cleanup\", &cleanup_arg, N_(\"default\"), N_(\"how to strip spaces and #comments from message\")),\n+\t\tOPT_CLEANUP(&cleanup_arg),\n \t\tOPT_BOOL(0, \"status\", &include_status, N_(\"include status in commit message template\")),\n \t\t{ OPTION_STRING, 'S', \"gpg-sign\", &sign_commit, N_(\"key-id\"),\n \t\t  N_(\"GPG sign commit\"), PARSE_OPT_OPTARG, NULL, (intptr_t) \"\" },\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 02f6bd1279..a3870fbdba 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -397,8 +397,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tOPT_FILENAME('F', \"file\", &msgfile, N_(\"read message from file\")),\n \t\tOPT_BOOL('e', \"edit\", &edit_flag, N_(\"force edit of tag message\")),\n \t\tOPT_BOOL('s', \"sign\", &opt.sign, N_(\"annotated and GPG-signed tag\")),\n-\t\tOPT_STRING(0, \"cleanup\", &cleanup_arg, N_(\"mode\"),\n-\t\t\tN_(\"how to strip spaces and #comments from message\")),\n+\t\tOPT_CLEANUP(&cleanup_arg),\n \t\tOPT_STRING('u', \"local-user\", &keyid, N_(\"key-id\"),\n \t\t\t\t\tN_(\"use another key to sign the tag\")),\n \t\tOPT__FORCE(&force, N_(\"replace the tag if exists\"), 0),\ndiff --git a/parse-options.h b/parse-options.h\nindex 74cce4e7fc..b93169337f 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -314,5 +314,6 @@ int parse_opt_passthru_argv(const struct option *, const char *, int);\n #define OPT_NO_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH(\"no-contains\", v, h, PARSE_OPT_NONEG)\n #define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH(\"with\", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)\n #define OPT_WITHOUT(v, h) _OPT_CONTAINS_OR_WITH(\"without\", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)\n+#define OPT_CLEANUP(v) OPT_STRING(0, \"cleanup\", v, N_(\"mode\"), N_(\"how to strip spaces and #comments from message\"))\n \n #endif\n-- \n2.21.0\n\n"},{"id":"374025","messageId":"20190417102330.24434-8-phillip.wood123@gmail.com","threadId":"49830","inReplyTo":"20190417102330.24434-1-phillip.wood123@gmail.com","subject":"[PATCH v10 07/10] merge: cleanup messages like commit","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-04-17T10:23:27Z","receivedAt":"2019-04-17T10:25:25Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Denton Liu <liu.denton@gmail.com>\n\nThis change allows git-merge messages to be cleaned up with the\ncommit.cleanup configuration or --cleanup option, just like how\ngit-commit does it.\n\nWe also give git-pull the option of --cleanup so that it can also take\nadvantage of this change.\n\nFinally, add testing to ensure that messages are properly cleaned up.\nNote that some newlines that were added to the commit message were\nremoved so that if a file were read via -F, it would be copied\nfaithfully.\n\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/merge-options.txt |  4 +++\n builtin/merge.c                 | 41 +++++++++++++++------\n builtin/pull.c                  | 12 +++++++\n t/t5521-pull-options.sh         |  8 +++++\n t/t7604-merge-custom-message.sh | 63 +++++++++++++++++++++++++++++++++\n wt-status.c                     | 12 +++++--\n wt-status.h                     |  1 +\n 7 files changed, 128 insertions(+), 13 deletions(-)\n\ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 92a7d936c1..bcf0b3e49c 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -32,6 +32,10 @@ they run `git merge`. To make it easier to adjust such scripts to the\n updated behaviour, the environment variable `GIT_MERGE_AUTOEDIT` can be\n set to `no` at the beginning of them.\n \n+--cleanup=<mode>::\n+\tThis option determines how the merge message will be cleaned up\n+\tbefore commiting. See linkgit:git-commit[1] for more details.\n+\n --ff::\n \tWhen the merge resolves as a fast-forward, only update the branch\n \tpointer, without creating a merge commit.  This is the default\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 5ce8946d39..e1f7b7342d 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -38,6 +38,7 @@\n #include \"tag.h\"\n #include \"alias.h\"\n #include \"commit-reach.h\"\n+#include \"wt-status.h\"\n \n #define DEFAULT_TWOHEAD (1<<0)\n #define DEFAULT_OCTOPUS (1<<1)\n@@ -98,6 +99,9 @@ enum ff_type {\n \n static enum ff_type fast_forward = FF_ALLOW;\n \n+static const char *cleanup_arg;\n+static enum commit_msg_cleanup_mode cleanup_mode;\n+\n static int option_parse_message(const struct option *opt,\n \t\t\t\tconst char *arg, int unset)\n {\n@@ -249,6 +253,7 @@ static struct option builtin_merge_options[] = {\n \t\tN_(\"perform a commit if the merge succeeds (default)\")),\n \tOPT_BOOL('e', \"edit\", &option_edit,\n \t\tN_(\"edit message before committing\")),\n+\tOPT_CLEANUP(&cleanup_arg),\n \tOPT_SET_INT(0, \"ff\", &fast_forward, N_(\"allow fast-forward (default)\"), FF_ALLOW),\n \tOPT_SET_INT_F(0, \"ff-only\", &fast_forward,\n \t\t      N_(\"abort if fast-forward is not possible\"),\n@@ -612,6 +617,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n \t\treturn git_config_string(&pull_twohead, k, v);\n \telse if (!strcmp(k, \"pull.octopus\"))\n \t\treturn git_config_string(&pull_octopus, k, v);\n+\telse if (!strcmp(k, \"commit.cleanup\"))\n+\t\treturn git_config_string(&cleanup_arg, k, v);\n \telse if (!strcmp(k, \"merge.renormalize\"))\n \t\toption_renormalize = git_config_bool(k, v);\n \telse if (!strcmp(k, \"merge.ff\")) {\n@@ -800,20 +807,33 @@ static void abort_commit(struct commit_list *remoteheads, const char *err_msg)\n static const char merge_editor_comment[] =\n N_(\"Please enter a commit message to explain why this merge is necessary,\\n\"\n    \"especially if it merges an updated upstream into a topic branch.\\n\"\n-   \"\\n\"\n-   \"Lines starting with '%c' will be ignored, and an empty message aborts\\n\"\n+   \"\\n\");\n+\n+static const char scissors_editor_comment[] =\n+N_(\"An empty message aborts the commit.\\n\");\n+\n+static const char no_scissors_editor_comment[] =\n+N_(\"Lines starting with '%c' will be ignored, and an empty message aborts\\n\"\n    \"the commit.\\n\");\n \n static void write_merge_heads(struct commit_list *);\n static void prepare_to_commit(struct commit_list *remoteheads)\n {\n \tstruct strbuf msg = STRBUF_INIT;\n \tstrbuf_addbuf(&msg, &merge_msg);\n-\tstrbuf_addch(&msg, '\\n');\n \tif (squash)\n \t\tBUG(\"the control must not reach here under --squash\");\n-\tif (0 < option_edit)\n-\t\tstrbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);\n+\tif (0 < option_edit) {\n+\t\tstrbuf_addch(&msg, '\\n');\n+\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {\n+\t\t\twt_status_append_cut_line(&msg);\n+\t\t\tstrbuf_commented_addf(&msg, \"\\n\");\n+\t\t}\n+\t\tstrbuf_commented_addf(&msg, _(merge_editor_comment));\n+\t\tstrbuf_commented_addf(&msg, _(cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS ?\n+\t\t\tscissors_editor_comment :\n+\t\t\tno_scissors_editor_comment), comment_line_char);\n+\t}\n \tif (signoff)\n \t\tappend_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);\n \twrite_merge_heads(remoteheads);\n@@ -832,7 +852,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)\n \t\tabort_commit(remoteheads, NULL);\n \n \tread_merge_msg(&msg);\n-\tstrbuf_stripspace(&msg, 0 < option_edit);\n+\tcleanup_message(&msg, cleanup_mode, 0);\n \tif (!msg.len)\n \t\tabort_commit(remoteheads, _(\"Empty commit message.\"));\n \tstrbuf_release(&merge_msg);\n@@ -880,7 +900,6 @@ static int finish_automerge(struct commit *head,\n \tparents = remoteheads;\n \tif (!head_subsumed || fast_forward == FF_NO)\n \t\tcommit_list_insert(head, &parents);\n-\tstrbuf_addch(&merge_msg, '\\n');\n \tprepare_to_commit(remoteheads);\n \tif (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,\n \t\t\t&result_commit, NULL, sign_commit))\n@@ -1301,6 +1320,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \t}\n \tresolve_undo_clear();\n \n+\tif (option_edit < 0)\n+\t\toption_edit = default_edit_option();\n+\n+\tcleanup_mode = get_cleanup_mode(cleanup_arg, 0 < option_edit);\n+\n \tif (verbosity < 0)\n \t\tshow_diffstat = 0;\n \n@@ -1386,9 +1410,6 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \t\t\tfast_forward = FF_NO;\n \t}\n \n-\tif (option_edit < 0)\n-\t\toption_edit = default_edit_option();\n-\n \tif (!use_strategies) {\n \t\tif (!remoteheads)\n \t\t\t; /* already up-to-date */\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 33db889955..5bf82b4b27 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -24,6 +24,7 @@\n #include \"lockfile.h\"\n #include \"wt-status.h\"\n #include \"commit-reach.h\"\n+#include \"sequencer.h\"\n \n enum rebase_type {\n \tREBASE_INVALID = -1,\n@@ -101,6 +102,7 @@ static char *opt_signoff;\n static char *opt_squash;\n static char *opt_commit;\n static char *opt_edit;\n+static char *cleanup_arg;\n static char *opt_ff;\n static char *opt_verify_signatures;\n static int opt_autostash = -1;\n@@ -168,6 +170,7 @@ static struct option pull_options[] = {\n \tOPT_PASSTHRU(0, \"edit\", &opt_edit, NULL,\n \t\tN_(\"edit message before committing\"),\n \t\tPARSE_OPT_NOARG),\n+\tOPT_CLEANUP(&cleanup_arg),\n \tOPT_PASSTHRU(0, \"ff\", &opt_ff, NULL,\n \t\tN_(\"allow fast-forward\"),\n \t\tPARSE_OPT_NOARG),\n@@ -644,6 +647,8 @@ static int run_merge(void)\n \t\targv_array_push(&args, opt_commit);\n \tif (opt_edit)\n \t\targv_array_push(&args, opt_edit);\n+\tif (cleanup_arg)\n+\t\targv_array_pushf(&args, \"--cleanup=%s\", cleanup_arg);\n \tif (opt_ff)\n \t\targv_array_push(&args, opt_ff);\n \tif (opt_verify_signatures)\n@@ -875,6 +880,13 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);\n \n+\tif (cleanup_arg)\n+\t\t/*\n+\t\t * this only checks the validity of cleanup_arg; we don't need\n+\t\t * a valid value for use_editor\n+\t\t */\n+\t\tget_cleanup_mode(cleanup_arg, 0);\n+\n \tparse_repo_refspecs(argc, argv, &repo, &refspecs);\n \n \tif (!opt_ff)\ndiff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh\nindex c19d8dbc9d..ccde8ba491 100755\n--- a/t/t5521-pull-options.sh\n+++ b/t/t5521-pull-options.sh\n@@ -77,6 +77,14 @@ test_expect_success 'git pull -q -v' '\n \ttest_must_be_empty out &&\n \ttest -s err)\n '\n+test_expect_success 'git pull --cleanup errors early on invalid argument' '\n+\tmkdir clonedcleanup &&\n+\t(cd clonedcleanup && git init &&\n+\ttest_must_fail git pull --cleanup invalid \"../parent\" >out 2>err &&\n+\ttest_must_be_empty out &&\n+\ttest -s err)\n+'\n+\n \n test_expect_success 'git pull --force' '\n \tmkdir clonedoldstyle &&\ndiff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh\nindex aba1279132..cd4f9607dc 100755\n--- a/t/t7604-merge-custom-message.sh\n+++ b/t/t7604-merge-custom-message.sh\n@@ -49,4 +49,67 @@ test_expect_success 'merge --log appends to custom message' '\n \ttest_cmp exp.log actual\n '\n \n+mesg_with_comment_and_newlines='\n+# text\n+\n+'\n+\n+test_expect_success 'prepare file with comment line and trailing newlines'  '\n+\tprintf \"%s\" \"$mesg_with_comment_and_newlines\" >expect\n+'\n+\n+test_expect_success 'cleanup commit messages (verbatim option)' '\n+\tgit reset --hard c1 &&\n+\tgit merge --cleanup=verbatim -F expect c2 &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e \"1,/^$/d\" raw >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'cleanup commit messages (whitespace option)' '\n+\tgit reset --hard c1 &&\n+\ttest_write_lines \"\" \"# text\" \"\" >text &&\n+\techo \"# text\" >expect &&\n+\tgit merge --cleanup=whitespace -F text c2 &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e \"1,/^$/d\" raw >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'cleanup merge messages (scissors option)' '\n+\tgit reset --hard c1 &&\n+\tcat >text <<-\\EOF &&\n+\n+\t# to be kept\n+\n+\t  # ------------------------ >8 ------------------------\n+\t# to be kept, too\n+\t# ------------------------ >8 ------------------------\n+\tto be removed\n+\t# ------------------------ >8 ------------------------\n+\tto be removed, too\n+\tEOF\n+\n+\tcat >expect <<-\\EOF &&\n+\t# to be kept\n+\n+\t  # ------------------------ >8 ------------------------\n+\t# to be kept, too\n+\tEOF\n+\tgit merge --cleanup=scissors -e -F text c2 &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e \"1,/^$/d\" raw >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'cleanup commit messages (strip option)' '\n+\tgit reset --hard c1 &&\n+\ttest_write_lines \"\" \"# text\" \"sample\" \"\" >text &&\n+\techo sample >expect &&\n+\tgit merge --cleanup=strip -F text c2 &&\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e \"1,/^$/d\" raw >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\ndiff --git a/wt-status.c b/wt-status.c\nindex 445a36204a..b81fcd428d 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1006,13 +1006,19 @@ size_t wt_status_locate_end(const char *s, size_t len)\n \treturn len;\n }\n \n-void wt_status_add_cut_line(FILE *fp)\n+void wt_status_append_cut_line(struct strbuf *buf)\n {\n \tconst char *explanation = _(\"Do not modify or remove the line above.\\nEverything below it will be ignored.\");\n+\n+\tstrbuf_commented_addf(buf, \"%s\", cut_line);\n+\tstrbuf_add_commented_lines(buf, explanation, strlen(explanation));\n+}\n+\n+void wt_status_add_cut_line(FILE *fp)\n+{\n \tstruct strbuf buf = STRBUF_INIT;\n \n-\tfprintf(fp, \"%c %s\", comment_line_char, cut_line);\n-\tstrbuf_add_commented_lines(&buf, explanation, strlen(explanation));\n+\twt_status_append_cut_line(&buf);\n \tfputs(buf.buf, fp);\n \tstrbuf_release(&buf);\n }\ndiff --git a/wt-status.h b/wt-status.h\nindex 3a95975032..64f1ddc9fd 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -129,6 +129,7 @@ struct wt_status {\n };\n \n size_t wt_status_locate_end(const char *s, size_t len);\n+void wt_status_append_cut_line(struct strbuf *buf);\n void wt_status_add_cut_line(FILE *fp);\n void wt_status_prepare(struct repository *r, struct wt_status *s);\n void wt_status_print(struct wt_status *s);\n-- \n2.21.0\n\n"},{"id":"374026","messageId":"20190417102330.24434-9-phillip.wood123@gmail.com","threadId":"49830","inReplyTo":"20190417102330.24434-1-phillip.wood123@gmail.com","subject":"[PATCH v10 08/10] merge: add scissors line on merge conflict","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-04-17T10:23:28Z","receivedAt":"2019-04-17T10:25:27Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Denton Liu <liu.denton@gmail.com>\n\nThis fixes a bug where the scissors line is placed after the Conflicts:\nsection, in the case where a merge conflict occurs and\ncommit.cleanup = scissors.\n\nNext, if commit.cleanup = scissors is specified, don't produce a\nscissors line in commit if one already exists in the MERGE_MSG file.\n\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/merge-options.txt |  7 +++--\n builtin/commit.c                | 22 ++++++++++++----\n builtin/merge.c                 | 14 ++++++++++\n t/t7600-merge.sh                | 46 +++++++++++++++++++++++++++++++++\n 4 files changed, 82 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex bcf0b3e49c..61876dbc33 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -33,8 +33,11 @@ updated behaviour, the environment variable `GIT_MERGE_AUTOEDIT` can be\n set to `no` at the beginning of them.\n \n --cleanup=<mode>::\n-\tThis option determines how the merge message will be cleaned up\n-\tbefore commiting. See linkgit:git-commit[1] for more details.\n+\tThis option determines how the merge message will be cleaned up before\n+\tcommiting. See linkgit:git-commit[1] for more details. In addition, if\n+\tthe '<mode>' is given a value of `scissors`, scissors will be appended\n+\tto `MERGE_MSG` before being passed on to the commit machinery in the\n+\tcase of a merge conflict.\n \n --ff::\n \tWhen the merge resolves as a fast-forward, only update the branch\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 8277da8474..5401a60cbe 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -668,6 +668,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \tconst char *hook_arg2 = NULL;\n \tint clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);\n \tint old_display_comment_prefix;\n+\tint merge_contains_scissors = 0;\n \n \t/* This checks and barfs if author is badly specified */\n \tdetermine_author_info(author_ident);\n@@ -728,6 +729,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\tstrbuf_addbuf(&sb, &message);\n \t\thook_arg1 = \"message\";\n \t} else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {\n+\t\tsize_t merge_msg_start;\n+\n \t\t/*\n \t\t * prepend SQUASH_MSG here if it exists and a\n \t\t * \"merge --squash\" was originally performed\n@@ -738,8 +741,16 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\thook_arg1 = \"squash\";\n \t\t} else\n \t\t\thook_arg1 = \"merge\";\n+\n+\t\tmerge_msg_start = sb.len;\n \t\tif (strbuf_read_file(&sb, git_path_merge_msg(the_repository), 0) < 0)\n \t\t\tdie_errno(_(\"could not read MERGE_MSG\"));\n+\n+\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\t    wt_status_locate_end(sb.buf + merge_msg_start,\n+\t\t\t\t\t sb.len - merge_msg_start) <\n+\t\t\t\tsb.len - merge_msg_start)\n+\t\t\tmerge_contains_scissors = 1;\n \t} else if (!stat(git_path_squash_msg(the_repository), &statbuf)) {\n \t\tif (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)\n \t\t\tdie_errno(_(\"could not read SQUASH_MSG\"));\n@@ -807,7 +818,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\tstruct ident_split ci, ai;\n \n \t\tif (whence != FROM_COMMIT) {\n-\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)\n+\t\t\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n+\t\t\t\t!merge_contains_scissors)\n \t\t\t\twt_status_add_cut_line(s->fp);\n \t\t\tstatus_printf_ln(s, GIT_COLOR_NORMAL,\n \t\t\t    whence == FROM_MERGE\n@@ -832,10 +844,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\nwith '%c' will be ignored, and an empty\"\n \t\t\t\t  \" message aborts the commit.\\n\"), comment_line_char);\n-\t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&\n-\t\t\t whence == FROM_COMMIT)\n-\t\t\twt_status_add_cut_line(s->fp);\n-\t\telse /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n+\t\telse if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {\n+\t\t\tif (whence == FROM_COMMIT && !merge_contains_scissors)\n+\t\t\t\twt_status_add_cut_line(s->fp);\n+\t\t} else /* COMMIT_MSG_CLEANUP_SPACE, that is. */\n \t\t\tstatus_printf(s, GIT_COLOR_NORMAL,\n \t\t\t\t_(\"Please enter the commit message for your changes.\"\n \t\t\t\t  \" Lines starting\\n\"\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex e1f7b7342d..03b9f7230a 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -920,6 +920,20 @@ static int suggest_conflicts(void)\n \tfilename = git_path_merge_msg(the_repository);\n \tfp = xfopen(filename, \"a\");\n \n+\t/*\n+\t * We can't use cleanup_mode because if we're not using the editor,\n+\t * get_cleanup_mode will return COMMIT_MSG_CLEANUP_SPACE instead, even\n+\t * though the message is meant to be processed later by git-commit.\n+\t * Thus, we will get the cleanup mode which is returned when we _are_\n+\t * using an editor.\n+\t */\n+\tif (get_cleanup_mode(cleanup_arg, 1) == COMMIT_MSG_CLEANUP_SCISSORS) {\n+\t    fputc('\\n', fp);\n+\t    wt_status_add_cut_line(fp);\n+\t    /* comments out the newline from append_conflicts_hint */\n+\t    fputc(comment_line_char, fp);\n+\t}\n+\n \tappend_conflicts_hint(&the_index, &msgbuf);\n \tfputs(msgbuf.buf, fp);\n \tstrbuf_release(&msgbuf);\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex 2f4c2801fb..7f9c68cbe7 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -246,6 +246,52 @@ test_expect_success 'merge --squash c3 with c7' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'merge c3 with c7 with commit.cleanup = scissors' '\n+\tgit config commit.cleanup scissors &&\n+\tgit reset --hard c3 &&\n+\ttest_must_fail git merge c7 &&\n+\tcat result.9z >file &&\n+\tgit commit --no-edit -a &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tMerge tag '\"'\"'c7'\"'\"'\n+\n+\t# ------------------------ >8 ------------------------\n+\t# Do not modify or remove the line above.\n+\t# Everything below it will be ignored.\n+\t#\n+\t# Conflicts:\n+\t#\tfile\n+\tEOF\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e '1,/^$/d' raw >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n+test_expect_success 'merge c3 with c7 with --squash commit.cleanup = scissors' '\n+\tgit config commit.cleanup scissors &&\n+\tgit reset --hard c3 &&\n+\ttest_must_fail git merge --squash c7 &&\n+\tcat result.9z >file &&\n+\tgit commit --no-edit -a &&\n+\n+\tcat >expect <<-EOF &&\n+\tSquashed commit of the following:\n+\n+\t$(git show -s c7)\n+\n+\t# ------------------------ >8 ------------------------\n+\t# Do not modify or remove the line above.\n+\t# Everything below it will be ignored.\n+\t#\n+\t# Conflicts:\n+\t#\tfile\n+\tEOF\n+\tgit cat-file commit HEAD >raw &&\n+\tsed -e '1,/^$/d' raw >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n test_debug 'git log --graph --decorate --oneline --all'\n \n test_expect_success 'merge c1 with c2 and c3' '\n-- \n2.21.0\n\n"},{"id":"374027","messageId":"20190417102330.24434-10-phillip.wood123@gmail.com","threadId":"49830","inReplyTo":"20190417102330.24434-1-phillip.wood123@gmail.com","subject":"[PATCH v10 09/10] sequencer.c: save and restore cleanup mode","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-04-17T10:23:29Z","receivedAt":"2019-04-17T10:25:27Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Denton Liu <liu.denton@gmail.com>\n\nIf the user specifies an explicit cleanup mode then save and restore it\nso that it is preserved by 'git cherry-pick --continue'.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n sequencer.c | 28 +++++++++++++++++++++++++++-\n 1 file changed, 27 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex b049951c34..3f4b0896e3 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -535,6 +535,24 @@ enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n \t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n }\n \n+/*\n+ * NB using int rather than enum cleanup_mode to stop clang's\n+ * -Wtautological-constant-out-of-range-compare complaining that the comparison\n+ * is always true.\n+ */\n+static const char *describe_cleanup_mode(int cleanup_mode)\n+{\n+\tstatic const char *modes[] = { \"whitespace\",\n+\t\t\t\t       \"verbatim\",\n+\t\t\t\t       \"scissors\",\n+\t\t\t\t       \"strip\" };\n+\n+\tif (cleanup_mode < ARRAY_SIZE(modes))\n+\t\treturn modes[cleanup_mode];\n+\n+\tBUG(\"invalid cleanup_mode provided (%d)\", cleanup_mode);\n+}\n+\n void append_conflicts_hint(struct index_state *istate,\n \t\t\t   struct strbuf *msgbuf)\n {\n@@ -2366,7 +2384,10 @@ static int populate_opts_cb(const char *key, const char *value, void *data)\n \t\topts->allow_rerere_auto =\n \t\t\tgit_config_bool_or_int(key, value, &error_flag) ?\n \t\t\t\tRERERE_AUTOUPDATE : RERERE_NOAUTOUPDATE;\n-\telse\n+\telse if (!strcmp(key, \"options.default-msg-cleanup\")) {\n+\t\topts->explicit_cleanup = 1;\n+\t\topts->default_msg_cleanup = get_cleanup_mode(value, 1);\n+\t} else\n \t\treturn error(_(\"invalid key: %s\"), key);\n \n \tif (!error_flag)\n@@ -2770,6 +2791,11 @@ static int save_opts(struct replay_opts *opts)\n \t\tres |= git_config_set_in_file_gently(opts_file, \"options.allow-rerere-auto\",\n \t\t\t\t\t\t     opts->allow_rerere_auto == RERERE_AUTOUPDATE ?\n \t\t\t\t\t\t     \"true\" : \"false\");\n+\n+\tif (opts->explicit_cleanup)\n+\t\tres |= git_config_set_in_file_gently(opts_file,\n+\t\t\t\t\"options.default-msg-cleanup\",\n+\t\t\t\tdescribe_cleanup_mode(opts->default_msg_cleanup));\n \treturn res;\n }\n \n-- \n2.21.0\n\n"},{"id":"374028","messageId":"20190417102330.24434-11-phillip.wood123@gmail.com","threadId":"49830","inReplyTo":"20190417102330.24434-1-phillip.wood123@gmail.com","subject":"[PATCH v10 10/10] cherry-pick/revert: add scissors line on merge conflict","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-04-17T10:23:30Z","receivedAt":"2019-04-17T10:25:30Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Denton Liu <liu.denton@gmail.com>\n\nFix a bug where the scissors line is placed after the Conflicts:\nsection, in the case where a merge conflict occurs and\ncommit.cleanup = scissors.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/git-cherry-pick.txt |  7 +++\n Documentation/git-revert.txt      |  7 +++\n builtin/merge.c                   | 10 +---\n builtin/revert.c                  |  7 +++\n sequencer.c                       | 16 ++++--\n sequencer.h                       |  3 +-\n t/t3507-cherry-pick-conflict.sh   | 86 +++++++++++++++++++++++++++++++\n 7 files changed, 122 insertions(+), 14 deletions(-)\n\ndiff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt\nindex b8cfeec67e..cbb663a843 100644\n--- a/Documentation/git-cherry-pick.txt\n+++ b/Documentation/git-cherry-pick.txt\n@@ -57,6 +57,13 @@ OPTIONS\n \tWith this option, 'git cherry-pick' will let you edit the commit\n \tmessage prior to committing.\n \n+--cleanup=<mode>::\n+\tThis option determines how the commit message will be cleaned up before\n+\tbeing passed on to the commit machinery. See linkgit:git-commit[1] for more\n+\tdetails. In particular, if the '<mode>' is given a value of `scissors`,\n+\tscissors will be appended to `MERGE_MSG` before being passed on in the case\n+\tof a conflict.\n+\n -x::\n \tWhen recording the commit, append a line that says\n \t\"(cherry picked from commit ...)\" to the original commit\ndiff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt\nindex 837707a8fd..7fd254df92 100644\n--- a/Documentation/git-revert.txt\n+++ b/Documentation/git-revert.txt\n@@ -66,6 +66,13 @@ more details.\n \tWith this option, 'git revert' will not start the commit\n \tmessage editor.\n \n+--cleanup=<mode>::\n+\tThis option determines how the commit message will be cleaned up before\n+\tbeing passed on to the commit machinery. See linkgit:git-commit[1] for more\n+\tdetails. In particular, if the '<mode>' is given a value of `scissors`,\n+\tscissors will be appended to `MERGE_MSG` before being passed on in the case\n+\tof a conflict.\n+\n -n::\n --no-commit::\n \tUsually the command automatically creates some commits with\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 03b9f7230a..e96f72af80 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -927,14 +927,8 @@ static int suggest_conflicts(void)\n \t * Thus, we will get the cleanup mode which is returned when we _are_\n \t * using an editor.\n \t */\n-\tif (get_cleanup_mode(cleanup_arg, 1) == COMMIT_MSG_CLEANUP_SCISSORS) {\n-\t    fputc('\\n', fp);\n-\t    wt_status_add_cut_line(fp);\n-\t    /* comments out the newline from append_conflicts_hint */\n-\t    fputc(comment_line_char, fp);\n-\t}\n-\n-\tappend_conflicts_hint(&the_index, &msgbuf);\n+\tappend_conflicts_hint(&the_index, &msgbuf,\n+\t\t\t      get_cleanup_mode(cleanup_arg, 1));\n \tfputs(msgbuf.buf, fp);\n \tstrbuf_release(&msgbuf);\n \tfclose(fp);\ndiff --git a/builtin/revert.c b/builtin/revert.c\nindex a47b53ceaf..d4dcedbdc6 100644\n--- a/builtin/revert.c\n+++ b/builtin/revert.c\n@@ -96,11 +96,13 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)\n {\n \tconst char * const * usage_str = revert_or_cherry_pick_usage(opts);\n \tconst char *me = action_name(opts);\n+\tconst char *cleanup_arg = NULL;\n \tint cmd = 0;\n \tstruct option base_options[] = {\n \t\tOPT_CMDMODE(0, \"quit\", &cmd, N_(\"end revert or cherry-pick sequence\"), 'q'),\n \t\tOPT_CMDMODE(0, \"continue\", &cmd, N_(\"resume revert or cherry-pick sequence\"), 'c'),\n \t\tOPT_CMDMODE(0, \"abort\", &cmd, N_(\"cancel revert or cherry-pick sequence\"), 'a'),\n+\t\tOPT_CLEANUP(&cleanup_arg),\n \t\tOPT_BOOL('n', \"no-commit\", &opts->no_commit, N_(\"don't automatically commit\")),\n \t\tOPT_BOOL('e', \"edit\", &opts->edit, N_(\"edit the commit message\")),\n \t\tOPT_NOOP_NOARG('r', NULL),\n@@ -137,6 +139,11 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)\n \tif (opts->keep_redundant_commits)\n \t\topts->allow_empty = 1;\n \n+\tif (cleanup_arg) {\n+\t\topts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);\n+\t\topts->explicit_cleanup = 1;\n+\t}\n+\n \t/* Check for incompatible command line arguments */\n \tif (cmd) {\n \t\tchar *this_operation;\ndiff --git a/sequencer.c b/sequencer.c\nindex 3f4b0896e3..cff07c1907 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -182,7 +182,7 @@ static int git_sequencer_config(const char *k, const char *v, void *cb)\n \t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;\n \t\t\topts->explicit_cleanup = 1;\n \t\t} else if (!strcmp(s, \"scissors\")) {\n-\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;\n+\t\t\topts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SCISSORS;\n \t\t\topts->explicit_cleanup = 1;\n \t\t} else {\n \t\t\twarning(_(\"invalid commit message cleanup mode '%s'\"),\n@@ -554,10 +554,16 @@ static const char *describe_cleanup_mode(int cleanup_mode)\n }\n \n void append_conflicts_hint(struct index_state *istate,\n-\t\t\t   struct strbuf *msgbuf)\n+\tstruct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode)\n {\n \tint i;\n \n+\tif (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {\n+\t\tstrbuf_addch(msgbuf, '\\n');\n+\t\twt_status_append_cut_line(msgbuf);\n+\t\tstrbuf_addch(msgbuf, comment_line_char);\n+\t}\n+\n \tstrbuf_addch(msgbuf, '\\n');\n \tstrbuf_commented_addf(msgbuf, \"Conflicts:\\n\");\n \tfor (i = 0; i < istate->cache_nr;) {\n@@ -625,7 +631,8 @@ static int do_recursive_merge(struct repository *r,\n \t\t\t_(action_name(opts)));\n \n \tif (!clean)\n-\t\tappend_conflicts_hint(r->index, msgbuf);\n+\t\tappend_conflicts_hint(r->index, msgbuf,\n+\t\t\t\t      opts->default_msg_cleanup);\n \n \treturn !clean;\n }\n@@ -944,7 +951,6 @@ static int run_git_commit(struct repository *r,\n \t\t\t  unsigned int flags)\n {\n \tstruct child_process cmd = CHILD_PROCESS_INIT;\n-\tconst char *value;\n \n \tif ((flags & CREATE_ROOT_COMMIT) && !(flags & AMEND_MSG)) {\n \t\tstruct strbuf msg = STRBUF_INIT, script = STRBUF_INIT;\n@@ -1014,7 +1020,7 @@ static int run_git_commit(struct repository *r,\n \t\targv_array_push(&cmd.args, \"-e\");\n \telse if (!(flags & CLEANUP_MSG) &&\n \t\t !opts->signoff && !opts->record_origin &&\n-\t\t git_config_get_value(\"commit.cleanup\", &value))\n+\t\t !opts->explicit_cleanup)\n \t\targv_array_push(&cmd.args, \"--cleanup=verbatim\");\n \n \tif ((flags & ALLOW_EMPTY))\ndiff --git a/sequencer.h b/sequencer.h\nindex 8295c8406f..7023da9b13 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -116,7 +116,8 @@ int rearrange_squash(struct repository *r);\n  */\n void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);\n \n-void append_conflicts_hint(struct index_state *istate, struct strbuf *msgbuf);\n+void append_conflicts_hint(struct index_state *istate,\n+\t\tstruct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode);\n enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n \tint use_editor);\n \ndiff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh\nindex 777fb23d18..a584b11c98 100755\n--- a/t/t3507-cherry-pick-conflict.sh\n+++ b/t/t3507-cherry-pick-conflict.sh\n@@ -189,6 +189,46 @@ test_expect_success 'failed cherry-pick registers participants in index' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success \\\n+\t'cherry-pick conflict, ensure commit.cleanup = scissors places scissors line properly' '\n+\tpristine_detach initial &&\n+\tgit config commit.cleanup scissors &&\n+\tcat <<-EOF >expected &&\n+\t\tpicked\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfoo\n+\t\tEOF\n+\n+\ttest_must_fail git cherry-pick picked &&\n+\n+\ttest_i18ncmp expected .git/MERGE_MSG\n+'\n+\n+test_expect_success \\\n+\t'cherry-pick conflict, ensure cleanup=scissors places scissors line properly' '\n+\tpristine_detach initial &&\n+\tgit config --unset commit.cleanup &&\n+\tcat <<-EOF >expected &&\n+\t\tpicked\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfoo\n+\t\tEOF\n+\n+\ttest_must_fail git cherry-pick --cleanup=scissors picked &&\n+\n+\ttest_i18ncmp expected .git/MERGE_MSG\n+'\n+\n test_expect_success 'failed cherry-pick describes conflict in work tree' '\n \tpristine_detach initial &&\n \tcat <<-EOF >expected &&\n@@ -335,6 +375,52 @@ test_expect_success 'revert conflict, diff3 -m style' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success \\\n+\t'revert conflict, ensure commit.cleanup = scissors places scissors line properly' '\n+\tpristine_detach initial &&\n+\tgit config commit.cleanup scissors &&\n+\tcat >expected <<-EOF &&\n+\t\tRevert \"picked\"\n+\n+\t\tThis reverts commit OBJID.\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfoo\n+\t\tEOF\n+\n+\ttest_must_fail git revert picked &&\n+\n+\tsed \"s/$OID_REGEX/OBJID/\" .git/MERGE_MSG >actual &&\n+\ttest_i18ncmp expected actual\n+'\n+\n+test_expect_success \\\n+\t'revert conflict, ensure cleanup=scissors places scissors line properly' '\n+\tpristine_detach initial &&\n+\tgit config --unset commit.cleanup &&\n+\tcat >expected <<-EOF &&\n+\t\tRevert \"picked\"\n+\n+\t\tThis reverts commit OBJID.\n+\n+\t\t# ------------------------ >8 ------------------------\n+\t\t# Do not modify or remove the line above.\n+\t\t# Everything below it will be ignored.\n+\t\t#\n+\t\t# Conflicts:\n+\t\t#\tfoo\n+\t\tEOF\n+\n+\ttest_must_fail git revert --cleanup=scissors picked &&\n+\n+\tsed \"s/$OID_REGEX/OBJID/\" .git/MERGE_MSG >actual &&\n+\ttest_i18ncmp expected actual\n+'\n+\n test_expect_success 'failed cherry-pick does not forget -s' '\n \tpristine_detach initial &&\n \ttest_must_fail git cherry-pick -s picked &&\n-- \n2.21.0\n\n"},{"id":"374057","messageId":"20190417170247.GA9636@dev-l","threadId":"49830","inReplyTo":"20190417102330.24434-10-phillip.wood123@gmail.com","subject":"Re: [PATCH v10 09/10] sequencer.c: save and restore cleanup mode","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-04-17T17:02:47Z","receivedAt":"2019-04-17T17:02:52Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"On Wed, Apr 17, 2019 at 11:23:29AM +0100, Phillip Wood wrote:\n> From: Denton Liu <liu.denton@gmail.com>\n\nWe should drop this line before applying the patch since Phillip did all\nof the hard work for this patch and he's the primary author.\n\n> \n> If the user specifies an explicit cleanup mode then save and restore it\n> so that it is preserved by 'git cherry-pick --continue'.\n> \n> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> ---\n>  sequencer.c | 28 +++++++++++++++++++++++++++-\n>  1 file changed, 27 insertions(+), 1 deletion(-)\n> \n> diff --git a/sequencer.c b/sequencer.c\n> index b049951c34..3f4b0896e3 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -535,6 +535,24 @@ enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n>  \t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n>  }\n>  \n> +/*\n> + * NB using int rather than enum cleanup_mode to stop clang's\n> + * -Wtautological-constant-out-of-range-compare complaining that the comparison\n> + * is always true.\n> + */\n> +static const char *describe_cleanup_mode(int cleanup_mode)\n> +{\n> +\tstatic const char *modes[] = { \"whitespace\",\n> +\t\t\t\t       \"verbatim\",\n> +\t\t\t\t       \"scissors\",\n> +\t\t\t\t       \"strip\" };\n> +\n> +\tif (cleanup_mode < ARRAY_SIZE(modes))\n> +\t\treturn modes[cleanup_mode];\n> +\n> +\tBUG(\"invalid cleanup_mode provided (%d)\", cleanup_mode);\n> +}\n> +\n>  void append_conflicts_hint(struct index_state *istate,\n>  \t\t\t   struct strbuf *msgbuf)\n>  {\n> @@ -2366,7 +2384,10 @@ static int populate_opts_cb(const char *key, const char *value, void *data)\n>  \t\topts->allow_rerere_auto =\n>  \t\t\tgit_config_bool_or_int(key, value, &error_flag) ?\n>  \t\t\t\tRERERE_AUTOUPDATE : RERERE_NOAUTOUPDATE;\n> -\telse\n> +\telse if (!strcmp(key, \"options.default-msg-cleanup\")) {\n> +\t\topts->explicit_cleanup = 1;\n> +\t\topts->default_msg_cleanup = get_cleanup_mode(value, 1);\n> +\t} else\n>  \t\treturn error(_(\"invalid key: %s\"), key);\n>  \n>  \tif (!error_flag)\n> @@ -2770,6 +2791,11 @@ static int save_opts(struct replay_opts *opts)\n>  \t\tres |= git_config_set_in_file_gently(opts_file, \"options.allow-rerere-auto\",\n>  \t\t\t\t\t\t     opts->allow_rerere_auto == RERERE_AUTOUPDATE ?\n>  \t\t\t\t\t\t     \"true\" : \"false\");\n> +\n> +\tif (opts->explicit_cleanup)\n> +\t\tres |= git_config_set_in_file_gently(opts_file,\n> +\t\t\t\t\"options.default-msg-cleanup\",\n> +\t\t\t\tdescribe_cleanup_mode(opts->default_msg_cleanup));\n>  \treturn res;\n>  }\n>  \n> -- \n> 2.21.0\n> \n"},{"id":"374067","messageId":"ccf75cb1-8afa-6de7-9f1e-cfc21d599ad3@gmail.com","threadId":"49830","inReplyTo":"20190417170247.GA9636@dev-l","subject":"Re: [PATCH v10 09/10] sequencer.c: save and restore cleanup mode","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-04-17T19:53:32Z","receivedAt":"2019-04-17T19:53:37Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 17/04/2019 18:02, Denton Liu wrote:\n> On Wed, Apr 17, 2019 at 11:23:29AM +0100, Phillip Wood wrote:\n>> From: Denton Liu <liu.denton@gmail.com>\n> \n> We should drop this line before applying the patch since Phillip did all\n> of the hard work for this patch and he's the primary author.\n> \n>>\n>> If the user specifies an explicit cleanup mode then save and restore it\n>> so that it is preserved by 'git cherry-pick --continue'.\n\nSorry when I removed your Signed-off-by: as you suggested I forget to \nchange the authorship of the patch\n\nBest Wishes\n\nPhillip\n\n>>\n>> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n>> ---\n>>   sequencer.c | 28 +++++++++++++++++++++++++++-\n>>   1 file changed, 27 insertions(+), 1 deletion(-)\n>>\n>> diff --git a/sequencer.c b/sequencer.c\n>> index b049951c34..3f4b0896e3 100644\n>> --- a/sequencer.c\n>> +++ b/sequencer.c\n>> @@ -535,6 +535,24 @@ enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n>>   \t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n>>   }\n>>   \n>> +/*\n>> + * NB using int rather than enum cleanup_mode to stop clang's\n>> + * -Wtautological-constant-out-of-range-compare complaining that the comparison\n>> + * is always true.\n>> + */\n>> +static const char *describe_cleanup_mode(int cleanup_mode)\n>> +{\n>> +\tstatic const char *modes[] = { \"whitespace\",\n>> +\t\t\t\t       \"verbatim\",\n>> +\t\t\t\t       \"scissors\",\n>> +\t\t\t\t       \"strip\" };\n>> +\n>> +\tif (cleanup_mode < ARRAY_SIZE(modes))\n>> +\t\treturn modes[cleanup_mode];\n>> +\n>> +\tBUG(\"invalid cleanup_mode provided (%d)\", cleanup_mode);\n>> +}\n>> +\n>>   void append_conflicts_hint(struct index_state *istate,\n>>   \t\t\t   struct strbuf *msgbuf)\n>>   {\n>> @@ -2366,7 +2384,10 @@ static int populate_opts_cb(const char *key, const char *value, void *data)\n>>   \t\topts->allow_rerere_auto =\n>>   \t\t\tgit_config_bool_or_int(key, value, &error_flag) ?\n>>   \t\t\t\tRERERE_AUTOUPDATE : RERERE_NOAUTOUPDATE;\n>> -\telse\n>> +\telse if (!strcmp(key, \"options.default-msg-cleanup\")) {\n>> +\t\topts->explicit_cleanup = 1;\n>> +\t\topts->default_msg_cleanup = get_cleanup_mode(value, 1);\n>> +\t} else\n>>   \t\treturn error(_(\"invalid key: %s\"), key);\n>>   \n>>   \tif (!error_flag)\n>> @@ -2770,6 +2791,11 @@ static int save_opts(struct replay_opts *opts)\n>>   \t\tres |= git_config_set_in_file_gently(opts_file, \"options.allow-rerere-auto\",\n>>   \t\t\t\t\t\t     opts->allow_rerere_auto == RERERE_AUTOUPDATE ?\n>>   \t\t\t\t\t\t     \"true\" : \"false\");\n>> +\n>> +\tif (opts->explicit_cleanup)\n>> +\t\tres |= git_config_set_in_file_gently(opts_file,\n>> +\t\t\t\t\"options.default-msg-cleanup\",\n>> +\t\t\t\tdescribe_cleanup_mode(opts->default_msg_cleanup));\n>>   \treturn res;\n>>   }\n>>   \n>> -- \n>> 2.21.0\n>>\n"},{"id":"374087","messageId":"xmqqk1frhovr.fsf@gitster-ct.c.googlers.com","threadId":"49830","inReplyTo":"20190417102330.24434-1-phillip.wood123@gmail.com","subject":"Re: [PATCH v10 00/10] Fix scissors bug during conflict","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-04-18T05:19:04Z","receivedAt":"2019-04-18T05:19:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>\n> With Denton's blessing I've rebased his patches on top of my patch to\n> fix the message cleanup with cherry-pick --signoff and -x [1]. I've\n>\n> [1] https://public-inbox.org/git/20190329110842.30604-1-phillip.wood123@gmail.com/\n\nHmph, I certainly saw that patch, but I do not recall seeing any\nin-depth review of it.  How ready is it?  Was it because it was\ntrivially obvious that we didn't see much activity on the patch?\n"},{"id":"374093","messageId":"c37e44d1-d088-2088-148e-fb1ee022ac41@gmail.com","threadId":"49830","inReplyTo":"xmqqk1frhovr.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v10 00/10] Fix scissors bug during conflict","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-04-18T09:14:32Z","receivedAt":"2019-04-18T09:14:37Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 18/04/2019 06:19, Junio C Hamano wrote:\n> Phillip Wood <phillip.wood123@gmail.com> writes:\n> \n>> From: Phillip Wood <phillip.wood@dunelm.org.uk>\n>>\n>> With Denton's blessing I've rebased his patches on top of my patch to\n>> fix the message cleanup with cherry-pick --signoff and -x [1]. I've\n\nNote that the base is a merge of that patch (which is based on maint) \nwith 041f5ea1cf (\"The third batch\", 2019-03-20)\n\n>> [1] https://public-inbox.org/git/20190329110842.30604-1-phillip.wood123@gmail.com/\n> \n> Hmph, I certainly saw that patch, but I do not recall seeing any\n> in-depth review of it.  How ready is it?  Was it because it was\n> trivially obvious that we didn't see much activity on the patch?\n\nI think it is ready (it's a fairly simple change). Dscho said he was \nhappy with it [1].\n\n[1] \nhttps://public-inbox.org/git/nycvar.QRO.7.76.6.1903291651340.41@tvgsbejvaqbjf.bet/\n\nBest Wishes\n\nPhillip\n"},{"id":"374121","messageId":"20190418172111.GA14273@dev-l","threadId":"49830","inReplyTo":"20190417170247.GA9636@dev-l","subject":"Re: [PATCH v10 09/10] sequencer.c: save and restore cleanup mode","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-04-18T17:21:11Z","receivedAt":"2019-04-18T17:21:16Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi Junio,\n\nOn Wed, Apr 17, 2019 at 10:02:47AM -0700, Denton Liu wrote:\n> On Wed, Apr 17, 2019 at 11:23:29AM +0100, Phillip Wood wrote:\n> > From: Denton Liu <liu.denton@gmail.com>\n> \n> We should drop this line before applying the patch since Phillip did all\n> of the hard work for this patch and he's the primary author.\n\nSorry, I made the mistake of suggesting that this line be dropped. This\nresulted in the patchset being queued with Phillip's gmail instead of\nhis dunelm email. We should probably change the authorship to\nPhillip Wood <phillip.wood@dunelm.org.uk>.\n\nThanks,\n\nDenton\n\n> \n> > \n> > If the user specifies an explicit cleanup mode then save and restore it\n> > so that it is preserved by 'git cherry-pick --continue'.\n> > \n> > Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n> > ---\n> >  sequencer.c | 28 +++++++++++++++++++++++++++-\n> >  1 file changed, 27 insertions(+), 1 deletion(-)\n> > \n> > diff --git a/sequencer.c b/sequencer.c\n> > index b049951c34..3f4b0896e3 100644\n> > --- a/sequencer.c\n> > +++ b/sequencer.c\n> > @@ -535,6 +535,24 @@ enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,\n> >  \t\tdie(_(\"Invalid cleanup mode %s\"), cleanup_arg);\n> >  }\n> >  \n> > +/*\n> > + * NB using int rather than enum cleanup_mode to stop clang's\n> > + * -Wtautological-constant-out-of-range-compare complaining that the comparison\n> > + * is always true.\n> > + */\n> > +static const char *describe_cleanup_mode(int cleanup_mode)\n> > +{\n> > +\tstatic const char *modes[] = { \"whitespace\",\n> > +\t\t\t\t       \"verbatim\",\n> > +\t\t\t\t       \"scissors\",\n> > +\t\t\t\t       \"strip\" };\n> > +\n> > +\tif (cleanup_mode < ARRAY_SIZE(modes))\n> > +\t\treturn modes[cleanup_mode];\n> > +\n> > +\tBUG(\"invalid cleanup_mode provided (%d)\", cleanup_mode);\n> > +}\n> > +\n> >  void append_conflicts_hint(struct index_state *istate,\n> >  \t\t\t   struct strbuf *msgbuf)\n> >  {\n> > @@ -2366,7 +2384,10 @@ static int populate_opts_cb(const char *key, const char *value, void *data)\n> >  \t\topts->allow_rerere_auto =\n> >  \t\t\tgit_config_bool_or_int(key, value, &error_flag) ?\n> >  \t\t\t\tRERERE_AUTOUPDATE : RERERE_NOAUTOUPDATE;\n> > -\telse\n> > +\telse if (!strcmp(key, \"options.default-msg-cleanup\")) {\n> > +\t\topts->explicit_cleanup = 1;\n> > +\t\topts->default_msg_cleanup = get_cleanup_mode(value, 1);\n> > +\t} else\n> >  \t\treturn error(_(\"invalid key: %s\"), key);\n> >  \n> >  \tif (!error_flag)\n> > @@ -2770,6 +2791,11 @@ static int save_opts(struct replay_opts *opts)\n> >  \t\tres |= git_config_set_in_file_gently(opts_file, \"options.allow-rerere-auto\",\n> >  \t\t\t\t\t\t     opts->allow_rerere_auto == RERERE_AUTOUPDATE ?\n> >  \t\t\t\t\t\t     \"true\" : \"false\");\n> > +\n> > +\tif (opts->explicit_cleanup)\n> > +\t\tres |= git_config_set_in_file_gently(opts_file,\n> > +\t\t\t\t\"options.default-msg-cleanup\",\n> > +\t\t\t\tdescribe_cleanup_mode(opts->default_msg_cleanup));\n> >  \treturn res;\n> >  }\n> >  \n> > -- \n> > 2.21.0\n> > \n"}]}