{"thread":{"id":"52766","subject":"[RFC PATCH 3/3] Enable merge commit message type autoselect logic by default","startedAt":"2020-02-09T13:26:37Z","lastAt":"2020-03-09T19:45:34Z","messageCount":11,"participants":["i.Dark_Templar","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"391393","messageId":"20200209131623.5827-4-darktemplar@dark-templar-archives.net","threadId":"52766","inReplyTo":"20200209131623.5827-1-darktemplar@dark-templar-archives.net","subject":"[RFC PATCH 3/3] Enable merge commit message type autoselect logic by default","fromName":"i.Dark_Templar","fromEmail":"darktemplar@dark-templar-archives.net","sentAt":"2020-02-09T13:16:23Z","receivedAt":"2020-02-09T13:26:37Z","isPatch":true,"sender":{"key":"darktemplar@dark-templar-archives.net","avatar":null},"body":"Signed-off-by: i.Dark_Templar <darktemplar@dark-templar-archives.net>\n---\n Documentation/config/fmt-merge-msg.txt | 2 +-\n builtin/fmt-merge-msg.c                | 2 +-\n 2 files changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config/fmt-merge-msg.txt b/Documentation/config/fmt-merge-msg.txt\nindex 3829095222..acd998cca8 100644\n--- a/Documentation/config/fmt-merge-msg.txt\n+++ b/Documentation/config/fmt-merge-msg.txt\n@@ -10,7 +10,7 @@ merge.log::\n \ttrue is a synonym for 20.\n \n merge.messageType::\n-\tConfigure default merge commit message type. `original` is used\n+\tConfigure default merge commit message type. `autoselect` is used\n \tif no value or an invalid value is set.\n \t+\n \t* `original` keeps old merge commit message format which takes only one line.\ndiff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c\nindex 0bc6ce5b05..8fa39030d3 100644\n--- a/builtin/fmt-merge-msg.c\n+++ b/builtin/fmt-merge-msg.c\n@@ -18,7 +18,7 @@ enum message_format {\n \tMESSAGE_FORMAT_ORIGINAL = 0,\n \tMESSAGE_FORMAT_MULTILINE = 1,\n \tMESSAGE_FORMAT_AUTOSELECT = 2,\n-\tMESSAGE_FORMAT_DEFAULT = MESSAGE_FORMAT_ORIGINAL\n+\tMESSAGE_FORMAT_DEFAULT = MESSAGE_FORMAT_AUTOSELECT\n };\n \n static const char * const fmt_merge_msg_usage[] = {\n-- \n2.24.1\n\n"},{"id":"391394","messageId":"20200209131623.5827-2-darktemplar@dark-templar-archives.net","threadId":"52766","inReplyTo":"20200209131623.5827-1-darktemplar@dark-templar-archives.net","subject":"[RFC PATCH 1/3] git-merge: add option to format default message using multiple lines","fromName":"i.Dark_Templar","fromEmail":"darktemplar@dark-templar-archives.net","sentAt":"2020-02-09T13:16:21Z","receivedAt":"2020-02-09T13:26:38Z","isPatch":true,"sender":{"key":"darktemplar@dark-templar-archives.net","avatar":null},"body":"If a lot of objects is merged at once, default commit message\ncould become one very long line, which might be inconvenient to read.\nThis change implements an option to change default autogenerated message\nso it'd take multiple lines, but each line wouldn't be long.\n\nAn artificial example.\n\nOriginal merge commit message:\n    Merge branch 'branch_with_some_long_name_1', remote-tracking branch 'clone/remote_branch_with_some_name', tags 'some_tag' and 'some_other_tag'; commit 'ae46a39cead2b42282abce725e90b561c06e94ba'; commit '33d0281e0eeb2a5e9907ebedc230e28c46865092' into merge7\n\nMultiline merge commit message:\n    Merge into merge8:\n    branch 'branch_with_some_long_name_1'\n    remote-tracking branch 'clone/remote_branch_with_some_name'\n    tag 'some_tag'\n    tag 'some_other_tag'\n    commit 'ae46a39cead2b42282abce725e90b561c06e94ba'\n    commit '33d0281e0eeb2a5e9907ebedc230e28c46865092'\n\nSigned-off-by: i.Dark_Templar <darktemplar@dark-templar-archives.net>\n---\n Documentation/config/fmt-merge-msg.txt |  6 +++\n builtin/fmt-merge-msg.c                | 66 +++++++++++++++++++++++++-\n 2 files changed, 70 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config/fmt-merge-msg.txt b/Documentation/config/fmt-merge-msg.txt\nindex c73cfa90b7..8c12540fa7 100644\n--- a/Documentation/config/fmt-merge-msg.txt\n+++ b/Documentation/config/fmt-merge-msg.txt\n@@ -8,3 +8,9 @@ merge.log::\n \tmost the specified number of one-line descriptions from the\n \tactual commits that are being merged.  Defaults to false, and\n \ttrue is a synonym for 20.\n+\n+merge.multilineMessage::\n+\tMake default merge commit message multiline. Every merged object\n+\twill be written using new line. This should ensure that\n+\tcommit message wouldn't become one very long line when\n+\tthere are a lot of merged objects.\ndiff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c\nindex 05a92c59d8..93d44b59d9 100644\n--- a/builtin/fmt-merge-msg.c\n+++ b/builtin/fmt-merge-msg.c\n@@ -20,6 +20,7 @@ static const char * const fmt_merge_msg_usage[] = {\n };\n \n static int use_branch_desc;\n+static int use_multiline_default_message = 0;\n \n int fmt_merge_msg_config(const char *key, const char *value, void *cb)\n {\n@@ -32,6 +33,8 @@ int fmt_merge_msg_config(const char *key, const char *value, void *cb)\n \t\t\tmerge_log_config = DEFAULT_MERGE_LOG_LEN;\n \t} else if (!strcmp(key, \"merge.branchdesc\")) {\n \t\tuse_branch_desc = git_config_bool(key, value);\n+\t} else if (!strcmp(key, \"merge.multilinemessage\")) {\n+\t\tuse_multiline_default_message = git_config_bool(key, value);\n \t} else {\n \t\treturn git_default_config(key, value, cb);\n \t}\n@@ -467,6 +470,61 @@ static void fmt_merge_msg_title(struct strbuf *out,\n \t\tstrbuf_addf(out, \" into %s\\n\", current_branch);\n }\n \n+static void fmt_merge_msg_title_multiline(struct strbuf *out,\n+\t\t\t\tconst char *current_branch)\n+{\n+\tint i = 0;\n+\tint j = 0;\n+\n+\tif (!strcmp(\"master\", current_branch))\n+\t\tstrbuf_addstr(out, \"Merge:\\n\");\n+\telse\n+\t\tstrbuf_addf(out, \"Merge into %s:\\n\", current_branch);\n+\n+\tfor (i = 0; i < srcs.nr; i++) {\n+\t\tstruct src_data *src_data = srcs.items[i].util;\n+\t\tint add_origin = 0;\n+\n+\t\tif (src_data->head_status == 1) {\n+\t\t\tstrbuf_addf(out, \"%s\\n\", srcs.items[i].string);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tadd_origin = strcmp(\".\", srcs.items[i].string);\n+\n+\t\tif (src_data->head_status == 3) {\n+\t\t\tif (!add_origin)\n+\t\t\t\tstrbuf_addstr(out, \"HEAD\\n\");\n+\t\t\telse\n+\t\t\t\tstrbuf_addf(out, \"HEAD of %s\\n\", srcs.items[i].string);\n+\t\t}\n+\t\tfor (j = 0; j < src_data->branch.nr; j++) {\n+\t\t\tif (!add_origin)\n+\t\t\t\tstrbuf_addf(out, \"branch %s\\n\", src_data->branch.items[j].string);\n+\t\t\telse\n+\t\t\t\tstrbuf_addf(out, \"branch %s of %s\\n\", src_data->branch.items[j].string, srcs.items[i].string);\n+\t\t}\n+\t\tfor (j = 0; j < src_data->r_branch.nr; j++) {\n+\t\t\tif (!add_origin)\n+\t\t\t\tstrbuf_addf(out, \"remote-tracking branch %s\\n\", src_data->r_branch.items[j].string);\n+\t\t\telse\n+\t\t\t\tstrbuf_addf(out, \"remote-tracking branch %s of %s\\n\", src_data->r_branch.items[j].string, srcs.items[i].string);\n+\t\t}\n+\t\tfor (j = 0; j < src_data->tag.nr; j++) {\n+\t\t\tif (!add_origin)\n+\t\t\t\tstrbuf_addf(out, \"tag %s\\n\", src_data->tag.items[j].string);\n+\t\t\telse\n+\t\t\t\tstrbuf_addf(out, \"tag %s of %s\\n\", src_data->tag.items[j].string, srcs.items[i].string);\n+\t\t}\n+\t\tfor (j = 0; j < src_data->generic.nr; j++) {\n+\t\t\tif (!add_origin)\n+\t\t\t\tstrbuf_addf(out, \"commit %s\\n\", src_data->generic.items[j].string);\n+\t\t\telse\n+\t\t\t\tstrbuf_addf(out, \"commit %s of %s\\n\", src_data->generic.items[j].string, srcs.items[i].string);\n+\t\t}\n+\t}\n+}\n+\n static void fmt_tag_signature(struct strbuf *tagbuf,\n \t\t\t      struct strbuf *sig,\n \t\t\t      const char *buf,\n@@ -634,8 +692,12 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,\n \t\t\tdie(\"error in line %d: %.*s\", i, len, p);\n \t}\n \n-\tif (opts->add_title && srcs.nr)\n-\t\tfmt_merge_msg_title(out, current_branch);\n+\tif (opts->add_title && srcs.nr) {\n+\t\tif (!use_multiline_default_message)\n+\t\t\tfmt_merge_msg_title(out, current_branch);\n+\t\telse\n+\t\t\tfmt_merge_msg_title_multiline(out, current_branch);\n+\t}\n \n \tif (origins.nr)\n \t\tfmt_merge_msg_sigs(out);\n-- \n2.24.1\n\n"},{"id":"391395","messageId":"20200209131623.5827-1-darktemplar@dark-templar-archives.net","threadId":"52766","inReplyTo":null,"subject":"[RFC PATCH 0/3] git-merge: update default commit message","fromName":"i.Dark_Templar","fromEmail":"darktemplar@dark-templar-archives.net","sentAt":"2020-02-09T13:16:20Z","receivedAt":"2020-02-09T13:32:31Z","isPatch":true,"sender":{"key":"darktemplar@dark-templar-archives.net","avatar":null},"body":"This series of patches updates default commit message for git-merge command.\n\nFirst patch adds alternative commit message formatting where\neach merged object is printed on new line and adds configuration option to enable it.\n\nNew format may look good for merges with a lot of objects, but\nfor merge with just one object old format may still look better.\nTo be able to use both formats, new format type 'autoselect' is added\nin patch 2, and configuration option for format selection is reworked.\n\nPatch 3 changes default message formatting to 'autoselect' introduced\nin patch 2, thus changing default behaviour of git.\n\ni.Dark_Templar (3):\n  git-merge: add option to format default message using multiple lines\n  Add merge commit message type autoselect logic\n  Enable merge commit message type autoselect logic by default\n\n Documentation/config/fmt-merge-msg.txt |  13 +++\n builtin/fmt-merge-msg.c                | 127 ++++++++++++++++++++++++-\n 2 files changed, 138 insertions(+), 2 deletions(-)\n\n--\n2.24.1\n\n"},{"id":"391396","messageId":"20200209131623.5827-3-darktemplar@dark-templar-archives.net","threadId":"52766","inReplyTo":"20200209131623.5827-1-darktemplar@dark-templar-archives.net","subject":"[RFC PATCH 2/3] Add merge commit message type autoselect logic","fromName":"i.Dark_Templar","fromEmail":"darktemplar@dark-templar-archives.net","sentAt":"2020-02-09T13:16:22Z","receivedAt":"2020-02-09T13:32:31Z","isPatch":true,"sender":{"key":"darktemplar@dark-templar-archives.net","avatar":null},"body":"Signed-off-by: i.Dark_Templar <darktemplar@dark-templar-archives.net>\n---\n Documentation/config/fmt-merge-msg.txt | 17 ++++--\n builtin/fmt-merge-msg.c                | 71 ++++++++++++++++++++++++--\n 2 files changed, 78 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/config/fmt-merge-msg.txt b/Documentation/config/fmt-merge-msg.txt\nindex 8c12540fa7..3829095222 100644\n--- a/Documentation/config/fmt-merge-msg.txt\n+++ b/Documentation/config/fmt-merge-msg.txt\n@@ -9,8 +9,15 @@ merge.log::\n \tactual commits that are being merged.  Defaults to false, and\n \ttrue is a synonym for 20.\n \n-merge.multilineMessage::\n-\tMake default merge commit message multiline. Every merged object\n-\twill be written using new line. This should ensure that\n-\tcommit message wouldn't become one very long line when\n-\tthere are a lot of merged objects.\n+merge.messageType::\n+\tConfigure default merge commit message type. `original` is used\n+\tif no value or an invalid value is set.\n+\t+\n+\t* `original` keeps old merge commit message format which takes only one line.\n+\t* `multiline` switches merge commit message to multiline mode. Every merged object\n+\twill be written using new line. This should ensure that commit message\n+\twouldn't become one very long line when there are a lot of merged objects.\n+\t* `autoselect`, as it's name implies, selects one of following types\n+\tusing arbitrary logic. Currently it selects `original` mode for merge\n+\twith only one additional object, otherwise switching to `multiline` mode,\n+\tbut this behaviour may be changed in future without any notice.\ndiff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c\nindex 93d44b59d9..0bc6ce5b05 100644\n--- a/builtin/fmt-merge-msg.c\n+++ b/builtin/fmt-merge-msg.c\n@@ -14,13 +14,20 @@\n #include \"repository.h\"\n #include \"commit-reach.h\"\n \n+enum message_format {\n+\tMESSAGE_FORMAT_ORIGINAL = 0,\n+\tMESSAGE_FORMAT_MULTILINE = 1,\n+\tMESSAGE_FORMAT_AUTOSELECT = 2,\n+\tMESSAGE_FORMAT_DEFAULT = MESSAGE_FORMAT_ORIGINAL\n+};\n+\n static const char * const fmt_merge_msg_usage[] = {\n \tN_(\"git fmt-merge-msg [-m <message>] [--log[=<n>] | --no-log] [--file <file>]\"),\n \tNULL\n };\n \n static int use_branch_desc;\n-static int use_multiline_default_message = 0;\n+static enum message_format default_message_format = MESSAGE_FORMAT_DEFAULT;\n \n int fmt_merge_msg_config(const char *key, const char *value, void *cb)\n {\n@@ -33,8 +40,16 @@ int fmt_merge_msg_config(const char *key, const char *value, void *cb)\n \t\t\tmerge_log_config = DEFAULT_MERGE_LOG_LEN;\n \t} else if (!strcmp(key, \"merge.branchdesc\")) {\n \t\tuse_branch_desc = git_config_bool(key, value);\n-\t} else if (!strcmp(key, \"merge.multilinemessage\")) {\n-\t\tuse_multiline_default_message = git_config_bool(key, value);\n+\t} else if (!strcmp(key, \"merge.messagetype\")) {\n+\t\tif (!value)\n+\t\t\treturn config_error_nonbool(key);\n+\n+\t\tif (!strcmp(value, \"autoselect\"))\n+\t\t\tdefault_message_format = MESSAGE_FORMAT_AUTOSELECT;\n+\t\telse if (!strcmp(value, \"original\"))\n+\t\t\tdefault_message_format = MESSAGE_FORMAT_ORIGINAL;\n+\t\telse if (!strcmp(value, \"multiline\"))\n+\t\t\tdefault_message_format = MESSAGE_FORMAT_MULTILINE;\n \t} else {\n \t\treturn git_default_config(key, value, cb);\n \t}\n@@ -525,6 +540,43 @@ static void fmt_merge_msg_title_multiline(struct strbuf *out,\n \t}\n }\n \n+static void fmt_merge_msg_title_autoselect(struct strbuf *out,\n+\t\t\t\tconst char *current_branch)\n+{\n+\tint i = 0;\n+\tint j = 0;\n+\tint objects_count = 0;\n+\n+\tfor (i = 0; i < srcs.nr; i++) {\n+\t\tstruct src_data *src_data = srcs.items[i].util;\n+\n+\t\tif (src_data->head_status == 1) {\n+\t\t\t++objects_count;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (src_data->head_status == 3) {\n+\t\t\t++objects_count;\n+\t\t}\n+\t\tfor (j = 0; j < src_data->branch.nr; j++) {\n+\t\t\t++objects_count;\n+\t\t}\n+\t\tfor (j = 0; j < src_data->r_branch.nr; j++) {\n+\t\t\t++objects_count;\n+\t\t}\n+\t\tfor (j = 0; j < src_data->tag.nr; j++) {\n+\t\t\t++objects_count;\n+\t\t}\n+\t\tfor (j = 0; j < src_data->generic.nr; j++) {\n+\t\t\t++objects_count;\n+\t\t}\n+\t}\n+\n+\tif (objects_count > 1)\n+\t\tfmt_merge_msg_title_multiline(out, current_branch);\n+\telse\n+\t\tfmt_merge_msg_title(out, current_branch);\n+}\n+\n static void fmt_tag_signature(struct strbuf *tagbuf,\n \t\t\t      struct strbuf *sig,\n \t\t\t      const char *buf,\n@@ -693,10 +745,19 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,\n \t}\n \n \tif (opts->add_title && srcs.nr) {\n-\t\tif (!use_multiline_default_message)\n+\t\tswitch (default_message_format) {\n+\t\tcase MESSAGE_FORMAT_ORIGINAL:\n \t\t\tfmt_merge_msg_title(out, current_branch);\n-\t\telse\n+\t\t\tbreak;\n+\t\tcase MESSAGE_FORMAT_MULTILINE:\n \t\t\tfmt_merge_msg_title_multiline(out, current_branch);\n+\t\t\tbreak;\n+\t\tcase MESSAGE_FORMAT_AUTOSELECT:\n+\t\t\tfmt_merge_msg_title_autoselect(out, current_branch);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tBUG(\"merge.messagetype value is not properly processed: %d\", (int) default_message_format);\n+\t\t}\n \t}\n \n \tif (origins.nr)\n-- \n2.24.1\n\n"},{"id":"391410","messageId":"xmqqtv3z1wq4.fsf@gitster-ct.c.googlers.com","threadId":"52766","inReplyTo":"20200209131623.5827-2-darktemplar@dark-templar-archives.net","subject":"Re: [RFC PATCH 1/3] git-merge: add option to format default message using multiple lines","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-02-09T17:44:51Z","receivedAt":"2020-02-09T17:44:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"i.Dark_Templar\" <darktemplar@dark-templar-archives.net> writes:\n\n> If a lot of objects is merged at once, default commit message\n> could become one very long line, which might be inconvenient to read.\n> This change implements an option to change default autogenerated message\n> so it'd take multiple lines, but each line wouldn't be long.\n> \n> An artificial example.\n>\n> Original merge commit message:\n>     Merge branch 'branch_with_some_long_name_1', remote-tracking branch 'clone/remote_branch_with_some_name', tags 'some_tag' and 'some_other_tag'; commit 'ae46a39cead2b42282abce725e90b561c06e94ba'; commit '33d0281e0eeb2a5e9907ebedc230e28c46865092' into merge7\n>\n> Multiline merge commit message:\n>     Merge into merge8:\n>     branch 'branch_with_some_long_name_1'\n>     remote-tracking branch 'clone/remote_branch_with_some_name'\n>     tag 'some_tag'\n>     tag 'some_other_tag'\n>     commit 'ae46a39cead2b42282abce725e90b561c06e94ba'\n>     commit '33d0281e0eeb2a5e9907ebedc230e28c46865092'\n\n\nHow would these commits appear in the \"git shorlog\" output?  Losing\nsome information is OK (after all, you are making the 'title' of the\nmerge commit less crowded to prefer 'simpler' summary in exchange),\nbut you'd need to strike a good balance what to discard.  Is the\nfact that the name of branch that got some unspecified new things\nwas 'merge8' the most important thing to convey?\n\nIf you make the commit 'title' a short one-line summary, you MUST\nhave a blank line after that line.  Otherwise, the 'where does the\ntitle of this commit object end?' logic will helpfully merge all the\nlines in the first paragraph (i.e. up to the first blank line) into\none long line, defeating your effort to make the summary simpler by\nlosing details.  That is:\n\n    Merge 6 commits into merge8\n\n    branch 'branch_with_some_long_name_1'\n    remote-tracking ...\n    ...\n\nThat's it for the 'mechanics', i.e. a discussion about how a good\ndesign of this 'feature' should look like.\n\nAbout the feature itself, I am not sure.  Even though I admit I was\nthe one who invented octupus merges, and it does make the history\nlook \"pretty\" in gitk when used judiciously, its practical benefit\nover repeated pairwise merges is doubtful.  Besides, it makes\nbisection less efficient.  So from that point of view, I am not sure\nif we want a feature to encourage creation of more octopus merges.\n\nI haven't read the code yet---I usually don't before figuring out if\nthe feature and its design makes sense---so I have no comment on the\nactual change at this moment.  I may send a separate review message\nlater.\n\nThanks.\n"},{"id":"391483","messageId":"2f0417b3-9e9e-f0db-ab11-92dd7cd2b29a@dark-templar-archives.net","threadId":"52766","inReplyTo":"xmqqtv3z1wq4.fsf@gitster-ct.c.googlers.com","subject":"Re: [RFC PATCH 1/3] git-merge: add option to format default message using multiple lines","fromName":"i.Dark_Templar","fromEmail":"darktemplar@dark-templar-archives.net","sentAt":"2020-02-10T18:51:02Z","receivedAt":"2020-02-10T18:51:13Z","isPatch":true,"sender":{"key":"darktemplar@dark-templar-archives.net","avatar":null},"body":"09.02.2020 20:44, Junio C Hamano пишет:\n> \"i.Dark_Templar\" <darktemplar@dark-templar-archives.net> writes:\n> \n>> If a lot of objects is merged at once, default commit message\n>> could become one very long line, which might be inconvenient to read.\n>> This change implements an option to change default autogenerated message\n>> so it'd take multiple lines, but each line wouldn't be long.\n>>\n>> An artificial example.\n>>\n>> Original merge commit message:\n>>     Merge branch 'branch_with_some_long_name_1', remote-tracking branch 'clone/remote_branch_with_some_name', tags 'some_tag' and 'some_other_tag'; commit 'ae46a39cead2b42282abce725e90b561c06e94ba'; commit '33d0281e0eeb2a5e9907ebedc230e28c46865092' into merge7\n>>\n>> Multiline merge commit message:\n>>     Merge into merge8:\n>>     branch 'branch_with_some_long_name_1'\n>>     remote-tracking branch 'clone/remote_branch_with_some_name'\n>>     tag 'some_tag'\n>>     tag 'some_other_tag'\n>>     commit 'ae46a39cead2b42282abce725e90b561c06e94ba'\n>>     commit '33d0281e0eeb2a5e9907ebedc230e28c46865092'\n> \n> \n> How would these commits appear in the \"git shorlog\" output?  Losing\n> some information is OK (after all, you are making the 'title' of the\n> merge commit less crowded to prefer 'simpler' summary in exchange),\n> but you'd need to strike a good balance what to discard.  Is the\n> fact that the name of branch that got some unspecified new things\n> was 'merge8' the most important thing to convey?\n> \n> If you make the commit 'title' a short one-line summary, you MUST\n> have a blank line after that line.  Otherwise, the 'where does the\n> title of this commit object end?' logic will helpfully merge all the\n> lines in the first paragraph (i.e. up to the first blank line) into\n> one long line, defeating your effort to make the summary simpler by\n> losing details.  That is:\n> \n>     Merge 6 commits into merge8\n> \n>     branch 'branch_with_some_long_name_1'\n>     remote-tracking ...\n>     ...\n> \n> That's it for the 'mechanics', i.e. a discussion about how a good\n> design of this 'feature' should look like.\n> \n> About the feature itself, I am not sure.  Even though I admit I was\n> the one who invented octupus merges, and it does make the history\n> look \"pretty\" in gitk when used judiciously, its practical benefit\n> over repeated pairwise merges is doubtful.  Besides, it makes\n> bisection less efficient.  So from that point of view, I am not sure\n> if we want a feature to encourage creation of more octopus merges.\n> \n> I haven't read the code yet---I usually don't before figuring out if\n> the feature and its design makes sense---so I have no comment on the\n> actual change at this moment.  I may send a separate review message\n> later.\n> \n> Thanks.\n> \n\nThank you for feedback.\n\nI totally forgot about title in commit message and short log. I think\nI'd like to take your suggestion and modify it a bit.\nI want to add some text before enumeration of commits into message like\nthis:\n\nMerge 6 commits into merge8\n\nFollowing commits are merged:\nbranch 'branch_with_some_long_name_1'\nremote-tracking ...\n...\n\nAs for octopus merge, there are still some situations where it has some\nadvantages over series of pairwise merges, although I agree that it\nmight be uncommon cases. I can name linking multiple previously\nunrelated histories as one such case. Bisect could be useless and\nhistory would look better in my opinion.\n\nIf this feature would be fine with updated commit message, I'll update\nmy changes and send updated patch series.\n"},{"id":"392958","messageId":"20200309120722.4987-1-darktemplar@dark-templar-archives.net","threadId":"52766","inReplyTo":"2f0417b3-9e9e-f0db-ab11-92dd7cd2b29a@dark-templar-archives.net","subject":"[RFC PATCH v2 0/2] git-merge: update default commit message","fromName":"i.Dark_Templar","fromEmail":"darktemplar@dark-templar-archives.net","sentAt":"2020-03-09T12:07:20Z","receivedAt":"2020-03-09T12:10:11Z","isPatch":true,"sender":{"key":"darktemplar@dark-templar-archives.net","avatar":null},"body":"This series of patches updates default commit message for git-merge command.\n\nFirst patch adds alternative commit message formatting where\neach merged object is printed on new line\nand adds configuration option to enable new message formatting\nwhen specified number of merged commits is detected.\n\nThis feature is turned off by default in first patch.\nSecond patch is optional and enables new behaviour\nwhen at least two additional commits are merged.\n\ni.Dark_Templar (2):\n  git-merge: add option to format default message using multiple lines\n  Enable multiline merge commit message by default\n\n Documentation/config/fmt-merge-msg.txt |   9 ++\n builtin/fmt-merge-msg.c                | 115 ++++++++++++++++++++++++-\n 2 files changed, 122 insertions(+), 2 deletions(-)\n\n--\n2.24.1\n\n"},{"id":"392959","messageId":"20200309120722.4987-2-darktemplar@dark-templar-archives.net","threadId":"52766","inReplyTo":"20200309120722.4987-1-darktemplar@dark-templar-archives.net","subject":"[RFC PATCH v2 1/2] git-merge: add option to format default message using multiple lines","fromName":"i.Dark_Templar","fromEmail":"darktemplar@dark-templar-archives.net","sentAt":"2020-03-09T12:07:21Z","receivedAt":"2020-03-09T12:10:14Z","isPatch":true,"sender":{"key":"darktemplar@dark-templar-archives.net","avatar":null},"body":"If a lot of objects is merged at once, default commit message\ncould become one very long line, which might be inconvenient to read.\nThis change implements an option to change default autogenerated message\nso it'd take multiple lines, but each line wouldn't be long.\n\nAn artificial example.\n\nOriginal merge commit message:\n    Merge branch 'branch_with_some_long_name_1', remote-tracking branch 'clone/remote_branch_with_some_name', tags 'some_tag' and 'some_other_tag'; commit 'ae46a39cead2b42282abce725e90b561c06e94ba'; commit '33d0281e0eeb2a5e9907ebedc230e28c46865092' into merge7\n\nMultiline merge commit message:\n    Merge 6 commits into merge8\n\n    Following commits are merged:\n    branch 'branch_with_some_long_name_1'\n    remote-tracking branch 'clone/remote_branch_with_some_name'\n    tag 'some_tag'\n    tag 'some_other_tag'\n    commit 'ae46a39cead2b42282abce725e90b561c06e94ba'\n    commit '33d0281e0eeb2a5e9907ebedc230e28c46865092'\n\nSigned-off-by: i.Dark_Templar <darktemplar@dark-templar-archives.net>\n---\n Documentation/config/fmt-merge-msg.txt |   9 ++\n builtin/fmt-merge-msg.c                | 115 ++++++++++++++++++++++++-\n 2 files changed, 122 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config/fmt-merge-msg.txt b/Documentation/config/fmt-merge-msg.txt\nindex c73cfa90b7..7aecf95b68 100644\n--- a/Documentation/config/fmt-merge-msg.txt\n+++ b/Documentation/config/fmt-merge-msg.txt\n@@ -8,3 +8,12 @@ merge.log::\n \tmost the specified number of one-line descriptions from the\n \tactual commits that are being merged.  Defaults to false, and\n \ttrue is a synonym for 20.\n+\n+merge.usemultiline::\n+\tSwitch merge commit message format to multiline format\n+\tif merged commits count is greater than or equal to specified\n+\tvalue. If specified value is zero or a negative one, this feature\n+\tis disabled.\n+\tWhen multiline commit message format is used, every merged commit\n+\twill be written using new line. This should ensure that commit message\n+\twouldn't become one very long line when there are a lot of merged commits.\ndiff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c\nindex 736f666f64..191824c962 100644\n--- a/builtin/fmt-merge-msg.c\n+++ b/builtin/fmt-merge-msg.c\n@@ -20,6 +20,7 @@ static const char * const fmt_merge_msg_usage[] = {\n };\n \n static int use_branch_desc;\n+static int use_multiline = -1;\n \n int fmt_merge_msg_config(const char *key, const char *value, void *cb)\n {\n@@ -32,6 +33,8 @@ int fmt_merge_msg_config(const char *key, const char *value, void *cb)\n \t\t\tmerge_log_config = DEFAULT_MERGE_LOG_LEN;\n \t} else if (!strcmp(key, \"merge.branchdesc\")) {\n \t\tuse_branch_desc = git_config_bool(key, value);\n+\t} else if (!strcmp(key, \"merge.usemultiline\")) {\n+\t\tuse_multiline = git_config_int(key, value);\n \t} else {\n \t\treturn git_default_config(key, value, cb);\n \t}\n@@ -413,6 +416,39 @@ static void shortlog(const char *name,\n \tstring_list_clear(&subjects, 0);\n }\n \n+static int fmt_merge_refs_count(void)\n+{\n+\tint i = 0;\n+\tint j = 0;\n+\tint objects_count = 0;\n+\n+\tfor (i = 0; i < srcs.nr; i++) {\n+\t\tstruct src_data *src_data = srcs.items[i].util;\n+\n+\t\tif (src_data->head_status == 1) {\n+\t\t\t++objects_count;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (src_data->head_status == 3) {\n+\t\t\t++objects_count;\n+\t\t}\n+\t\tfor (j = 0; j < src_data->branch.nr; j++) {\n+\t\t\t++objects_count;\n+\t\t}\n+\t\tfor (j = 0; j < src_data->r_branch.nr; j++) {\n+\t\t\t++objects_count;\n+\t\t}\n+\t\tfor (j = 0; j < src_data->tag.nr; j++) {\n+\t\t\t++objects_count;\n+\t\t}\n+\t\tfor (j = 0; j < src_data->generic.nr; j++) {\n+\t\t\t++objects_count;\n+\t\t}\n+\t}\n+\n+\treturn objects_count;\n+}\n+\n static void fmt_merge_msg_title(struct strbuf *out,\n \t\t\t\tconst char *current_branch)\n {\n@@ -467,6 +503,68 @@ static void fmt_merge_msg_title(struct strbuf *out,\n \t\tstrbuf_addf(out, \" into %s\\n\", current_branch);\n }\n \n+static void fmt_merge_msg_title_multiline(struct strbuf *out,\n+\t\t\t\tconst char *current_branch, int count)\n+{\n+\tint i = 0;\n+\tint j = 0;\n+\n+\tif (!strcmp(\"master\", current_branch))\n+\t\tstrbuf_addf(out, \"Merge %d %s\\n\", count, (count == 1) ? \"commit\" : \"commits\");\n+\telse\n+\t\tstrbuf_addf(out, \"Merge %d %s into %s\\n\", count, (count == 1) ? \"commit\" : \"commits\", current_branch);\n+\n+\tstrbuf_addch(out, '\\n');\n+\n+\tif (count == 1)\n+\t\tstrbuf_addstr(out, \"Following commit is merged:\\n\");\n+\telse\n+\t\tstrbuf_addstr(out, \"Following commits are merged:\\n\");\n+\n+\tfor (i = 0; i < srcs.nr; i++) {\n+\t\tstruct src_data *src_data = srcs.items[i].util;\n+\t\tint add_origin = 0;\n+\n+\t\tif (src_data->head_status == 1) {\n+\t\t\tstrbuf_addf(out, \"%s\\n\", srcs.items[i].string);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tadd_origin = strcmp(\".\", srcs.items[i].string);\n+\n+\t\tif (src_data->head_status == 3) {\n+\t\t\tif (!add_origin)\n+\t\t\t\tstrbuf_addstr(out, \"HEAD\\n\");\n+\t\t\telse\n+\t\t\t\tstrbuf_addf(out, \"HEAD of %s\\n\", srcs.items[i].string);\n+\t\t}\n+\t\tfor (j = 0; j < src_data->branch.nr; j++) {\n+\t\t\tif (!add_origin)\n+\t\t\t\tstrbuf_addf(out, \"branch %s\\n\", src_data->branch.items[j].string);\n+\t\t\telse\n+\t\t\t\tstrbuf_addf(out, \"branch %s of %s\\n\", src_data->branch.items[j].string, srcs.items[i].string);\n+\t\t}\n+\t\tfor (j = 0; j < src_data->r_branch.nr; j++) {\n+\t\t\tif (!add_origin)\n+\t\t\t\tstrbuf_addf(out, \"remote-tracking branch %s\\n\", src_data->r_branch.items[j].string);\n+\t\t\telse\n+\t\t\t\tstrbuf_addf(out, \"remote-tracking branch %s of %s\\n\", src_data->r_branch.items[j].string, srcs.items[i].string);\n+\t\t}\n+\t\tfor (j = 0; j < src_data->tag.nr; j++) {\n+\t\t\tif (!add_origin)\n+\t\t\t\tstrbuf_addf(out, \"tag %s\\n\", src_data->tag.items[j].string);\n+\t\t\telse\n+\t\t\t\tstrbuf_addf(out, \"tag %s of %s\\n\", src_data->tag.items[j].string, srcs.items[i].string);\n+\t\t}\n+\t\tfor (j = 0; j < src_data->generic.nr; j++) {\n+\t\t\tif (!add_origin)\n+\t\t\t\tstrbuf_addf(out, \"commit %s\\n\", src_data->generic.items[j].string);\n+\t\t\telse\n+\t\t\t\tstrbuf_addf(out, \"commit %s of %s\\n\", src_data->generic.items[j].string, srcs.items[i].string);\n+\t\t}\n+\t}\n+}\n+\n static void fmt_tag_signature(struct strbuf *tagbuf,\n \t\t\t      struct strbuf *sig,\n \t\t\t      const char *buf,\n@@ -631,8 +729,21 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,\n \t\t\tdie(\"error in line %d: %.*s\", i, len, p);\n \t}\n \n-\tif (opts->add_title && srcs.nr)\n-\t\tfmt_merge_msg_title(out, current_branch);\n+\tif (opts->add_title && srcs.nr) {\n+\t\tif (use_multiline <= 0) {\n+\t\t\tfmt_merge_msg_title(out, current_branch);\n+\t\t} else {\n+\t\t\tint count;\n+\n+\t\t\tcount = fmt_merge_refs_count();\n+\n+\t\t\tif (count >= use_multiline) {\n+\t\t\t\tfmt_merge_msg_title_multiline(out, current_branch, count);\n+\t\t\t} else {\n+\t\t\t\tfmt_merge_msg_title(out, current_branch);\n+\t\t\t}\n+\t\t}\n+\t}\n \n \tif (origins.nr)\n \t\tfmt_merge_msg_sigs(out);\n-- \n2.24.1\n\n"},{"id":"392960","messageId":"20200309120722.4987-3-darktemplar@dark-templar-archives.net","threadId":"52766","inReplyTo":"20200309120722.4987-1-darktemplar@dark-templar-archives.net","subject":"[RFC PATCH v2 2/2] Enable multiline merge commit message by default","fromName":"i.Dark_Templar","fromEmail":"darktemplar@dark-templar-archives.net","sentAt":"2020-03-09T12:07:22Z","receivedAt":"2020-03-09T12:10:16Z","isPatch":true,"sender":{"key":"darktemplar@dark-templar-archives.net","avatar":null},"body":"Signed-off-by: i.Dark_Templar <darktemplar@dark-templar-archives.net>\n---\n builtin/fmt-merge-msg.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c\nindex 191824c962..ab445110b0 100644\n--- a/builtin/fmt-merge-msg.c\n+++ b/builtin/fmt-merge-msg.c\n@@ -20,7 +20,7 @@ static const char * const fmt_merge_msg_usage[] = {\n };\n \n static int use_branch_desc;\n-static int use_multiline = -1;\n+static int use_multiline = 2;\n \n int fmt_merge_msg_config(const char *key, const char *value, void *cb)\n {\n-- \n2.24.1\n\n"},{"id":"392967","messageId":"xmqqblp51owb.fsf@gitster-ct.c.googlers.com","threadId":"52766","inReplyTo":"20200309120722.4987-2-darktemplar@dark-templar-archives.net","subject":"Re: [RFC PATCH v2 1/2] git-merge: add option to format default message using multiple lines","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-03-09T16:20:04Z","receivedAt":"2020-03-09T16:20:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"i.Dark_Templar\" <darktemplar@dark-templar-archives.net> writes:\n\n> +static int use_multiline = -1;\n>  \n>  int fmt_merge_msg_config(const char *key, const char *value, void *cb)\n>  {\n> @@ -32,6 +33,8 @@ int fmt_merge_msg_config(const char *key, const char *value, void *cb)\n>  \t\t\tmerge_log_config = DEFAULT_MERGE_LOG_LEN;\n>  \t} else if (!strcmp(key, \"merge.branchdesc\")) {\n>  \t\tuse_branch_desc = git_config_bool(key, value);\n> +\t} else if (!strcmp(key, \"merge.usemultiline\")) {\n> +\t\tuse_multiline = git_config_int(key, value);\n>  \t} else {\n>  \t\treturn git_default_config(key, value, cb);\n>  \t}\n> @@ -413,6 +416,39 @@ static void shortlog(const char *name,\n>  \tstring_list_clear(&subjects, 0);\n>  }\n>  \n> +static int fmt_merge_refs_count(void)\n> +{\n> +\tint i = 0;\n> +\tint j = 0;\n> +\tint objects_count = 0;\n\nCall it \"object_count\" and drop \"j\"; that's shorter.\n\nAlso, this is a \"STATIC\" function, an implementation detail of the\nfmt_merge_msg program.  There is no need to repeat that fact by\nreplicating fmt_merge_ in its name to differenciate with other\nthings, unlike the functions that are visible from other places.\n\nJust call it \"count_srcs()\" or something like that and do not use\nthe word \"ref\" in its name, as \"merging refs\" is probably too\nancient, limiting and misleading concept---we do not merge \"refs\".\nWe merge commits (and more recently) tags, and \"refs\" is merely one\nof the ways to name these things that are merged.\n\n\n> +\tfor (i = 0; i < srcs.nr; i++) {\n> +\t\tstruct src_data *src_data = srcs.items[i].util;\n> +\n> +\t\tif (src_data->head_status == 1) {\n> +\t\t\t++objects_count;\n> +\t\t\tcontinue;\n> +\t\t}\n> +\t\tif (src_data->head_status == 3) {\n> +\t\t\t++objects_count;\n> +\t\t}\n\nThis part deserves commenting.  The bit #0 of head_status is special\nin that it signals a pull of the default branch (HEAD) of the remote\nrepository, and it is special because in that case (and only in that\ncase) none of the string lists in src_data is updated, so the number\nof merged heads is one more than the case where the bit #0 is off.\n\n> +\t\tfor (j = 0; j < src_data->branch.nr; j++) {\n> +\t\t\t++objects_count;\n> +\t\t}\n> +\t\tfor (j = 0; j < src_data->r_branch.nr; j++) {\n> +\t\t\t++objects_count;\n> +\t\t}\n> +\t\tfor (j = 0; j < src_data->tag.nr; j++) {\n> +\t\t\t++objects_count;\n> +\t\t}\n> +\t\tfor (j = 0; j < src_data->generic.nr; j++) {\n> +\t\t\t++objects_count;\n> +\t\t}\n\nIsn't it sufficient to add .nr to object_count?  Why increment\nobject_count by one as many times as j counts up from 0 to .nr?\n\nIf the merge involves only the remote HEAD, then the lists are all\nempty, isn't it?  I do not see the point of treating the case where\nhead_status is 3 == 1|2 any special, so the following is sufficient\nas the replacement for the above and should be far easier to follow,\nno?  Or am I missing something subtle in your version?\n\n\t\tif (src_data->head_status & 01)\n\t\t\t/* \n                         * the set of merged heads includes the\n                         * default branch of the remote, aka HEAD,\n\t\t\t * which is not counted in any of the lists\n\t\t\t * in src_data.\n\t\t\t */\n\t\t\tobject_count++;\n\n                object_count += (src->data->branch.nr +\n                                 src->data->r_branch.nr +\n                                 src->data->tag.nr +\n                                 src->data->generic.nr);\n\nBy the way, if you read the original code, you may have noticed that\nour codebase prefers post-increment over pre-increment, when the\ndifference does not matter (in other words, \"a = ++b;\" is perfectly\nlegit; it is just that we do not say \"++b;\" when \"b++;\" would do).\n\n> +\t}\n> +\n> +\treturn objects_count;\n> +}\n> +\n>  static void fmt_merge_msg_title(struct strbuf *out,\n>  \t\t\t\tconst char *current_branch)\n>  {\n> @@ -467,6 +503,68 @@ static void fmt_merge_msg_title(struct strbuf *out,\n>  \t\tstrbuf_addf(out, \" into %s\\n\", current_branch);\n>  }\n>  \n> +static void fmt_merge_msg_title_multiline(struct strbuf *out,\n> +\t\t\t\tconst char *current_branch, int count)\n> +{\n> +\tint i = 0;\n> +\tint j = 0;\n> +\n> +\tif (!strcmp(\"master\", current_branch))\n> +\t\tstrbuf_addf(out, \"Merge %d %s\\n\", count, (count == 1) ? \"commit\" : \"commits\");\n> +\telse\n> +\t\tstrbuf_addf(out, \"Merge %d %s into %s\\n\", count, (count == 1) ? \"commit\" : \"commits\", current_branch);\n\nOverlong lines.\n\n> +\tstrbuf_addch(out, '\\n');\n> +\n> +\tif (count == 1)\n> +\t\tstrbuf_addstr(out, \"Following commit is merged:\\n\");\n> +\telse\n> +\t\tstrbuf_addstr(out, \"Following commits are merged:\\n\");\n\nThe above makes me wonder if you are better off having \"if there is\nonly one, do these things, otherwise do these other things\" at the\ntop level, i.e.\n\n\tif (count == 1) {\n\t\tstrbuf_addstr(out, \"Merge 1 commit\");\n\t\tif (strcmp(current_branch, \"master\"))\n\t\t\tstrbuf_addstr(out, \" into %s\", current_branch);\n\t\tstrbuf_addstr(out, \"\\n\\nFollowing commit is ...\");\n\t} else {\n\t\t...similarly...\n\t}\n\nBut stepping back a bit, is there a point in handing count==1 case\nin this new format in the first place?  Why waste the most precious\ninformation real estate, which is the title line, to say a lot less\ninformative \"merge 1 commit\" instead of what topic is merged there?\n\nAlso, I am not sure if \"Following commits are merged\" is a good\nmessage for a few reasons.\n\n - Obviously, we allow pulling and merging a signed tag and in that\n   case, we are merging a tag, not a commit.\n\n - When we pull a topic branch and nothing else, the above code\n   would say \"Merge 1 commit\", but there may probably be more than\n   one commit on the history leading to that commit we are merging\n   into our history.\n\n\nWhat actually is happening, instead of \"merge 1 commit\", is that we\nare merging one lineage of history, which may have one or more\ncommits.  The phrase suitable to call the lineage of history may be\n\"a topic branch\", but it may be \"a release tag\", in which case what\nyou are merging would be the entire history built while developing\ntowards that tag that you have been missing.\n\n"},{"id":"392971","messageId":"39919b91-fae6-d7c7-1c1e-acb3cc7f63b1@dark-templar-archives.net","threadId":"52766","inReplyTo":"xmqqblp51owb.fsf@gitster-ct.c.googlers.com","subject":"Re: [RFC PATCH v2 1/2] git-merge: add option to format default message using multiple lines","fromName":"i.Dark_Templar","fromEmail":"darktemplar@dark-templar-archives.net","sentAt":"2020-03-09T19:45:24Z","receivedAt":"2020-03-09T19:45:34Z","isPatch":true,"sender":{"key":"darktemplar@dark-templar-archives.net","avatar":null},"body":"Hi\n\nThank you for your feedback!\n\n09.03.2020 19:20, Junio C Hamano пишет:\n> \"i.Dark_Templar\" <darktemplar@dark-templar-archives.net> writes:\n> \n>> +static int use_multiline = -1;\n>>  \n>>  int fmt_merge_msg_config(const char *key, const char *value, void *cb)\n>>  {\n>> @@ -32,6 +33,8 @@ int fmt_merge_msg_config(const char *key, const char *value, void *cb)\n>>  \t\t\tmerge_log_config = DEFAULT_MERGE_LOG_LEN;\n>>  \t} else if (!strcmp(key, \"merge.branchdesc\")) {\n>>  \t\tuse_branch_desc = git_config_bool(key, value);\n>> +\t} else if (!strcmp(key, \"merge.usemultiline\")) {\n\nI'm wondering if this config option name could be improved, but out of\nideas at the moment. Maybe you have some ideas?\n\n>> +\t\tuse_multiline = git_config_int(key, value);\n>>  \t} else {\n>>  \t\treturn git_default_config(key, value, cb);\n>>  \t}\n>> @@ -413,6 +416,39 @@ static void shortlog(const char *name,\n>>  \tstring_list_clear(&subjects, 0);\n>>  }\n>>  \n>> +static int fmt_merge_refs_count(void)\n>> +{\n>> +\tint i = 0;\n>> +\tint j = 0;\n>> +\tint objects_count = 0;\n> \n> Call it \"object_count\" and drop \"j\"; that's shorter.\n> \n> Also, this is a \"STATIC\" function, an implementation detail of the\n> fmt_merge_msg program.  There is no need to repeat that fact by\n> replicating fmt_merge_ in its name to differenciate with other\n> things, unlike the functions that are visible from other places.\n> \n> Just call it \"count_srcs()\" or something like that and do not use\n> the word \"ref\" in its name, as \"merging refs\" is probably too\n> ancient, limiting and misleading concept---we do not merge \"refs\".\n> We merge commits (and more recently) tags, and \"refs\" is merely one\n> of the ways to name these things that are merged.\n> \n> \n\nThanks, I'll rename this function to 'count_srcs'.\n\n>> +\tfor (i = 0; i < srcs.nr; i++) {\n>> +\t\tstruct src_data *src_data = srcs.items[i].util;\n>> +\n>> +\t\tif (src_data->head_status == 1) {\n>> +\t\t\t++objects_count;\n>> +\t\t\tcontinue;\n>> +\t\t}\n>> +\t\tif (src_data->head_status == 3) {\n>> +\t\t\t++objects_count;\n>> +\t\t}\n> \n> This part deserves commenting.  The bit #0 of head_status is special\n> in that it signals a pull of the default branch (HEAD) of the remote\n> repository, and it is special because in that case (and only in that\n> case) none of the string lists in src_data is updated, so the number\n> of merged heads is one more than the case where the bit #0 is off.\n> \n\nI didn't know about such details. Thanks.\n\n>> +\t\tfor (j = 0; j < src_data->branch.nr; j++) {\n>> +\t\t\t++objects_count;\n>> +\t\t}\n>> +\t\tfor (j = 0; j < src_data->r_branch.nr; j++) {\n>> +\t\t\t++objects_count;\n>> +\t\t}\n>> +\t\tfor (j = 0; j < src_data->tag.nr; j++) {\n>> +\t\t\t++objects_count;\n>> +\t\t}\n>> +\t\tfor (j = 0; j < src_data->generic.nr; j++) {\n>> +\t\t\t++objects_count;\n>> +\t\t}\n> \n> Isn't it sufficient to add .nr to object_count?  Why increment\n> object_count by one as many times as j counts up from 0 to .nr?\n> \n\nThat's my fault, I overlooked optimization here.\n\n> If the merge involves only the remote HEAD, then the lists are all\n> empty, isn't it?  I do not see the point of treating the case where\n> head_status is 3 == 1|2 any special, so the following is sufficient\n> as the replacement for the above and should be far easier to follow,\n> no?  Or am I missing something subtle in your version?\n> \n> \t\tif (src_data->head_status & 01)\n> \t\t\t/* \n>                          * the set of merged heads includes the\n>                          * default branch of the remote, aka HEAD,\n> \t\t\t * which is not counted in any of the lists\n> \t\t\t * in src_data.\n> \t\t\t */\n> \t\t\tobject_count++;\n> \n>                 object_count += (src->data->branch.nr +\n>                                  src->data->r_branch.nr +\n>                                  src->data->tag.nr +\n>                                  src->data->generic.nr);\n> \n> By the way, if you read the original code, you may have noticed that\n> our codebase prefers post-increment over pre-increment, when the\n> difference does not matter (in other words, \"a = ++b;\" is perfectly\n> legit; it is just that we do not say \"++b;\" when \"b++;\" would do).\n> \n\nI copied behaviour of 'fmt_merge_msg_title' function here. Thanks for\nclarifying special cases and optimizing it. I didn't know most of these\ndetails about head_status and branches/tags/etc lists. I'll replace it\nwith your optimized version.\n\n>> +\t}\n>> +\n>> +\treturn objects_count;\n>> +}\n>> +\n>>  static void fmt_merge_msg_title(struct strbuf *out,\n>>  \t\t\t\tconst char *current_branch)\n>>  {\n>> @@ -467,6 +503,68 @@ static void fmt_merge_msg_title(struct strbuf *out,\n>>  \t\tstrbuf_addf(out, \" into %s\\n\", current_branch);\n>>  }\n>>  \n>> +static void fmt_merge_msg_title_multiline(struct strbuf *out,\n>> +\t\t\t\tconst char *current_branch, int count)\n>> +{\n>> +\tint i = 0;\n>> +\tint j = 0;\n>> +\n>> +\tif (!strcmp(\"master\", current_branch))\n>> +\t\tstrbuf_addf(out, \"Merge %d %s\\n\", count, (count == 1) ? \"commit\" : \"commits\");\n>> +\telse\n>> +\t\tstrbuf_addf(out, \"Merge %d %s into %s\\n\", count, (count == 1) ? \"commit\" : \"commits\", current_branch);\n> \n> Overlong lines.\n> \n\nThanks, I'll fix it.\n\n>> +\tstrbuf_addch(out, '\\n');\n>> +\n>> +\tif (count == 1)\n>> +\t\tstrbuf_addstr(out, \"Following commit is merged:\\n\");\n>> +\telse\n>> +\t\tstrbuf_addstr(out, \"Following commits are merged:\\n\");\n> \n> The above makes me wonder if you are better off having \"if there is\n> only one, do these things, otherwise do these other things\" at the\n> top level, i.e.\n> \n> \tif (count == 1) {\n> \t\tstrbuf_addstr(out, \"Merge 1 commit\");\n> \t\tif (strcmp(current_branch, \"master\"))\n> \t\t\tstrbuf_addstr(out, \" into %s\", current_branch);\n> \t\tstrbuf_addstr(out, \"\\n\\nFollowing commit is ...\");\n> \t} else {\n> \t\t...similarly...\n> \t}\n> \n> But stepping back a bit, is there a point in handing count==1 case\n> in this new format in the first place?  Why waste the most precious\n> information real estate, which is the title line, to say a lot less\n> informative \"merge 1 commit\" instead of what topic is merged there?\n> \n\nBy default, this new format is disabled. In patch 2 it's enabled only\nwhen at least 2 new objects are merged. Due to that new message format\nshouldn't ever be used with default settings for only 1 merged object,\nbut user could set new configuration option to '1' and hit this code path.\n\nIf such format for just 1 merged object is undesired, I can modify code\nand documentation to make sure it turns on only when at least 2 objects\nare merged, and get rid of 1 merged object use-case after that.\n\n> Also, I am not sure if \"Following commits are merged\" is a good\n> message for a few reasons.\n> \n>  - Obviously, we allow pulling and merging a signed tag and in that\n>    case, we are merging a tag, not a commit.\n> \n>  - When we pull a topic branch and nothing else, the above code\n>    would say \"Merge 1 commit\", but there may probably be more than\n>    one commit on the history leading to that commit we are merging\n>    into our history.\n> \n> \n> What actually is happening, instead of \"merge 1 commit\", is that we\n> are merging one lineage of history, which may have one or more\n> commits.  The phrase suitable to call the lineage of history may be\n> \"a topic branch\", but it may be \"a release tag\", in which case what\n> you are merging would be the entire history built while developing\n> towards that tag that you have been missing.\n> \n\nI'll describe a bit why I used a word 'commit' in this code. When\ndeciding how to name merged objects, I came down to two options: just\n'object' or 'commit'. I used 'commit' because 'man git-merge' usually\nuses this word. What word would you want to use here instead? An\n'object'? 'History'? Something else?\n"}]}