{"thread":{"id":"42743","subject":"[PATCH 0/9] Use merge_recursive() directly in the builtin am","startedAt":"2016-06-29T11:36:49Z","lastAt":"2016-08-10T21:16:43Z","messageCount":262,"participants":["Johannes Schindelin","Junio C Hamano","Eric Sunshine","Johannes Sixt","Jeff King","Eric Wong","Duy Nguyen","Jakub Narębski","Stefan Beller","Richard Ipsum","Philip Oakley","Josh Triplett","Lars Schneider","Michael Haggerty","Michael J Gruber"],"isPatch":true,"patchVersion":1,"patchTotal":9},"messages":[{"id":"290466","messageId":"cover.1467199553.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":null,"subject":"[PATCH 0/9] Use merge_recursive() directly in the builtin am","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-29T11:36:36Z","receivedAt":"2016-06-29T11:36:49Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This is the long-awaited re-roll of the attempt to avoid spawning\nmerge-recursive from the builtin am and use merge_recursive() directly\ninstead.\n\nAs indicated in the message of the final commit, the performance\nimprovement is modest, if noticable.\n\nThe *real* reason for the reroll is that I need a libified recursive\nmerge to accelerate the interactive rebase by teaching the sequencer to\ndo rebase -i's grunt work.\n\nIn other words, this is one of those 13 (now 14) patch series leading up\nto a faster interactive rebase.\n\nIt did take quite a while to go through the code \"with a fine-toothed\ncomb\", and it did turn up a few surprises.\n\nFor example, the recursive merge calls remove_file() a couple of times\nbut actually does not always care about the return value, and rightfully\nso. When updating a working tree, for example, where a file was\nreplaced by a directory, the code may create the directory first\n(implicitly deleting the file) and only then attempt to remove the file,\nfailing because it is a directory already.\n\nOne might argue that this logic is flawed, then, and I would agree.\nRight now my focus is on rebase -i and I want to avoid getting\nside-tracked by the recursive merge logic. So the clean-up will need to\nwait for another day (or week).\n\nWe also need to be extra careful to retain backwards-compatibility. The\ntest script t6022-merge-rename.sh, for example, verifies that `git pull`\nexits with status 128 in case of a fatal error. To that end, we need to\nmake sure that fatal errors are handled by existing (builtin) users via\nexit(128) (or die(), which calls exit(128) at the end). New users (such\nas a builtin helper doing rebase -i's grunt work) may want to print some\nhelpful advice what happened and how to get out of this mess before\nerroring out.\n\nIn contrast to the original attempt at libifying merge_recursive() (as\npart of fixing a regression in the builtin am which wanted to print some\nadvice, but could not, because the merge machinery die()d before it\ncould), I no longer use the \"gently\" flag. Better to get it right to\nbegin with: fatal errors are indicated by a negative return value. No\ndying without proper cause.\n\nIn an earlier iteration of this patch series which was not sent to the\nmailing list, I used the special return vale -128 to indicate \"real\nfatal errors\". This turned out to be unnecessary: returning -1 always,\nto indicate that the operation could not complete successfully, is the\nappropriate way to handle all errors.\n\nAs this patch series touches rather important code, I would really\nappreciate thorough reviews, with a particular focus on what regressions\nthis patch series might introduce.\n\n\nJohannes Schindelin (8):\n  Report bugs consistently\n  merge-recursive: clarify code in was_tracked()\n  Prepare the builtins for a libified merge_recursive()\n  merge_recursive: abort properly upon errors\n  merge-recursive: avoid returning a wholesale struct\n  merge-recursive: allow write_tree_from_memory() to error out\n  merge-recursive: handle return values indicating errors\n  merge-recursive: switch to returning errors instead of dying\n\nJunio C Hamano (1):\n  am: make a direct call to merge_recursive\n\n builtin/am.c           |  27 ++--\n builtin/checkout.c     |   4 +-\n builtin/ls-files.c     |   3 +-\n builtin/merge.c        |   4 +\n builtin/update-index.c |   2 +-\n grep.c                 |   8 +-\n imap-send.c            |   2 +-\n merge-recursive.c      | 397 ++++++++++++++++++++++++++++++-------------------\n sequencer.c            |   4 +\n sha1_file.c            |   4 +-\n trailer.c              |   2 +-\n transport.c            |   2 +-\n wt-status.c            |   4 +-\n 13 files changed, 279 insertions(+), 184 deletions(-)\n\nPublished-As: https://github.com/dscho/git/releases/tag/am-3-merge-recursive-direct-v1\n-- \n2.9.0.268.gcabc8b0\n\nbase-commit: cf4c2cfe52be5bd973a4838f73a35d3959ce2f43\n"},{"id":"290467","messageId":"8615dc276828a3f99a27ff2eda9909548a7d435e.1467199553.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467199553.git.johannes.schindelin@gmx.de","subject":"[PATCH 1/9] Report bugs consistently","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-29T11:36:41Z","receivedAt":"2016-06-29T11:36:55Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The vast majority of error messages in Git's source code which report a\nbug use the convention to prefix the message with \"BUG:\".\n\nAs part of cleaning up merge-recursive to stop die()ing except in case of\ndetected bugs, let's just make the remainder of the bug reports consistent\nwith the de facto rule.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/ls-files.c     |  3 ++-\n builtin/update-index.c |  2 +-\n grep.c                 |  8 ++++----\n imap-send.c            |  2 +-\n merge-recursive.c      | 13 ++++++-------\n sha1_file.c            |  4 ++--\n trailer.c              |  2 +-\n transport.c            |  2 +-\n wt-status.c            |  4 ++--\n 9 files changed, 20 insertions(+), 20 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex f02e3d2..00ea91a 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -118,7 +118,8 @@ static void show_killed_files(struct dir_struct *dir)\n \t\t\t\t */\n \t\t\t\tpos = cache_name_pos(ent->name, ent->len);\n \t\t\t\tif (0 <= pos)\n-\t\t\t\t\tdie(\"bug in show-killed-files\");\n+\t\t\t\t\tdie(\"BUG: killed-file %.*s not found\",\n+\t\t\t\t\t\tent->len, ent->name);\n \t\t\t\tpos = -pos - 1;\n \t\t\t\twhile (pos < active_nr &&\n \t\t\t\t       ce_stage(active_cache[pos]))\ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex 6cdfd5f..ba04b19 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -1146,7 +1146,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)\n \t\treport(_(\"Untracked cache enabled for '%s'\"), get_git_work_tree());\n \t\tbreak;\n \tdefault:\n-\t\tdie(\"Bug: bad untracked_cache value: %d\", untracked_cache);\n+\t\tdie(\"BUG: bad untracked_cache value: %d\", untracked_cache);\n \t}\n \n \tif (active_cache_changed) {\ndiff --git a/grep.c b/grep.c\nindex 1e15b62..f1ca0a0 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -643,10 +643,10 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)\n \n \tfor (p = opt->header_list; p; p = p->next) {\n \t\tif (p->token != GREP_PATTERN_HEAD)\n-\t\t\tdie(\"bug: a non-header pattern in grep header list.\");\n+\t\t\tdie(\"BUG: a non-header pattern in grep header list.\");\n \t\tif (p->field < GREP_HEADER_FIELD_MIN ||\n \t\t    GREP_HEADER_FIELD_MAX <= p->field)\n-\t\t\tdie(\"bug: unknown header field %d\", p->field);\n+\t\t\tdie(\"BUG: unknown header field %d\", p->field);\n \t\tcompile_regexp(p, opt);\n \t}\n \n@@ -659,7 +659,7 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)\n \n \t\th = compile_pattern_atom(&pp);\n \t\tif (!h || pp != p->next)\n-\t\t\tdie(\"bug: malformed header expr\");\n+\t\t\tdie(\"BUG: malformed header expr\");\n \t\tif (!header_group[p->field]) {\n \t\t\theader_group[p->field] = h;\n \t\t\tcontinue;\n@@ -1464,7 +1464,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle\n \t\tcase GREP_BINARY_TEXT:\n \t\t\tbreak;\n \t\tdefault:\n-\t\t\tdie(\"bug: unknown binary handling mode\");\n+\t\t\tdie(\"BUG: unknown binary handling mode\");\n \t\t}\n \t}\n \ndiff --git a/imap-send.c b/imap-send.c\nindex 938c691..cd39805 100644\n--- a/imap-send.c\n+++ b/imap-send.c\n@@ -511,7 +511,7 @@ static int nfsnprintf(char *buf, int blen, const char *fmt, ...)\n \n \tva_start(va, fmt);\n \tif (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)\n-\t\tdie(\"Fatal: buffer too small. Please report a bug.\");\n+\t\tdie(\"BUG: buffer too small (%d < %d)\", ret, blen);\n \tva_end(va);\n \treturn ret;\n }\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 65cb5d6..98f4632 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -259,7 +259,7 @@ struct tree *write_tree_from_memory(struct merge_options *o)\n \t\t\t\tfprintf(stderr, \"BUG: %d %.*s\\n\", ce_stage(ce),\n \t\t\t\t\t(int)ce_namelen(ce), ce->name);\n \t\t}\n-\t\tdie(\"Bug in merge-recursive.c\");\n+\t\tdie(\"BUG: unmerged index entries in merge-recursive.c\");\n \t}\n \n \tif (!active_cache_tree)\n@@ -955,9 +955,8 @@ static struct merge_file_info merge_file_1(struct merge_options *o,\n \n \t\t\tif (!sha_eq(a->sha1, b->sha1))\n \t\t\t\tresult.clean = 0;\n-\t\t} else {\n-\t\t\tdie(_(\"unsupported object type in the tree\"));\n-\t\t}\n+\t\t} else\n+\t\t\tdie(_(\"BUG: unsupported object type in the tree\"));\n \t}\n \n \treturn result;\n@@ -1343,7 +1342,7 @@ static int process_renames(struct merge_options *o,\n \t\t\tconst char *ren2_dst = ren2->pair->two->path;\n \t\t\tenum rename_type rename_type;\n \t\t\tif (strcmp(ren1_src, ren2_src) != 0)\n-\t\t\t\tdie(\"ren1_src != ren2_src\");\n+\t\t\t\tdie(\"BUG: ren1_src != ren2_src\");\n \t\t\tren2->dst_entry->processed = 1;\n \t\t\tren2->processed = 1;\n \t\t\tif (strcmp(ren1_dst, ren2_dst) != 0) {\n@@ -1377,7 +1376,7 @@ static int process_renames(struct merge_options *o,\n \t\t\tren2 = lookup->util;\n \t\t\tren2_dst = ren2->pair->two->path;\n \t\t\tif (strcmp(ren1_dst, ren2_dst) != 0)\n-\t\t\t\tdie(\"ren1_dst != ren2_dst\");\n+\t\t\t\tdie(\"BUG: ren1_dst != ren2_dst\");\n \n \t\t\tclean_merge = 0;\n \t\t\tren2->processed = 1;\n@@ -1853,7 +1852,7 @@ int merge_trees(struct merge_options *o,\n \t\tfor (i = 0; i < entries->nr; i++) {\n \t\t\tstruct stage_data *e = entries->items[i].util;\n \t\t\tif (!e->processed)\n-\t\t\t\tdie(_(\"Unprocessed path??? %s\"),\n+\t\t\t\tdie(_(\"BUG: unprocessed path??? %s\"),\n \t\t\t\t    entries->items[i].string);\n \t\t}\n \ndiff --git a/sha1_file.c b/sha1_file.c\nindex d5e1121..aa7006c 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -795,7 +795,7 @@ void close_all_packs(void)\n \n \tfor (p = packed_git; p; p = p->next)\n \t\tif (p->do_not_close)\n-\t\t\tdie(\"BUG! Want to close pack marked 'do-not-close'\");\n+\t\t\tdie(\"BUG: Want to close pack marked 'do-not-close'\");\n \t\telse\n \t\t\tclose_pack(p);\n }\n@@ -2330,7 +2330,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,\n \tcase OBJ_OFS_DELTA:\n \tcase OBJ_REF_DELTA:\n \t\tif (data)\n-\t\t\tdie(\"BUG in unpack_entry: left loop at a valid delta\");\n+\t\t\tdie(\"BUG: unpack_entry: left loop at a valid delta\");\n \t\tbreak;\n \tcase OBJ_COMMIT:\n \tcase OBJ_TREE:\ndiff --git a/trailer.c b/trailer.c\nindex 8e48a5c..c6ea9ac 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -562,7 +562,7 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)\n \t\t\twarning(_(\"unknown value '%s' for key '%s'\"), value, conf_key);\n \t\tbreak;\n \tdefault:\n-\t\tdie(\"internal bug in trailer.c\");\n+\t\tdie(\"BUG: trailer.c: unhandled type %d\", type);\n \t}\n \treturn 0;\n }\ndiff --git a/transport.c b/transport.c\nindex 095e61f..52bf997 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -563,7 +563,7 @@ void transport_take_over(struct transport *transport,\n \tstruct git_transport_data *data;\n \n \tif (!transport->smart_options)\n-\t\tdie(\"Bug detected: Taking over transport requires non-NULL \"\n+\t\tdie(\"BUG: taking over transport requires non-NULL \"\n \t\t    \"smart_options field.\");\n \n \tdata = xcalloc(1, sizeof(*data));\ndiff --git a/wt-status.c b/wt-status.c\nindex 4ce4e35..311ae7c 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -263,7 +263,7 @@ static const char *wt_status_unmerged_status_string(int stagemask)\n \tcase 7:\n \t\treturn _(\"both modified:\");\n \tdefault:\n-\t\tdie(_(\"bug: unhandled unmerged status %x\"), stagemask);\n+\t\tdie(_(\"BUG: unhandled unmerged status %x\"), stagemask);\n \t}\n }\n \n@@ -388,7 +388,7 @@ static void wt_status_print_change_data(struct wt_status *s,\n \tstatus_printf(s, color(WT_STATUS_HEADER, s), \"\\t\");\n \twhat = wt_status_diff_status_string(status);\n \tif (!what)\n-\t\tdie(_(\"bug: unhandled diff status %c\"), status);\n+\t\tdie(_(\"BUG: unhandled diff status %c\"), status);\n \tlen = label_width - utf8_strwidth(what);\n \tassert(len >= 0);\n \tif (status == DIFF_STATUS_COPIED || status == DIFF_STATUS_RENAMED)\n-- \n2.9.0.268.gcabc8b0\n\n\n"},{"id":"290468","messageId":"dd3e2cf842fd5e11e31914aa55b8b995e8d3d75c.1467199553.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467199553.git.johannes.schindelin@gmx.de","subject":"[PATCH 2/9] merge-recursive: clarify code in was_tracked()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-29T11:36:45Z","receivedAt":"2016-06-29T11:36:58Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It can be puzzling to see that was_tracked() tries to match an index\nentry by name even if cache_name_pos() returned a negative value. Let's\nclarify that cache_name_pos() implicitly looks for stage 0, while we are\nalso okay with finding other stages.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 98f4632..bcb53f0 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -658,6 +658,7 @@ static int was_tracked(const char *path)\n {\n \tint pos = cache_name_pos(path, strlen(path));\n \n+\t/* cache_name_pos() looks for stage == 0, so pos may be < 0 */\n \tif (pos < 0)\n \t\tpos = -1 - pos;\n \twhile (pos < active_nr &&\n-- \n2.9.0.268.gcabc8b0\n\n\n"},{"id":"290469","messageId":"81a74b02ac714a4fa3734dfb774cff6dea3a3471.1467199553.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467199553.git.johannes.schindelin@gmx.de","subject":"[PATCH 4/9] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-29T11:36:51Z","receivedAt":"2016-06-29T11:37:04Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"There are a couple of places where return values indicating errors\nare ignored. Let's teach them manners.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 10 ++++++++--\n 1 file changed, 8 insertions(+), 2 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex bcb53f0..c4ece96 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1944,8 +1944,9 @@ int merge_recursive(struct merge_options *o,\n \t\tsaved_b2 = o->branch2;\n \t\to->branch1 = \"Temporary merge branch 1\";\n \t\to->branch2 = \"Temporary merge branch 2\";\n-\t\tmerge_recursive(o, merged_common_ancestors, iter->item,\n-\t\t\t\tNULL, &merged_common_ancestors);\n+\t\tif (merge_recursive(o, merged_common_ancestors, iter->item,\n+\t\t\t\tNULL, &merged_common_ancestors) < 0)\n+\t\t\treturn -1;\n \t\to->branch1 = saved_b1;\n \t\to->branch2 = saved_b2;\n \t\to->call_depth--;\n@@ -1961,6 +1962,8 @@ int merge_recursive(struct merge_options *o,\n \to->ancestor = \"merged common ancestors\";\n \tclean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree,\n \t\t\t    &mrtree);\n+\tif (clean < 0)\n+\t\treturn clean;\n \n \tif (o->call_depth) {\n \t\t*result = make_virtual_commit(mrtree, \"merged tree\");\n@@ -2017,6 +2020,9 @@ int merge_recursive_generic(struct merge_options *o,\n \thold_locked_index(lock, 1);\n \tclean = merge_recursive(o, head_commit, next_commit, ca,\n \t\t\tresult);\n+\tif (clean < 0)\n+\t\treturn clean;\n+\n \tif (active_cache_changed &&\n \t    write_locked_index(&the_index, lock, COMMIT_LOCK))\n \t\treturn error(_(\"Unable to write index.\"));\n-- \n2.9.0.268.gcabc8b0\n\n\n"},{"id":"290470","messageId":"753eabc5193c148c67e64ed5d070b6ff08f51d82.1467199553.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467199553.git.johannes.schindelin@gmx.de","subject":"[PATCH 3/9] Prepare the builtins for a libified merge_recursive()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-29T11:36:48Z","receivedAt":"2016-06-29T11:37:10Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"A truly libified function does not die() just for fun. As such, the\nrecursive merge will convert all die() calls to return -1 instead in the\nnext commits, giving the caller a chance at least to print some helpful\nmessage.\n\nLet's prepare the builtins for this fatal error condition, even if we do\nnot really do more than imitating the previous die()'s exit(128): this is\nwhat callers of e.g. `git merge` have come to expect.\n\nNote that the callers of the sequencer (revert and cherry-pick) already\nfail fast even for the return value -1; The only difference is that they\nnow get a chance to say \"<command> failed\".\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/checkout.c | 4 +++-\n builtin/merge.c    | 4 ++++\n sequencer.c        | 4 ++++\n 3 files changed, 11 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex c3486bd..14312f7 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -567,8 +567,10 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\to.ancestor = old->name;\n \t\t\to.branch1 = new->name;\n \t\t\to.branch2 = \"local\";\n-\t\t\tmerge_trees(&o, new->commit->tree, work,\n+\t\t\tret = merge_trees(&o, new->commit->tree, work,\n \t\t\t\told->commit->tree, &result);\n+\t\t\tif (ret < 0)\n+\t\t\t\texit(128);\n \t\t\tret = reset_tree(new->commit->tree, opts, 0,\n \t\t\t\t\t writeout_error);\n \t\t\tif (ret)\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex b555a1b..133b853 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -682,6 +682,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,\n \t\thold_locked_index(&lock, 1);\n \t\tclean = merge_recursive(&o, head,\n \t\t\t\tremoteheads->item, reversed, &result);\n+\t\tif (clean < 0)\n+\t\t\texit(128);\n \t\tif (active_cache_changed &&\n \t\t    write_locked_index(&the_index, &lock, COMMIT_LOCK))\n \t\t\tdie (_(\"unable to write %s\"), get_index_file());\n@@ -1550,6 +1552,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \t\tret = try_merge_strategy(use_strategies[i]->name,\n \t\t\t\t\t common, remoteheads,\n \t\t\t\t\t head_commit, head_arg);\n+\t\tif (ret < 0)\n+\t\t\texit(128);\n \t\tif (!option_commit && !ret) {\n \t\t\tmerge_was_ok = 1;\n \t\t\t/*\ndiff --git a/sequencer.c b/sequencer.c\nindex c6362d6..13b794a 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -293,6 +293,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \tclean = merge_trees(&o,\n \t\t\t    head_tree,\n \t\t\t    next_tree, base_tree, &result);\n+\tif (clean < 0)\n+\t\treturn clean;\n \n \tif (active_cache_changed &&\n \t    write_locked_index(&the_index, &index_lock, COMMIT_LOCK))\n@@ -561,6 +563,8 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || opts->action == REPLAY_REVERT) {\n \t\tres = do_recursive_merge(base, next, base_label, next_label,\n \t\t\t\t\t head, &msgbuf, opts);\n+\t\tif (res < 0)\n+\t\t\treturn res;\n \t\twrite_message(&msgbuf, git_path_merge_msg());\n \t} else {\n \t\tstruct commit_list *common = NULL;\n-- \n2.9.0.268.gcabc8b0\n\n\n"},{"id":"290471","messageId":"a9646a7f2ea07ecbe1ef22f7360afc8c0d950535.1467199553.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467199553.git.johannes.schindelin@gmx.de","subject":"[PATCH 6/9] merge-recursive: allow write_tree_from_memory() to error out","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-29T11:37:01Z","receivedAt":"2016-06-29T11:37:14Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It is possible that a tree cannot be written (think: disk full). We\nwill want to give the caller a chance to clean up instead of letting\nthe program die() in such a case.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex d56651c9..6ab7dfc 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1875,8 +1875,8 @@ int merge_trees(struct merge_options *o,\n \telse\n \t\tclean = 1;\n \n-\tif (o->call_depth)\n-\t\t*result = write_tree_from_memory(o);\n+\tif (o->call_depth && !(*result = write_tree_from_memory(o)))\n+\t\treturn -1;\n \n \treturn clean;\n }\n-- \n2.9.0.268.gcabc8b0\n\n\n"},{"id":"290472","messageId":"4f2817208cf2f7d4e839ddb6818bf652b0aa633c.1467199553.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467199553.git.johannes.schindelin@gmx.de","subject":"[PATCH 7/9] merge-recursive: handle return values indicating errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-29T11:37:04Z","receivedAt":"2016-06-29T11:37:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We are about to libify the recursive merge machinery, where we only\ndie() in case of a bug or memory contention. To that end, we must heed\nnegative return values as indicating errors.\n\nThis requires our functions to be careful to pass through error\nconditions in call chains, and for quite a few functions this means\nthat they have to return values to begin with.\n\nThe next step will be to convert the places where we currently die() to\nreturn negative values (read: -1) instead.\n\nNote that we ignore errors reported by make_room_for_path(), consistent\nwith the previous behavior (update_file_flags() used the return value of\nmake_room_for_path() only to indicate an early return, but not a fatal\nerror): if the error is really a fatal error, we will notice later; If\nnot, it was not that serious a problem to begin with. (Witnesses in\nfavor of this reasoning are t4151-am-abort and t7610-mergetool, which\nwould start failing if we stopped on errors reported by\nmake_room_for_path()).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 251 ++++++++++++++++++++++++++++++++++--------------------\n 1 file changed, 158 insertions(+), 93 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 6ab7dfc..bb075e3 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -266,8 +266,10 @@ struct tree *write_tree_from_memory(struct merge_options *o)\n \t\tactive_cache_tree = cache_tree();\n \n \tif (!cache_tree_fully_valid(active_cache_tree) &&\n-\t    cache_tree_update(&the_index, 0) < 0)\n-\t\tdie(_(\"error building trees\"));\n+\t    cache_tree_update(&the_index, 0) < 0) {\n+\t\terror(_(\"error building trees\"));\n+\t\treturn NULL;\n+\t}\n \n \tresult = lookup_tree(active_cache_tree->sha1);\n \n@@ -548,19 +550,17 @@ static int update_stages(const char *path, const struct diff_filespec *o,\n \t */\n \tint clear = 1;\n \tint options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_SKIP_DFCHECK;\n+\tint ret = 0;\n+\n \tif (clear)\n-\t\tif (remove_file_from_cache(path))\n-\t\t\treturn -1;\n-\tif (o)\n-\t\tif (add_cacheinfo(o->mode, o->sha1, path, 1, 0, options))\n-\t\t\treturn -1;\n-\tif (a)\n-\t\tif (add_cacheinfo(a->mode, a->sha1, path, 2, 0, options))\n-\t\t\treturn -1;\n-\tif (b)\n-\t\tif (add_cacheinfo(b->mode, b->sha1, path, 3, 0, options))\n-\t\t\treturn -1;\n-\treturn 0;\n+\t\tret = remove_file_from_cache(path);\n+\tif (!ret && o)\n+\t\tret = add_cacheinfo(o->mode, o->sha1, path, 1, 0, options);\n+\tif (!ret && a)\n+\t\tret = add_cacheinfo(a->mode, a->sha1, path, 2, 0, options);\n+\tif (!ret && b)\n+\t\tret = add_cacheinfo(b->mode, b->sha1, path, 3, 0, options);\n+\treturn ret;\n }\n \n static void update_entry(struct stage_data *entry,\n@@ -736,7 +736,7 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n }\n \n-static void update_file_flags(struct merge_options *o,\n+static int update_file_flags(struct merge_options *o,\n \t\t\t      const unsigned char *sha,\n \t\t\t      unsigned mode,\n \t\t\t      const char *path,\n@@ -777,8 +777,7 @@ static void update_file_flags(struct merge_options *o,\n \n \t\tif (make_room_for_path(o, path) < 0) {\n \t\t\tupdate_wd = 0;\n-\t\t\tfree(buf);\n-\t\t\tgoto update_index;\n+\t\t\tgoto free_buf;\n \t\t}\n \t\tif (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) {\n \t\t\tint fd;\n@@ -801,20 +800,22 @@ static void update_file_flags(struct merge_options *o,\n \t\t} else\n \t\t\tdie(_(\"do not know what to do with %06o %s '%s'\"),\n \t\t\t    mode, sha1_to_hex(sha), path);\n+free_buf:\n \t\tfree(buf);\n \t}\n  update_index:\n \tif (update_cache)\n \t\tadd_cacheinfo(mode, sha, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n+\treturn 0;\n }\n \n-static void update_file(struct merge_options *o,\n+static int update_file(struct merge_options *o,\n \t\t\tint clean,\n \t\t\tconst unsigned char *sha,\n \t\t\tunsigned mode,\n \t\t\tconst char *path)\n {\n-\tupdate_file_flags(o, sha, mode, path, o->call_depth || clean, !o->call_depth);\n+\treturn update_file_flags(o, sha, mode, path, o->call_depth || clean, !o->call_depth);\n }\n \n /* Low level file merging, update and removal */\n@@ -1010,7 +1011,7 @@ static int merge_file_one(struct merge_options *o,\n \treturn merge_file_1(o, &one, &a, &b, branch1, branch2, mfi);\n }\n \n-static void handle_change_delete(struct merge_options *o,\n+static int handle_change_delete(struct merge_options *o,\n \t\t\t\t const char *path,\n \t\t\t\t const unsigned char *o_sha, int o_mode,\n \t\t\t\t const unsigned char *a_sha, int a_mode,\n@@ -1018,6 +1019,7 @@ static void handle_change_delete(struct merge_options *o,\n \t\t\t\t const char *change, const char *change_past)\n {\n \tchar *renamed = NULL;\n+\tint ret = 0;\n \tif (dir_in_way(path, !o->call_depth)) {\n \t\trenamed = unique_path(o, path, a_sha ? o->branch1 : o->branch2);\n \t}\n@@ -1028,21 +1030,23 @@ static void handle_change_delete(struct merge_options *o,\n \t\t * correct; since there is no true \"middle point\" between\n \t\t * them, simply reuse the base version for virtual merge base.\n \t\t */\n-\t\tremove_file_from_cache(path);\n-\t\tupdate_file(o, 0, o_sha, o_mode, renamed ? renamed : path);\n+\t\tret = remove_file_from_cache(path);\n+\t\tif (!ret)\n+\t\t\tret = update_file(o, 0, o_sha, o_mode,\n+\t\t\t\t\t  renamed ? renamed : path);\n \t} else if (!a_sha) {\n \t\tif (!renamed) {\n \t\t\toutput(o, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n \t\t\t       \"and %s in %s. Version %s of %s left in tree.\"),\n \t\t\t       change, path, o->branch1, change_past,\n \t\t\t       o->branch2, o->branch2, path);\n-\t\t\tupdate_file(o, 0, b_sha, b_mode, path);\n+\t\t\tret = update_file(o, 0, b_sha, b_mode, path);\n \t\t} else {\n \t\t\toutput(o, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n \t\t\t       \"and %s in %s. Version %s of %s left in tree at %s.\"),\n \t\t\t       change, path, o->branch1, change_past,\n \t\t\t       o->branch2, o->branch2, path, renamed);\n-\t\t\tupdate_file(o, 0, b_sha, b_mode, renamed);\n+\t\t\tret = update_file(o, 0, b_sha, b_mode, renamed);\n \t\t}\n \t} else {\n \t\tif (!renamed) {\n@@ -1055,7 +1059,7 @@ static void handle_change_delete(struct merge_options *o,\n \t\t\t       \"and %s in %s. Version %s of %s left in tree at %s.\"),\n \t\t\t       change, path, o->branch2, change_past,\n \t\t\t       o->branch1, o->branch1, path, renamed);\n-\t\t\tupdate_file(o, 0, a_sha, a_mode, renamed);\n+\t\t\tret = update_file(o, 0, a_sha, a_mode, renamed);\n \t\t}\n \t\t/*\n \t\t * No need to call update_file() on path when !renamed, since\n@@ -1065,9 +1069,11 @@ static void handle_change_delete(struct merge_options *o,\n \t\t */\n \t}\n \tfree(renamed);\n+\n+\treturn ret;\n }\n \n-static void conflict_rename_delete(struct merge_options *o,\n+static int conflict_rename_delete(struct merge_options *o,\n \t\t\t\t   struct diff_filepair *pair,\n \t\t\t\t   const char *rename_branch,\n \t\t\t\t   const char *other_branch)\n@@ -1078,6 +1084,7 @@ static void conflict_rename_delete(struct merge_options *o,\n \tconst unsigned char *b_sha = NULL;\n \tint a_mode = 0;\n \tint b_mode = 0;\n+\tint ret = 0;\n \n \tif (rename_branch == o->branch1) {\n \t\ta_sha = dest->sha1;\n@@ -1087,21 +1094,22 @@ static void conflict_rename_delete(struct merge_options *o,\n \t\tb_mode = dest->mode;\n \t}\n \n-\thandle_change_delete(o,\n+\tret = handle_change_delete(o,\n \t\t\t     o->call_depth ? orig->path : dest->path,\n \t\t\t     orig->sha1, orig->mode,\n \t\t\t     a_sha, a_mode,\n \t\t\t     b_sha, b_mode,\n \t\t\t     _(\"rename\"), _(\"renamed\"));\n-\n-\tif (o->call_depth) {\n-\t\tremove_file_from_cache(dest->path);\n-\t} else {\n-\t\tupdate_stages(dest->path, NULL,\n+\tif (ret < 0)\n+\t\treturn ret;\n+\tif (o->call_depth)\n+\t\tret = remove_file_from_cache(dest->path);\n+\telse\n+\t\tret = update_stages(dest->path, NULL,\n \t\t\t      rename_branch == o->branch1 ? dest : NULL,\n \t\t\t      rename_branch == o->branch1 ? NULL : dest);\n-\t}\n \n+\treturn ret;\n }\n \n static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,\n@@ -1117,7 +1125,7 @@ static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,\n \treturn target;\n }\n \n-static void handle_file(struct merge_options *o,\n+static int handle_file(struct merge_options *o,\n \t\t\tstruct diff_filespec *rename,\n \t\t\tint stage,\n \t\t\tstruct rename_conflict_info *ci)\n@@ -1127,6 +1135,7 @@ static void handle_file(struct merge_options *o,\n \tconst char *cur_branch, *other_branch;\n \tstruct diff_filespec other;\n \tstruct diff_filespec *add;\n+\tint ret;\n \n \tif (stage == 2) {\n \t\tdst_entry = ci->dst_entry1;\n@@ -1141,7 +1150,8 @@ static void handle_file(struct merge_options *o,\n \tadd = filespec_from_entry(&other, dst_entry, stage ^ 1);\n \tif (add) {\n \t\tchar *add_name = unique_path(o, rename->path, other_branch);\n-\t\tupdate_file(o, 0, add->sha1, add->mode, add_name);\n+\t\tif ((ret = update_file(o, 0, add->sha1, add->mode, add_name)))\n+\t\t\treturn ret;\n \n \t\tremove_file(o, 0, rename->path, 0);\n \t\tdst_name = unique_path(o, rename->path, cur_branch);\n@@ -1152,23 +1162,27 @@ static void handle_file(struct merge_options *o,\n \t\t\t       rename->path, other_branch, dst_name);\n \t\t}\n \t}\n-\tupdate_file(o, 0, rename->sha1, rename->mode, dst_name);\n-\tif (stage == 2)\n-\t\tupdate_stages(rename->path, NULL, rename, add);\n+\tif ((ret = update_file(o, 0, rename->sha1, rename->mode, dst_name)))\n+\t\t; /* fall through, do allow dst_name to be released */\n+\telse if (stage == 2)\n+\t\tret = update_stages(rename->path, NULL, rename, add);\n \telse\n-\t\tupdate_stages(rename->path, NULL, add, rename);\n+\t\tret = update_stages(rename->path, NULL, add, rename);\n \n \tif (dst_name != rename->path)\n \t\tfree(dst_name);\n+\n+\treturn ret;\n }\n \n-static void conflict_rename_rename_1to2(struct merge_options *o,\n+static int conflict_rename_rename_1to2(struct merge_options *o,\n \t\t\t\t\tstruct rename_conflict_info *ci)\n {\n \t/* One file was renamed in both branches, but to different names. */\n \tstruct diff_filespec *one = ci->pair1->one;\n \tstruct diff_filespec *a = ci->pair1->two;\n \tstruct diff_filespec *b = ci->pair2->two;\n+\tint ret = 0;\n \n \toutput(o, 1, _(\"CONFLICT (rename/rename): \"\n \t       \"Rename \\\"%s\\\"->\\\"%s\\\" in branch \\\"%s\\\" \"\n@@ -1181,12 +1195,12 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n \t\tstruct diff_filespec other;\n \t\tstruct diff_filespec *add;\n \n-\t\tif (merge_file_one(o, one->path,\n+\t\tif ((ret = merge_file_one(o, one->path,\n \t\t\t\t one->sha1, one->mode,\n \t\t\t\t a->sha1, a->mode,\n \t\t\t\t b->sha1, b->mode,\n-\t\t\t\t ci->branch1, ci->branch2, &mfi) < 0)\n-\t\t\treturn;\n+\t\t\t\t ci->branch1, ci->branch2, &mfi)))\n+\t\t\treturn ret;\n \n \t\t/*\n \t\t * FIXME: For rename/add-source conflicts (if we could detect\n@@ -1194,7 +1208,8 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n \t\t * pathname and then either rename the add-source file to that\n \t\t * unique path, or use that unique path instead of src here.\n \t\t */\n-\t\tupdate_file(o, 0, mfi.sha, mfi.mode, one->path);\n+\t\tif ((ret = update_file(o, 0, mfi.sha, mfi.mode, one->path)))\n+\t\t\treturn ret;\n \n \t\t/*\n \t\t * Above, we put the merged content at the merge-base's\n@@ -1205,22 +1220,31 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n \t\t * resolving the conflict at that path in its favor.\n \t\t */\n \t\tadd = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);\n-\t\tif (add)\n-\t\t\tupdate_file(o, 0, add->sha1, add->mode, a->path);\n+\t\tif (add) {\n+\t\t\tif ((ret = update_file(o, 0, add->sha1, add->mode,\n+\t\t\t\t\ta->path)))\n+\t\t\t\treturn ret;\n+\t\t}\n \t\telse\n \t\t\tremove_file_from_cache(a->path);\n \t\tadd = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);\n-\t\tif (add)\n-\t\t\tupdate_file(o, 0, add->sha1, add->mode, b->path);\n+\t\tif (add) {\n+\t\t\tif ((ret = update_file(o, 0, add->sha1, add->mode,\n+\t\t\t\t\tb->path)))\n+\t\t\t\treturn ret;\n+\t\t}\n \t\telse\n \t\t\tremove_file_from_cache(b->path);\n \t} else {\n-\t\thandle_file(o, a, 2, ci);\n-\t\thandle_file(o, b, 3, ci);\n+\t\tif ((ret = handle_file(o, a, 2, ci)) ||\n+\t\t    (ret = handle_file(o, b, 3, ci)))\n+\t\t\treturn ret;\n \t}\n+\n+\treturn ret;\n }\n \n-static void conflict_rename_rename_2to1(struct merge_options *o,\n+static int conflict_rename_rename_2to1(struct merge_options *o,\n \t\t\t\t\tstruct rename_conflict_info *ci)\n {\n \t/* Two files, a & b, were renamed to the same thing, c. */\n@@ -1231,6 +1255,7 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \tchar *path = c1->path; /* == c2->path */\n \tstruct merge_file_info mfi_c1;\n \tstruct merge_file_info mfi_c2;\n+\tint ret;\n \n \toutput(o, 1, _(\"CONFLICT (rename/rename): \"\n \t       \"Rename %s->%s in %s. \"\n@@ -1241,13 +1266,13 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \tremove_file(o, 1, a->path, o->call_depth || would_lose_untracked(a->path));\n \tremove_file(o, 1, b->path, o->call_depth || would_lose_untracked(b->path));\n \n-\tif (merge_file_special_markers(o, a, c1, &ci->ren1_other,\n+\tif ((ret = merge_file_special_markers(o, a, c1, &ci->ren1_other,\n \t\t\t\t\t    o->branch1, c1->path,\n-\t\t\t\t\t    o->branch2, ci->ren1_other.path, &mfi_c1) < 0 ||\n-\t    merge_file_special_markers(o, b, &ci->ren2_other, c2,\n+\t\t\t\t\t    o->branch2, ci->ren1_other.path, &mfi_c1)) ||\n+\t    (ret = merge_file_special_markers(o, b, &ci->ren2_other, c2,\n \t\t\t\t\t    o->branch1, ci->ren2_other.path,\n-\t\t\t\t\t    o->branch2, c2->path, &mfi_c2) < 0)\n-\t\treturn;\n+\t\t\t\t\t    o->branch2, c2->path, &mfi_c2)))\n+\t\treturn ret;\n \n \tif (o->call_depth) {\n \t\t/*\n@@ -1258,26 +1283,32 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \t\t * again later for the non-recursive merge.\n \t\t */\n \t\tremove_file(o, 0, path, 0);\n-\t\tupdate_file(o, 0, mfi_c1.sha, mfi_c1.mode, a->path);\n-\t\tupdate_file(o, 0, mfi_c2.sha, mfi_c2.mode, b->path);\n+\t\tret = update_file(o, 0, mfi_c1.sha, mfi_c1.mode, a->path);\n+\t\tif (!ret)\n+\t\t\tret = update_file(o, 0, mfi_c2.sha, mfi_c2.mode,\n+\t\t\t\tb->path);\n \t} else {\n \t\tchar *new_path1 = unique_path(o, path, ci->branch1);\n \t\tchar *new_path2 = unique_path(o, path, ci->branch2);\n \t\toutput(o, 1, _(\"Renaming %s to %s and %s to %s instead\"),\n \t\t       a->path, new_path1, b->path, new_path2);\n \t\tremove_file(o, 0, path, 0);\n-\t\tupdate_file(o, 0, mfi_c1.sha, mfi_c1.mode, new_path1);\n-\t\tupdate_file(o, 0, mfi_c2.sha, mfi_c2.mode, new_path2);\n+\t\tret = update_file(o, 0, mfi_c1.sha, mfi_c1.mode, new_path1);\n+\t\tif (!ret)\n+\t\t\tret = update_file(o, 0, mfi_c2.sha, mfi_c2.mode,\n+\t\t\t\tnew_path2);\n \t\tfree(new_path2);\n \t\tfree(new_path1);\n \t}\n+\n+\treturn ret;\n }\n \n static int process_renames(struct merge_options *o,\n \t\t\t   struct string_list *a_renames,\n \t\t\t   struct string_list *b_renames)\n {\n-\tint clean_merge = 1, i, j;\n+\tint clean_merge = 1, i, j, ret;\n \tstruct string_list a_by_dst = STRING_LIST_INIT_NODUP;\n \tstruct string_list b_by_dst = STRING_LIST_INIT_NODUP;\n \tconst struct rename *sre;\n@@ -1453,12 +1484,14 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t * update_file_flags() instead of\n \t\t\t\t * update_file().\n \t\t\t\t */\n-\t\t\t\tupdate_file_flags(o,\n+\t\t\t\tret = update_file_flags(o,\n \t\t\t\t\t\t  ren1->pair->two->sha1,\n \t\t\t\t\t\t  ren1->pair->two->mode,\n \t\t\t\t\t\t  ren1_dst,\n \t\t\t\t\t\t  1, /* update_cache */\n \t\t\t\t\t\t  0  /* update_wd    */);\n+\t\t\t\tif (ret)\n+\t\t\t\t\tclean_merge = ret;\n \t\t\t} else if (!sha_eq(dst_other.sha1, null_sha1)) {\n \t\t\t\tclean_merge = 0;\n \t\t\t\ttry_merge = 1;\n@@ -1468,23 +1501,29 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t       ren1_dst, branch2);\n \t\t\t\tif (o->call_depth) {\n \t\t\t\t\tstruct merge_file_info mfi;\n-\t\t\t\t\tif (merge_file_one(o, ren1_dst, null_sha1, 0,\n+\t\t\t\t\tif ((ret = merge_file_one(o, ren1_dst, null_sha1, 0,\n \t\t\t\t\t\t\t ren1->pair->two->sha1, ren1->pair->two->mode,\n \t\t\t\t\t\t\t dst_other.sha1, dst_other.mode,\n-\t\t\t\t\t\t\t branch1, branch2, &mfi) < 0)\n-\t\t\t\t\t\treturn -1;\n+\t\t\t\t\t\t\t branch1, branch2, &mfi))) {\n+\t\t\t\t\t\tclean_merge = ret;\n+\t\t\t\t\t\tgoto cleanup_and_return;\n+\t\t\t\t\t}\n \t\t\t\t\toutput(o, 1, _(\"Adding merged %s\"), ren1_dst);\n-\t\t\t\t\tupdate_file(o, 0, mfi.sha, mfi.mode, ren1_dst);\n+\t\t\t\t\tif ((ret = update_file(o, 0, mfi.sha, mfi.mode, ren1_dst)))\n+\t\t\t\t\t\tclean_merge = ret;\n \t\t\t\t\ttry_merge = 0;\n \t\t\t\t} else {\n \t\t\t\t\tchar *new_path = unique_path(o, ren1_dst, branch2);\n \t\t\t\t\toutput(o, 1, _(\"Adding as %s instead\"), new_path);\n-\t\t\t\t\tupdate_file(o, 0, dst_other.sha1, dst_other.mode, new_path);\n+\t\t\t\t\tif ((ret = update_file(o, 0, dst_other.sha1, dst_other.mode, new_path)))\n+\t\t\t\t\t\tclean_merge = ret;\n \t\t\t\t\tfree(new_path);\n \t\t\t\t}\n \t\t\t} else\n \t\t\t\ttry_merge = 1;\n \n+\t\t\tif (clean_merge < 0)\n+\t\t\t\tgoto cleanup_and_return;\n \t\t\tif (try_merge) {\n \t\t\t\tstruct diff_filespec *one, *a, *b;\n \t\t\t\tsrc_other.path = (char *)ren1_src;\n@@ -1511,6 +1550,7 @@ static int process_renames(struct merge_options *o,\n \t\t\t}\n \t\t}\n \t}\n+cleanup_and_return:\n \tstring_list_clear(&a_by_dst, 0);\n \tstring_list_clear(&b_by_dst, 0);\n \n@@ -1573,13 +1613,13 @@ error_return:\n \treturn ret;\n }\n \n-static void handle_modify_delete(struct merge_options *o,\n+static int handle_modify_delete(struct merge_options *o,\n \t\t\t\t const char *path,\n \t\t\t\t unsigned char *o_sha, int o_mode,\n \t\t\t\t unsigned char *a_sha, int a_mode,\n \t\t\t\t unsigned char *b_sha, int b_mode)\n {\n-\thandle_change_delete(o,\n+\treturn handle_change_delete(o,\n \t\t\t     path,\n \t\t\t     o_sha, o_mode,\n \t\t\t     a_sha, a_mode,\n@@ -1599,6 +1639,7 @@ static int merge_content(struct merge_options *o,\n \tstruct merge_file_info mfi;\n \tstruct diff_filespec one, a, b;\n \tunsigned df_conflict_remains = 0;\n+\tint ret;\n \n \tif (!o_sha) {\n \t\treason = _(\"add/add\");\n@@ -1628,10 +1669,10 @@ static int merge_content(struct merge_options *o,\n \t\tif (dir_in_way(path, !o->call_depth))\n \t\t\tdf_conflict_remains = 1;\n \t}\n-\tif (merge_file_special_markers(o, &one, &a, &b,\n+\tif ((ret = merge_file_special_markers(o, &one, &a, &b,\n \t\t\t\t\t o->branch1, path1,\n-\t\t\t\t\t o->branch2, path2, &mfi) < 0)\n-\t\treturn -1;\n+\t\t\t\t\t o->branch2, path2, &mfi)))\n+\t\treturn ret;\n \n \tif (mfi.clean && !df_conflict_remains &&\n \t    sha_eq(mfi.sha, a_sha) && mfi.mode == a_mode) {\n@@ -1658,7 +1699,8 @@ static int merge_content(struct merge_options *o,\n \t\toutput(o, 1, _(\"CONFLICT (%s): Merge conflict in %s\"),\n \t\t\t\treason, path);\n \t\tif (rename_conflict_info && !df_conflict_remains)\n-\t\t\tupdate_stages(path, &one, &a, &b);\n+\t\t\tif ((ret = update_stages(path, &one, &a, &b)))\n+\t\t\t\treturn ret;\n \t}\n \n \tif (df_conflict_remains) {\n@@ -1666,27 +1708,33 @@ static int merge_content(struct merge_options *o,\n \t\tif (o->call_depth) {\n \t\t\tremove_file_from_cache(path);\n \t\t} else {\n-\t\t\tif (!mfi.clean)\n-\t\t\t\tupdate_stages(path, &one, &a, &b);\n-\t\t\telse {\n+\t\t\tif (!mfi.clean) {\n+\t\t\t\tif ((ret = update_stages(path, &one, &a, &b)))\n+\t\t\t\t\treturn ret;\n+\t\t\t} else {\n \t\t\t\tint file_from_stage2 = was_tracked(path);\n \t\t\t\tstruct diff_filespec merged;\n \t\t\t\thashcpy(merged.sha1, mfi.sha);\n \t\t\t\tmerged.mode = mfi.mode;\n \n-\t\t\t\tupdate_stages(path, NULL,\n+\t\t\t\tif ((ret = update_stages(path, NULL,\n \t\t\t\t\t      file_from_stage2 ? &merged : NULL,\n-\t\t\t\t\t      file_from_stage2 ? NULL : &merged);\n+\t\t\t\t\t      file_from_stage2 ? NULL : &merged)))\n+\t\t\t\t\treturn ret;\n \t\t\t}\n \n \t\t}\n \t\tnew_path = unique_path(o, path, rename_conflict_info->branch1);\n \t\toutput(o, 1, _(\"Adding as %s instead\"), new_path);\n-\t\tupdate_file(o, 0, mfi.sha, mfi.mode, new_path);\n+\t\tif ((ret = update_file(o, 0, mfi.sha, mfi.mode, new_path))) {\n+\t\t\tfree(new_path);\n+\t\t\treturn ret;\n+\t\t}\n \t\tfree(new_path);\n \t\tmfi.clean = 0;\n \t} else {\n-\t\tupdate_file(o, mfi.clean, mfi.sha, mfi.mode, path);\n+\t\tif ((ret = update_file(o, mfi.clean, mfi.sha, mfi.mode, path)))\n+\t\t\treturn ret;\n \t}\n \treturn mfi.clean;\n \n@@ -1696,7 +1744,7 @@ static int merge_content(struct merge_options *o,\n static int process_entry(struct merge_options *o,\n \t\t\t const char *path, struct stage_data *entry)\n {\n-\tint clean_merge = 1;\n+\tint clean_merge = 1, ret;\n \tint normalize = o->renormalize;\n \tunsigned o_mode = entry->stages[1].mode;\n \tunsigned a_mode = entry->stages[2].mode;\n@@ -1717,17 +1765,23 @@ static int process_entry(struct merge_options *o,\n \t\t\tbreak;\n \t\tcase RENAME_DELETE:\n \t\t\tclean_merge = 0;\n-\t\t\tconflict_rename_delete(o, conflict_info->pair1,\n+\t\t\tif ((ret = conflict_rename_delete(o,\n+\t\t\t\t\t       conflict_info->pair1,\n \t\t\t\t\t       conflict_info->branch1,\n-\t\t\t\t\t       conflict_info->branch2);\n+\t\t\t\t\t       conflict_info->branch2)))\n+\t\t\t\tclean_merge = ret;\n \t\t\tbreak;\n \t\tcase RENAME_ONE_FILE_TO_TWO:\n \t\t\tclean_merge = 0;\n-\t\t\tconflict_rename_rename_1to2(o, conflict_info);\n+\t\t\tif ((ret = conflict_rename_rename_1to2(o,\n+\t\t\t\t\tconflict_info)))\n+\t\t\t\tclean_merge = ret;\n \t\t\tbreak;\n \t\tcase RENAME_TWO_FILES_TO_ONE:\n \t\t\tclean_merge = 0;\n-\t\t\tconflict_rename_rename_2to1(o, conflict_info);\n+\t\t\tif ((ret = conflict_rename_rename_2to1(o,\n+\t\t\t\t\tconflict_info)))\n+\t\t\t\tclean_merge = ret;\n \t\t\tbreak;\n \t\tdefault:\n \t\t\tentry->processed = 0;\n@@ -1747,8 +1801,9 @@ static int process_entry(struct merge_options *o,\n \t\t} else {\n \t\t\t/* Modify/delete; deleted side may have put a directory in the way */\n \t\t\tclean_merge = 0;\n-\t\t\thandle_modify_delete(o, path, o_sha, o_mode,\n-\t\t\t\t\t     a_sha, a_mode, b_sha, b_mode);\n+\t\t\tif ((ret = handle_modify_delete(o, path, o_sha, o_mode,\n+\t\t\t\t\ta_sha, a_mode, b_sha, b_mode)))\n+\t\t\t\tclean_merge = ret;\n \t\t}\n \t} else if ((!o_sha && a_sha && !b_sha) ||\n \t\t   (!o_sha && !a_sha && b_sha)) {\n@@ -1780,14 +1835,18 @@ static int process_entry(struct merge_options *o,\n \t\t\toutput(o, 1, _(\"CONFLICT (%s): There is a directory with name %s in %s. \"\n \t\t\t       \"Adding %s as %s\"),\n \t\t\t       conf, path, other_branch, path, new_path);\n-\t\t\tupdate_file(o, 0, sha, mode, new_path);\n-\t\t\tif (o->call_depth)\n+\t\t\tret = update_file(o, 0, sha, mode, new_path);\n+\t\t\tif (ret)\n+\t\t\t\tclean_merge = ret;\n+\t\t\telse if (o->call_depth)\n \t\t\t\tremove_file_from_cache(path);\n \t\t\tfree(new_path);\n \t\t} else {\n \t\t\toutput(o, 2, _(\"Adding %s\"), path);\n \t\t\t/* do not overwrite file if already present */\n-\t\t\tupdate_file_flags(o, sha, mode, path, 1, !a_sha);\n+\t\t\tif ((ret = update_file_flags(o, sha, mode, path, 1,\n+\t\t\t\t\t!a_sha)))\n+\t\t\t\tclean_merge = ret;\n \t\t}\n \t} else if (a_sha && b_sha) {\n \t\t/* Case C: Added in both (check for same permissions) and */\n@@ -1850,12 +1909,18 @@ int merge_trees(struct merge_options *o,\n \t\tre_head  = get_renames(o, head, common, head, merge, entries);\n \t\tre_merge = get_renames(o, merge, common, head, merge, entries);\n \t\tclean = process_renames(o, re_head, re_merge);\n+\t\tif (clean < 0)\n+\t\t\treturn clean;\n \t\tfor (i = entries->nr-1; 0 <= i; i--) {\n \t\t\tconst char *path = entries->items[i].string;\n \t\t\tstruct stage_data *e = entries->items[i].util;\n-\t\t\tif (!e->processed\n-\t\t\t\t&& !process_entry(o, path, e))\n-\t\t\t\tclean = 0;\n+\t\t\tif (!e->processed) {\n+\t\t\t\tint ret = process_entry(o, path, e);\n+\t\t\t\tif (!ret)\n+\t\t\t\t\tclean = 0;\n+\t\t\t\telse if (ret < 0)\n+\t\t\t\t\treturn ret;\n+\t\t\t}\n \t\t}\n \t\tfor (i = 0; i < entries->nr; i++) {\n \t\t\tstruct stage_data *e = entries->items[i].util;\n-- \n2.9.0.268.gcabc8b0\n\n\n"},{"id":"290473","messageId":"c33e9cdb1ec6cbebcc3124a62b7b9d52b92cf6c9.1467199553.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467199553.git.johannes.schindelin@gmx.de","subject":"[PATCH 5/9] merge-recursive: avoid returning a wholesale struct","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-29T11:36:54Z","receivedAt":"2016-06-29T11:37:22Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It is technically allowed, as per C89, for functions' return type to\nbe complete structs (i.e. *not* just pointers to structs), but it is\nbad practice.\n\nThis is a very late attempt to contain the damage done by this developer\nin 6d297f8 (Status update on merge-recursive in C, 2006-07-08) which\nintroduced such a return type.\n\nIt will also help the current effort to libify merge-recursive.c, as\nit will allow us to return proper error codes later.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 93 ++++++++++++++++++++++++++++++-------------------------\n 1 file changed, 50 insertions(+), 43 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex c4ece96..d56651c9 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -888,47 +888,47 @@ static int merge_3way(struct merge_options *o,\n \treturn merge_status;\n }\n \n-static struct merge_file_info merge_file_1(struct merge_options *o,\n+static int merge_file_1(struct merge_options *o,\n \t\t\t\t\t   const struct diff_filespec *one,\n \t\t\t\t\t   const struct diff_filespec *a,\n \t\t\t\t\t   const struct diff_filespec *b,\n \t\t\t\t\t   const char *branch1,\n-\t\t\t\t\t   const char *branch2)\n+\t\t\t\t\t   const char *branch2,\n+\t\t\t\t\t   struct merge_file_info *result)\n {\n-\tstruct merge_file_info result;\n-\tresult.merge = 0;\n-\tresult.clean = 1;\n+\tresult->merge = 0;\n+\tresult->clean = 1;\n \n \tif ((S_IFMT & a->mode) != (S_IFMT & b->mode)) {\n-\t\tresult.clean = 0;\n+\t\tresult->clean = 0;\n \t\tif (S_ISREG(a->mode)) {\n-\t\t\tresult.mode = a->mode;\n-\t\t\thashcpy(result.sha, a->sha1);\n+\t\t\tresult->mode = a->mode;\n+\t\t\thashcpy(result->sha, a->sha1);\n \t\t} else {\n-\t\t\tresult.mode = b->mode;\n-\t\t\thashcpy(result.sha, b->sha1);\n+\t\t\tresult->mode = b->mode;\n+\t\t\thashcpy(result->sha, b->sha1);\n \t\t}\n \t} else {\n \t\tif (!sha_eq(a->sha1, one->sha1) && !sha_eq(b->sha1, one->sha1))\n-\t\t\tresult.merge = 1;\n+\t\t\tresult->merge = 1;\n \n \t\t/*\n \t\t * Merge modes\n \t\t */\n \t\tif (a->mode == b->mode || a->mode == one->mode)\n-\t\t\tresult.mode = b->mode;\n+\t\t\tresult->mode = b->mode;\n \t\telse {\n-\t\t\tresult.mode = a->mode;\n+\t\t\tresult->mode = a->mode;\n \t\t\tif (b->mode != one->mode) {\n-\t\t\t\tresult.clean = 0;\n-\t\t\t\tresult.merge = 1;\n+\t\t\t\tresult->clean = 0;\n+\t\t\t\tresult->merge = 1;\n \t\t\t}\n \t\t}\n \n \t\tif (sha_eq(a->sha1, b->sha1) || sha_eq(a->sha1, one->sha1))\n-\t\t\thashcpy(result.sha, b->sha1);\n+\t\t\thashcpy(result->sha, b->sha1);\n \t\telse if (sha_eq(b->sha1, one->sha1))\n-\t\t\thashcpy(result.sha, a->sha1);\n+\t\t\thashcpy(result->sha, a->sha1);\n \t\telse if (S_ISREG(a->mode)) {\n \t\t\tmmbuffer_t result_buf;\n \t\t\tint merge_status;\n@@ -940,62 +940,63 @@ static struct merge_file_info merge_file_1(struct merge_options *o,\n \t\t\t\tdie(_(\"Failed to execute internal merge\"));\n \n \t\t\tif (write_sha1_file(result_buf.ptr, result_buf.size,\n-\t\t\t\t\t    blob_type, result.sha))\n+\t\t\t\t\t    blob_type, result->sha))\n \t\t\t\tdie(_(\"Unable to add %s to database\"),\n \t\t\t\t    a->path);\n \n \t\t\tfree(result_buf.ptr);\n-\t\t\tresult.clean = (merge_status == 0);\n+\t\t\tresult->clean = (merge_status == 0);\n \t\t} else if (S_ISGITLINK(a->mode)) {\n-\t\t\tresult.clean = merge_submodule(result.sha,\n+\t\t\tresult->clean = merge_submodule(result->sha,\n \t\t\t\t\t\t       one->path, one->sha1,\n \t\t\t\t\t\t       a->sha1, b->sha1,\n \t\t\t\t\t\t       !o->call_depth);\n \t\t} else if (S_ISLNK(a->mode)) {\n-\t\t\thashcpy(result.sha, a->sha1);\n+\t\t\thashcpy(result->sha, a->sha1);\n \n \t\t\tif (!sha_eq(a->sha1, b->sha1))\n-\t\t\t\tresult.clean = 0;\n+\t\t\t\tresult->clean = 0;\n \t\t} else\n \t\t\tdie(_(\"BUG: unsupported object type in the tree\"));\n \t}\n \n-\treturn result;\n+\treturn 0;\n }\n \n-static struct merge_file_info\n-merge_file_special_markers(struct merge_options *o,\n+static int merge_file_special_markers(struct merge_options *o,\n \t\t\t   const struct diff_filespec *one,\n \t\t\t   const struct diff_filespec *a,\n \t\t\t   const struct diff_filespec *b,\n \t\t\t   const char *branch1,\n \t\t\t   const char *filename1,\n \t\t\t   const char *branch2,\n-\t\t\t   const char *filename2)\n+\t\t\t   const char *filename2,\n+\t\t\t   struct merge_file_info *mfi)\n {\n \tchar *side1 = NULL;\n \tchar *side2 = NULL;\n-\tstruct merge_file_info mfi;\n+\tint ret;\n \n \tif (filename1)\n \t\tside1 = xstrfmt(\"%s:%s\", branch1, filename1);\n \tif (filename2)\n \t\tside2 = xstrfmt(\"%s:%s\", branch2, filename2);\n \n-\tmfi = merge_file_1(o, one, a, b,\n-\t\t\t   side1 ? side1 : branch1, side2 ? side2 : branch2);\n+\tret = merge_file_1(o, one, a, b,\n+\t\tside1 ? side1 : branch1, side2 ? side2 : branch2, mfi);\n \tfree(side1);\n \tfree(side2);\n-\treturn mfi;\n+\treturn ret;\n }\n \n-static struct merge_file_info merge_file_one(struct merge_options *o,\n+static int merge_file_one(struct merge_options *o,\n \t\t\t\t\t const char *path,\n \t\t\t\t\t const unsigned char *o_sha, int o_mode,\n \t\t\t\t\t const unsigned char *a_sha, int a_mode,\n \t\t\t\t\t const unsigned char *b_sha, int b_mode,\n \t\t\t\t\t const char *branch1,\n-\t\t\t\t\t const char *branch2)\n+\t\t\t\t\t const char *branch2,\n+\t\t\t\t\t struct merge_file_info *mfi)\n {\n \tstruct diff_filespec one, a, b;\n \n@@ -1006,7 +1007,7 @@ static struct merge_file_info merge_file_one(struct merge_options *o,\n \ta.mode = a_mode;\n \thashcpy(b.sha1, b_sha);\n \tb.mode = b_mode;\n-\treturn merge_file_1(o, &one, &a, &b, branch1, branch2);\n+\treturn merge_file_1(o, &one, &a, &b, branch1, branch2, mfi);\n }\n \n static void handle_change_delete(struct merge_options *o,\n@@ -1179,11 +1180,14 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n \t\tstruct merge_file_info mfi;\n \t\tstruct diff_filespec other;\n \t\tstruct diff_filespec *add;\n-\t\tmfi = merge_file_one(o, one->path,\n+\n+\t\tif (merge_file_one(o, one->path,\n \t\t\t\t one->sha1, one->mode,\n \t\t\t\t a->sha1, a->mode,\n \t\t\t\t b->sha1, b->mode,\n-\t\t\t\t ci->branch1, ci->branch2);\n+\t\t\t\t ci->branch1, ci->branch2, &mfi) < 0)\n+\t\t\treturn;\n+\n \t\t/*\n \t\t * FIXME: For rename/add-source conflicts (if we could detect\n \t\t * such), this is wrong.  We should instead find a unique\n@@ -1237,12 +1241,13 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \tremove_file(o, 1, a->path, o->call_depth || would_lose_untracked(a->path));\n \tremove_file(o, 1, b->path, o->call_depth || would_lose_untracked(b->path));\n \n-\tmfi_c1 = merge_file_special_markers(o, a, c1, &ci->ren1_other,\n+\tif (merge_file_special_markers(o, a, c1, &ci->ren1_other,\n \t\t\t\t\t    o->branch1, c1->path,\n-\t\t\t\t\t    o->branch2, ci->ren1_other.path);\n-\tmfi_c2 = merge_file_special_markers(o, b, &ci->ren2_other, c2,\n+\t\t\t\t\t    o->branch2, ci->ren1_other.path, &mfi_c1) < 0 ||\n+\t    merge_file_special_markers(o, b, &ci->ren2_other, c2,\n \t\t\t\t\t    o->branch1, ci->ren2_other.path,\n-\t\t\t\t\t    o->branch2, c2->path);\n+\t\t\t\t\t    o->branch2, c2->path, &mfi_c2) < 0)\n+\t\treturn;\n \n \tif (o->call_depth) {\n \t\t/*\n@@ -1463,10 +1468,11 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t       ren1_dst, branch2);\n \t\t\t\tif (o->call_depth) {\n \t\t\t\t\tstruct merge_file_info mfi;\n-\t\t\t\t\tmfi = merge_file_one(o, ren1_dst, null_sha1, 0,\n+\t\t\t\t\tif (merge_file_one(o, ren1_dst, null_sha1, 0,\n \t\t\t\t\t\t\t ren1->pair->two->sha1, ren1->pair->two->mode,\n \t\t\t\t\t\t\t dst_other.sha1, dst_other.mode,\n-\t\t\t\t\t\t\t branch1, branch2);\n+\t\t\t\t\t\t\t branch1, branch2, &mfi) < 0)\n+\t\t\t\t\t\treturn -1;\n \t\t\t\t\toutput(o, 1, _(\"Adding merged %s\"), ren1_dst);\n \t\t\t\t\tupdate_file(o, 0, mfi.sha, mfi.mode, ren1_dst);\n \t\t\t\t\ttry_merge = 0;\n@@ -1622,9 +1628,10 @@ static int merge_content(struct merge_options *o,\n \t\tif (dir_in_way(path, !o->call_depth))\n \t\t\tdf_conflict_remains = 1;\n \t}\n-\tmfi = merge_file_special_markers(o, &one, &a, &b,\n+\tif (merge_file_special_markers(o, &one, &a, &b,\n \t\t\t\t\t o->branch1, path1,\n-\t\t\t\t\t o->branch2, path2);\n+\t\t\t\t\t o->branch2, path2, &mfi) < 0)\n+\t\treturn -1;\n \n \tif (mfi.clean && !df_conflict_remains &&\n \t    sha_eq(mfi.sha, a_sha) && mfi.mode == a_mode) {\n-- \n2.9.0.268.gcabc8b0\n\n\n"},{"id":"290474","messageId":"06c09ab4d684c239ae4ae03373c7cc7afb3be60b.1467199553.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467199553.git.johannes.schindelin@gmx.de","subject":"[PATCH 8/9] merge-recursive: switch to returning errors instead of dying","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-29T11:37:09Z","receivedAt":"2016-06-29T11:37:29Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The recursive merge machinery is supposed to be a library function, i.e.\nit should return an error when it fails. Originally the functions were\npart of the builtin \"merge-recursive\", though, where it was simpler to\ncall die() and be done with error handling.\n\nThe existing callers were already prepared to detect negative return\nvalues to indicate errors and to behave as previously: exit with code 128\n(which is the same thing that die() does, after printing the message).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 55 ++++++++++++++++++++++++++++++-------------------------\n 1 file changed, 30 insertions(+), 25 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex bb075e3..d5a593c 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -710,12 +710,10 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \t/* Make sure leading directories are created */\n \tstatus = safe_create_leading_directories_const(path);\n \tif (status) {\n-\t\tif (status == SCLD_EXISTS) {\n+\t\tif (status == SCLD_EXISTS)\n \t\t\t/* something else exists */\n-\t\t\terror(msg, path, _(\": perhaps a D/F conflict?\"));\n-\t\t\treturn -1;\n-\t\t}\n-\t\tdie(msg, path, \"\");\n+\t\t\treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n+\t\treturn error(msg, path, \"\");\n \t}\n \n \t/*\n@@ -743,6 +741,8 @@ static int update_file_flags(struct merge_options *o,\n \t\t\t      int update_cache,\n \t\t\t      int update_wd)\n {\n+\tint ret = 0;\n+\n \tif (o->call_depth)\n \t\tupdate_wd = 0;\n \n@@ -763,9 +763,11 @@ static int update_file_flags(struct merge_options *o,\n \n \t\tbuf = read_sha1_file(sha, &type, &size);\n \t\tif (!buf)\n-\t\t\tdie(_(\"cannot read object %s '%s'\"), sha1_to_hex(sha), path);\n-\t\tif (type != OBJ_BLOB)\n-\t\t\tdie(_(\"blob expected for %s '%s'\"), sha1_to_hex(sha), path);\n+\t\t\treturn error(_(\"cannot read object %s '%s'\"), sha1_to_hex(sha), path);\n+\t\tif (type != OBJ_BLOB) {\n+\t\t\tret = error(_(\"blob expected for %s '%s'\"), sha1_to_hex(sha), path);\n+\t\t\tgoto free_buf;\n+\t\t}\n \t\tif (S_ISREG(mode)) {\n \t\t\tstruct strbuf strbuf = STRBUF_INIT;\n \t\t\tif (convert_to_working_tree(path, buf, size, &strbuf)) {\n@@ -786,8 +788,10 @@ static int update_file_flags(struct merge_options *o,\n \t\t\telse\n \t\t\t\tmode = 0666;\n \t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n-\t\t\tif (fd < 0)\n-\t\t\t\tdie_errno(_(\"failed to open '%s'\"), path);\n+\t\t\tif (fd < 0) {\n+\t\t\t\tret = error_errno(_(\"failed to open '%s'\"), path);\n+\t\t\t\tgoto free_buf;\n+\t\t\t}\n \t\t\twrite_in_full(fd, buf, size);\n \t\t\tclose(fd);\n \t\t} else if (S_ISLNK(mode)) {\n@@ -795,18 +799,18 @@ static int update_file_flags(struct merge_options *o,\n \t\t\tsafe_create_leading_directories_const(path);\n \t\t\tunlink(path);\n \t\t\tif (symlink(lnk, path))\n-\t\t\t\tdie_errno(_(\"failed to symlink '%s'\"), path);\n+\t\t\t\tret = error_errno(_(\"failed to symlink '%s'\"), path);\n \t\t\tfree(lnk);\n \t\t} else\n-\t\t\tdie(_(\"do not know what to do with %06o %s '%s'\"),\n-\t\t\t    mode, sha1_to_hex(sha), path);\n+\t\t\tret = error_errno(_(\"do not know what to do with %06o %s '%s'\"),\n+\t\t\t\tmode, sha1_to_hex(sha), path);\n free_buf:\n \t\tfree(buf);\n \t}\n  update_index:\n-\tif (update_cache)\n+\tif (!ret && update_cache)\n \t\tadd_cacheinfo(mode, sha, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n-\treturn 0;\n+\treturn ret;\n }\n \n static int update_file(struct merge_options *o,\n@@ -932,20 +936,22 @@ static int merge_file_1(struct merge_options *o,\n \t\t\thashcpy(result->sha, a->sha1);\n \t\telse if (S_ISREG(a->mode)) {\n \t\t\tmmbuffer_t result_buf;\n-\t\t\tint merge_status;\n+\t\t\tint ret = 0, merge_status;\n \n \t\t\tmerge_status = merge_3way(o, &result_buf, one, a, b,\n \t\t\t\t\t\t  branch1, branch2);\n \n \t\t\tif ((merge_status < 0) || !result_buf.ptr)\n-\t\t\t\tdie(_(\"Failed to execute internal merge\"));\n+\t\t\t\tret = error(_(\"Failed to execute internal merge\"));\n \n-\t\t\tif (write_sha1_file(result_buf.ptr, result_buf.size,\n+\t\t\tif (!ret && write_sha1_file(result_buf.ptr, result_buf.size,\n \t\t\t\t\t    blob_type, result->sha))\n-\t\t\t\tdie(_(\"Unable to add %s to database\"),\n-\t\t\t\t    a->path);\n+\t\t\t\tret = error(_(\"Unable to add %s to database\"),\n+\t\t\t\t\ta->path);\n \n \t\t\tfree(result_buf.ptr);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n \t\t\tresult->clean = (merge_status == 0);\n \t\t} else if (S_ISGITLINK(a->mode)) {\n \t\t\tresult->clean = merge_submodule(result->sha,\n@@ -1861,7 +1867,7 @@ static int process_entry(struct merge_options *o,\n \t\t */\n \t\tremove_file(o, 1, path, !a_mode);\n \t} else\n-\t\tdie(_(\"Fatal merge failure, shouldn't happen.\"));\n+\t\treturn error(_(\"Fatal merge failure, shouldn't happen.\"));\n \n \treturn clean_merge;\n }\n@@ -1889,11 +1895,10 @@ int merge_trees(struct merge_options *o,\n \n \tif (code != 0) {\n \t\tif (show(o, 4) || o->call_depth)\n-\t\t\tdie(_(\"merging of trees %s and %s failed\"),\n+\t\t\terror(_(\"merging of trees %s and %s failed\"),\n \t\t\t    oid_to_hex(&head->object.oid),\n \t\t\t    oid_to_hex(&merge->object.oid));\n-\t\telse\n-\t\t\texit(128);\n+\t\treturn -1;\n \t}\n \n \tif (unmerged_cache()) {\n@@ -2024,7 +2029,7 @@ int merge_recursive(struct merge_options *o,\n \t\to->call_depth--;\n \n \t\tif (!merged_common_ancestors)\n-\t\t\tdie(_(\"merge returned no commit\"));\n+\t\t\treturn error(_(\"merge returned no commit\"));\n \t}\n \n \tdiscard_cache();\n-- \n2.9.0.268.gcabc8b0\n\n\n"},{"id":"290475","messageId":"dc58115e23c8d942b3ff6270b43719bc841becbb.1467199553.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467199553.git.johannes.schindelin@gmx.de","subject":"[PATCH 9/9] am: make a direct call to merge_recursive","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-29T11:38:27Z","receivedAt":"2016-06-29T11:38:35Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Junio C Hamano <gitster@pobox.com>\n\nInstead of spawning merge-recursive via run_command() in\nrun_fallback_merge_recursive(), make a direct call to the internal\nmerge_recursive_generic().\n\nHere is a quick benchmark result, applying a patch for b4391657\n(merge: drop 'git merge <message> HEAD <commit>' syntax, 2015-03-25)\nthat was still cooking in 'next' on 4b1fd356 (git-multimail: update\nto release 1.2.0, 2015-10-11) which was the tip of 'master' at some\nstage, on an x86-64 running Ubuntu:\n\n      real    0m0.169s                      real    0m0.163s\n      user    0m0.108s                      user    0m0.134s\n      sys     0m0.068s                      sys     0m0.033s\n\n      real    0m0.175s                      real    0m0.161s\n      user    0m0.110s                      user    0m0.120s\n      sys     0m0.066s                      sys     0m0.047s\n\n      real    0m0.168s                      real    0m0.162s\n      user    0m0.124s                      user    0m0.114s\n      sys     0m0.045s                      sys     0m0.051s\n\n      real    0m0.167s                      real    0m0.152s\n      user    0m0.124s                      user    0m0.122s\n      sys     0m0.045s                      sys     0m0.031s\n\n      real    0m0.169s                      real    0m0.164s\n      user    0m0.131s                      user    0m0.129s\n      sys     0m0.043s                      sys     0m0.041s\n\nLeft-hand side shows the original, right-hand side shows the result\nof this optimization.\n\nTimings on Windows:\n\noriginal:\n0.00user 0.01system 0:00.29elapsed\n0.00user 0.00system 0:00.25elapsed\n0.01user 0.00system 0:00.24elapsed\n0.01user 0.00system 0:00.26elapsed\n0.00user 0.01system 0:00.23elapsed\n\nwith optimization:\n0.00user 0.01system 0:00.22elapsed\n0.00user 0.00system 0:00.25elapsed\n0.00user 0.01system 0:00.22elapsed\n0.00user 0.00system 0:00.22elapsed\n0.01user 0.00system 0:00.21elapsed\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\tIt feels *slightly* wrong to submit your own patch to review,\n\thowever, please keep in mind that\n\n\t1) I changed the patch (o.gently does not exist anymore, so I do\n\t   not set it), and\n\n\t2) I added my own timings performed on Windows.\n\n builtin/am.c | 27 ++++++++++++++-------------\n 1 file changed, 14 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/am.c b/builtin/am.c\nindex 3dfe70b..dd41154 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -1587,25 +1587,26 @@ static int run_fallback_merge_recursive(const struct am_state *state,\n \t\t\t\t\tunsigned char *our_tree,\n \t\t\t\t\tunsigned char *his_tree)\n {\n-\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tconst unsigned char *bases[1] = {orig_tree};\n+\tstruct merge_options o;\n+\tstruct commit *result;\n+\tchar *his_tree_name;\n \tint status;\n \n-\tcp.git_cmd = 1;\n+\tinit_merge_options(&o);\n+\n+\to.branch1 = \"HEAD\";\n+\this_tree_name = xstrfmt(\"%.*s\", linelen(state->msg), state->msg);\n+\to.branch2 = his_tree_name;\n \n-\targv_array_pushf(&cp.env_array, \"GITHEAD_%s=%.*s\",\n-\t\t\t sha1_to_hex(his_tree), linelen(state->msg), state->msg);\n \tif (state->quiet)\n-\t\targv_array_push(&cp.env_array, \"GIT_MERGE_VERBOSITY=0\");\n+\t\to.verbosity = 0;\n \n-\targv_array_push(&cp.args, \"merge-recursive\");\n-\targv_array_push(&cp.args, sha1_to_hex(orig_tree));\n-\targv_array_push(&cp.args, \"--\");\n-\targv_array_push(&cp.args, sha1_to_hex(our_tree));\n-\targv_array_push(&cp.args, sha1_to_hex(his_tree));\n+\tstatus = merge_recursive_generic(&o, our_tree, his_tree, 1, bases, &result);\n+\tif (status < 0)\n+\t\texit(128);\n+\tfree(his_tree_name);\n \n-\tstatus = run_command(&cp) ? (-1) : 0;\n-\tdiscard_cache();\n-\tread_cache();\n \treturn status;\n }\n \n-- \n2.9.0.268.gcabc8b0\n"},{"id":"290503","messageId":"alpine.DEB.2.20.1606291709270.12947@virtualbox","threadId":"42743","inReplyTo":"8615dc276828a3f99a27ff2eda9909548a7d435e.1467199553.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 1/9] Report bugs consistently","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-29T15:11:12Z","receivedAt":"2016-06-29T15:18:04Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 29 Jun 2016, Johannes Schindelin wrote:\n\n> diff --git a/imap-send.c b/imap-send.c\n> index 938c691..cd39805 100644\n> --- a/imap-send.c\n> +++ b/imap-send.c\n> @@ -511,7 +511,7 @@ static int nfsnprintf(char *buf, int blen, const char *fmt, ...)\n>  \n>  \tva_start(va, fmt);\n>  \tif (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)\n> -\t\tdie(\"Fatal: buffer too small. Please report a bug.\");\n> +\t\tdie(\"BUG: buffer too small (%d < %d)\", ret, blen);\n>  \tva_end(va);\n>  \treturn ret;\n>  }\n\nOy vey. Travis CI found a bug here, thanks to clang's quite smart\nchecking. This here fixup is needed (will make that part of v2, but wait\nfor comments):\n\n-- snipsnap --\nFrom: Johannes Schindelin <johannes.schindelin@gmx.de>\nSubject: [PATCH] fixup! Report bugs consistently\n\n---\n imap-send.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/imap-send.c b/imap-send.c\nindex cd39805..369f72a 100644\n--- a/imap-send.c\n+++ b/imap-send.c\n@@ -506,7 +506,7 @@ static char *next_arg(char **s)\n \n static int nfsnprintf(char *buf, int blen, const char *fmt, ...)\n {\n-\tint ret;\n+\tint ret = -1;\n \tva_list va;\n \n \tva_start(va, fmt);\n-- \n2.9.0.270.g810e421\n\n\n"},{"id":"290514","messageId":"xmqqpoqz51o8.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"dc58115e23c8d942b3ff6270b43719bc841becbb.1467199553.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 9/9] am: make a direct call to merge_recursive","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-29T17:45:43Z","receivedAt":"2016-06-29T17:45:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> From: Junio C Hamano <gitster@pobox.com>\n\nDid I write this thing?\n\nHaving two sets of numbers that illustrated that this is not really\na useful optimization in the bigger picture looks vaguely familiar\n(e.g. $gmane/279417), but the numbers are different.\n\n> \tIt feels *slightly* wrong to submit your own patch to review,\n> \thowever, please keep in mind that\n>\n> \t1) I changed the patch (o.gently does not exist anymore, so I do\n> \t   not set it), and\n>\n> \t2) I added my own timings performed on Windows.\n\nIt probably is much less confusing if you take the authorship,\npossibly with a passing reference to whatever I wrote as the source\nof inspiration in the log message, or something.  I do not think I\ndeserve a credit in this 9-patch series.\n\nI haven't read the other 8 patches, so I cannot comment on it yet.\n\n>  builtin/am.c | 27 ++++++++++++++-------------\n>  1 file changed, 14 insertions(+), 13 deletions(-)\n>\n> diff --git a/builtin/am.c b/builtin/am.c\n> index 3dfe70b..dd41154 100644\n> --- a/builtin/am.c\n> +++ b/builtin/am.c\n> @@ -1587,25 +1587,26 @@ static int run_fallback_merge_recursive(const struct am_state *state,\n>  \t\t\t\t\tunsigned char *our_tree,\n>  \t\t\t\t\tunsigned char *his_tree)\n>  {\n> -\tstruct child_process cp = CHILD_PROCESS_INIT;\n> +\tconst unsigned char *bases[1] = {orig_tree};\n> +\tstruct merge_options o;\n> +\tstruct commit *result;\n> +\tchar *his_tree_name;\n>  \tint status;\n>  \n> -\tcp.git_cmd = 1;\n> +\tinit_merge_options(&o);\n> +\n> +\to.branch1 = \"HEAD\";\n> +\this_tree_name = xstrfmt(\"%.*s\", linelen(state->msg), state->msg);\n> +\to.branch2 = his_tree_name;\n>  \n> -\targv_array_pushf(&cp.env_array, \"GITHEAD_%s=%.*s\",\n> -\t\t\t sha1_to_hex(his_tree), linelen(state->msg), state->msg);\n>  \tif (state->quiet)\n> -\t\targv_array_push(&cp.env_array, \"GIT_MERGE_VERBOSITY=0\");\n> +\t\to.verbosity = 0;\n>  \n> -\targv_array_push(&cp.args, \"merge-recursive\");\n> -\targv_array_push(&cp.args, sha1_to_hex(orig_tree));\n> -\targv_array_push(&cp.args, \"--\");\n> -\targv_array_push(&cp.args, sha1_to_hex(our_tree));\n> -\targv_array_push(&cp.args, sha1_to_hex(his_tree));\n> +\tstatus = merge_recursive_generic(&o, our_tree, his_tree, 1, bases, &result);\n> +\tif (status < 0)\n> +\t\texit(128);\n> +\tfree(his_tree_name);\n>  \n> -\tstatus = run_command(&cp) ? (-1) : 0;\n> -\tdiscard_cache();\n> -\tread_cache();\n>  \treturn status;\n>  }\n"},{"id":"290517","messageId":"CAPig+cSHy=2VaNP5gpwqKN4vuCBrOUy39L0i9xcda8m3zx+GPA@mail.gmail.com","threadId":"42743","inReplyTo":"8615dc276828a3f99a27ff2eda9909548a7d435e.1467199553.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 1/9] Report bugs consistently","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-06-29T18:12:11Z","receivedAt":"2016-06-29T18:12:46Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Jun 29, 2016 at 7:36 AM, Johannes Schindelin\n<johannes.schindelin@gmx.de> wrote:\n> The vast majority of error messages in Git's source code which report a\n> bug use the convention to prefix the message with \"BUG:\".\n> [...]\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n> diff --git a/merge-recursive.c b/merge-recursive.c\n> @@ -1853,7 +1852,7 @@ int merge_trees(struct merge_options *o,\n> -                               die(_(\"Unprocessed path??? %s\"),\n> +                               die(_(\"BUG: unprocessed path??? %s\"),\n\nThis and others downcase the first word (which is consistent with\nmodern practice)...\n\n> diff --git a/sha1_file.c b/sha1_file.c\n> @@ -795,7 +795,7 @@ void close_all_packs(void)\n> -                       die(\"BUG! Want to close pack marked 'do-not-close'\");\n> +                       die(\"BUG: Want to close pack marked 'do-not-close'\");\n\n...but this one neglects to.\n"},{"id":"290519","messageId":"xmqq4m8b4zdd.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"dd3e2cf842fd5e11e31914aa55b8b995e8d3d75c.1467199553.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 2/9] merge-recursive: clarify code in was_tracked()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-29T18:35:26Z","receivedAt":"2016-06-29T18:35:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> It can be puzzling to see that was_tracked() tries to match an index\n> entry by name even if cache_name_pos() returned a negative value. Let's\n> clarify that cache_name_pos() implicitly looks for stage 0, while we are\n> also okay with finding other stages.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  merge-recursive.c | 1 +\n>  1 file changed, 1 insertion(+)\n>\n> diff --git a/merge-recursive.c b/merge-recursive.c\n> index 98f4632..bcb53f0 100644\n> --- a/merge-recursive.c\n> +++ b/merge-recursive.c\n> @@ -658,6 +658,7 @@ static int was_tracked(const char *path)\n>  {\n>  \tint pos = cache_name_pos(path, strlen(path));\n>  \n> +\t/* cache_name_pos() looks for stage == 0, so pos may be < 0 */\n\nIt returns >= if found at stage #0, or a negative (counting from -1)\nto indicate where the path would be inserted if it were to be added\nat stage #0.\n\nThe new comment does not explain how \"pos may be < 0\" leads to\n\"hence pos = -1 - pos is the right thing to do here\".  It is\nmisleading and we probably are better off without.\n\n>  \tif (pos < 0)\n>  \t\tpos = -1 - pos;\n>  \twhile (pos < active_nr &&\n"},{"id":"290522","messageId":"xmqqziq33ju2.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"753eabc5193c148c67e64ed5d070b6ff08f51d82.1467199553.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 3/9] Prepare the builtins for a libified merge_recursive()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-29T18:56:21Z","receivedAt":"2016-06-29T19:04:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> A truly libified function does not die() just for fun.\n\nThe sentence is wasting bits.  After all, a helper function in\nrun-once-and-exit program does not die() just for fun, either.\n\nSo what's more interesting to know for the readers?\n\n> As such, the\n> recursive merge will convert all die() calls to return -1 instead in the\n> next commits, giving the caller a chance at least to print some helpful\n> message.\n>\n> Let's prepare the builtins for this fatal error condition, even if we do\n> not really do more than imitating the previous die()'s exit(128): this is\n> what callers of e.g. `git merge` have come to expect.\n\nOne thing missing is your design decision and justification.\n\nFor example, the above explanation hints that the original code in\nthis hunk expected merge_trees() to die() with some useful message\nwhen there is an error condition, but merge_trees() is going to be\nimproved not to die() itself and return -1 instead to signal an\nerror, so that the caller can react more flexibly, and this is a\nstep to prepare for the version of merge_trees() that no longer\ndies.\n\n> -\t\t\tmerge_trees(&o, new->commit->tree, work,\n> +\t\t\tret = merge_trees(&o, new->commit->tree, work,\n>  \t\t\t\told->commit->tree, &result);\n> +\t\t\tif (ret < 0)\n> +\t\t\t\texit(128);\n\nThe postimage of the patch tells us that the caller is now\nresponsible for exiting with status 128, but neither the proposed\nlog message nor the above hunk tells us where the message the\noriginal code must have given to the end user from die() inside\nmerge_trees().  The updated caller just exits, so a natural guess is\nthat the calls to die() have been changed to fprintf(stderr) with\nthe patch.\n\nBut that does not mesh very well with the stated objective of the\npatch.  The callers want flexibility to do their own error handling,\nincluding giving their own message, so letting merge_trees() to\nstill write the same message to the standard error stream would not\nwork well for them.  A caller may want to do merge_trees() just to\nsee if it succeeds, without wanting to give _any_ indication of that\nis happening to the user, because it has an alternate/fallback code\nif merge_trees() fails, for example (analogy: \"am -3\" first tries a\nstraight patch application before fallking back to 3-way merge; it\nmay not want to show the error from the first attempt).\n\nThe reader _can_ guess that this step ignores the error-message\nissue, and improving it later (or keep ignoring that issue) might be\nOK in the context of this patch series, but it is necessary to be\nupfront to the readers what the design choices were and which one of\nthose choices the proposed patch adopted as its design for them to\nbe able to evaluate the patch series correctly.\n\nOne design alternative we've seen used in our code to help callers\nwho want no default messages is to pass struct strbuf &err down the\ncallchain and collect the default messages without emitting them\ndirectly to the standard error stream when an error is diagnosed.  I\ndo not know if that pattern is applicable or should be applied to\nthis codepath.\n\n> Note that the callers of the sequencer (revert and cherry-pick) already\n> fail fast even for the return value -1; The only difference is that they\n> now get a chance to say \"<command> failed\".\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  builtin/checkout.c | 4 +++-\n>  builtin/merge.c    | 4 ++++\n>  sequencer.c        | 4 ++++\n>  3 files changed, 11 insertions(+), 1 deletion(-)\n>\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index c3486bd..14312f7 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -567,8 +567,10 @@ static int merge_working_tree(const struct checkout_opts *opts,\n>  \t\t\to.ancestor = old->name;\n>  \t\t\to.branch1 = new->name;\n>  \t\t\to.branch2 = \"local\";\n> -\t\t\tmerge_trees(&o, new->commit->tree, work,\n> +\t\t\tret = merge_trees(&o, new->commit->tree, work,\n>  \t\t\t\told->commit->tree, &result);\n> +\t\t\tif (ret < 0)\n> +\t\t\t\texit(128);\n>  \t\t\tret = reset_tree(new->commit->tree, opts, 0,\n>  \t\t\t\t\t writeout_error);\n>  \t\t\tif (ret)\n> diff --git a/builtin/merge.c b/builtin/merge.c\n> index b555a1b..133b853 100644\n> --- a/builtin/merge.c\n> +++ b/builtin/merge.c\n> @@ -682,6 +682,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,\n>  \t\thold_locked_index(&lock, 1);\n>  \t\tclean = merge_recursive(&o, head,\n>  \t\t\t\tremoteheads->item, reversed, &result);\n> +\t\tif (clean < 0)\n> +\t\t\texit(128);\n>  \t\tif (active_cache_changed &&\n>  \t\t    write_locked_index(&the_index, &lock, COMMIT_LOCK))\n>  \t\t\tdie (_(\"unable to write %s\"), get_index_file());\n> @@ -1550,6 +1552,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n>  \t\tret = try_merge_strategy(use_strategies[i]->name,\n>  \t\t\t\t\t common, remoteheads,\n>  \t\t\t\t\t head_commit, head_arg);\n> +\t\tif (ret < 0)\n> +\t\t\texit(128);\n>  \t\tif (!option_commit && !ret) {\n>  \t\t\tmerge_was_ok = 1;\n>  \t\t\t/*\n> diff --git a/sequencer.c b/sequencer.c\n> index c6362d6..13b794a 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -293,6 +293,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n>  \tclean = merge_trees(&o,\n>  \t\t\t    head_tree,\n>  \t\t\t    next_tree, base_tree, &result);\n> +\tif (clean < 0)\n> +\t\treturn clean;\n>  \n>  \tif (active_cache_changed &&\n>  \t    write_locked_index(&the_index, &index_lock, COMMIT_LOCK))\n> @@ -561,6 +563,8 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n>  \tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || opts->action == REPLAY_REVERT) {\n>  \t\tres = do_recursive_merge(base, next, base_label, next_label,\n>  \t\t\t\t\t head, &msgbuf, opts);\n> +\t\tif (res < 0)\n> +\t\t\treturn res;\n>  \t\twrite_message(&msgbuf, git_path_merge_msg());\n>  \t} else {\n>  \t\tstruct commit_list *common = NULL;\n"},{"id":"290527","messageId":"xmqqvb0r3gi4.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"81a74b02ac714a4fa3734dfb774cff6dea3a3471.1467199553.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 4/9] merge_recursive: abort properly upon errors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-29T20:08:19Z","receivedAt":"2016-06-29T20:09:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> There are a couple of places where return values indicating errors\n> are ignored. Let's teach them manners.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  merge-recursive.c | 10 ++++++++--\n>  1 file changed, 8 insertions(+), 2 deletions(-)\n>\n> diff --git a/merge-recursive.c b/merge-recursive.c\n> index bcb53f0..c4ece96 100644\n> --- a/merge-recursive.c\n> +++ b/merge-recursive.c\n> @@ -1944,8 +1944,9 @@ int merge_recursive(struct merge_options *o,\n>  \t\tsaved_b2 = o->branch2;\n>  \t\to->branch1 = \"Temporary merge branch 1\";\n>  \t\to->branch2 = \"Temporary merge branch 2\";\n> -\t\tmerge_recursive(o, merged_common_ancestors, iter->item,\n> -\t\t\t\tNULL, &merged_common_ancestors);\n> +\t\tif (merge_recursive(o, merged_common_ancestors, iter->item,\n> +\t\t\t\tNULL, &merged_common_ancestors) < 0)\n> +\t\t\treturn -1;\n>  \t\to->branch1 = saved_b1;\n>  \t\to->branch2 = saved_b2;\n>  \t\to->call_depth--;\n\nOK, this early return (and others in this patch) are only for\nnegative (i.e. error) cases, and \"attempted a merge, resulted in\nconflicts\" cases are handled as before.\n\nWhich is good.\n\nI wonder if o->branch[12] need to be restored, though.  The only\nsensible thing the caller can do is to punt, but would it expect to\nbe able to do some error reporting based on these fields, e.g.\nprintf(\"merge of %s and %s failed\", o->branch1, o->branch2) or\nsomething?  In addition to that kind of \"state restoration\", we may\nneed to watch out for resource leaks, but I think there is none at\nleast these three early returns.\n\nThanks.\n\n> @@ -1961,6 +1962,8 @@ int merge_recursive(struct merge_options *o,\n>  \to->ancestor = \"merged common ancestors\";\n>  \tclean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree,\n>  \t\t\t    &mrtree);\n> +\tif (clean < 0)\n> +\t\treturn clean;\n>  \n>  \tif (o->call_depth) {\n>  \t\t*result = make_virtual_commit(mrtree, \"merged tree\");\n> @@ -2017,6 +2020,9 @@ int merge_recursive_generic(struct merge_options *o,\n>  \thold_locked_index(lock, 1);\n>  \tclean = merge_recursive(o, head_commit, next_commit, ca,\n>  \t\t\tresult);\n> +\tif (clean < 0)\n> +\t\treturn clean;\n> +\n>  \tif (active_cache_changed &&\n>  \t    write_locked_index(&the_index, lock, COMMIT_LOCK))\n>  \t\treturn error(_(\"Unable to write index.\"));\n"},{"id":"290529","messageId":"xmqqr3bf3fvr.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"c33e9cdb1ec6cbebcc3124a62b7b9d52b92cf6c9.1467199553.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 5/9] merge-recursive: avoid returning a wholesale struct","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-29T20:21:44Z","receivedAt":"2016-06-29T20:23:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> It is technically allowed, as per C89, for functions' return type to\n> be complete structs (i.e. *not* just pointers to structs), but it is\n> bad practice.\n\nNot necessarily.\n\n> This is a very late attempt to contain the damage done by this developer\n> in 6d297f8 (Status update on merge-recursive in C, 2006-07-08) which\n> introduced such a return type.\n>\n> It will also help the current effort to libify merge-recursive.c, as\n> it will allow us to return proper error codes later.\n\nBut this part of the motivation does make sense.  Having to pass an\nextra int &error_code field, only because the return value is\nalready used for something else, is a lot more weird.\n\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  merge-recursive.c | 93 ++++++++++++++++++++++++++++++-------------------------\n>  1 file changed, 50 insertions(+), 43 deletions(-)\n>\n> diff --git a/merge-recursive.c b/merge-recursive.c\n> index c4ece96..d56651c9 100644\n> --- a/merge-recursive.c\n> +++ b/merge-recursive.c\n> @@ -888,47 +888,47 @@ static int merge_3way(struct merge_options *o,\n>  \treturn merge_status;\n>  }\n>  \n> -static struct merge_file_info merge_file_1(struct merge_options *o,\n> +static int merge_file_1(struct merge_options *o,\n>  \t\t\t\t\t   const struct diff_filespec *one,\n>  \t\t\t\t\t   const struct diff_filespec *a,\n>  \t\t\t\t\t   const struct diff_filespec *b,\n>  \t\t\t\t\t   const char *branch1,\n> -\t\t\t\t\t   const char *branch2)\n> +\t\t\t\t\t   const char *branch2,\n> +\t\t\t\t\t   struct merge_file_info *result)\n>  {\n> -\tstruct merge_file_info result;\n> -\tresult.merge = 0;\n> -\tresult.clean = 1;\n> +\tresult->merge = 0;\n> +\tresult->clean = 1;\n>  \n>  \tif ((S_IFMT & a->mode) != (S_IFMT & b->mode)) {\n> -\t\tresult.clean = 0;\n> +\t\tresult->clean = 0;\n>  \t\tif (S_ISREG(a->mode)) {\n> -\t\t\tresult.mode = a->mode;\n> -\t\t\thashcpy(result.sha, a->sha1);\n> +\t\t\tresult->mode = a->mode;\n> +\t\t\thashcpy(result->sha, a->sha1);\n>  \t\t} else {\n> -\t\t\tresult.mode = b->mode;\n> -\t\t\thashcpy(result.sha, b->sha1);\n> +\t\t\tresult->mode = b->mode;\n> +\t\t\thashcpy(result->sha, b->sha1);\n>  \t\t}\n>  \t} else {\n>  \t\tif (!sha_eq(a->sha1, one->sha1) && !sha_eq(b->sha1, one->sha1))\n> -\t\t\tresult.merge = 1;\n> +\t\t\tresult->merge = 1;\n>  \n>  \t\t/*\n>  \t\t * Merge modes\n>  \t\t */\n>  \t\tif (a->mode == b->mode || a->mode == one->mode)\n> -\t\t\tresult.mode = b->mode;\n> +\t\t\tresult->mode = b->mode;\n>  \t\telse {\n> -\t\t\tresult.mode = a->mode;\n> +\t\t\tresult->mode = a->mode;\n>  \t\t\tif (b->mode != one->mode) {\n> -\t\t\t\tresult.clean = 0;\n> -\t\t\t\tresult.merge = 1;\n> +\t\t\t\tresult->clean = 0;\n> +\t\t\t\tresult->merge = 1;\n>  \t\t\t}\n>  \t\t}\n>  \n>  \t\tif (sha_eq(a->sha1, b->sha1) || sha_eq(a->sha1, one->sha1))\n> -\t\t\thashcpy(result.sha, b->sha1);\n> +\t\t\thashcpy(result->sha, b->sha1);\n>  \t\telse if (sha_eq(b->sha1, one->sha1))\n> -\t\t\thashcpy(result.sha, a->sha1);\n> +\t\t\thashcpy(result->sha, a->sha1);\n>  \t\telse if (S_ISREG(a->mode)) {\n>  \t\t\tmmbuffer_t result_buf;\n>  \t\t\tint merge_status;\n> @@ -940,62 +940,63 @@ static struct merge_file_info merge_file_1(struct merge_options *o,\n>  \t\t\t\tdie(_(\"Failed to execute internal merge\"));\n>  \n>  \t\t\tif (write_sha1_file(result_buf.ptr, result_buf.size,\n> -\t\t\t\t\t    blob_type, result.sha))\n> +\t\t\t\t\t    blob_type, result->sha))\n>  \t\t\t\tdie(_(\"Unable to add %s to database\"),\n>  \t\t\t\t    a->path);\n>  \n>  \t\t\tfree(result_buf.ptr);\n> -\t\t\tresult.clean = (merge_status == 0);\n> +\t\t\tresult->clean = (merge_status == 0);\n>  \t\t} else if (S_ISGITLINK(a->mode)) {\n> -\t\t\tresult.clean = merge_submodule(result.sha,\n> +\t\t\tresult->clean = merge_submodule(result->sha,\n>  \t\t\t\t\t\t       one->path, one->sha1,\n>  \t\t\t\t\t\t       a->sha1, b->sha1,\n>  \t\t\t\t\t\t       !o->call_depth);\n>  \t\t} else if (S_ISLNK(a->mode)) {\n> -\t\t\thashcpy(result.sha, a->sha1);\n> +\t\t\thashcpy(result->sha, a->sha1);\n>  \n>  \t\t\tif (!sha_eq(a->sha1, b->sha1))\n> -\t\t\t\tresult.clean = 0;\n> +\t\t\t\tresult->clean = 0;\n>  \t\t} else\n>  \t\t\tdie(_(\"BUG: unsupported object type in the tree\"));\n>  \t}\n>  \n> -\treturn result;\n> +\treturn 0;\n>  }\n>  \n> -static struct merge_file_info\n> -merge_file_special_markers(struct merge_options *o,\n> +static int merge_file_special_markers(struct merge_options *o,\n>  \t\t\t   const struct diff_filespec *one,\n>  \t\t\t   const struct diff_filespec *a,\n>  \t\t\t   const struct diff_filespec *b,\n>  \t\t\t   const char *branch1,\n>  \t\t\t   const char *filename1,\n>  \t\t\t   const char *branch2,\n> -\t\t\t   const char *filename2)\n> +\t\t\t   const char *filename2,\n> +\t\t\t   struct merge_file_info *mfi)\n>  {\n>  \tchar *side1 = NULL;\n>  \tchar *side2 = NULL;\n> -\tstruct merge_file_info mfi;\n> +\tint ret;\n>  \n>  \tif (filename1)\n>  \t\tside1 = xstrfmt(\"%s:%s\", branch1, filename1);\n>  \tif (filename2)\n>  \t\tside2 = xstrfmt(\"%s:%s\", branch2, filename2);\n>  \n> -\tmfi = merge_file_1(o, one, a, b,\n> -\t\t\t   side1 ? side1 : branch1, side2 ? side2 : branch2);\n> +\tret = merge_file_1(o, one, a, b,\n> +\t\tside1 ? side1 : branch1, side2 ? side2 : branch2, mfi);\n>  \tfree(side1);\n>  \tfree(side2);\n> -\treturn mfi;\n> +\treturn ret;\n>  }\n>  \n> -static struct merge_file_info merge_file_one(struct merge_options *o,\n> +static int merge_file_one(struct merge_options *o,\n>  \t\t\t\t\t const char *path,\n>  \t\t\t\t\t const unsigned char *o_sha, int o_mode,\n>  \t\t\t\t\t const unsigned char *a_sha, int a_mode,\n>  \t\t\t\t\t const unsigned char *b_sha, int b_mode,\n>  \t\t\t\t\t const char *branch1,\n> -\t\t\t\t\t const char *branch2)\n> +\t\t\t\t\t const char *branch2,\n> +\t\t\t\t\t struct merge_file_info *mfi)\n>  {\n>  \tstruct diff_filespec one, a, b;\n>  \n> @@ -1006,7 +1007,7 @@ static struct merge_file_info merge_file_one(struct merge_options *o,\n>  \ta.mode = a_mode;\n>  \thashcpy(b.sha1, b_sha);\n>  \tb.mode = b_mode;\n> -\treturn merge_file_1(o, &one, &a, &b, branch1, branch2);\n> +\treturn merge_file_1(o, &one, &a, &b, branch1, branch2, mfi);\n>  }\n>  \n>  static void handle_change_delete(struct merge_options *o,\n> @@ -1179,11 +1180,14 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n>  \t\tstruct merge_file_info mfi;\n>  \t\tstruct diff_filespec other;\n>  \t\tstruct diff_filespec *add;\n> -\t\tmfi = merge_file_one(o, one->path,\n> +\n> +\t\tif (merge_file_one(o, one->path,\n>  \t\t\t\t one->sha1, one->mode,\n>  \t\t\t\t a->sha1, a->mode,\n>  \t\t\t\t b->sha1, b->mode,\n> -\t\t\t\t ci->branch1, ci->branch2);\n> +\t\t\t\t ci->branch1, ci->branch2, &mfi) < 0)\n> +\t\t\treturn;\n> +\n>  \t\t/*\n>  \t\t * FIXME: For rename/add-source conflicts (if we could detect\n>  \t\t * such), this is wrong.  We should instead find a unique\n> @@ -1237,12 +1241,13 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n>  \tremove_file(o, 1, a->path, o->call_depth || would_lose_untracked(a->path));\n>  \tremove_file(o, 1, b->path, o->call_depth || would_lose_untracked(b->path));\n>  \n> -\tmfi_c1 = merge_file_special_markers(o, a, c1, &ci->ren1_other,\n> +\tif (merge_file_special_markers(o, a, c1, &ci->ren1_other,\n>  \t\t\t\t\t    o->branch1, c1->path,\n> -\t\t\t\t\t    o->branch2, ci->ren1_other.path);\n> -\tmfi_c2 = merge_file_special_markers(o, b, &ci->ren2_other, c2,\n> +\t\t\t\t\t    o->branch2, ci->ren1_other.path, &mfi_c1) < 0 ||\n> +\t    merge_file_special_markers(o, b, &ci->ren2_other, c2,\n>  \t\t\t\t\t    o->branch1, ci->ren2_other.path,\n> -\t\t\t\t\t    o->branch2, c2->path);\n> +\t\t\t\t\t    o->branch2, c2->path, &mfi_c2) < 0)\n> +\t\treturn;\n>  \n>  \tif (o->call_depth) {\n>  \t\t/*\n> @@ -1463,10 +1468,11 @@ static int process_renames(struct merge_options *o,\n>  \t\t\t\t       ren1_dst, branch2);\n>  \t\t\t\tif (o->call_depth) {\n>  \t\t\t\t\tstruct merge_file_info mfi;\n> -\t\t\t\t\tmfi = merge_file_one(o, ren1_dst, null_sha1, 0,\n> +\t\t\t\t\tif (merge_file_one(o, ren1_dst, null_sha1, 0,\n>  \t\t\t\t\t\t\t ren1->pair->two->sha1, ren1->pair->two->mode,\n>  \t\t\t\t\t\t\t dst_other.sha1, dst_other.mode,\n> -\t\t\t\t\t\t\t branch1, branch2);\n> +\t\t\t\t\t\t\t branch1, branch2, &mfi) < 0)\n> +\t\t\t\t\t\treturn -1;\n>  \t\t\t\t\toutput(o, 1, _(\"Adding merged %s\"), ren1_dst);\n>  \t\t\t\t\tupdate_file(o, 0, mfi.sha, mfi.mode, ren1_dst);\n>  \t\t\t\t\ttry_merge = 0;\n> @@ -1622,9 +1628,10 @@ static int merge_content(struct merge_options *o,\n>  \t\tif (dir_in_way(path, !o->call_depth))\n>  \t\t\tdf_conflict_remains = 1;\n>  \t}\n> -\tmfi = merge_file_special_markers(o, &one, &a, &b,\n> +\tif (merge_file_special_markers(o, &one, &a, &b,\n>  \t\t\t\t\t o->branch1, path1,\n> -\t\t\t\t\t o->branch2, path2);\n> +\t\t\t\t\t o->branch2, path2, &mfi) < 0)\n> +\t\treturn -1;\n>  \n>  \tif (mfi.clean && !df_conflict_remains &&\n>  \t    sha_eq(mfi.sha, a_sha) && mfi.mode == a_mode) {\n"},{"id":"290533","messageId":"xmqqfurv3ejz.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"8615dc276828a3f99a27ff2eda9909548a7d435e.1467199553.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 1/9] Report bugs consistently","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-29T20:50:24Z","receivedAt":"2016-06-29T20:50:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> The vast majority of error messages in Git's source code which report a\n> bug use the convention to prefix the message with \"BUG:\".\n\nGood thing to do.\n\nBut if we were to review and apply a 200+ line patch, I wonder if we\nwant to go one step further to allow us to write\n\n    BUG(\"killed-file %s not found\", name);\n\ninstead.\n\n"},{"id":"290537","messageId":"xmqqbn2j3dt0.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"4f2817208cf2f7d4e839ddb6818bf652b0aa633c.1467199553.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 7/9] merge-recursive: handle return values indicating errors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-29T21:06:35Z","receivedAt":"2016-06-29T21:06:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> diff --git a/merge-recursive.c b/merge-recursive.c\n> index 6ab7dfc..bb075e3 100644\n> --- a/merge-recursive.c\n> +++ b/merge-recursive.c\n> @@ -266,8 +266,10 @@ struct tree *write_tree_from_memory(struct merge_options *o)\n>  \t\tactive_cache_tree = cache_tree();\n>  \n>  \tif (!cache_tree_fully_valid(active_cache_tree) &&\n> -\t    cache_tree_update(&the_index, 0) < 0)\n> -\t\tdie(_(\"error building trees\"));\n> +\t    cache_tree_update(&the_index, 0) < 0) {\n> +\t\terror(_(\"error building trees\"));\n> +\t\treturn NULL;\n> +\t}\n\nThis actually is a BUG(), isn't it?  We have already verified that\nthe cache is merged, so cache_tree_update() ought to be able to come\nup with the whole-tree hash.\n\n> @@ -548,19 +550,17 @@ static int update_stages(const char *path, const struct diff_filespec *o,\n>  \t */\n>  \tint clear = 1;\n>  \tint options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_SKIP_DFCHECK;\n> +\tint ret = 0;\n> +\n>  \tif (clear)\n> -\t\tif (remove_file_from_cache(path))\n> -\t\t\treturn -1;\n> -\tif (o)\n> -\t\tif (add_cacheinfo(o->mode, o->sha1, path, 1, 0, options))\n> -\t\t\treturn -1;\n> -\tif (a)\n> -\t\tif (add_cacheinfo(a->mode, a->sha1, path, 2, 0, options))\n> -\t\t\treturn -1;\n> -\tif (b)\n> -\t\tif (add_cacheinfo(b->mode, b->sha1, path, 3, 0, options))\n> -\t\t\treturn -1;\n> -\treturn 0;\n> +\t\tret = remove_file_from_cache(path);\n> +\tif (!ret && o)\n> +\t\tret = add_cacheinfo(o->mode, o->sha1, path, 1, 0, options);\n> +\tif (!ret && a)\n> +\t\tret = add_cacheinfo(a->mode, a->sha1, path, 2, 0, options);\n> +\tif (!ret && b)\n> +\t\tret = add_cacheinfo(b->mode, b->sha1, path, 3, 0, options);\n> +\treturn ret;\n>  }\n\nAren't the preimage and the postimage doing the same thing?  The\nonly two differences I spot are (1) it is clear in the original that\nthe returned value is -1 in the error case, even if the error\nconvention of remove_file_from_cache() and add_cacheinfo() were \"0\nis good, others are bad\"; and (2) the control flow is easier to\nfollow in the original.\n\n> @@ -736,7 +736,7 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n>  \treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n>  }\n>  \n> -static void update_file_flags(struct merge_options *o,\n> +static int update_file_flags(struct merge_options *o,\n>  \t\t\t      const unsigned char *sha,\n>  \t\t\t      unsigned mode,\n>  \t\t\t      const char *path,\n> @@ -777,8 +777,7 @@ static void update_file_flags(struct merge_options *o,\n>  \n>  \t\tif (make_room_for_path(o, path) < 0) {\n>  \t\t\tupdate_wd = 0;\n> -\t\t\tfree(buf);\n> -\t\t\tgoto update_index;\n> +\t\t\tgoto free_buf;\n>  \t\t}\n>  \t\tif (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) {\n>  \t\t\tint fd;\n> @@ -801,20 +800,22 @@ static void update_file_flags(struct merge_options *o,\n>  \t\t} else\n>  \t\t\tdie(_(\"do not know what to do with %06o %s '%s'\"),\n>  \t\t\t    mode, sha1_to_hex(sha), path);\n> +free_buf:\n>  \t\tfree(buf);\n\nI somehow find the above change harder to follow than the original.\n\n>  \t}\n>   update_index:\n>  \tif (update_cache)\n>  \t\tadd_cacheinfo(mode, sha, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n> +\treturn 0;\n>  }\n>  \n\nThis one is in line with the stated goal of the patch.\n\n> @@ -1028,21 +1030,23 @@ static void handle_change_delete(struct merge_options *o,\n>  \t\t * correct; since there is no true \"middle point\" between\n>  \t\t * them, simply reuse the base version for virtual merge base.\n>  \t\t */\n> -\t\tremove_file_from_cache(path);\n> -\t\tupdate_file(o, 0, o_sha, o_mode, renamed ? renamed : path);\n> +\t\tret = remove_file_from_cache(path);\n> +\t\tif (!ret)\n> +\t\t\tret = update_file(o, 0, o_sha, o_mode,\n> +\t\t\t\t\t  renamed ? renamed : path);\n\nAs you noted in the log message, this does change the behaviour.  If\nremove returns non-zero for whatever reason, we still did update()\nin the original, but we no longer do.  Does this have negative\neffect to the overall codeflow?\n\nOr, assuming that everybody returns -1 for errors, perhaps\n\n\tret = remove();\n        ret |= update();\n\nmay be a more faithful and safe conversion?\n\n> @@ -1087,21 +1094,22 @@ static void conflict_rename_delete(struct merge_options *o,\n>  \t\tb_mode = dest->mode;\n>  \t}\n>  \n> -\thandle_change_delete(o,\n> +\tret = handle_change_delete(o,\n>  \t\t\t     o->call_depth ? orig->path : dest->path,\n>  \t\t\t     orig->sha1, orig->mode,\n>  \t\t\t     a_sha, a_mode,\n>  \t\t\t     b_sha, b_mode,\n>  \t\t\t     _(\"rename\"), _(\"renamed\"));\n> -\n> -\tif (o->call_depth) {\n> -\t\tremove_file_from_cache(dest->path);\n> -\t} else {\n> -\t\tupdate_stages(dest->path, NULL,\n> +\tif (ret < 0)\n> +\t\treturn ret;\n> +\tif (o->call_depth)\n> +\t\tret = remove_file_from_cache(dest->path);\n> +\telse\n> +\t\tret = update_stages(dest->path, NULL,\n>  \t\t\t      rename_branch == o->branch1 ? dest : NULL,\n>  \t\t\t      rename_branch == o->branch1 ? NULL : dest);\n> -\t}\n\nSimilarly, if handle_change_delete() returns non-zero, we no longer\ncall remove() or update().  Is that a good behaviour change?\n\n> -static void handle_file(struct merge_options *o,\n> +static int handle_file(struct merge_options *o,\n>  \t\t\tstruct diff_filespec *rename,\n>  \t\t\tint stage,\n>  \t\t\tstruct rename_conflict_info *ci)\n\nLikewise.\n\n> -static void conflict_rename_rename_1to2(struct merge_options *o,\n> +static int conflict_rename_rename_1to2(struct merge_options *o,\n>  \t\t\t\t\tstruct rename_conflict_info *ci)\n>  {\n> ...\n> -\t\tif (merge_file_one(o, one->path,\n> +\t\tif ((ret = merge_file_one(o, one->path,\n>  \t\t\t\t one->sha1, one->mode,\n>  \t\t\t\t a->sha1, a->mode,\n>  \t\t\t\t b->sha1, b->mode,\n> -\t\t\t\t ci->branch1, ci->branch2, &mfi) < 0)\n> -\t\t\treturn;\n> +\t\t\t\t ci->branch1, ci->branch2, &mfi)))\n> +\t\t\treturn ret;\n\nThis does not change behaviour.\n\n> @@ -1194,7 +1208,8 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n>  \t\t * pathname and then either rename the add-source file to that\n>  \t\t * unique path, or use that unique path instead of src here.\n>  \t\t */\n> -\t\tupdate_file(o, 0, mfi.sha, mfi.mode, one->path);\n> +\t\tif ((ret = update_file(o, 0, mfi.sha, mfi.mode, one->path)))\n> +\t\t\treturn ret;\n\nBut this does.\n\n> @@ -1205,22 +1220,31 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n>  \t\t * resolving the conflict at that path in its favor.\n>  \t\t */\n>  \t\tadd = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);\n> -\t\tif (add)\n> -\t\t\tupdate_file(o, 0, add->sha1, add->mode, a->path);\n> +\t\tif (add) {\n> +\t\t\tif ((ret = update_file(o, 0, add->sha1, add->mode,\n> +\t\t\t\t\ta->path)))\n> +\t\t\t\treturn ret;\n> +\t\t}\n\nSo does this.\n\n\n>  \t\telse\n>  \t\t\tremove_file_from_cache(a->path);\n>  \t\tadd = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);\n> -\t\tif (add)\n> -\t\t\tupdate_file(o, 0, add->sha1, add->mode, b->path);\n> +\t\tif (add) {\n> +\t\t\tif ((ret = update_file(o, 0, add->sha1, add->mode,\n> +\t\t\t\t\tb->path)))\n> +\t\t\t\treturn ret;\n> +\t\t}\n\nAnd this.\n\n>  \t} else {\n> -\t\thandle_file(o, a, 2, ci);\n> -\t\thandle_file(o, b, 3, ci);\n> +\t\tif ((ret = handle_file(o, a, 2, ci)) ||\n> +\t\t    (ret = handle_file(o, b, 3, ci)))\n> +\t\t\treturn ret;\n\nAnd this.\n"},{"id":"290538","messageId":"xmqq7fd73d6s.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"06c09ab4d684c239ae4ae03373c7cc7afb3be60b.1467199553.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 8/9] merge-recursive: switch to returning errors instead of dying","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-29T21:19:55Z","receivedAt":"2016-06-29T21:20:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> @@ -743,6 +741,8 @@ static int update_file_flags(struct merge_options *o,\n>  \t\t\t      int update_cache,\n>  \t\t\t      int update_wd)\n> ...\n> +\t\t\tret = error_errno(_(\"do not know what to do with %06o %s '%s'\"),\n> +\t\t\t\tmode, sha1_to_hex(sha), path);\n>  free_buf:\n\nOK, with a few more users of this label, it no longer looks so\nstrange to me to have this label here.\n\nAt least, match the indentation level to the existing one we see\nbelow, though?\n\n>  \t\tfree(buf);\n>  \t}\n>   update_index:\n> -\tif (update_cache)\n> +\tif (!ret && update_cache)\n>  \t\tadd_cacheinfo(mode, sha, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n\nUnlike my reaction to \"if (!ret) do this\" in an earlier patch, I do\nthink this change is sensible.  We wouldn't have reached here in the\noriginal code if we saw errors, and some of the variable used to\ncall add_cacheinfo() at this point may be garbage when ret is\nnon-zero, i.e. we know we earlier saw an error.\n\n> -\treturn 0;\n> +\treturn ret;\n>  }\n> ...\n>  \t\t\tif ((merge_status < 0) || !result_buf.ptr)\n> -\t\t\t\tdie(_(\"Failed to execute internal merge\"));\n> +\t\t\t\tret = error(_(\"Failed to execute internal merge\"));\n>  \n> -\t\t\tif (write_sha1_file(result_buf.ptr, result_buf.size,\n> +\t\t\tif (!ret && write_sha1_file(result_buf.ptr, result_buf.size,\n>  \t\t\t\t\t    blob_type, result->sha))\n\nLikewise.\n\n> @@ -1861,7 +1867,7 @@ static int process_entry(struct merge_options *o,\n>  \t\t */\n>  \t\tremove_file(o, 1, path, !a_mode);\n>  \t} else\n> -\t\tdie(_(\"Fatal merge failure, shouldn't happen.\"));\n> +\t\treturn error(_(\"Fatal merge failure, shouldn't happen.\"));\n\nIsn't this BUG()?\n\n"},{"id":"290540","messageId":"xmqq37nv3d19.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"dc58115e23c8d942b3ff6270b43719bc841becbb.1467199553.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 9/9] am: make a direct call to merge_recursive","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-29T21:23:14Z","receivedAt":"2016-06-29T21:23:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> -\tcp.git_cmd = 1;\n> +\tinit_merge_options(&o);\n> +\n> +\to.branch1 = \"HEAD\";\n> +\this_tree_name = xstrfmt(\"%.*s\", linelen(state->msg), state->msg);\n> +\to.branch2 = his_tree_name;\n>  \n> -\targv_array_pushf(&cp.env_array, \"GITHEAD_%s=%.*s\",\n> -\t\t\t sha1_to_hex(his_tree), linelen(state->msg), state->msg);\n>  \tif (state->quiet)\n> -\t\targv_array_push(&cp.env_array, \"GIT_MERGE_VERBOSITY=0\");\n> +\t\to.verbosity = 0;\n>  \n> -\targv_array_push(&cp.args, \"merge-recursive\");\n> -\targv_array_push(&cp.args, sha1_to_hex(orig_tree));\n> -\targv_array_push(&cp.args, \"--\");\n> -\targv_array_push(&cp.args, sha1_to_hex(our_tree));\n> -\targv_array_push(&cp.args, sha1_to_hex(his_tree));\n> +\tstatus = merge_recursive_generic(&o, our_tree, his_tree, 1, bases, &result);\n> +\tif (status < 0)\n> +\t\texit(128);\n> +\tfree(his_tree_name);\n>  \n> -\tstatus = run_command(&cp) ? (-1) : 0;\n> -\tdiscard_cache();\n> -\tread_cache();\n>  \treturn status;\n>  }\n\nIs this a correct conversion?\n\nWe used to prepare the command line and called run_command() and\nwithout dying returned status with the error status from the\nmerge-recursive that was spawned by run_command().\n\nThe new code does not report failure to the caller and instead dies.\n\n"},{"id":"290560","messageId":"5774B039.8080802@kdbg.org","threadId":"42743","inReplyTo":"8615dc276828a3f99a27ff2eda9909548a7d435e.1467199553.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 1/9] Report bugs consistently","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2016-06-30T05:38:01Z","receivedAt":"2016-06-30T05:38:34Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 29.06.2016 um 13:36 schrieb Johannes Schindelin:\n> @@ -955,9 +955,8 @@ static struct merge_file_info merge_file_1(struct merge_options *o,\n>\n>   \t\t\tif (!sha_eq(a->sha1, b->sha1))\n>   \t\t\t\tresult.clean = 0;\n> -\t\t} else {\n> -\t\t\tdie(_(\"unsupported object type in the tree\"));\n> -\t\t}\n> +\t\t} else\n> +\t\t\tdie(_(\"BUG: unsupported object type in the tree\"));\n\nWould it perhaps make sense to remove the _() markup (here and a few \nmore instances in this patch)? It's simpler for developers to find the \ncode location when a \"BUG:\" is reported untranslated.\n\n-- Hannes\n\n"},{"id":"290570","messageId":"alpine.DEB.2.20.1606301040470.12947@virtualbox","threadId":"42743","inReplyTo":"CAPig+cSHy=2VaNP5gpwqKN4vuCBrOUy39L0i9xcda8m3zx+GPA@mail.gmail.com","subject":"Re: [PATCH 1/9] Report bugs consistently","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-30T08:41:00Z","receivedAt":"2016-06-30T08:41:44Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Eric,\n\nOn Wed, 29 Jun 2016, Eric Sunshine wrote:\n\n> On Wed, Jun 29, 2016 at 7:36 AM, Johannes Schindelin\n> <johannes.schindelin@gmx.de> wrote:\n> > The vast majority of error messages in Git's source code which report a\n> > bug use the convention to prefix the message with \"BUG:\".\n> > [...]\n> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > ---\n> > diff --git a/merge-recursive.c b/merge-recursive.c\n> > @@ -1853,7 +1852,7 @@ int merge_trees(struct merge_options *o,\n> > -                               die(_(\"Unprocessed path??? %s\"),\n> > +                               die(_(\"BUG: unprocessed path??? %s\"),\n> \n> This and others downcase the first word (which is consistent with\n> modern practice)...\n> \n> > diff --git a/sha1_file.c b/sha1_file.c\n> > @@ -795,7 +795,7 @@ void close_all_packs(void)\n> > -                       die(\"BUG! Want to close pack marked 'do-not-close'\");\n> > +                       die(\"BUG: Want to close pack marked 'do-not-close'\");\n> \n> ...but this one neglects to.\n\nThanks! Will be fixed as part of v2.\n\nCiao,\nDscho\n"},{"id":"290571","messageId":"alpine.DEB.2.20.1606301041380.12947@virtualbox","threadId":"42743","inReplyTo":"xmqqfurv3ejz.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 1/9] Report bugs consistently","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-30T08:42:37Z","receivedAt":"2016-06-30T08:43:08Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 29 Jun 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > The vast majority of error messages in Git's source code which report a\n> > bug use the convention to prefix the message with \"BUG:\".\n> \n> Good thing to do.\n> \n> But if we were to review and apply a 200+ line patch, I wonder if we\n> want to go one step further to allow us to write\n> \n>     BUG(\"killed-file %s not found\", name);\n> \n> instead.\n\nIf the idea is to make it easier to find, I would wager a guess that\n'die(\"BUG:' would be just as good a search term. Even better, I think,\nbecause 'BUG' would also match comments.\n\nCiao,\nDscho\n"},{"id":"290572","messageId":"alpine.DEB.2.20.1606301026360.12947@virtualbox","threadId":"42743","inReplyTo":"xmqqpoqz51o8.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 9/9] am: make a direct call to merge_recursive","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-30T08:38:44Z","receivedAt":"2016-06-30T08:46:42Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 29 Jun 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > From: Junio C Hamano <gitster@pobox.com>\n> \n> Did I write this thing?\n\nYes, you did. It was db05d6194d3f9ea9e64163944961d5f6e85302be as part of\npu@{2016-06-15}.\n\n> Having two sets of numbers that illustrated that this is not really\n> a useful optimization in the bigger picture looks vaguely familiar\n> (e.g. $gmane/279417), but the numbers are different.\n\nI beg to differ. While the performance improvement is not huge, even your\ntoy experiment shows it is significant, i.e. noticeable.\n\n> > \tIt feels *slightly* wrong to submit your own patch to review,\n> > \thowever, please keep in mind that\n> >\n> > \t1) I changed the patch (o.gently does not exist anymore, so I do\n> > \t   not set it), and\n> >\n> > \t2) I added my own timings performed on Windows.\n> \n> It probably is much less confusing if you take the authorship,\n> possibly with a passing reference to whatever I wrote as the source\n> of inspiration in the log message, or something.  I do not think I\n> deserve a credit in this 9-patch series.\n\nThe patch is an almost verbatim copy of db05d6194, I only removed the\nnow-obsolete \"gently\" setting, is all.\n\nThat means that I feel really, really uneasy about claiming authorship\nbecause I did not write it.\n\nIf you really want me to, I will take custody of this patch and rewrite\nthe commit message as well using --reset--author, of course.\n\nCiao,\nDscho\n"},{"id":"290573","messageId":"alpine.DEB.2.20.1606301043320.12947@virtualbox","threadId":"42743","inReplyTo":"5774B039.8080802@kdbg.org","subject":"Re: [PATCH 1/9] Report bugs consistently","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-06-30T08:45:13Z","receivedAt":"2016-06-30T08:50:33Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Hannes,\n\nOn Thu, 30 Jun 2016, Johannes Sixt wrote:\n\n> Am 29.06.2016 um 13:36 schrieb Johannes Schindelin:\n> > @@ -955,9 +955,8 @@ static struct merge_file_info merge_file_1(struct\n> > merge_options *o,\n> >\n> >      if (!sha_eq(a->sha1, b->sha1))\n> >   \t\t\t\tresult.clean = 0;\n> > -\t\t} else {\n> > -\t\t\tdie(_(\"unsupported object type in the tree\"));\n> > -\t\t}\n> > +\t\t} else\n> > +\t\t\tdie(_(\"BUG: unsupported object type in the tree\"));\n> \n> Would it perhaps make sense to remove the _() markup (here and a few more\n> instances in this patch)? It's simpler for developers to find the code\n> location when a \"BUG:\" is reported untranslated.\n\nI would agree, but the purpose of this patch was not to fix that, but to\nfix the inconsistency of the message.\n\nMaybe as an add-on patch, with *all* 'die(_(\"BUG:' instances converted?\nThat would be even more outside the purview of my patch series than\ntouching the bug reports outside merge-recursive.c, though.\n\nCiao,\nDscho\n"},{"id":"290581","messageId":"20160630092333.GB24964@sigill.intra.peff.net","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1606301041380.12947@virtualbox","subject":"Re: [PATCH 1/9] Report bugs consistently","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-06-30T09:23:34Z","receivedAt":"2016-06-30T09:23:41Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Jun 30, 2016 at 10:42:37AM +0200, Johannes Schindelin wrote:\n\n> > > The vast majority of error messages in Git's source code which report a\n> > > bug use the convention to prefix the message with \"BUG:\".\n> > \n> > Good thing to do.\n> > \n> > But if we were to review and apply a 200+ line patch, I wonder if we\n> > want to go one step further to allow us to write\n> > \n> >     BUG(\"killed-file %s not found\", name);\n> > \n> > instead.\n> \n> If the idea is to make it easier to find, I would wager a guess that\n> 'die(\"BUG:' would be just as good a search term. Even better, I think,\n> because 'BUG' would also match comments.\n\nI have been tempted to switch to BUG(), because it would make it easy to\ncall abort() and get a coredump (and therefore a stack trace). On the\nother hand:\n\n  - we could always trigger such behavior in die() by looking for \"BUG:\" in\n    the output string. :)\n\n  - it's also sometimes useful to get a stack trace from a regular\n    non-bug die(). So maybe something optional like:\n\n      if (git_env_bool(\"GIT_ABORT_ON_DIE\", 0))\n              abort();\n\n    would be helpful (since you have to turn it on ahead of time, you\n    could also just run the program under gdb, of course; however, I\n    sometimes find that it's hard to get gdb where you want it because\n    git spawns so many sub-programs. Or maybe I just need to get better\n    at using gdb's child-following options).\n\nThe other thing BUG() would get us is that we could turn it into a macro\n(on systems with vararg macros) and report things like __FILE__ and\n__LINE__.  In practice, though our BUG messages are unique enough that\nthere is no problem finding the source.\n\n-Peff\n"},{"id":"290650","messageId":"alpine.DEB.2.20.1607011215010.12947@virtualbox","threadId":"42743","inReplyTo":"xmqqvb0r3gi4.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 4/9] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-01T10:16:30Z","receivedAt":"2016-07-01T10:17:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 29 Jun 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > There are a couple of places where return values indicating errors\n> > are ignored. Let's teach them manners.\n> >\n> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > ---\n> >  merge-recursive.c | 10 ++++++++--\n> >  1 file changed, 8 insertions(+), 2 deletions(-)\n> >\n> > diff --git a/merge-recursive.c b/merge-recursive.c\n> > index bcb53f0..c4ece96 100644\n> > --- a/merge-recursive.c\n> > +++ b/merge-recursive.c\n> > @@ -1944,8 +1944,9 @@ int merge_recursive(struct merge_options *o,\n> >  \t\tsaved_b2 = o->branch2;\n> >  \t\to->branch1 = \"Temporary merge branch 1\";\n> >  \t\to->branch2 = \"Temporary merge branch 2\";\n> > -\t\tmerge_recursive(o, merged_common_ancestors, iter->item,\n> > -\t\t\t\tNULL, &merged_common_ancestors);\n> > +\t\tif (merge_recursive(o, merged_common_ancestors, iter->item,\n> > +\t\t\t\tNULL, &merged_common_ancestors) < 0)\n> > +\t\t\treturn -1;\n> >  \t\to->branch1 = saved_b1;\n> >  \t\to->branch2 = saved_b2;\n> >  \t\to->call_depth--;\n> \n> OK, this early return (and others in this patch) are only for\n> negative (i.e. error) cases, and \"attempted a merge, resulted in\n> conflicts\" cases are handled as before.\n> \n> Which is good.\n> \n> I wonder if o->branch[12] need to be restored, though.  The only\n> sensible thing the caller can do is to punt, but would it expect to\n> be able to do some error reporting based on these fields, e.g.\n> printf(\"merge of %s and %s failed\", o->branch1, o->branch2) or\n> something?  In addition to that kind of \"state restoration\", we may\n> need to watch out for resource leaks, but I think there is none at\n> least these three early returns.\n\nI do not think that the caller can do anything sensible with *o after we\nreturn an error: it means that we failed *somewhere* in that operation,\nbut does not say exactly where.\n\nCiao,\nDscho\n"},{"id":"290651","messageId":"alpine.DEB.2.20.1607011123550.12947@virtualbox","threadId":"42743","inReplyTo":"xmqqziq33ju2.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 3/9] Prepare the builtins for a libified merge_recursive()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-01T10:14:10Z","receivedAt":"2016-07-01T10:22:57Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 29 Jun 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > A truly libified function does not die() just for fun.\n> \n> The sentence is wasting bits.  After all, a helper function in\n> run-once-and-exit program does not die() just for fun, either.\n\nGiven that I had a hard time when reviewing Christian's apply patches to\ndrive home the message that it is not okay for library functions to call\ndie() or exit(), I happen to disagree with your notion that this sentence\nis wasting bits.\n\nThis sentence does not so much target *you* personally as audience, but\nthe occasional reader of the log who wonders: \"Why don't we just call\ndie()? We would not have to worry about passing back the return value\nthrough all those long call chains...\"\n\n> > As such, the recursive merge will convert all die() calls to return -1\n> > instead in the next commits, giving the caller a chance at least to\n> > print some helpful message.\n> >\n> > Let's prepare the builtins for this fatal error condition, even if we do\n> > not really do more than imitating the previous die()'s exit(128): this is\n> > what callers of e.g. `git merge` have come to expect.\n> \n> One thing missing is your design decision and justification.\n> \n> For example, the above explanation hints that the original code in\n> this hunk expected merge_trees() to die() with some useful message\n> when there is an error condition, but merge_trees() is going to be\n> improved not to die() itself and return -1 instead to signal an\n> error, so that the caller can react more flexibly, and this is a\n> step to prepare for the version of merge_trees() that no longer\n> dies.\n\nOkay, I will replace the \"As such...\" paragraph with a modified version of\nyour paraphrased explanation.\n\n> > -\t\t\tmerge_trees(&o, new->commit->tree, work,\n> > +\t\t\tret = merge_trees(&o, new->commit->tree, work,\n> >  \t\t\t\told->commit->tree, &result);\n> > +\t\t\tif (ret < 0)\n> > +\t\t\t\texit(128);\n> \n> The postimage of the patch tells us that the caller is now\n> responsible for exiting with status 128, but neither the proposed\n> log message nor the above hunk tells us where the message the\n> original code must have given to the end user from die() inside\n> merge_trees().  The updated caller just exits, so a natural guess is\n> that the calls to die() have been changed to fprintf(stderr) with\n> the patch.\n\nEven more natural is it to guess that the code will call error(), just\nlike we do almost everywhere else.\n\nBut you are right, I do not have to leave the reader guessing. Better to\nerr on the side of being slightly verbose than to be so concise that\nnobody understands what I mean.\n\n> But that does not mesh very well with the stated objective of the\n> patch.  The callers want flexibility to do their own error handling,\n> including giving their own message, so letting merge_trees() to\n> still write the same message to the standard error stream would not\n> work well for them.  A caller may want to do merge_trees() just to\n> see if it succeeds, without wanting to give _any_ indication of that\n> is happening to the user, because it has an alternate/fallback code\n> if merge_trees() fails, for example (analogy: \"am -3\" first tries a\n> straight patch application before fallking back to 3-way merge; it\n> may not want to show the error from the first attempt).\n> \n> The reader _can_ guess that this step ignores the error-message\n> issue, and improving it later (or keep ignoring that issue) might be\n> OK in the context of this patch series, but it is necessary to be\n> upfront to the readers what the design choices were and which one of\n> those choices the proposed patch adopted as its design for them to\n> be able to evaluate the patch series correctly.\n\nTo be honest, I did not even think about the error message issue because\nmy primary concern is to teach the sequencer to perform rebase -i's grunt\nwork. And while we usually suppress the output of the commands in rebase\n-i, we do show them in case of errors.\n\nIt will make things more complex, unfortunately, even if it will be\nstraight-forward: there is already a strbuf and a flag in struct\nmerge_options to collect output. The merge_options are simply not passed\nthrough to all of the previously die()ing functions yet.\n\nI won't have time to get this implemented this week, unfortunately. So\nplease do not expect the next iteration of this patch series before next\nweek.\n\nI could imagine that you wanted even more fine-grained control, where we\nhave a range of return values indicating different error conditions.\nHowever, I already spent two weeks' worth of work to get this far, and\nwould like to defer that task to the developer who will actually need\nthese fine-grained return values (if we ever will need them).\n\nCiao,\nDscho\n\nP.S.: For the future, would you mind deleting the quoted remainder of my\npatches when there are no further comments? I deleted a footer of 73\nunnecessary lines in this mail. It's no big deal if this is too tedious,\nbut it would make my life easier.\n"},{"id":"290652","messageId":"alpine.DEB.2.20.1607011057180.12947@virtualbox","threadId":"42743","inReplyTo":"xmqq4m8b4zdd.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 2/9] merge-recursive: clarify code in was_tracked()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-01T09:23:47Z","receivedAt":"2016-07-01T10:23:01Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 29 Jun 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > It can be puzzling to see that was_tracked() tries to match an index\n> > entry by name even if cache_name_pos() returned a negative value. Let's\n> > clarify that cache_name_pos() implicitly looks for stage 0, while we are\n> > also okay with finding other stages.\n> >\n> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > ---\n> >  merge-recursive.c | 1 +\n> >  1 file changed, 1 insertion(+)\n> >\n> > diff --git a/merge-recursive.c b/merge-recursive.c\n> > index 98f4632..bcb53f0 100644\n> > --- a/merge-recursive.c\n> > +++ b/merge-recursive.c\n> > @@ -658,6 +658,7 @@ static int was_tracked(const char *path)\n> >  {\n> >  \tint pos = cache_name_pos(path, strlen(path));\n> >  \n> > +\t/* cache_name_pos() looks for stage == 0, so pos may be < 0 */\n> \n> It returns >= if found at stage #0, or a negative (counting from -1)\n> to indicate where the path would be inserted if it were to be added\n> at stage #0.\n\nRight. Please note that nothing in the function call to cache_name_pos()\nspecifies that we are looking for any particular stage. You are probably\ntoo intimate with the implementation details, so naturally you assume that\ncache_name_pos() looks for a precise match *with stage 0*. I am not that\nfamiliar with that part of the implementation, so I *was* puzzled.\n\n> The new comment does not explain how \"pos may be < 0\" leads to\n> \"hence pos = -1 - pos is the right thing to do here\".  It is\n> misleading and we probably are better off without.\n\nI agree that the comment is not very good currently. But I disagree that\nwe are better off without any comment here.\n\nSo maybe a comment is not really enough here. In case the entry is in\nstage 0, we will have an exact match, so there is no need to go to the\nlengths of a while() loop.\n\nI would like to propose this diff instead (it is larger, but with a net\nsavings of one line):\n\n-- snipsnap --\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex d5a593c..0eda51a 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -658,24 +658,22 @@ static int was_tracked(const char *path)\n {\n \tint pos = cache_name_pos(path, strlen(path));\n \n-\tif (pos < 0)\n-\t\tpos = -1 - pos;\n-\twhile (pos < active_nr &&\n-\t       !strcmp(path, active_cache[pos]->name)) {\n+\tif (pos >= 0)\n+\t\treturn pos < active_nr;\n+\t/*\n+\t * cache_name_pos() looks for stage == 0, even if we did not ask for\n+\t * it. Let's look for stage == 2 now.\n+\t */\n+\tfor (pos = -1 - pos; pos < active_nr &&\n+\t     !strcmp(path, active_cache[pos]->name); pos++)\n \t\t/*\n \t\t * If stage #0, it is definitely tracked.\n \t\t * If it has stage #2 then it was tracked\n \t\t * before this merge started.  All other\n \t\t * cases the path was not tracked.\n \t\t */\n-\t\tswitch (ce_stage(active_cache[pos])) {\n-\t\tcase 0:\n-\t\tcase 2:\n+\t\tif (ce_stage(active_cache[pos]) == 2)\n \t\t\treturn 1;\n-\t\t}\n-\t\tpos++;\n-\t}\n \treturn 0;\n }\n \n\n"},{"id":"290655","messageId":"alpine.DEB.2.20.1607011246400.12947@virtualbox","threadId":"42743","inReplyTo":"xmqqbn2j3dt0.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 7/9] merge-recursive: handle return values indicating errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-01T11:08:45Z","receivedAt":"2016-07-01T11:08:58Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 29 Jun 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > diff --git a/merge-recursive.c b/merge-recursive.c\n> > index 6ab7dfc..bb075e3 100644\n> > --- a/merge-recursive.c\n> > +++ b/merge-recursive.c\n> > @@ -266,8 +266,10 @@ struct tree *write_tree_from_memory(struct merge_options *o)\n> >  \t\tactive_cache_tree = cache_tree();\n> >  \n> >  \tif (!cache_tree_fully_valid(active_cache_tree) &&\n> > -\t    cache_tree_update(&the_index, 0) < 0)\n> > -\t\tdie(_(\"error building trees\"));\n> > +\t    cache_tree_update(&the_index, 0) < 0) {\n> > +\t\terror(_(\"error building trees\"));\n> > +\t\treturn NULL;\n> > +\t}\n> \n> This actually is a BUG(), isn't it?  We have already verified that\n> the cache is merged, so cache_tree_update() ought to be able to come\n> up with the whole-tree hash.\n\nI am not so sure. What if the disk is full?\n\n> > @@ -548,19 +550,17 @@ static int update_stages(const char *path, const struct diff_filespec *o,\n> >  \t */\n> >  \tint clear = 1;\n> >  \tint options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_SKIP_DFCHECK;\n> > +\tint ret = 0;\n> > +\n> >  \tif (clear)\n> > -\t\tif (remove_file_from_cache(path))\n> > -\t\t\treturn -1;\n> > -\tif (o)\n> > -\t\tif (add_cacheinfo(o->mode, o->sha1, path, 1, 0, options))\n> > -\t\t\treturn -1;\n> > -\tif (a)\n> > -\t\tif (add_cacheinfo(a->mode, a->sha1, path, 2, 0, options))\n> > -\t\t\treturn -1;\n> > -\tif (b)\n> > -\t\tif (add_cacheinfo(b->mode, b->sha1, path, 3, 0, options))\n> > -\t\t\treturn -1;\n> > -\treturn 0;\n> > +\t\tret = remove_file_from_cache(path);\n> > +\tif (!ret && o)\n> > +\t\tret = add_cacheinfo(o->mode, o->sha1, path, 1, 0, options);\n> > +\tif (!ret && a)\n> > +\t\tret = add_cacheinfo(a->mode, a->sha1, path, 2, 0, options);\n> > +\tif (!ret && b)\n> > +\t\tret = add_cacheinfo(b->mode, b->sha1, path, 3, 0, options);\n> > +\treturn ret;\n> >  }\n> \n> Aren't the preimage and the postimage doing the same thing?  The\n> only two differences I spot are (1) it is clear in the original that\n> the returned value is -1 in the error case, even if the error\n> convention of remove_file_from_cache() and add_cacheinfo() were \"0\n> is good, others are bad\"; and (2) the control flow is easier to\n> follow in the original.\n\nAh crap. This is a late left-over from the time when I thought I had to\nintroduce a \"really fatal error\" condition, i.e. return -128. I thought\nthat there might be errors that need to be handled differently, and\ntherefore it was essential that the return value -128 would not be\nconverted into a -1.\n\nWill revert this hunk.\n\n> > @@ -736,7 +736,7 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n> >  \treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n> >  }\n> >  \n> > -static void update_file_flags(struct merge_options *o,\n> > +static int update_file_flags(struct merge_options *o,\n> >  \t\t\t      const unsigned char *sha,\n> >  \t\t\t      unsigned mode,\n> >  \t\t\t      const char *path,\n> > @@ -777,8 +777,7 @@ static void update_file_flags(struct merge_options *o,\n> >  \n> >  \t\tif (make_room_for_path(o, path) < 0) {\n> >  \t\t\tupdate_wd = 0;\n> > -\t\t\tfree(buf);\n> > -\t\t\tgoto update_index;\n> > +\t\t\tgoto free_buf;\n> >  \t\t}\n> >  \t\tif (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) {\n> >  \t\t\tint fd;\n> > @@ -801,20 +800,22 @@ static void update_file_flags(struct merge_options *o,\n> >  \t\t} else\n> >  \t\t\tdie(_(\"do not know what to do with %06o %s '%s'\"),\n> >  \t\t\t    mode, sha1_to_hex(sha), path);\n> > +free_buf:\n> >  \t\tfree(buf);\n> \n> I somehow find the above change harder to follow than the original.\n\nYeah, you noticed when commenting on the next patch why I did it. I added\na comment to the commit message.\n\n> >  \t}\n> >   update_index:\n> >  \tif (update_cache)\n> >  \t\tadd_cacheinfo(mode, sha, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n> > +\treturn 0;\n> >  }\n> >  \n> \n> This one is in line with the stated goal of the patch.\n> \n> > @@ -1028,21 +1030,23 @@ static void handle_change_delete(struct merge_options *o,\n> >  \t\t * correct; since there is no true \"middle point\" between\n> >  \t\t * them, simply reuse the base version for virtual merge base.\n> >  \t\t */\n> > -\t\tremove_file_from_cache(path);\n> > -\t\tupdate_file(o, 0, o_sha, o_mode, renamed ? renamed : path);\n> > +\t\tret = remove_file_from_cache(path);\n> > +\t\tif (!ret)\n> > +\t\t\tret = update_file(o, 0, o_sha, o_mode,\n> > +\t\t\t\t\t  renamed ? renamed : path);\n> \n> As you noted in the log message, this does change the behaviour.  If\n> remove returns non-zero for whatever reason, we still did update()\n> in the original, but we no longer do.  Does this have negative\n> effect to the overall codeflow?\n\nPlease note that remove_file_from_index() does not, in fact, anything but\n0 for the moment. So the practical use of testing the return value is nil\nwith Git's current source code.\n\nIt is more about future-proofing than anything else.\n\nAs to this incantation specifically, if we ever fail in\nremove_file_from_cache(), I would expect the code to fail fast. That is\nhow I implemented it in this patch.\n\n> > @@ -1087,21 +1094,22 @@ static void conflict_rename_delete(struct merge_options *o,\n> >  \t\tb_mode = dest->mode;\n> >  \t}\n> >  \n> > -\thandle_change_delete(o,\n> > +\tret = handle_change_delete(o,\n> >  \t\t\t     o->call_depth ? orig->path : dest->path,\n> >  \t\t\t     orig->sha1, orig->mode,\n> >  \t\t\t     a_sha, a_mode,\n> >  \t\t\t     b_sha, b_mode,\n> >  \t\t\t     _(\"rename\"), _(\"renamed\"));\n> > -\n> > -\tif (o->call_depth) {\n> > -\t\tremove_file_from_cache(dest->path);\n> > -\t} else {\n> > -\t\tupdate_stages(dest->path, NULL,\n> > +\tif (ret < 0)\n> > +\t\treturn ret;\n> > +\tif (o->call_depth)\n> > +\t\tret = remove_file_from_cache(dest->path);\n> > +\telse\n> > +\t\tret = update_stages(dest->path, NULL,\n> >  \t\t\t      rename_branch == o->branch1 ? dest : NULL,\n> >  \t\t\t      rename_branch == o->branch1 ? NULL : dest);\n> > -\t}\n> \n> Similarly, if handle_change_delete() returns non-zero, we no longer\n> call remove() or update().  Is that a good behaviour change?\n\nPlease note that handle_change_delete() did not return any value before\nthis patch series. In those cases that now return -1, it previously\ndie()d.\n\nMy patch ensures that the latter functions are not called in the error\ncase. Just like before.\n\n> > -static void handle_file(struct merge_options *o,\n> > +static int handle_file(struct merge_options *o,\n> >  \t\t\tstruct diff_filespec *rename,\n> >  \t\t\tint stage,\n> >  \t\t\tstruct rename_conflict_info *ci)\n> \n> Likewise.\n\nDitto.\n\n> > -static void conflict_rename_rename_1to2(struct merge_options *o,\n> > +static int conflict_rename_rename_1to2(struct merge_options *o,\n> >  \t\t\t\t\tstruct rename_conflict_info *ci)\n> >  {\n> > ...\n> > -\t\tif (merge_file_one(o, one->path,\n> > +\t\tif ((ret = merge_file_one(o, one->path,\n> >  \t\t\t\t one->sha1, one->mode,\n> >  \t\t\t\t a->sha1, a->mode,\n> >  \t\t\t\t b->sha1, b->mode,\n> > -\t\t\t\t ci->branch1, ci->branch2, &mfi) < 0)\n> > -\t\t\treturn;\n> > +\t\t\t\t ci->branch1, ci->branch2, &mfi)))\n> > +\t\t\treturn ret;\n> \n> This does not change behaviour.\n> \n> > @@ -1194,7 +1208,8 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n> >  \t\t * pathname and then either rename the add-source file to that\n> >  \t\t * unique path, or use that unique path instead of src here.\n> >  \t\t */\n> > -\t\tupdate_file(o, 0, mfi.sha, mfi.mode, one->path);\n> > +\t\tif ((ret = update_file(o, 0, mfi.sha, mfi.mode, one->path)))\n> > +\t\t\treturn ret;\n> \n> But this does.\n\nAs stated above: it does not. In case of an error in update_file() (which\nis now reported via a return value, in contrast to the previous behavior),\njust like before, we skip the rest of the function.\n\nThe difference is that we now clean up our resources and return an error\ninstead of die()ing.\n\n> > @@ -1205,22 +1220,31 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n> >  \t\t * resolving the conflict at that path in its favor.\n> >  \t\t */\n> >  \t\tadd = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);\n> > -\t\tif (add)\n> > -\t\t\tupdate_file(o, 0, add->sha1, add->mode, a->path);\n> > +\t\tif (add) {\n> > +\t\t\tif ((ret = update_file(o, 0, add->sha1, add->mode,\n> > +\t\t\t\t\ta->path)))\n> > +\t\t\t\treturn ret;\n> > +\t\t}\n> \n> So does this.\n\nSame as above (i.e. same behavior as previously).\n\n> >  \t\telse\n> >  \t\t\tremove_file_from_cache(a->path);\n> >  \t\tadd = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);\n> > -\t\tif (add)\n> > -\t\t\tupdate_file(o, 0, add->sha1, add->mode, b->path);\n> > +\t\tif (add) {\n> > +\t\t\tif ((ret = update_file(o, 0, add->sha1, add->mode,\n> > +\t\t\t\t\tb->path)))\n> > +\t\t\t\treturn ret;\n> > +\t\t}\n> \n> And this.\n\nYep. Same behavior.\n\n> >  \t} else {\n> > -\t\thandle_file(o, a, 2, ci);\n> > -\t\thandle_file(o, b, 3, ci);\n> > +\t\tif ((ret = handle_file(o, a, 2, ci)) ||\n> > +\t\t    (ret = handle_file(o, b, 3, ci)))\n> > +\t\t\treturn ret;\n> \n> And this.\n\nYep. Same behavior.\n\nCiao,\nDscho\n"},{"id":"290656","messageId":"alpine.DEB.2.20.1607011310050.12947@virtualbox","threadId":"42743","inReplyTo":"xmqq7fd73d6s.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 8/9] merge-recursive: switch to returning errors instead of dying","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-01T11:14:22Z","receivedAt":"2016-07-01T11:14:33Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 29 Jun 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > @@ -743,6 +741,8 @@ static int update_file_flags(struct merge_options *o,\n> >  \t\t\t      int update_cache,\n> >  \t\t\t      int update_wd)\n> > ...\n> > +\t\t\tret = error_errno(_(\"do not know what to do with %06o %s '%s'\"),\n> > +\t\t\t\tmode, sha1_to_hex(sha), path);\n> >  free_buf:\n> \n> OK, with a few more users of this label, it no longer looks so\n> strange to me to have this label here.\n> \n> At least, match the indentation level to the existing one we see\n> below, though?\n\nOkay, fixed.\n\nAlthough I have to say that I do not quite understand the meaning of the\none-space indentation, in particular since the later \"error_return\" label\nlacks it.\n\n> >  \t\tfree(buf);\n> >  \t}\n> >   update_index:\n> > [...]\n> > @@ -1861,7 +1867,7 @@ static int process_entry(struct merge_options *o,\n> >  \t\t */\n> >  \t\tremove_file(o, 1, path, !a_mode);\n> >  \t} else\n> > -\t\tdie(_(\"Fatal merge failure, shouldn't happen.\"));\n> > +\t\treturn error(_(\"Fatal merge failure, shouldn't happen.\"));\n> \n> Isn't this BUG()?\n\nI guess it is! Fixed.\n\nCiao,\nDscho\n"},{"id":"290657","messageId":"alpine.DEB.2.20.1607011442510.12947@virtualbox","threadId":"42743","inReplyTo":"xmqq37nv3d19.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 9/9] am: make a direct call to merge_recursive","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-01T12:46:03Z","receivedAt":"2016-07-01T12:46:25Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 29 Jun 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > -\tcp.git_cmd = 1;\n> > +\tinit_merge_options(&o);\n> > +\n> > +\to.branch1 = \"HEAD\";\n> > +\this_tree_name = xstrfmt(\"%.*s\", linelen(state->msg), state->msg);\n> > +\to.branch2 = his_tree_name;\n> >  \n> > -\targv_array_pushf(&cp.env_array, \"GITHEAD_%s=%.*s\",\n> > -\t\t\t sha1_to_hex(his_tree), linelen(state->msg), state->msg);\n> >  \tif (state->quiet)\n> > -\t\targv_array_push(&cp.env_array, \"GIT_MERGE_VERBOSITY=0\");\n> > +\t\to.verbosity = 0;\n> >  \n> > -\targv_array_push(&cp.args, \"merge-recursive\");\n> > -\targv_array_push(&cp.args, sha1_to_hex(orig_tree));\n> > -\targv_array_push(&cp.args, \"--\");\n> > -\targv_array_push(&cp.args, sha1_to_hex(our_tree));\n> > -\targv_array_push(&cp.args, sha1_to_hex(his_tree));\n> > +\tstatus = merge_recursive_generic(&o, our_tree, his_tree, 1, bases, &result);\n> > +\tif (status < 0)\n> > +\t\texit(128);\n> > +\tfree(his_tree_name);\n> >  \n> > -\tstatus = run_command(&cp) ? (-1) : 0;\n> > -\tdiscard_cache();\n> > -\tread_cache();\n> >  \treturn status;\n> >  }\n> \n> Is this a correct conversion?\n> \n> We used to prepare the command line and called run_command() and\n> without dying returned status with the error status from the\n> merge-recursive that was spawned by run_command().\n> \n> The new code does not report failure to the caller and instead dies.\n\nTrue, this is incorrect.\n\nI took a step back and realized that the most appropriate course of\naction would be to revert the commit that calls run_command() to begin\nwith. This also solves the authorship issue.\n\nAnd while at it, I also noticed another sore to my eye that I had noticed\nrepeatedly and now fix as part of this patch series.\n\nThanks,\nDscho\n"},{"id":"290662","messageId":"alpine.DEB.2.20.1607011542210.12947@virtualbox","threadId":"42743","inReplyTo":"xmqqr3bf3fvr.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 5/9] merge-recursive: avoid returning a wholesale struct","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-01T13:48:33Z","receivedAt":"2016-07-01T13:49:50Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 29 Jun 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > It is technically allowed, as per C89, for functions' return type to\n> > be complete structs (i.e. *not* just pointers to structs), but it is\n> > bad practice.\n> \n> Not necessarily.\n\nOkay, I fixed it.\n\n> > This is a very late attempt to contain the damage done by this developer\n> > in 6d297f8 (Status update on merge-recursive in C, 2006-07-08) which\n> > introduced such a return type.\n> >\n> > It will also help the current effort to libify merge-recursive.c, as\n> > it will allow us to return proper error codes later.\n> \n> But this part of the motivation does make sense.  Having to pass an\n> extra int &error_code field, only because the return value is\n> already used for something else, is a lot more weird.\n\nWhile I still think it is inelegant to return a whole struct\n(understanding the machine code aspects of it), I toned it down to say\nthat we just do not use that construct in Git's source code. And I kept\nthe paragraph that you found more convincing, of course.\n\nCiao,\nDscho\n\nP.S.: If it is not too much of a problem, may I ask you to simply delete\nremainders of my patches when replying and not commenting on them? I just\ndeleted 226 lines after verifying that you really did not respond to any\npart of it in the unhelpful Alpine client (which I still use because it\n*still* fits much better with my workflow than anything else, by a lot).\nAgain, not a big deal if it would make your life more painful.\n"},{"id":"290663","messageId":"alpine.DEB.2.20.1607011548560.12947@virtualbox","threadId":"42743","inReplyTo":"20160630092333.GB24964@sigill.intra.peff.net","subject":"Re: [PATCH 1/9] Report bugs consistently","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-01T13:51:33Z","receivedAt":"2016-07-01T13:52:42Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Peff,\n\nOn Thu, 30 Jun 2016, Jeff King wrote:\n\n> On Thu, Jun 30, 2016 at 10:42:37AM +0200, Johannes Schindelin wrote:\n> \n> > > > The vast majority of error messages in Git's source code which report a\n> > > > bug use the convention to prefix the message with \"BUG:\".\n> > > \n> > > Good thing to do.\n> > > \n> > > But if we were to review and apply a 200+ line patch, I wonder if we\n> > > want to go one step further to allow us to write\n> > > \n> > >     BUG(\"killed-file %s not found\", name);\n> > > \n> > > instead.\n> > \n> > If the idea is to make it easier to find, I would wager a guess that\n> > 'die(\"BUG:' would be just as good a search term. Even better, I think,\n> > because 'BUG' would also match comments.\n> \n> I have been tempted to switch to BUG(), because it would make it easy to\n> call abort() and get a coredump (and therefore a stack trace).\n\nPlease keep in mind that abort() does not produce stackdumps with MinGW.\nSo at least Windows developers would not be better off.\n\n> On the other hand:\n> \n>   - we could always trigger such behavior in die() by looking for \"BUG:\" in\n>     the output string. :)\n> \n>   - it's also sometimes useful to get a stack trace from a regular\n>     non-bug die(). So maybe something optional like:\n> \n>       if (git_env_bool(\"GIT_ABORT_ON_DIE\", 0))\n>               abort();\n> \n>     would be helpful (since you have to turn it on ahead of time, you\n>     could also just run the program under gdb, of course; however, I\n>     sometimes find that it's hard to get gdb where you want it because\n>     git spawns so many sub-programs. Or maybe I just need to get better\n>     at using gdb's child-following options).\n\nHeh. I still find myself using that good old trick where I set a variable,\nloop while it is set and print out the pid, waiting for a debugger to\nattach and re-set that variable.\n\n> The other thing BUG() would get us is that we could turn it into a macro\n> (on systems with vararg macros) and report things like __FILE__ and\n> __LINE__.  In practice, though our BUG messages are unique enough that\n> there is no problem finding the source.\n\nThat would be very nice *also* for error() messages. But I guess we cannot\nhave it, vararg macros being a feature rather than a standard.\n\nCiao,\nDscho\n"},{"id":"290667","messageId":"20160701150242.GA20898@dcvr.yhbt.net","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1607011542210.12947@virtualbox","subject":"Re: [PATCH 5/9] merge-recursive: avoid returning a wholesale struct","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2016-07-01T15:02:42Z","receivedAt":"2016-07-01T15:02:49Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> P.S.: If it is not too much of a problem, may I ask you to simply delete\n> remainders of my patches when replying and not commenting on them? I just\n> deleted 226 lines after verifying that you really did not respond to any\n> part of it in the unhelpful Alpine client (which I still use because it\n> *still* fits much better with my workflow than anything else, by a lot).\n> Again, not a big deal if it would make your life more painful.\n\nI find the over-quoting annoying, too.  Fwiw, my muttrc has:\n\nbind pager , skip-quoted\n\nWhich allows me to hit the comma key to skip over quoted text.\nNot sure if Alpine or other clients have something similar.\nBut I'd rather not waste the keystroke/bandwidth/storage.\n"},{"id":"290669","messageId":"xmqq7fd51ijr.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1607011057180.12947@virtualbox","subject":"Re: [PATCH 2/9] merge-recursive: clarify code in was_tracked()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-01T15:31:36Z","receivedAt":"2016-07-01T15:35:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> I agree that the comment is not very good currently. But I disagree that\n> we are better off without any comment here.\n\nI meant we are better off without your particular version of comment\nwhich is misleading.  I am all for a better comment to help those\nwho are new to the codepath.\n\n> I would like to propose this diff instead (it is larger, but with a net\n> savings of one line):\n>\n> -- snipsnap --\n> diff --git a/merge-recursive.c b/merge-recursive.c\n> index d5a593c..0eda51a 100644\n> --- a/merge-recursive.c\n> +++ b/merge-recursive.c\n> @@ -658,24 +658,22 @@ static int was_tracked(const char *path)\n>  {\n>  \tint pos = cache_name_pos(path, strlen(path));\n>  \n> -\tif (pos < 0)\n> -\t\tpos = -1 - pos;\n> -\twhile (pos < active_nr &&\n> -\t       !strcmp(path, active_cache[pos]->name)) {\n> +\tif (pos >= 0)\n> +\t\treturn pos < active_nr;\n> +\t/*\n> +\t * cache_name_pos() looks for stage == 0, even if we did not ask for\n> +\t * it. Let's look for stage == 2 now.\n> +\t */\n\nI think this keeps the same phrasing from the original that makes\nthe comment misleading.  It \"looks for stage == 0\" is not the whole\nstory but only half.  It looks for a place to insert the path at\nstage #0\" is.  Your half is used by the \"if (0 <= pos)\" you split\nout into a separate statement above already, and the untold half is\nneeded to explain why this loop is correct.\n\nIt returns the place to insert stage #0 entry, so if you are looking\nfor stage #1 or higher, you only have to loop while the path\nmatches, because the entries are sorted by <path, stage>.\n\nAnd with that understanding, there is no strong reason to special\ncase \"ah, we found stage #0 entry\" at all.\n\n> +\tfor (pos = -1 - pos; pos < active_nr &&\n> +\t     !strcmp(path, active_cache[pos]->name); pos++)\n>  \t\t/*\n>  \t\t * If stage #0, it is definitely tracked.\n>  \t\t * If it has stage #2 then it was tracked\n>  \t\t * before this merge started.  All other\n>  \t\t * cases the path was not tracked.\n>  \t\t */\n> -\t\tswitch (ce_stage(active_cache[pos])) {\n> -\t\tcase 0:\n> -\t\tcase 2:\n> +\t\tif (ce_stage(active_cache[pos]) == 2)\n>  \t\t\treturn 1;\n> -\t\t}\n> -\t\tpos++;\n> -\t}\n>  \treturn 0;\n>  }\n>  \n"},{"id":"290670","messageId":"xmqq37nt1i0k.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1607011123550.12947@virtualbox","subject":"Re: [PATCH 3/9] Prepare the builtins for a libified merge_recursive()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-01T15:43:07Z","receivedAt":"2016-07-01T15:43:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> > A truly libified function does not die() just for fun.\n>> \n>> The sentence is wasting bits.  After all, a helper function in\n>> run-once-and-exit program does not die() just for fun, either.\n>\n> This sentence does not so much target *you* personally as audience, but\n> the occasional reader of the log who wonders: \"Why don't we just call\n> die()? We would not have to worry about passing back the return value\n> through all those long call chains...\"\n\nI was (and I am still) reacting mostly to \"just for fun\".\n\n> Even more natural is it to guess that the code will call error(), just\n> like we do almost everywhere else.\n> ...\n>> But that does not mesh very well with the stated objective of the\n>> patch.\n> ...\n> I could imagine that you wanted even more fine-grained control, where we\n> have a range of return values indicating different error conditions.\n\nI personally don't.  I was pointing out the discrepancy between what\nthe introduction says, i.e. \"this way is way more flexible for the\ncallers when they want to do their own error handling\", and what the\ncode actually does.  If the explanation said \"This series does not\ngive the full flexibility potential callers may desire yet, but at\nleast gives enough flexibility to do 'I do not want the called\nfunction to die, but append my own error message before I die\nmyself'.\", that is certainly an understandable stance to take, I\nwould say.\n"},{"id":"290671","messageId":"xmqqy45lz708.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1607011215010.12947@virtualbox","subject":"Re: [PATCH 4/9] merge_recursive: abort properly upon errors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-01T15:56:55Z","receivedAt":"2016-07-01T15:57:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> >  \t\tsaved_b2 = o->branch2;\n>> >  \t\to->branch1 = \"Temporary merge branch 1\";\n>> >  \t\to->branch2 = \"Temporary merge branch 2\";\n>> > -\t\tmerge_recursive(o, merged_common_ancestors, iter->item,\n>> > -\t\t\t\tNULL, &merged_common_ancestors);\n>> > +\t\tif (merge_recursive(o, merged_common_ancestors, iter->item,\n>> > +\t\t\t\tNULL, &merged_common_ancestors) < 0)\n>> > +\t\t\treturn -1;\n>> >  \t\to->branch1 = saved_b1;\n>> >  \t\to->branch2 = saved_b2;\n>> >  \t\to->call_depth--;\n>> \n>> I wonder if o->branch[12] need to be restored, though.  The only\n>> sensible thing the caller can do is to punt,...\n>\n> I do not think that the caller can do anything sensible with *o after we\n> return an error...\n\nThat is totally up to what this patch does, isn't it?\n\nBy deliberately keeping o->branch[12] to point at the temporary\nnames and not restoring, this patch declares \"the caller cannot do\nanything sensible with *o\".  If it restores, the caller still can.\nEven with this step as-is, the caller can tell at which recursion\nlevel the merge failed by looking at o->call_depth, for example.\n\nI do not think the current set of callers, and a new one you will be\nintroducing, would prefer to be able to do something with *o after\nthe failure return from this function, so in that sense, I do not\ncare deeply either way.\n\nBut if the patch is making a policy decision \"*o is undefined upon\nerror return from this function\", it would help people who want to\nbuild on top of this codebase to add that to the comment before the\nfunction, wouldn't it?\n\n"},{"id":"290672","messageId":"xmqqtwg9z6q0.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1606301026360.12947@virtualbox","subject":"Re: [PATCH 9/9] am: make a direct call to merge_recursive","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-01T16:03:03Z","receivedAt":"2016-07-01T16:03:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> On Wed, 29 Jun 2016, Junio C Hamano wrote:\n>\n>> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n>> \n>> > From: Junio C Hamano <gitster@pobox.com>\n>> \n>> Did I write this thing?\n>\n> Yes, you did. It was db05d6194d3f9ea9e64163944961d5f6e85302be as part of\n> pu@{2016-06-15}.\n\nAh, OK, that is quite old, dating from Oct 2015.  Thanks for a\npointer.\n\n"},{"id":"290693","messageId":"20160701181624.GB16695@sigill.intra.peff.net","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1607011548560.12947@virtualbox","subject":"Re: [PATCH 1/9] Report bugs consistently","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-07-01T18:16:24Z","receivedAt":"2016-07-01T18:16:31Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Jul 01, 2016 at 03:51:33PM +0200, Johannes Schindelin wrote:\n\n> > > If the idea is to make it easier to find, I would wager a guess that\n> > > 'die(\"BUG:' would be just as good a search term. Even better, I think,\n> > > because 'BUG' would also match comments.\n> > \n> > I have been tempted to switch to BUG(), because it would make it easy to\n> > call abort() and get a coredump (and therefore a stack trace).\n> \n> Please keep in mind that abort() does not produce stackdumps with MinGW.\n> So at least Windows developers would not be better off.\n\nIt doesn't in Linux either. But you can use the core file to get a\nbacktrace. Do you not get cores at all in Windows? Yuck. :)\n\n> Heh. I still find myself using that good old trick where I set a variable,\n> loop while it is set and print out the pid, waiting for a debugger to\n> attach and re-set that variable.\n\nThat's a new one to me. Gross. :)\n\nUsually I can coax git into running \"gdbserver ... git foo\" instead of\ngit foo\" via config. But sometimes the values are hard-coded (for\ninstance, the way we call pack-objects, which I recently made a bit more\nflexible).\n\n> > The other thing BUG() would get us is that we could turn it into a macro\n> > (on systems with vararg macros) and report things like __FILE__ and\n> > __LINE__.  In practice, though our BUG messages are unique enough that\n> > there is no problem finding the source.\n> \n> That would be very nice *also* for error() messages. But I guess we cannot\n> have it, vararg macros being a feature rather than a standard.\n\nIt is a standard, it's just C99, which we've been rather slow to\nembrace.\n\nHowever we have prior art in the trace code. If your system supports\nvariadic macros, then we define the macros to pass the file/line info.\nOtherwise, we skip the macros, and those names point directly to\nfunctions.\n\nSo it would not be too hard to implement. I've never bothered, though,\nbecause in my experience the interesting thing is almost never \"on what\nline was error() called\", but rather \"what line called the function that\ncalled error()\". I.e., the stack trace.\n\nI looked a while ago at trying to auto-generate stack traces in git\nusing something like GNU's backtrace (obviously not portable, but it's a\nstart). My conclusion was that the simplest thing is to just generate a\ncore and run \"gdb -ex bt\" on it.\n\n-Peff\n"},{"id":"290731","messageId":"CACsJy8A1ZU8VgBYmQAVC6LmXMVgt5CgvC_w0Y7Y6oX88RFO3dw@mail.gmail.com","threadId":"42743","inReplyTo":"8615dc276828a3f99a27ff2eda9909548a7d435e.1467199553.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 1/9] Report bugs consistently","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-07-02T05:11:40Z","receivedAt":"2016-07-02T05:12:57Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Jun 29, 2016 at 1:36 PM, Johannes Schindelin\n<johannes.schindelin@gmx.de> wrote:\n> @@ -955,9 +955,8 @@ static struct merge_file_info merge_file_1(struct merge_options *o,\n>\n>                         if (!sha_eq(a->sha1, b->sha1))\n>                                 result.clean = 0;\n> -               } else {\n> -                       die(_(\"unsupported object type in the tree\"));\n> -               }\n> +               } else\n> +                       die(_(\"BUG: unsupported object type in the tree\"));\n\nAs a message targeting developers, we do not need to mark this for\ntranslation. There are a couple other _() in this patch that should be\nremoved as well.\n-- \nDuy\n"},{"id":"290735","messageId":"alpine.DEB.2.20.1607020906560.12947@virtualbox","threadId":"42743","inReplyTo":"xmqq7fd51ijr.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 2/9] merge-recursive: clarify code in was_tracked()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-02T07:20:24Z","receivedAt":"2016-07-02T07:20:41Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Fri, 1 Jul 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > I would like to propose this diff instead (it is larger, but with a net\n> > savings of one line):\n> >\n> > -- snipsnap --\n> > diff --git a/merge-recursive.c b/merge-recursive.c\n> > index d5a593c..0eda51a 100644\n> > --- a/merge-recursive.c\n> > +++ b/merge-recursive.c\n> > @@ -658,24 +658,22 @@ static int was_tracked(const char *path)\n> >  {\n> >  \tint pos = cache_name_pos(path, strlen(path));\n> >  \n> > -\tif (pos < 0)\n> > -\t\tpos = -1 - pos;\n> > -\twhile (pos < active_nr &&\n> > -\t       !strcmp(path, active_cache[pos]->name)) {\n> > +\tif (pos >= 0)\n> > +\t\treturn pos < active_nr;\n> > +\t/*\n> > +\t * cache_name_pos() looks for stage == 0, even if we did not ask for\n> > +\t * it. Let's look for stage == 2 now.\n> > +\t */\n> \n> I think this keeps the same phrasing from the original that makes\n> the comment misleading.  It \"looks for stage == 0\" is not the whole\n> story but only half.\n\nYes, it is the relevant part of the story to explain why we're not done\nwhen pos < 0.\n\nTo understand why we're not done yet, the crucial point is *not* that the\nreturn value encodes the insert position. The crucial point is that\ndespite asking for an index entry matching a specific name, we might not\nfind one, *even if there is one*. And the reason is that cache_name_pos()\ndoes not quite do what the name suggests.\n\nI am sorry to disagree with you here: I really find it important to\ndocument this potential misunderstanding.\n\n> It looks for a place to insert the path at stage #0\" is.  Your half is\n> used by the \"if (0 <= pos)\" you split out into a separate statement\n> above already, and the untold half is needed to explain why this loop is\n> correct.\n\nTrue. I did not bother to document that part. Because even if I was\npuzzled by the logic handling a negative return value, the assignment \"pos\n= -1 - pos\" made it very clear to me what was happening. I am not *that*\neasily puzzled.\n\n> It returns the place to insert stage #0 entry, so if you are looking\n> for stage #1 or higher, you only have to loop while the path\n> matches, because the entries are sorted by <path, stage>.\n> \n> And with that understanding, there is no strong reason to special\n> case \"ah, we found stage #0 entry\" at all.\n\nThere is one, and I mentioned it. In the common case (i.e. we found an\nentry right away because it is in stage 0), the loop unnecessarily\ncompares the name *again*: index_name_pos() already performed that\ncomparison and if it returned a non-negative value, we know that that\ncomparison was successful.\n\nI find the combination of clarification, code reduction, and separation\nbetween conflated cases (stage 0 does not need that loop at all)\ncompelling enough to state that my patch is an improvement overall. We can\ndiscuss about wording and what details to mention in the comments, of\ncourse.\n\nCiao,\nDscho\n"},{"id":"290736","messageId":"alpine.DEB.2.20.1607020921200.12947@virtualbox","threadId":"42743","inReplyTo":"xmqq37nt1i0k.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 3/9] Prepare the builtins for a libified merge_recursive()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-02T07:24:16Z","receivedAt":"2016-07-02T07:24:28Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Fri, 1 Jul 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> >> > A truly libified function does not die() just for fun.\n> >> \n> >> The sentence is wasting bits.  After all, a helper function in\n> >> run-once-and-exit program does not die() just for fun, either.\n> >\n> > This sentence does not so much target *you* personally as audience, but\n> > the occasional reader of the log who wonders: \"Why don't we just call\n> > die()? We would not have to worry about passing back the return value\n> > through all those long call chains...\"\n> \n> I was (and I am still) reacting mostly to \"just for fun\".\n\nYeah, sorry, that part was lost on me.\n\n> > Even more natural is it to guess that the code will call error(), just\n> > like we do almost everywhere else.\n> > ...\n> >> But that does not mesh very well with the stated objective of the\n> >> patch.\n> > ...\n> > I could imagine that you wanted even more fine-grained control, where we\n> > have a range of return values indicating different error conditions.\n> \n> I personally don't.  I was pointing out the discrepancy between what\n> the introduction says, i.e. \"this way is way more flexible for the\n> callers when they want to do their own error handling\", and what the\n> code actually does.  If the explanation said \"This series does not\n> give the full flexibility potential callers may desire yet, but at\n> least gives enough flexibility to do 'I do not want the called\n> function to die, but append my own error message before I die\n> myself'.\", that is certainly an understandable stance to take, I\n> would say.\n\nAh, but the message did not say \"error message handling\", but \"error\nhandling\". With my limited command of the English language, I tried to\nconvey that this patch allows the callers to do something when the called\noperation reported an error. Previously they did not get the chance.\n\nCiao,\nDscho\n"},{"id":"290738","messageId":"alpine.DEB.2.20.1607020924410.12947@virtualbox","threadId":"42743","inReplyTo":"CACsJy8A1ZU8VgBYmQAVC6LmXMVgt5CgvC_w0Y7Y6oX88RFO3dw@mail.gmail.com","subject":"Re: [PATCH 1/9] Report bugs consistently","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-02T07:25:53Z","receivedAt":"2016-07-02T07:32:50Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Duy,\n\nOn Sat, 2 Jul 2016, Duy Nguyen wrote:\n\n> On Wed, Jun 29, 2016 at 1:36 PM, Johannes Schindelin\n> <johannes.schindelin@gmx.de> wrote:\n> > @@ -955,9 +955,8 @@ static struct merge_file_info merge_file_1(struct merge_options *o,\n> >\n> >                         if (!sha_eq(a->sha1, b->sha1))\n> >                                 result.clean = 0;\n> > -               } else {\n> > -                       die(_(\"unsupported object type in the tree\"));\n> > -               }\n> > +               } else\n> > +                       die(_(\"BUG: unsupported object type in the tree\"));\n> \n> As a message targeting developers, we do not need to mark this for\n> translation. There are a couple other _() in this patch that should be\n> removed as well.\n\nYes, Hannes already pointed that out.\n\nMy answer is the same: it is not the purpose of this patch series to fix\nthis, and therefore it retains the previous behavior.\n\nCiao,\nDscho\n"},{"id":"290741","messageId":"CACsJy8CobWYjpjkkaG=wFK+zUyF3Z9CtFku7eprnX=_08y6KpA@mail.gmail.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1607020924410.12947@virtualbox","subject":"Re: [PATCH 1/9] Report bugs consistently","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-07-02T08:01:20Z","receivedAt":"2016-07-02T08:01:54Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sat, Jul 2, 2016 at 9:25 AM, Johannes Schindelin\n<Johannes.Schindelin@gmx.de> wrote:\n> Hi Duy,\n>\n> On Sat, 2 Jul 2016, Duy Nguyen wrote:\n>\n>> On Wed, Jun 29, 2016 at 1:36 PM, Johannes Schindelin\n>> <johannes.schindelin@gmx.de> wrote:\n>> > @@ -955,9 +955,8 @@ static struct merge_file_info merge_file_1(struct merge_options *o,\n>> >\n>> >                         if (!sha_eq(a->sha1, b->sha1))\n>> >                                 result.clean = 0;\n>> > -               } else {\n>> > -                       die(_(\"unsupported object type in the tree\"));\n>> > -               }\n>> > +               } else\n>> > +                       die(_(\"BUG: unsupported object type in the tree\"));\n>>\n>> As a message targeting developers, we do not need to mark this for\n>> translation. There are a couple other _() in this patch that should be\n>> removed as well.\n>\n> Yes, Hannes already pointed that out.\n\nAh.. sorry I didn't read the whole thread.\n\n> My answer is the same: it is not the purpose of this patch series to fix\n> this, and therefore it retains the previous behavior.\n\nYou're changing the string and adding more work to translators. So\neither leave the string untouched, or drop _().\n-- \nDuy\n"},{"id":"290747","messageId":"alpine.DEB.2.20.1607021324330.12947@virtualbox","threadId":"42743","inReplyTo":"xmqqy45lz708.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 4/9] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-02T11:30:25Z","receivedAt":"2016-07-02T11:30:41Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Fri, 1 Jul 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> >> >  \t\tsaved_b2 = o->branch2;\n> >> >  \t\to->branch1 = \"Temporary merge branch 1\";\n> >> >  \t\to->branch2 = \"Temporary merge branch 2\";\n> >> > -\t\tmerge_recursive(o, merged_common_ancestors, iter->item,\n> >> > -\t\t\t\tNULL, &merged_common_ancestors);\n> >> > +\t\tif (merge_recursive(o, merged_common_ancestors, iter->item,\n> >> > +\t\t\t\tNULL, &merged_common_ancestors) < 0)\n> >> > +\t\t\treturn -1;\n> >> >  \t\to->branch1 = saved_b1;\n> >> >  \t\to->branch2 = saved_b2;\n> >> >  \t\to->call_depth--;\n> >> \n> >> I wonder if o->branch[12] need to be restored, though.  The only\n> >> sensible thing the caller can do is to punt,...\n> >\n> > I do not think that the caller can do anything sensible with *o after we\n> > return an error...\n> \n> That is totally up to what this patch does, isn't it?\n\nNo, not really. We do not really know *where* in the recursive merge the\nfailure happened.\n\nAll we know is that it happened while trying to merge the temporary\nbranches.\n\n> By deliberately keeping o->branch[12] to point at the temporary\n> names and not restoring, this patch declares \"the caller cannot do\n> anything sensible with *o\".  If it restores, the caller still can.\n\nBut would restoring the branch names not give the false impression that\nthe error occurred during a different phase of the recursive merge?\n\n> Even with this step as-is, the caller can tell at which recursion\n> level the merge failed by looking at o->call_depth, for example.\n\nWell, not really:\n\n1) please note the o->call_depth-- that is *also* skipped in case of an\n   error after my patch, and\n\n2) what use is the recursion level if an arbitrary number of merges could\n   happen at the same recursion depth?\n\nIn short, I do not think that resetting those values in *o could bring any\nbenefit to the caller, short of introducing those fine-grained error\nvalues I mentioned elsewhere.\n\nCiao,\nDscho\n"},{"id":"290814","messageId":"d50b69901158ee10cb375a7b1c3c3c600bace010.1467717729.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467717729.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 01/17] Verify that `git pull --rebase` shows the helpful advice when failing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-05T11:23:01Z","receivedAt":"2016-07-05T11:23:30Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n t/t5520-pull.sh | 30 ++++++++++++++++++++++++++++++\n 1 file changed, 30 insertions(+)\n\ndiff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\nindex 3159956..b281d6f 100755\n--- a/t/t5520-pull.sh\n+++ b/t/t5520-pull.sh\n@@ -255,6 +255,36 @@ test_expect_success '--rebase' '\n \ttest new = \"$(git show HEAD:file2)\"\n '\n \n+test_expect_success '--rebase with conflicts shows advice' '\n+\ttest_when_finished \"git rebase --abort; git checkout -f to-rebase\" &&\n+\tgit checkout -b seq &&\n+\tprintf \"1\\\\n2\\\\n3\\\\n4\\\\n5\\\\n\" >seq.txt &&\n+\tgit add seq.txt &&\n+\ttest_tick &&\n+\tgit commit -m \"Add seq.txt\" &&\n+\tprintf \"6\\\\n\" >>seq.txt &&\n+\ttest_tick &&\n+\tgit commit -m \"Append to seq.txt\" seq.txt &&\n+\tgit checkout -b with-conflicts HEAD^ &&\n+\tprintf \"conflicting\\\\n\" >>seq.txt &&\n+\ttest_tick &&\n+\tgit commit -m \"Create conflict\" seq.txt &&\n+\ttest_must_fail git pull --rebase . seq 2>err >out &&\n+\tgrep \"When you have resolved this problem\" out\n+'\n+test_expect_success 'failed --rebase shows advice' '\n+\ttest_when_finished \"git rebase --abort; git checkout -f to-rebase\" &&\n+\tgit checkout -b diverging &&\n+\ttest_commit attributes .gitattributes \"* text=auto\" attrs &&\n+\tsha1=\"$(printf \"1\\\\r\\\\n\" | git hash-object -w --stdin)\" &&\n+\tgit update-index --cacheinfo 0644 $sha1 file &&\n+\tgit commit -m v1-with-cr &&\n+\tgit checkout -f -b fails-to-rebase HEAD^ &&\n+\ttest_commit v2-without-cr file \"2\" file2-lf &&\n+\ttest_must_fail git pull --rebase . diverging 2>err >out &&\n+\tgrep \"When you have resolved this problem\" out\n+'\n+\n test_expect_success '--rebase fails with multiple branches' '\n \tgit reset --hard before-rebase &&\n \ttest_must_fail git pull --rebase . copy master 2>err &&\n-- \n2.9.0.280.g32e2a70\n\n\n"},{"id":"290815","messageId":"cover.1467717729.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467199553.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 00/17] Use merge_recursive() directly in the builtin am","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-05T11:22:51Z","receivedAt":"2016-07-05T11:23:35Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This is the second iteration of the long-awaited re-roll of the attempt to\navoid spawning merge-recursive from the builtin am and use merge_recursive()\ndirectly instead.\n\nThe *real* reason for the reroll is that I need a libified recursive\nmerge to accelerate the interactive rebase by teaching the sequencer to\ndo rebase -i's grunt work.\n\nIn this endeavor, we need to be extra careful to retain backwards\ncompatibility. The test script t6022-merge-rename.sh, for example, verifies\nthat `git pull` exits with status 128 in case of a fatal error. To that end,\nwe need to make sure that fatal errors are handled by existing (builtin)\nusers via exit(128) (or die(), which calls exit(128) at the end).  New users\n(such as a builtin helper doing rebase -i's grunt work) may want to print\nsome helpful advice what happened and how to get out of this mess before\nerroring out.\n\nThe changes relative to the first iteration of this patch series:\n\n- a variable that could be used uninitialized is now initialized (thanks,\n  Travis & clang!)\n\n- several commit messages were touched up (and I hope y'all agree, improved)\n\n- an unnecessary hunk was reverted (this was a left-over from an\n  unpublished iteration that needed to retain return values faithfully, i.e.\n  it made a difference between -1 and -128 as error value)\n\n- Junio's patch to use recursive_merge() directly in the builtin am was\n  replaced by a different solution\n\n- an error message was identified as, and converted into, a bug report\n  instead\n\n- the code in was_tracked() now avoids a loop when it is unnecessary,\n  and further clarifies why we keep looking when cache_name_pos() did\n  not find the entry we asked for\n\n- die(\"BUG: ...\") statements are no longer translated\n\n- one die(\"BUG: ...\") report that continued in upper-case after the \"BUG:\"\n  prefix was fixed\n\n- I addressed a gender bias that has been bugging me ever since I noticed it\n\n- recursive merge's error messages are now printed after flushing the\n  output buffer (instead of swallowing that output)\n\n- callers of the recursive merge can now ask that the output buffer not be\n  flushed, but retained without printing it instead. This gives the caller the\n  control about handling errors which Junio asked for.\n\n- some long-standing bugs have been recognized and addressed:\n\n  - when the recursive merge failed, it lost the buffered output\n\n  - the output buffer of the recursive merge was never released\n\n  - some stdout/stderr interference that we tried to address in 2007 is\n    now fully addressed (the progress output could be printed in the\n    middle of the commit title because the latter was still directly printed\n    to stdout, which is buffered, instead of being buffered and flushed)\n\n- a lot of unnecessary 'ret' variables are gone now: originally, I wanted to\n  retain the *exact* return value, but now errors are indicated by -1,\n  always\n\n- lastly, I remembered that my original attempt at fixing the pull --rebase\n  issue contained a test case, and I forward-ported that, and augmented it\n\nSo while I addressed all comments, I also went through the patch series a\ncouple of times myself and whatever bugged me, I tried to resolve, too.\n\nThis patch series touches rather important code. Now that I addressed\nconcerns such as fixing translated bug reports, I would appreciate thorough\nreviews with a focus on the critical parts of the code, those that could\nresult in regressions.\n\n\nJohannes Schindelin (17):\n  Verify that `git pull --rebase` shows the helpful advice when failing\n  Report bugs consistently\n  Avoid translating bug messages\n  merge-recursive: clarify code in was_tracked()\n  Prepare the builtins for a libified merge_recursive()\n  merge_recursive: abort properly upon errors\n  merge-recursive: avoid returning a wholesale struct\n  merge-recursive: allow write_tree_from_memory() to error out\n  merge-recursive: handle return values indicating errors\n  merge-recursive: switch to returning errors instead of dying\n  am: counteract gender bias\n  am -3: use merge_recursive() directly again\n  merge-recursive: flush output buffer before printing error messages\n  merge-recursive: write the commit title in one go\n  merge-recursive: offer an option to retain the output in 'obuf'\n  Ensure that the output buffer is released after calling merge_trees()\n  merge-recursive: flush output buffer even when erroring out\n\n builtin/am.c           |  55 ++----\n builtin/checkout.c     |   5 +-\n builtin/ls-files.c     |   3 +-\n builtin/merge.c        |   2 +\n builtin/update-index.c |   2 +-\n grep.c                 |   8 +-\n imap-send.c            |   4 +-\n merge-recursive.c      | 490 +++++++++++++++++++++++++++++--------------------\n merge-recursive.h      |   2 +-\n sequencer.c            |   5 +\n sha1_file.c            |   4 +-\n t/t5520-pull.sh        |  30 +++\n trailer.c              |   2 +-\n transport.c            |   2 +-\n wt-status.c            |   4 +-\n 15 files changed, 369 insertions(+), 249 deletions(-)\n\nPublished-As: https://github.com/dscho/git/releases/tag/am-3-merge-recursive-direct-v2\nInterdiff vs v1:\n\n diff --git a/builtin/am.c b/builtin/am.c\n index dd41154..be652f9 100644\n --- a/builtin/am.c\n +++ b/builtin/am.c\n @@ -1578,45 +1578,16 @@ static int build_fake_ancestor(const struct am_state *state, const char *index_f\n  }\n  \n  /**\n - * Do the three-way merge using fake ancestor, his tree constructed\n - * from the fake ancestor and the postimage of the patch, and our\n - * state.\n - */\n -static int run_fallback_merge_recursive(const struct am_state *state,\n -\t\t\t\t\tunsigned char *orig_tree,\n -\t\t\t\t\tunsigned char *our_tree,\n -\t\t\t\t\tunsigned char *his_tree)\n -{\n -\tconst unsigned char *bases[1] = {orig_tree};\n -\tstruct merge_options o;\n -\tstruct commit *result;\n -\tchar *his_tree_name;\n -\tint status;\n -\n -\tinit_merge_options(&o);\n -\n -\to.branch1 = \"HEAD\";\n -\this_tree_name = xstrfmt(\"%.*s\", linelen(state->msg), state->msg);\n -\to.branch2 = his_tree_name;\n -\n -\tif (state->quiet)\n -\t\to.verbosity = 0;\n -\n -\tstatus = merge_recursive_generic(&o, our_tree, his_tree, 1, bases, &result);\n -\tif (status < 0)\n -\t\texit(128);\n -\tfree(his_tree_name);\n -\n -\treturn status;\n -}\n -\n -/**\n   * Attempt a threeway merge, using index_path as the temporary index.\n   */\n  static int fall_back_threeway(const struct am_state *state, const char *index_path)\n  {\n -\tunsigned char orig_tree[GIT_SHA1_RAWSZ], his_tree[GIT_SHA1_RAWSZ],\n +\tunsigned char orig_tree[GIT_SHA1_RAWSZ], her_tree[GIT_SHA1_RAWSZ],\n  \t\t      our_tree[GIT_SHA1_RAWSZ];\n +\tconst unsigned char *bases[1] = {orig_tree};\n +\tstruct merge_options o;\n +\tstruct commit *result;\n +\tchar *her_tree_name;\n  \n  \tif (get_sha1(\"HEAD\", our_tree) < 0)\n  \t\thashcpy(our_tree, EMPTY_TREE_SHA1_BIN);\n @@ -1652,7 +1623,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n  \t\treturn error(_(\"Did you hand edit your patch?\\n\"\n  \t\t\t\t\"It does not apply to blobs recorded in its index.\"));\n  \n -\tif (write_index_as_tree(his_tree, &the_index, index_path, 0, NULL))\n +\tif (write_index_as_tree(her_tree, &the_index, index_path, 0, NULL))\n  \t\treturn error(\"could not write tree\");\n  \n  \tsay(state, stdout, _(\"Falling back to patching base and 3-way merge...\"));\n @@ -1662,17 +1633,28 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n  \n  \t/*\n  \t * This is not so wrong. Depending on which base we picked, orig_tree\n -\t * may be wildly different from ours, but his_tree has the same set of\n +\t * may be wildly different from ours, but her_tree has the same set of\n  \t * wildly different changes in parts the patch did not touch, so\n  \t * recursive ends up canceling them, saying that we reverted all those\n  \t * changes.\n  \t */\n  \n -\tif (run_fallback_merge_recursive(state, orig_tree, our_tree, his_tree)) {\n +\tinit_merge_options(&o);\n +\n +\to.branch1 = \"HEAD\";\n +\ther_tree_name = xstrfmt(\"%.*s\", linelen(state->msg), state->msg);\n +\to.branch2 = her_tree_name;\n +\n +\tif (state->quiet)\n +\t\to.verbosity = 0;\n +\n +\tif (merge_recursive_generic(&o, our_tree, her_tree, 1, bases, &result)) {\n  \t\trerere(state->allow_rerere_autoupdate);\n +\t\tfree(her_tree_name);\n  \t\treturn error(_(\"Failed to merge in the changes.\"));\n  \t}\n  \n +\tfree(her_tree_name);\n  \treturn 0;\n  }\n  \n diff --git a/builtin/checkout.c b/builtin/checkout.c\n index 14312f7..ced4ac4 100644\n --- a/builtin/checkout.c\n +++ b/builtin/checkout.c\n @@ -573,6 +573,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n  \t\t\t\texit(128);\n  \t\t\tret = reset_tree(new->commit->tree, opts, 0,\n  \t\t\t\t\t writeout_error);\n +\t\t\tstrbuf_release(&o.obuf);\n  \t\t\tif (ret)\n  \t\t\t\treturn ret;\n  \t\t}\n diff --git a/builtin/merge.c b/builtin/merge.c\n index 133b853..7b898db 100644\n --- a/builtin/merge.c\n +++ b/builtin/merge.c\n @@ -1552,8 +1552,6 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n  \t\tret = try_merge_strategy(use_strategies[i]->name,\n  \t\t\t\t\t common, remoteheads,\n  \t\t\t\t\t head_commit, head_arg);\n -\t\tif (ret < 0)\n -\t\t\texit(128);\n  \t\tif (!option_commit && !ret) {\n  \t\t\tmerge_was_ok = 1;\n  \t\t\t/*\n diff --git a/imap-send.c b/imap-send.c\n index cd39805..369f72a 100644\n --- a/imap-send.c\n +++ b/imap-send.c\n @@ -506,7 +506,7 @@ static char *next_arg(char **s)\n  \n  static int nfsnprintf(char *buf, int blen, const char *fmt, ...)\n  {\n -\tint ret;\n +\tint ret = -1;\n  \tva_list va;\n  \n  \tva_start(va, fmt);\n diff --git a/merge-recursive.c b/merge-recursive.c\n index d5a593c..d94f853 100644\n --- a/merge-recursive.c\n +++ b/merge-recursive.c\n @@ -23,6 +23,37 @@\n  #include \"dir.h\"\n  #include \"submodule.h\"\n  \n +static void flush_output(struct merge_options *o)\n +{\n +\tif (o->buffer_output < 2 && o->obuf.len) {\n +\t\tfputs(o->obuf.buf, stdout);\n +\t\tstrbuf_reset(&o->obuf);\n +\t}\n +}\n +\n +static int err(struct merge_options *o, const char *err, ...)\n +{\n +\tva_list params;\n +\n +\tva_start(params, err);\n +\tif (o->buffer_output < 2)\n +\t\tflush_output(o);\n +\telse {\n +\t\tstrbuf_complete(&o->obuf, '\\n');\n +\t\tstrbuf_addstr(&o->obuf, \"error: \");\n +\t}\n +\tstrbuf_vaddf(&o->obuf, err, params);\n +\tif (o->buffer_output > 1)\n +\t\tstrbuf_addch(&o->obuf, '\\n');\n +\telse {\n +\t\terror(\"%s\", o->obuf.buf);\n +\t\tstrbuf_reset(&o->obuf);\n +\t}\n +\tva_end(params);\n +\n +\treturn -1;\n +}\n +\n  static struct tree *shift_tree_object(struct tree *one, struct tree *two,\n  \t\t\t\t      const char *subtree_shift)\n  {\n @@ -148,14 +179,6 @@ static int show(struct merge_options *o, int v)\n  \treturn (!o->call_depth && o->verbosity >= v) || o->verbosity >= 5;\n  }\n  \n -static void flush_output(struct merge_options *o)\n -{\n -\tif (o->obuf.len) {\n -\t\tfputs(o->obuf.buf, stdout);\n -\t\tstrbuf_reset(&o->obuf);\n -\t}\n -}\n -\n  __attribute__((format (printf, 3, 4)))\n  static void output(struct merge_options *o, int v, const char *fmt, ...)\n  {\n @@ -177,28 +200,30 @@ static void output(struct merge_options *o, int v, const char *fmt, ...)\n  \n  static void output_commit_title(struct merge_options *o, struct commit *commit)\n  {\n -\tint i;\n -\tflush_output(o);\n -\tfor (i = o->call_depth; i--;)\n -\t\tfputs(\"  \", stdout);\n +\tstrbuf_addchars(&o->obuf, ' ', o->call_depth * 2);\n  \tif (commit->util)\n -\t\tprintf(\"virtual %s\\n\", merge_remote_util(commit)->name);\n +\t\tstrbuf_addf(&o->obuf, \"virtual %s\\n\",\n +\t\t\tmerge_remote_util(commit)->name);\n  \telse {\n -\t\tprintf(\"%s \", find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV));\n +\t\tstrbuf_addf(&o->obuf, \"%s \",\n +\t\t\tfind_unique_abbrev(commit->object.oid.hash,\n +\t\t\t\tDEFAULT_ABBREV));\n  \t\tif (parse_commit(commit) != 0)\n -\t\t\tprintf(_(\"(bad commit)\\n\"));\n +\t\t\tstrbuf_addf(&o->obuf, _(\"(bad commit)\\n\"));\n  \t\telse {\n  \t\t\tconst char *title;\n  \t\t\tconst char *msg = get_commit_buffer(commit, NULL);\n  \t\t\tint len = find_commit_subject(msg, &title);\n  \t\t\tif (len)\n -\t\t\t\tprintf(\"%.*s\\n\", len, title);\n +\t\t\t\tstrbuf_addf(&o->obuf, \"%.*s\\n\", len, title);\n  \t\t\tunuse_commit_buffer(commit, msg);\n  \t\t}\n  \t}\n +\tflush_output(o);\n  }\n  \n -static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,\n +static int add_cacheinfo(struct merge_options *o,\n +\t\tunsigned int mode, const unsigned char *sha1,\n  \t\tconst char *path, int stage, int refresh, int options)\n  {\n  \tstruct cache_entry *ce;\n @@ -206,7 +231,7 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,\n  \t\t\t      (refresh ? (CE_MATCH_REFRESH |\n  \t\t\t\t\t  CE_MATCH_IGNORE_MISSING) : 0 ));\n  \tif (!ce)\n -\t\treturn error(_(\"addinfo_cache failed for path '%s'\"), path);\n +\t\treturn err(o, _(\"addinfo_cache failed for path '%s'\"), path);\n  \treturn add_cache_entry(ce, options);\n  }\n  \n @@ -267,7 +292,7 @@ struct tree *write_tree_from_memory(struct merge_options *o)\n  \n  \tif (!cache_tree_fully_valid(active_cache_tree) &&\n  \t    cache_tree_update(&the_index, 0) < 0) {\n -\t\terror(_(\"error building trees\"));\n +\t\terr(o, _(\"error building trees\"));\n  \t\treturn NULL;\n  \t}\n  \n @@ -535,7 +560,8 @@ static struct string_list *get_renames(struct merge_options *o,\n  \treturn renames;\n  }\n  \n -static int update_stages(const char *path, const struct diff_filespec *o,\n +static int update_stages(struct merge_options *opt, const char *path,\n +\t\t\t const struct diff_filespec *o,\n  \t\t\t const struct diff_filespec *a,\n  \t\t\t const struct diff_filespec *b)\n  {\n @@ -550,17 +576,19 @@ static int update_stages(const char *path, const struct diff_filespec *o,\n  \t */\n  \tint clear = 1;\n  \tint options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_SKIP_DFCHECK;\n -\tint ret = 0;\n -\n  \tif (clear)\n -\t\tret = remove_file_from_cache(path);\n -\tif (!ret && o)\n -\t\tret = add_cacheinfo(o->mode, o->sha1, path, 1, 0, options);\n -\tif (!ret && a)\n -\t\tret = add_cacheinfo(a->mode, a->sha1, path, 2, 0, options);\n -\tif (!ret && b)\n -\t\tret = add_cacheinfo(b->mode, b->sha1, path, 3, 0, options);\n -\treturn ret;\n +\t\tif (remove_file_from_cache(path))\n +\t\t\treturn -1;\n +\tif (o)\n +\t\tif (add_cacheinfo(opt, o->mode, o->sha1, path, 1, 0, options))\n +\t\t\treturn -1;\n +\tif (a)\n +\t\tif (add_cacheinfo(opt, a->mode, a->sha1, path, 2, 0, options))\n +\t\t\treturn -1;\n +\tif (b)\n +\t\tif (add_cacheinfo(opt, b->mode, b->sha1, path, 3, 0, options))\n +\t\t\treturn -1;\n +\treturn 0;\n  }\n  \n  static void update_entry(struct stage_data *entry,\n @@ -658,24 +686,22 @@ static int was_tracked(const char *path)\n  {\n  \tint pos = cache_name_pos(path, strlen(path));\n  \n -\t/* cache_name_pos() looks for stage == 0, so pos may be < 0 */\n -\tif (pos < 0)\n -\t\tpos = -1 - pos;\n -\twhile (pos < active_nr &&\n -\t       !strcmp(path, active_cache[pos]->name)) {\n +\tif (pos >= 0)\n +\t\treturn pos < active_nr;\n +\t/*\n +\t * cache_name_pos() looks for stage == 0, even if we did not ask for\n +\t * it. Let's look for stage == 2 now.\n +\t */\n +\tfor (pos = -1 - pos; pos < active_nr &&\n +\t     !strcmp(path, active_cache[pos]->name); pos++)\n  \t\t/*\n  \t\t * If stage #0, it is definitely tracked.\n  \t\t * If it has stage #2 then it was tracked\n  \t\t * before this merge started.  All other\n  \t\t * cases the path was not tracked.\n  \t\t */\n -\t\tswitch (ce_stage(active_cache[pos])) {\n -\t\tcase 0:\n -\t\tcase 2:\n +\t\tif (ce_stage(active_cache[pos]) == 2)\n  \t\t\treturn 1;\n -\t\t}\n -\t\tpos++;\n -\t}\n  \treturn 0;\n  }\n  \n @@ -712,8 +738,8 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n  \tif (status) {\n  \t\tif (status == SCLD_EXISTS)\n  \t\t\t/* something else exists */\n -\t\t\treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n -\t\treturn error(msg, path, \"\");\n +\t\t\treturn err(o, msg, path, _(\": perhaps a D/F conflict?\"));\n +\t\treturn err(o, msg, path, \"\");\n  \t}\n  \n  \t/*\n @@ -721,7 +747,7 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n  \t * tracking it.\n  \t */\n  \tif (would_lose_untracked(path))\n -\t\treturn error(_(\"refusing to lose untracked file at '%s'\"),\n +\t\treturn err(o, _(\"refusing to lose untracked file at '%s'\"),\n  \t\t\t     path);\n  \n  \t/* Successful unlink is good.. */\n @@ -731,7 +757,7 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n  \tif (errno == ENOENT)\n  \t\treturn 0;\n  \t/* .. but not some other error (who really cares what?) */\n -\treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n +\treturn err(o, msg, path, _(\": perhaps a D/F conflict?\"));\n  }\n  \n  static int update_file_flags(struct merge_options *o,\n @@ -763,9 +789,9 @@ static int update_file_flags(struct merge_options *o,\n  \n  \t\tbuf = read_sha1_file(sha, &type, &size);\n  \t\tif (!buf)\n -\t\t\treturn error(_(\"cannot read object %s '%s'\"), sha1_to_hex(sha), path);\n +\t\t\treturn err(o, _(\"cannot read object %s '%s'\"), sha1_to_hex(sha), path);\n  \t\tif (type != OBJ_BLOB) {\n -\t\t\tret = error(_(\"blob expected for %s '%s'\"), sha1_to_hex(sha), path);\n +\t\t\tret = err(o, _(\"blob expected for %s '%s'\"), sha1_to_hex(sha), path);\n  \t\t\tgoto free_buf;\n  \t\t}\n  \t\tif (S_ISREG(mode)) {\n @@ -789,7 +815,8 @@ static int update_file_flags(struct merge_options *o,\n  \t\t\t\tmode = 0666;\n  \t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n  \t\t\tif (fd < 0) {\n -\t\t\t\tret = error_errno(_(\"failed to open '%s'\"), path);\n +\t\t\t\tret = err(o, _(\"failed to open '%s': %s\"),\n +\t\t\t\t\tpath, strerror(errno));\n  \t\t\t\tgoto free_buf;\n  \t\t\t}\n  \t\t\twrite_in_full(fd, buf, size);\n @@ -799,17 +826,18 @@ static int update_file_flags(struct merge_options *o,\n  \t\t\tsafe_create_leading_directories_const(path);\n  \t\t\tunlink(path);\n  \t\t\tif (symlink(lnk, path))\n -\t\t\t\tret = error_errno(_(\"failed to symlink '%s'\"), path);\n +\t\t\t\tret = err(o, _(\"failed to symlink '%s': %s\"),\n +\t\t\t\t\tpath, strerror(errno));\n  \t\t\tfree(lnk);\n  \t\t} else\n -\t\t\tret = error_errno(_(\"do not know what to do with %06o %s '%s'\"),\n +\t\t\tret = err(o, _(\"do not know what to do with %06o %s '%s'\"),\n  \t\t\t\tmode, sha1_to_hex(sha), path);\n -free_buf:\n + free_buf:\n  \t\tfree(buf);\n  \t}\n   update_index:\n  \tif (!ret && update_cache)\n -\t\tadd_cacheinfo(mode, sha, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n +\t\tadd_cacheinfo(o, mode, sha, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n  \treturn ret;\n  }\n  \n @@ -942,11 +970,11 @@ static int merge_file_1(struct merge_options *o,\n  \t\t\t\t\t\t  branch1, branch2);\n  \n  \t\t\tif ((merge_status < 0) || !result_buf.ptr)\n -\t\t\t\tret = error(_(\"Failed to execute internal merge\"));\n +\t\t\t\tret = err(o, _(\"Failed to execute internal merge\"));\n  \n  \t\t\tif (!ret && write_sha1_file(result_buf.ptr, result_buf.size,\n  \t\t\t\t\t    blob_type, result->sha))\n -\t\t\t\tret = error(_(\"Unable to add %s to database\"),\n +\t\t\t\tret = err(o, _(\"Unable to add %s to database\"),\n  \t\t\t\t\ta->path);\n  \n  \t\t\tfree(result_buf.ptr);\n @@ -964,7 +992,7 @@ static int merge_file_1(struct merge_options *o,\n  \t\t\tif (!sha_eq(a->sha1, b->sha1))\n  \t\t\t\tresult->clean = 0;\n  \t\t} else\n -\t\t\tdie(_(\"BUG: unsupported object type in the tree\"));\n +\t\t\tdie(\"BUG: unsupported object type in the tree\");\n  \t}\n  \n  \treturn 0;\n @@ -1090,7 +1118,6 @@ static int conflict_rename_delete(struct merge_options *o,\n  \tconst unsigned char *b_sha = NULL;\n  \tint a_mode = 0;\n  \tint b_mode = 0;\n -\tint ret = 0;\n  \n  \tif (rename_branch == o->branch1) {\n  \t\ta_sha = dest->sha1;\n @@ -1100,22 +1127,19 @@ static int conflict_rename_delete(struct merge_options *o,\n  \t\tb_mode = dest->mode;\n  \t}\n  \n -\tret = handle_change_delete(o,\n +\tif (handle_change_delete(o,\n  \t\t\t     o->call_depth ? orig->path : dest->path,\n  \t\t\t     orig->sha1, orig->mode,\n  \t\t\t     a_sha, a_mode,\n  \t\t\t     b_sha, b_mode,\n -\t\t\t     _(\"rename\"), _(\"renamed\"));\n -\tif (ret < 0)\n -\t\treturn ret;\n +\t\t\t     _(\"rename\"), _(\"renamed\")))\n +\t\treturn -1;\n  \tif (o->call_depth)\n -\t\tret = remove_file_from_cache(dest->path);\n +\t\treturn remove_file_from_cache(dest->path);\n  \telse\n -\t\tret = update_stages(dest->path, NULL,\n +\t\treturn update_stages(o, dest->path, NULL,\n  \t\t\t      rename_branch == o->branch1 ? dest : NULL,\n  \t\t\t      rename_branch == o->branch1 ? NULL : dest);\n -\n -\treturn ret;\n  }\n  \n  static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,\n @@ -1156,8 +1180,8 @@ static int handle_file(struct merge_options *o,\n  \tadd = filespec_from_entry(&other, dst_entry, stage ^ 1);\n  \tif (add) {\n  \t\tchar *add_name = unique_path(o, rename->path, other_branch);\n -\t\tif ((ret = update_file(o, 0, add->sha1, add->mode, add_name)))\n -\t\t\treturn ret;\n +\t\tif (update_file(o, 0, add->sha1, add->mode, add_name))\n +\t\t\treturn -1;\n  \n  \t\tremove_file(o, 0, rename->path, 0);\n  \t\tdst_name = unique_path(o, rename->path, cur_branch);\n @@ -1171,9 +1195,9 @@ static int handle_file(struct merge_options *o,\n  \tif ((ret = update_file(o, 0, rename->sha1, rename->mode, dst_name)))\n  \t\t; /* fall through, do allow dst_name to be released */\n  \telse if (stage == 2)\n -\t\tret = update_stages(rename->path, NULL, rename, add);\n +\t\tret = update_stages(o, rename->path, NULL, rename, add);\n  \telse\n -\t\tret = update_stages(rename->path, NULL, add, rename);\n +\t\tret = update_stages(o, rename->path, NULL, add, rename);\n  \n  \tif (dst_name != rename->path)\n  \t\tfree(dst_name);\n @@ -1188,7 +1212,6 @@ static int conflict_rename_rename_1to2(struct merge_options *o,\n  \tstruct diff_filespec *one = ci->pair1->one;\n  \tstruct diff_filespec *a = ci->pair1->two;\n  \tstruct diff_filespec *b = ci->pair2->two;\n -\tint ret = 0;\n  \n  \toutput(o, 1, _(\"CONFLICT (rename/rename): \"\n  \t       \"Rename \\\"%s\\\"->\\\"%s\\\" in branch \\\"%s\\\" \"\n @@ -1201,12 +1224,12 @@ static int conflict_rename_rename_1to2(struct merge_options *o,\n  \t\tstruct diff_filespec other;\n  \t\tstruct diff_filespec *add;\n  \n -\t\tif ((ret = merge_file_one(o, one->path,\n +\t\tif (merge_file_one(o, one->path,\n  \t\t\t\t one->sha1, one->mode,\n  \t\t\t\t a->sha1, a->mode,\n  \t\t\t\t b->sha1, b->mode,\n -\t\t\t\t ci->branch1, ci->branch2, &mfi)))\n -\t\t\treturn ret;\n +\t\t\t\t ci->branch1, ci->branch2, &mfi))\n +\t\t\treturn -1;\n  \n  \t\t/*\n  \t\t * FIXME: For rename/add-source conflicts (if we could detect\n @@ -1214,8 +1237,8 @@ static int conflict_rename_rename_1to2(struct merge_options *o,\n  \t\t * pathname and then either rename the add-source file to that\n  \t\t * unique path, or use that unique path instead of src here.\n  \t\t */\n -\t\tif ((ret = update_file(o, 0, mfi.sha, mfi.mode, one->path)))\n -\t\t\treturn ret;\n +\t\tif (update_file(o, 0, mfi.sha, mfi.mode, one->path))\n +\t\t\treturn -1;\n  \n  \t\t/*\n  \t\t * Above, we put the merged content at the merge-base's\n @@ -1227,27 +1250,22 @@ static int conflict_rename_rename_1to2(struct merge_options *o,\n  \t\t */\n  \t\tadd = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);\n  \t\tif (add) {\n -\t\t\tif ((ret = update_file(o, 0, add->sha1, add->mode,\n -\t\t\t\t\ta->path)))\n -\t\t\t\treturn ret;\n +\t\t\tif (update_file(o, 0, add->sha1, add->mode, a->path))\n +\t\t\t\treturn -1;\n  \t\t}\n  \t\telse\n  \t\t\tremove_file_from_cache(a->path);\n  \t\tadd = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);\n  \t\tif (add) {\n -\t\t\tif ((ret = update_file(o, 0, add->sha1, add->mode,\n -\t\t\t\t\tb->path)))\n -\t\t\t\treturn ret;\n +\t\t\tif (update_file(o, 0, add->sha1, add->mode, b->path))\n +\t\t\t\treturn -1;\n  \t\t}\n  \t\telse\n  \t\t\tremove_file_from_cache(b->path);\n -\t} else {\n -\t\tif ((ret = handle_file(o, a, 2, ci)) ||\n -\t\t    (ret = handle_file(o, b, 3, ci)))\n -\t\t\treturn ret;\n -\t}\n +\t} else if (handle_file(o, a, 2, ci) || handle_file(o, b, 3, ci))\n +\t\treturn -1;\n  \n -\treturn ret;\n +\treturn 0;\n  }\n  \n  static int conflict_rename_rename_2to1(struct merge_options *o,\n @@ -1272,13 +1290,13 @@ static int conflict_rename_rename_2to1(struct merge_options *o,\n  \tremove_file(o, 1, a->path, o->call_depth || would_lose_untracked(a->path));\n  \tremove_file(o, 1, b->path, o->call_depth || would_lose_untracked(b->path));\n  \n -\tif ((ret = merge_file_special_markers(o, a, c1, &ci->ren1_other,\n +\tif (merge_file_special_markers(o, a, c1, &ci->ren1_other,\n  \t\t\t\t\t    o->branch1, c1->path,\n -\t\t\t\t\t    o->branch2, ci->ren1_other.path, &mfi_c1)) ||\n -\t    (ret = merge_file_special_markers(o, b, &ci->ren2_other, c2,\n +\t\t\t\t\t    o->branch2, ci->ren1_other.path, &mfi_c1) ||\n +\t    merge_file_special_markers(o, b, &ci->ren2_other, c2,\n  \t\t\t\t\t    o->branch1, ci->ren2_other.path,\n -\t\t\t\t\t    o->branch2, c2->path, &mfi_c2)))\n -\t\treturn ret;\n +\t\t\t\t\t    o->branch2, c2->path, &mfi_c2))\n +\t\treturn -1;\n  \n  \tif (o->call_depth) {\n  \t\t/*\n @@ -1314,7 +1332,7 @@ static int process_renames(struct merge_options *o,\n  \t\t\t   struct string_list *a_renames,\n  \t\t\t   struct string_list *b_renames)\n  {\n -\tint clean_merge = 1, i, j, ret;\n +\tint clean_merge = 1, i, j;\n  \tstruct string_list a_by_dst = STRING_LIST_INIT_NODUP;\n  \tstruct string_list b_by_dst = STRING_LIST_INIT_NODUP;\n  \tconst struct rename *sre;\n @@ -1490,14 +1508,13 @@ static int process_renames(struct merge_options *o,\n  \t\t\t\t * update_file_flags() instead of\n  \t\t\t\t * update_file().\n  \t\t\t\t */\n -\t\t\t\tret = update_file_flags(o,\n +\t\t\t\tif (update_file_flags(o,\n  \t\t\t\t\t\t  ren1->pair->two->sha1,\n  \t\t\t\t\t\t  ren1->pair->two->mode,\n  \t\t\t\t\t\t  ren1_dst,\n  \t\t\t\t\t\t  1, /* update_cache */\n -\t\t\t\t\t\t  0  /* update_wd    */);\n -\t\t\t\tif (ret)\n -\t\t\t\t\tclean_merge = ret;\n +\t\t\t\t\t\t  0  /* update_wd    */))\n +\t\t\t\t\tclean_merge = -1;\n  \t\t\t} else if (!sha_eq(dst_other.sha1, null_sha1)) {\n  \t\t\t\tclean_merge = 0;\n  \t\t\t\ttry_merge = 1;\n @@ -1507,22 +1524,22 @@ static int process_renames(struct merge_options *o,\n  \t\t\t\t       ren1_dst, branch2);\n  \t\t\t\tif (o->call_depth) {\n  \t\t\t\t\tstruct merge_file_info mfi;\n -\t\t\t\t\tif ((ret = merge_file_one(o, ren1_dst, null_sha1, 0,\n +\t\t\t\t\tif (merge_file_one(o, ren1_dst, null_sha1, 0,\n  \t\t\t\t\t\t\t ren1->pair->two->sha1, ren1->pair->two->mode,\n  \t\t\t\t\t\t\t dst_other.sha1, dst_other.mode,\n -\t\t\t\t\t\t\t branch1, branch2, &mfi))) {\n -\t\t\t\t\t\tclean_merge = ret;\n +\t\t\t\t\t\t\t branch1, branch2, &mfi)) {\n +\t\t\t\t\t\tclean_merge = -1;\n  \t\t\t\t\t\tgoto cleanup_and_return;\n  \t\t\t\t\t}\n  \t\t\t\t\toutput(o, 1, _(\"Adding merged %s\"), ren1_dst);\n -\t\t\t\t\tif ((ret = update_file(o, 0, mfi.sha, mfi.mode, ren1_dst)))\n -\t\t\t\t\t\tclean_merge = ret;\n +\t\t\t\t\tif (update_file(o, 0, mfi.sha, mfi.mode, ren1_dst))\n +\t\t\t\t\t\tclean_merge = -1;\n  \t\t\t\t\ttry_merge = 0;\n  \t\t\t\t} else {\n  \t\t\t\t\tchar *new_path = unique_path(o, ren1_dst, branch2);\n  \t\t\t\t\toutput(o, 1, _(\"Adding as %s instead\"), new_path);\n -\t\t\t\t\tif ((ret = update_file(o, 0, dst_other.sha1, dst_other.mode, new_path)))\n -\t\t\t\t\t\tclean_merge = ret;\n +\t\t\t\t\tif (update_file(o, 0, dst_other.sha1, dst_other.mode, new_path))\n +\t\t\t\t\t\tclean_merge = -1;\n  \t\t\t\t\tfree(new_path);\n  \t\t\t\t}\n  \t\t\t} else\n @@ -1568,23 +1585,25 @@ static unsigned char *stage_sha(const unsigned char *sha, unsigned mode)\n  \treturn (is_null_sha1(sha) || mode == 0) ? NULL: (unsigned char *)sha;\n  }\n  \n -static int read_sha1_strbuf(const unsigned char *sha1, struct strbuf *dst)\n +static int read_sha1_strbuf(struct merge_options *o,\n +\tconst unsigned char *sha1, struct strbuf *dst)\n  {\n  \tvoid *buf;\n  \tenum object_type type;\n  \tunsigned long size;\n  \tbuf = read_sha1_file(sha1, &type, &size);\n  \tif (!buf)\n -\t\treturn error(_(\"cannot read object %s\"), sha1_to_hex(sha1));\n +\t\treturn err(o, _(\"cannot read object %s\"), sha1_to_hex(sha1));\n  \tif (type != OBJ_BLOB) {\n  \t\tfree(buf);\n -\t\treturn error(_(\"object %s is not a blob\"), sha1_to_hex(sha1));\n +\t\treturn err(o, _(\"object %s is not a blob\"), sha1_to_hex(sha1));\n  \t}\n  \tstrbuf_attach(dst, buf, size, size + 1);\n  \treturn 0;\n  }\n  \n -static int blob_unchanged(const unsigned char *o_sha,\n +static int blob_unchanged(struct merge_options *opt,\n +\t\t\t  const unsigned char *o_sha,\n  \t\t\t  unsigned o_mode,\n  \t\t\t  const unsigned char *a_sha,\n  \t\t\t  unsigned a_mode,\n @@ -1602,7 +1621,7 @@ static int blob_unchanged(const unsigned char *o_sha,\n  \t\treturn 0;\n  \n  \tassert(o_sha && a_sha);\n -\tif (read_sha1_strbuf(o_sha, &o) || read_sha1_strbuf(a_sha, &a))\n +\tif (read_sha1_strbuf(opt, o_sha, &o) || read_sha1_strbuf(opt, a_sha, &a))\n  \t\tgoto error_return;\n  \t/*\n  \t * Note: binary | is used so that both renormalizations are\n @@ -1645,7 +1664,6 @@ static int merge_content(struct merge_options *o,\n  \tstruct merge_file_info mfi;\n  \tstruct diff_filespec one, a, b;\n  \tunsigned df_conflict_remains = 0;\n -\tint ret;\n  \n  \tif (!o_sha) {\n  \t\treason = _(\"add/add\");\n @@ -1675,10 +1693,10 @@ static int merge_content(struct merge_options *o,\n  \t\tif (dir_in_way(path, !o->call_depth))\n  \t\t\tdf_conflict_remains = 1;\n  \t}\n -\tif ((ret = merge_file_special_markers(o, &one, &a, &b,\n +\tif (merge_file_special_markers(o, &one, &a, &b,\n  \t\t\t\t\t o->branch1, path1,\n -\t\t\t\t\t o->branch2, path2, &mfi)))\n -\t\treturn ret;\n +\t\t\t\t\t o->branch2, path2, &mfi))\n +\t\treturn -1;\n  \n  \tif (mfi.clean && !df_conflict_remains &&\n  \t    sha_eq(mfi.sha, a_sha) && mfi.mode == a_mode) {\n @@ -1692,7 +1710,7 @@ static int merge_content(struct merge_options *o,\n  \t\t */\n  \t\tpath_renamed_outside_HEAD = !path2 || !strcmp(path, path2);\n  \t\tif (!path_renamed_outside_HEAD) {\n -\t\t\tadd_cacheinfo(mfi.mode, mfi.sha, path,\n +\t\t\tadd_cacheinfo(o, mfi.mode, mfi.sha, path,\n  \t\t\t\t      0, (!o->call_depth), 0);\n  \t\t\treturn mfi.clean;\n  \t\t}\n @@ -1705,8 +1723,8 @@ static int merge_content(struct merge_options *o,\n  \t\toutput(o, 1, _(\"CONFLICT (%s): Merge conflict in %s\"),\n  \t\t\t\treason, path);\n  \t\tif (rename_conflict_info && !df_conflict_remains)\n -\t\t\tif ((ret = update_stages(path, &one, &a, &b)))\n -\t\t\t\treturn ret;\n +\t\t\tif (update_stages(o, path, &one, &a, &b))\n +\t\t\t\treturn -1;\n  \t}\n  \n  \tif (df_conflict_remains) {\n @@ -1715,42 +1733,39 @@ static int merge_content(struct merge_options *o,\n  \t\t\tremove_file_from_cache(path);\n  \t\t} else {\n  \t\t\tif (!mfi.clean) {\n -\t\t\t\tif ((ret = update_stages(path, &one, &a, &b)))\n -\t\t\t\t\treturn ret;\n +\t\t\t\tif (update_stages(o, path, &one, &a, &b))\n +\t\t\t\t\treturn -1;\n  \t\t\t} else {\n  \t\t\t\tint file_from_stage2 = was_tracked(path);\n  \t\t\t\tstruct diff_filespec merged;\n  \t\t\t\thashcpy(merged.sha1, mfi.sha);\n  \t\t\t\tmerged.mode = mfi.mode;\n  \n -\t\t\t\tif ((ret = update_stages(path, NULL,\n +\t\t\t\tif (update_stages(o, path, NULL,\n  \t\t\t\t\t      file_from_stage2 ? &merged : NULL,\n -\t\t\t\t\t      file_from_stage2 ? NULL : &merged)))\n -\t\t\t\t\treturn ret;\n +\t\t\t\t\t      file_from_stage2 ? NULL : &merged))\n +\t\t\t\t\treturn -1;\n  \t\t\t}\n  \n  \t\t}\n  \t\tnew_path = unique_path(o, path, rename_conflict_info->branch1);\n  \t\toutput(o, 1, _(\"Adding as %s instead\"), new_path);\n -\t\tif ((ret = update_file(o, 0, mfi.sha, mfi.mode, new_path))) {\n +\t\tif (update_file(o, 0, mfi.sha, mfi.mode, new_path)) {\n  \t\t\tfree(new_path);\n -\t\t\treturn ret;\n +\t\t\treturn -1;\n  \t\t}\n  \t\tfree(new_path);\n  \t\tmfi.clean = 0;\n -\t} else {\n -\t\tif ((ret = update_file(o, mfi.clean, mfi.sha, mfi.mode, path)))\n -\t\t\treturn ret;\n -\t}\n +\t} else if (update_file(o, mfi.clean, mfi.sha, mfi.mode, path))\n +\t\treturn -1;\n  \treturn mfi.clean;\n -\n  }\n  \n  /* Per entry merge function */\n  static int process_entry(struct merge_options *o,\n  \t\t\t const char *path, struct stage_data *entry)\n  {\n -\tint clean_merge = 1, ret;\n +\tint clean_merge = 1;\n  \tint normalize = o->renormalize;\n  \tunsigned o_mode = entry->stages[1].mode;\n  \tunsigned a_mode = entry->stages[2].mode;\n @@ -1771,23 +1786,21 @@ static int process_entry(struct merge_options *o,\n  \t\t\tbreak;\n  \t\tcase RENAME_DELETE:\n  \t\t\tclean_merge = 0;\n -\t\t\tif ((ret = conflict_rename_delete(o,\n +\t\t\tif (conflict_rename_delete(o,\n  \t\t\t\t\t       conflict_info->pair1,\n  \t\t\t\t\t       conflict_info->branch1,\n -\t\t\t\t\t       conflict_info->branch2)))\n -\t\t\t\tclean_merge = ret;\n +\t\t\t\t\t       conflict_info->branch2))\n +\t\t\t\tclean_merge = -1;\n  \t\t\tbreak;\n  \t\tcase RENAME_ONE_FILE_TO_TWO:\n  \t\t\tclean_merge = 0;\n -\t\t\tif ((ret = conflict_rename_rename_1to2(o,\n -\t\t\t\t\tconflict_info)))\n -\t\t\t\tclean_merge = ret;\n +\t\t\tif (conflict_rename_rename_1to2(o, conflict_info))\n +\t\t\t\tclean_merge = -1;\n  \t\t\tbreak;\n  \t\tcase RENAME_TWO_FILES_TO_ONE:\n  \t\t\tclean_merge = 0;\n -\t\t\tif ((ret = conflict_rename_rename_2to1(o,\n -\t\t\t\t\tconflict_info)))\n -\t\t\t\tclean_merge = ret;\n +\t\t\tif (conflict_rename_rename_2to1(o, conflict_info))\n +\t\t\t\tclean_merge = -1;\n  \t\t\tbreak;\n  \t\tdefault:\n  \t\t\tentry->processed = 0;\n @@ -1796,8 +1809,8 @@ static int process_entry(struct merge_options *o,\n  \t} else if (o_sha && (!a_sha || !b_sha)) {\n  \t\t/* Case A: Deleted in one */\n  \t\tif ((!a_sha && !b_sha) ||\n -\t\t    (!b_sha && blob_unchanged(o_sha, o_mode, a_sha, a_mode, normalize, path)) ||\n -\t\t    (!a_sha && blob_unchanged(o_sha, o_mode, b_sha, b_mode, normalize, path))) {\n +\t\t    (!b_sha && blob_unchanged(o, o_sha, o_mode, a_sha, a_mode, normalize, path)) ||\n +\t\t    (!a_sha && blob_unchanged(o, o_sha, o_mode, b_sha, b_mode, normalize, path))) {\n  \t\t\t/* Deleted in both or deleted in one and\n  \t\t\t * unchanged in the other */\n  \t\t\tif (a_sha)\n @@ -1807,9 +1820,9 @@ static int process_entry(struct merge_options *o,\n  \t\t} else {\n  \t\t\t/* Modify/delete; deleted side may have put a directory in the way */\n  \t\t\tclean_merge = 0;\n -\t\t\tif ((ret = handle_modify_delete(o, path, o_sha, o_mode,\n -\t\t\t\t\ta_sha, a_mode, b_sha, b_mode)))\n -\t\t\t\tclean_merge = ret;\n +\t\t\tif (handle_modify_delete(o, path, o_sha, o_mode,\n +\t\t\t\t\t\t a_sha, a_mode, b_sha, b_mode))\n +\t\t\t\tclean_merge = -1;\n  \t\t}\n  \t} else if ((!o_sha && a_sha && !b_sha) ||\n  \t\t   (!o_sha && !a_sha && b_sha)) {\n @@ -1841,18 +1854,16 @@ static int process_entry(struct merge_options *o,\n  \t\t\toutput(o, 1, _(\"CONFLICT (%s): There is a directory with name %s in %s. \"\n  \t\t\t       \"Adding %s as %s\"),\n  \t\t\t       conf, path, other_branch, path, new_path);\n -\t\t\tret = update_file(o, 0, sha, mode, new_path);\n -\t\t\tif (ret)\n -\t\t\t\tclean_merge = ret;\n +\t\t\tif (update_file(o, 0, sha, mode, new_path))\n +\t\t\t\tclean_merge = -1;\n  \t\t\telse if (o->call_depth)\n  \t\t\t\tremove_file_from_cache(path);\n  \t\t\tfree(new_path);\n  \t\t} else {\n  \t\t\toutput(o, 2, _(\"Adding %s\"), path);\n  \t\t\t/* do not overwrite file if already present */\n -\t\t\tif ((ret = update_file_flags(o, sha, mode, path, 1,\n -\t\t\t\t\t!a_sha)))\n -\t\t\t\tclean_merge = ret;\n +\t\t\tif (update_file_flags(o, sha, mode, path, 1, !a_sha))\n +\t\t\t\tclean_merge = -1;\n  \t\t}\n  \t} else if (a_sha && b_sha) {\n  \t\t/* Case C: Added in both (check for same permissions) and */\n @@ -1867,7 +1878,7 @@ static int process_entry(struct merge_options *o,\n  \t\t */\n  \t\tremove_file(o, 1, path, !a_mode);\n  \t} else\n -\t\treturn error(_(\"Fatal merge failure, shouldn't happen.\"));\n +\t\tdie(\"BUG: fatal merge failure, shouldn't happen.\");\n  \n  \treturn clean_merge;\n  }\n @@ -1895,7 +1906,7 @@ int merge_trees(struct merge_options *o,\n  \n  \tif (code != 0) {\n  \t\tif (show(o, 4) || o->call_depth)\n -\t\t\terror(_(\"merging of trees %s and %s failed\"),\n +\t\t\terr(o, _(\"merging of trees %s and %s failed\"),\n  \t\t\t    oid_to_hex(&head->object.oid),\n  \t\t\t    oid_to_hex(&merge->object.oid));\n  \t\treturn -1;\n @@ -1930,7 +1941,7 @@ int merge_trees(struct merge_options *o,\n  \t\tfor (i = 0; i < entries->nr; i++) {\n  \t\t\tstruct stage_data *e = entries->items[i].util;\n  \t\t\tif (!e->processed)\n -\t\t\t\tdie(_(\"BUG: unprocessed path??? %s\"),\n +\t\t\t\tdie(\"BUG: unprocessed path??? %s\",\n  \t\t\t\t    entries->items[i].string);\n  \t\t}\n  \n @@ -2029,7 +2040,7 @@ int merge_recursive(struct merge_options *o,\n  \t\to->call_depth--;\n  \n  \t\tif (!merged_common_ancestors)\n -\t\t\treturn error(_(\"merge returned no commit\"));\n +\t\t\treturn err(o, _(\"merge returned no commit\"));\n  \t}\n  \n  \tdiscard_cache();\n @@ -2039,6 +2050,7 @@ int merge_recursive(struct merge_options *o,\n  \to->ancestor = \"merged common ancestors\";\n  \tclean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree,\n  \t\t\t    &mrtree);\n +\tflush_output(o);\n  \tif (clean < 0)\n  \t\treturn clean;\n  \n @@ -2047,7 +2059,8 @@ int merge_recursive(struct merge_options *o,\n  \t\tcommit_list_insert(h1, &(*result)->parents);\n  \t\tcommit_list_insert(h2, &(*result)->parents->next);\n  \t}\n -\tflush_output(o);\n +\tif (o->buffer_output < 2)\n +\t\tstrbuf_release(&o->obuf);\n  \tif (show(o, 2))\n  \t\tdiff_warn_rename_limit(\"merge.renamelimit\",\n  \t\t\t\t       o->needed_rename_limit, 0);\n @@ -2088,7 +2101,7 @@ int merge_recursive_generic(struct merge_options *o,\n  \t\tfor (i = 0; i < num_base_list; ++i) {\n  \t\t\tstruct commit *base;\n  \t\t\tif (!(base = get_ref(base_list[i], sha1_to_hex(base_list[i]))))\n -\t\t\t\treturn error(_(\"Could not parse object '%s'\"),\n +\t\t\t\treturn err(o, _(\"Could not parse object '%s'\"),\n  \t\t\t\t\tsha1_to_hex(base_list[i]));\n  \t\t\tcommit_list_insert(base, &ca);\n  \t\t}\n @@ -2102,7 +2115,7 @@ int merge_recursive_generic(struct merge_options *o,\n  \n  \tif (active_cache_changed &&\n  \t    write_locked_index(&the_index, lock, COMMIT_LOCK))\n -\t\treturn error(_(\"Unable to write index.\"));\n +\t\treturn err(o, _(\"Unable to write index.\"));\n  \n  \treturn clean ? 0 : 1;\n  }\n diff --git a/merge-recursive.h b/merge-recursive.h\n index 52f0201..407d4fc 100644\n --- a/merge-recursive.h\n +++ b/merge-recursive.h\n @@ -13,7 +13,7 @@ struct merge_options {\n  \t\tMERGE_RECURSIVE_THEIRS\n  \t} recursive_variant;\n  \tconst char *subtree_shift;\n -\tunsigned buffer_output : 1;\n +\tunsigned buffer_output : 2; /* 1: output at end, 2: keep buffered */\n  \tunsigned renormalize : 1;\n  \tlong xdl_opts;\n  \tint verbosity;\n diff --git a/sequencer.c b/sequencer.c\n index 13b794a..8ceeb1b 100644\n --- a/sequencer.c\n +++ b/sequencer.c\n @@ -293,6 +293,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n  \tclean = merge_trees(&o,\n  \t\t\t    head_tree,\n  \t\t\t    next_tree, base_tree, &result);\n +\tstrbuf_release(&o.obuf);\n  \tif (clean < 0)\n  \t\treturn clean;\n  \n diff --git a/sha1_file.c b/sha1_file.c\n index aa7006c..5085fe0 100644\n --- a/sha1_file.c\n +++ b/sha1_file.c\n @@ -795,7 +795,7 @@ void close_all_packs(void)\n  \n  \tfor (p = packed_git; p; p = p->next)\n  \t\tif (p->do_not_close)\n -\t\t\tdie(\"BUG: Want to close pack marked 'do-not-close'\");\n +\t\t\tdie(\"BUG: want to close pack marked 'do-not-close'\");\n  \t\telse\n  \t\t\tclose_pack(p);\n  }\n diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\n index 3159956..b281d6f 100755\n --- a/t/t5520-pull.sh\n +++ b/t/t5520-pull.sh\n @@ -255,6 +255,36 @@ test_expect_success '--rebase' '\n  \ttest new = \"$(git show HEAD:file2)\"\n  '\n  \n +test_expect_success '--rebase with conflicts shows advice' '\n +\ttest_when_finished \"git rebase --abort; git checkout -f to-rebase\" &&\n +\tgit checkout -b seq &&\n +\tprintf \"1\\\\n2\\\\n3\\\\n4\\\\n5\\\\n\" >seq.txt &&\n +\tgit add seq.txt &&\n +\ttest_tick &&\n +\tgit commit -m \"Add seq.txt\" &&\n +\tprintf \"6\\\\n\" >>seq.txt &&\n +\ttest_tick &&\n +\tgit commit -m \"Append to seq.txt\" seq.txt &&\n +\tgit checkout -b with-conflicts HEAD^ &&\n +\tprintf \"conflicting\\\\n\" >>seq.txt &&\n +\ttest_tick &&\n +\tgit commit -m \"Create conflict\" seq.txt &&\n +\ttest_must_fail git pull --rebase . seq 2>err >out &&\n +\tgrep \"When you have resolved this problem\" out\n +'\n +test_expect_success 'failed --rebase shows advice' '\n +\ttest_when_finished \"git rebase --abort; git checkout -f to-rebase\" &&\n +\tgit checkout -b diverging &&\n +\ttest_commit attributes .gitattributes \"* text=auto\" attrs &&\n +\tsha1=\"$(printf \"1\\\\r\\\\n\" | git hash-object -w --stdin)\" &&\n +\tgit update-index --cacheinfo 0644 $sha1 file &&\n +\tgit commit -m v1-with-cr &&\n +\tgit checkout -f -b fails-to-rebase HEAD^ &&\n +\ttest_commit v2-without-cr file \"2\" file2-lf &&\n +\ttest_must_fail git pull --rebase . diverging 2>err >out &&\n +\tgrep \"When you have resolved this problem\" out\n +'\n +\n  test_expect_success '--rebase fails with multiple branches' '\n  \tgit reset --hard before-rebase &&\n  \ttest_must_fail git pull --rebase . copy master 2>err &&\n diff --git a/wt-status.c b/wt-status.c\n index 311ae7c..75c1162 100644\n --- a/wt-status.c\n +++ b/wt-status.c\n @@ -263,7 +263,7 @@ static const char *wt_status_unmerged_status_string(int stagemask)\n  \tcase 7:\n  \t\treturn _(\"both modified:\");\n  \tdefault:\n -\t\tdie(_(\"BUG: unhandled unmerged status %x\"), stagemask);\n +\t\tdie(\"BUG: unhandled unmerged status %x\", stagemask);\n  \t}\n  }\n  \n @@ -388,7 +388,7 @@ static void wt_status_print_change_data(struct wt_status *s,\n  \tstatus_printf(s, color(WT_STATUS_HEADER, s), \"\\t\");\n  \twhat = wt_status_diff_status_string(status);\n  \tif (!what)\n -\t\tdie(_(\"BUG: unhandled diff status %c\"), status);\n +\t\tdie(\"BUG: unhandled diff status %c\", status);\n  \tlen = label_width - utf8_strwidth(what);\n  \tassert(len >= 0);\n  \tif (status == DIFF_STATUS_COPIED || status == DIFF_STATUS_RENAMED)\n\n-- \n2.9.0.280.g32e2a70\n\nbase-commit: cf4c2cfe52be5bd973a4838f73a35d3959ce2f43\n"},{"id":"290816","messageId":"fdb0efbeb0b41c0d9976b2d66df90d2366f81ca1.1467717729.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467717729.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 02/17] Report bugs consistently","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-05T11:23:10Z","receivedAt":"2016-07-05T11:23:39Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The vast majority of error messages in Git's source code which report a\nbug use the convention to prefix the message with \"BUG:\".\n\nAs part of cleaning up merge-recursive to stop die()ing except in case of\ndetected bugs, let's just make the remainder of the bug reports consistent\nwith the de facto rule.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/ls-files.c     |  3 ++-\n builtin/update-index.c |  2 +-\n grep.c                 |  8 ++++----\n imap-send.c            |  4 ++--\n merge-recursive.c      | 15 +++++++--------\n sha1_file.c            |  4 ++--\n trailer.c              |  2 +-\n transport.c            |  2 +-\n wt-status.c            |  4 ++--\n 9 files changed, 22 insertions(+), 22 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex f02e3d2..00ea91a 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -118,7 +118,8 @@ static void show_killed_files(struct dir_struct *dir)\n \t\t\t\t */\n \t\t\t\tpos = cache_name_pos(ent->name, ent->len);\n \t\t\t\tif (0 <= pos)\n-\t\t\t\t\tdie(\"bug in show-killed-files\");\n+\t\t\t\t\tdie(\"BUG: killed-file %.*s not found\",\n+\t\t\t\t\t\tent->len, ent->name);\n \t\t\t\tpos = -pos - 1;\n \t\t\t\twhile (pos < active_nr &&\n \t\t\t\t       ce_stage(active_cache[pos]))\ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex 6cdfd5f..ba04b19 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -1146,7 +1146,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)\n \t\treport(_(\"Untracked cache enabled for '%s'\"), get_git_work_tree());\n \t\tbreak;\n \tdefault:\n-\t\tdie(\"Bug: bad untracked_cache value: %d\", untracked_cache);\n+\t\tdie(\"BUG: bad untracked_cache value: %d\", untracked_cache);\n \t}\n \n \tif (active_cache_changed) {\ndiff --git a/grep.c b/grep.c\nindex 1e15b62..f1ca0a0 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -643,10 +643,10 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)\n \n \tfor (p = opt->header_list; p; p = p->next) {\n \t\tif (p->token != GREP_PATTERN_HEAD)\n-\t\t\tdie(\"bug: a non-header pattern in grep header list.\");\n+\t\t\tdie(\"BUG: a non-header pattern in grep header list.\");\n \t\tif (p->field < GREP_HEADER_FIELD_MIN ||\n \t\t    GREP_HEADER_FIELD_MAX <= p->field)\n-\t\t\tdie(\"bug: unknown header field %d\", p->field);\n+\t\t\tdie(\"BUG: unknown header field %d\", p->field);\n \t\tcompile_regexp(p, opt);\n \t}\n \n@@ -659,7 +659,7 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)\n \n \t\th = compile_pattern_atom(&pp);\n \t\tif (!h || pp != p->next)\n-\t\t\tdie(\"bug: malformed header expr\");\n+\t\t\tdie(\"BUG: malformed header expr\");\n \t\tif (!header_group[p->field]) {\n \t\t\theader_group[p->field] = h;\n \t\t\tcontinue;\n@@ -1464,7 +1464,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle\n \t\tcase GREP_BINARY_TEXT:\n \t\t\tbreak;\n \t\tdefault:\n-\t\t\tdie(\"bug: unknown binary handling mode\");\n+\t\t\tdie(\"BUG: unknown binary handling mode\");\n \t\t}\n \t}\n \ndiff --git a/imap-send.c b/imap-send.c\nindex 938c691..369f72a 100644\n--- a/imap-send.c\n+++ b/imap-send.c\n@@ -506,12 +506,12 @@ static char *next_arg(char **s)\n \n static int nfsnprintf(char *buf, int blen, const char *fmt, ...)\n {\n-\tint ret;\n+\tint ret = -1;\n \tva_list va;\n \n \tva_start(va, fmt);\n \tif (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)\n-\t\tdie(\"Fatal: buffer too small. Please report a bug.\");\n+\t\tdie(\"BUG: buffer too small (%d < %d)\", ret, blen);\n \tva_end(va);\n \treturn ret;\n }\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 65cb5d6..9849439 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -259,7 +259,7 @@ struct tree *write_tree_from_memory(struct merge_options *o)\n \t\t\t\tfprintf(stderr, \"BUG: %d %.*s\\n\", ce_stage(ce),\n \t\t\t\t\t(int)ce_namelen(ce), ce->name);\n \t\t}\n-\t\tdie(\"Bug in merge-recursive.c\");\n+\t\tdie(\"BUG: unmerged index entries in merge-recursive.c\");\n \t}\n \n \tif (!active_cache_tree)\n@@ -955,9 +955,8 @@ static struct merge_file_info merge_file_1(struct merge_options *o,\n \n \t\t\tif (!sha_eq(a->sha1, b->sha1))\n \t\t\t\tresult.clean = 0;\n-\t\t} else {\n-\t\t\tdie(_(\"unsupported object type in the tree\"));\n-\t\t}\n+\t\t} else\n+\t\t\tdie(_(\"BUG: unsupported object type in the tree\"));\n \t}\n \n \treturn result;\n@@ -1343,7 +1342,7 @@ static int process_renames(struct merge_options *o,\n \t\t\tconst char *ren2_dst = ren2->pair->two->path;\n \t\t\tenum rename_type rename_type;\n \t\t\tif (strcmp(ren1_src, ren2_src) != 0)\n-\t\t\t\tdie(\"ren1_src != ren2_src\");\n+\t\t\t\tdie(\"BUG: ren1_src != ren2_src\");\n \t\t\tren2->dst_entry->processed = 1;\n \t\t\tren2->processed = 1;\n \t\t\tif (strcmp(ren1_dst, ren2_dst) != 0) {\n@@ -1377,7 +1376,7 @@ static int process_renames(struct merge_options *o,\n \t\t\tren2 = lookup->util;\n \t\t\tren2_dst = ren2->pair->two->path;\n \t\t\tif (strcmp(ren1_dst, ren2_dst) != 0)\n-\t\t\t\tdie(\"ren1_dst != ren2_dst\");\n+\t\t\t\tdie(\"BUG: ren1_dst != ren2_dst\");\n \n \t\t\tclean_merge = 0;\n \t\t\tren2->processed = 1;\n@@ -1795,7 +1794,7 @@ static int process_entry(struct merge_options *o,\n \t\t */\n \t\tremove_file(o, 1, path, !a_mode);\n \t} else\n-\t\tdie(_(\"Fatal merge failure, shouldn't happen.\"));\n+\t\tdie(_(\"BUG: fatal merge failure, shouldn't happen.\"));\n \n \treturn clean_merge;\n }\n@@ -1853,7 +1852,7 @@ int merge_trees(struct merge_options *o,\n \t\tfor (i = 0; i < entries->nr; i++) {\n \t\t\tstruct stage_data *e = entries->items[i].util;\n \t\t\tif (!e->processed)\n-\t\t\t\tdie(_(\"Unprocessed path??? %s\"),\n+\t\t\t\tdie(_(\"BUG: unprocessed path??? %s\"),\n \t\t\t\t    entries->items[i].string);\n \t\t}\n \ndiff --git a/sha1_file.c b/sha1_file.c\nindex d5e1121..5085fe0 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -795,7 +795,7 @@ void close_all_packs(void)\n \n \tfor (p = packed_git; p; p = p->next)\n \t\tif (p->do_not_close)\n-\t\t\tdie(\"BUG! Want to close pack marked 'do-not-close'\");\n+\t\t\tdie(\"BUG: want to close pack marked 'do-not-close'\");\n \t\telse\n \t\t\tclose_pack(p);\n }\n@@ -2330,7 +2330,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,\n \tcase OBJ_OFS_DELTA:\n \tcase OBJ_REF_DELTA:\n \t\tif (data)\n-\t\t\tdie(\"BUG in unpack_entry: left loop at a valid delta\");\n+\t\t\tdie(\"BUG: unpack_entry: left loop at a valid delta\");\n \t\tbreak;\n \tcase OBJ_COMMIT:\n \tcase OBJ_TREE:\ndiff --git a/trailer.c b/trailer.c\nindex 8e48a5c..c6ea9ac 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -562,7 +562,7 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)\n \t\t\twarning(_(\"unknown value '%s' for key '%s'\"), value, conf_key);\n \t\tbreak;\n \tdefault:\n-\t\tdie(\"internal bug in trailer.c\");\n+\t\tdie(\"BUG: trailer.c: unhandled type %d\", type);\n \t}\n \treturn 0;\n }\ndiff --git a/transport.c b/transport.c\nindex 095e61f..52bf997 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -563,7 +563,7 @@ void transport_take_over(struct transport *transport,\n \tstruct git_transport_data *data;\n \n \tif (!transport->smart_options)\n-\t\tdie(\"Bug detected: Taking over transport requires non-NULL \"\n+\t\tdie(\"BUG: taking over transport requires non-NULL \"\n \t\t    \"smart_options field.\");\n \n \tdata = xcalloc(1, sizeof(*data));\ndiff --git a/wt-status.c b/wt-status.c\nindex 4ce4e35..311ae7c 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -263,7 +263,7 @@ static const char *wt_status_unmerged_status_string(int stagemask)\n \tcase 7:\n \t\treturn _(\"both modified:\");\n \tdefault:\n-\t\tdie(_(\"bug: unhandled unmerged status %x\"), stagemask);\n+\t\tdie(_(\"BUG: unhandled unmerged status %x\"), stagemask);\n \t}\n }\n \n@@ -388,7 +388,7 @@ static void wt_status_print_change_data(struct wt_status *s,\n \tstatus_printf(s, color(WT_STATUS_HEADER, s), \"\\t\");\n \twhat = wt_status_diff_status_string(status);\n \tif (!what)\n-\t\tdie(_(\"bug: unhandled diff status %c\"), status);\n+\t\tdie(_(\"BUG: unhandled diff status %c\"), status);\n \tlen = label_width - utf8_strwidth(what);\n \tassert(len >= 0);\n \tif (status == DIFF_STATUS_COPIED || status == DIFF_STATUS_RENAMED)\n-- \n2.9.0.280.g32e2a70\n\n\n"},{"id":"290818","messageId":"69fc0a26854c89f35c1fb0e61d15f1fce82bef1c.1467717730.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467717729.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 04/17] merge-recursive: clarify code in was_tracked()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-05T11:23:22Z","receivedAt":"2016-07-05T11:23:47Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It can be puzzling to see that was_tracked() asks to get an index entry\nby name, but does not take a negative return value for an answer.\n\nThe reason we have to do this is that cache_name_pos() only looks for\nentries in stage 0, even if nobody asked for any stage in particular.\n\nLet's rewrite the logic a little bit, to handle the easy case early: if\ncache_name_pos() returned a non-negative position, we know it is a match,\nand we do not even have to compare the name again (cache_name_pos() did\nthat for us already). We can say right away: yes, this file was tracked.\n\nOnly if there was no exact match do we need to look harder for any\nmatching entry in stage 2.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 19 +++++++++----------\n 1 file changed, 9 insertions(+), 10 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex e51f8fc..66ce27c 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -658,23 +658,22 @@ static int was_tracked(const char *path)\n {\n \tint pos = cache_name_pos(path, strlen(path));\n \n-\tif (pos < 0)\n-\t\tpos = -1 - pos;\n-\twhile (pos < active_nr &&\n-\t       !strcmp(path, active_cache[pos]->name)) {\n+\tif (pos >= 0)\n+\t\treturn pos < active_nr;\n+\t/*\n+\t * cache_name_pos() looks for stage == 0, even if we did not ask for\n+\t * it. Let's look for stage == 2 now.\n+\t */\n+\tfor (pos = -1 - pos; pos < active_nr &&\n+\t     !strcmp(path, active_cache[pos]->name); pos++)\n \t\t/*\n \t\t * If stage #0, it is definitely tracked.\n \t\t * If it has stage #2 then it was tracked\n \t\t * before this merge started.  All other\n \t\t * cases the path was not tracked.\n \t\t */\n-\t\tswitch (ce_stage(active_cache[pos])) {\n-\t\tcase 0:\n-\t\tcase 2:\n+\t\tif (ce_stage(active_cache[pos]) == 2)\n \t\t\treturn 1;\n-\t\t}\n-\t\tpos++;\n-\t}\n \treturn 0;\n }\n \n-- \n2.9.0.280.g32e2a70\n\n\n"},{"id":"290819","messageId":"dec17f8412357d5c99cd29f6cc2bd4fefa2971de.1467717729.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467717729.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 03/17] Avoid translating bug messages","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-05T11:23:16Z","receivedAt":"2016-07-05T11:23:52Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"While working on the patch series that avoids die()ing in recursive\nmerges, the issue came up that bug reports (i.e. die(\"BUG: ...\")\nconstructs) should never be translated, as the target audience is the\nGit developer community, not necessarily the current user, and hence\na translated message would make it *harder* to address the problem.\n\nSo let's stop translating the obvious ones. As it is really, really\noutside the purview of this patch series to see whether there are more\ndie() statements that report bugs and are currently translated, that\ntask is left for another day and patch.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 6 +++---\n wt-status.c       | 4 ++--\n 2 files changed, 5 insertions(+), 5 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 9849439..e51f8fc 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -956,7 +956,7 @@ static struct merge_file_info merge_file_1(struct merge_options *o,\n \t\t\tif (!sha_eq(a->sha1, b->sha1))\n \t\t\t\tresult.clean = 0;\n \t\t} else\n-\t\t\tdie(_(\"BUG: unsupported object type in the tree\"));\n+\t\t\tdie(\"BUG: unsupported object type in the tree\");\n \t}\n \n \treturn result;\n@@ -1794,7 +1794,7 @@ static int process_entry(struct merge_options *o,\n \t\t */\n \t\tremove_file(o, 1, path, !a_mode);\n \t} else\n-\t\tdie(_(\"BUG: fatal merge failure, shouldn't happen.\"));\n+\t\tdie(\"BUG: fatal merge failure, shouldn't happen.\");\n \n \treturn clean_merge;\n }\n@@ -1852,7 +1852,7 @@ int merge_trees(struct merge_options *o,\n \t\tfor (i = 0; i < entries->nr; i++) {\n \t\t\tstruct stage_data *e = entries->items[i].util;\n \t\t\tif (!e->processed)\n-\t\t\t\tdie(_(\"BUG: unprocessed path??? %s\"),\n+\t\t\t\tdie(\"BUG: unprocessed path??? %s\",\n \t\t\t\t    entries->items[i].string);\n \t\t}\n \ndiff --git a/wt-status.c b/wt-status.c\nindex 311ae7c..75c1162 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -263,7 +263,7 @@ static const char *wt_status_unmerged_status_string(int stagemask)\n \tcase 7:\n \t\treturn _(\"both modified:\");\n \tdefault:\n-\t\tdie(_(\"BUG: unhandled unmerged status %x\"), stagemask);\n+\t\tdie(\"BUG: unhandled unmerged status %x\", stagemask);\n \t}\n }\n \n@@ -388,7 +388,7 @@ static void wt_status_print_change_data(struct wt_status *s,\n \tstatus_printf(s, color(WT_STATUS_HEADER, s), \"\\t\");\n \twhat = wt_status_diff_status_string(status);\n \tif (!what)\n-\t\tdie(_(\"BUG: unhandled diff status %c\"), status);\n+\t\tdie(\"BUG: unhandled diff status %c\", status);\n \tlen = label_width - utf8_strwidth(what);\n \tassert(len >= 0);\n \tif (status == DIFF_STATUS_COPIED || status == DIFF_STATUS_RENAMED)\n-- \n2.9.0.280.g32e2a70\n\n\n"},{"id":"290820","messageId":"a816492a3b22749849eef64dce279e313709fbe8.1467717730.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467717729.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 06/17] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-05T11:23:34Z","receivedAt":"2016-07-05T11:24:14Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"There are a couple of places where return values indicating errors\nare ignored. Let's teach them manners.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 10 ++++++++--\n 1 file changed, 8 insertions(+), 2 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 66ce27c..716488b 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1942,8 +1942,9 @@ int merge_recursive(struct merge_options *o,\n \t\tsaved_b2 = o->branch2;\n \t\to->branch1 = \"Temporary merge branch 1\";\n \t\to->branch2 = \"Temporary merge branch 2\";\n-\t\tmerge_recursive(o, merged_common_ancestors, iter->item,\n-\t\t\t\tNULL, &merged_common_ancestors);\n+\t\tif (merge_recursive(o, merged_common_ancestors, iter->item,\n+\t\t\t\tNULL, &merged_common_ancestors) < 0)\n+\t\t\treturn -1;\n \t\to->branch1 = saved_b1;\n \t\to->branch2 = saved_b2;\n \t\to->call_depth--;\n@@ -1959,6 +1960,8 @@ int merge_recursive(struct merge_options *o,\n \to->ancestor = \"merged common ancestors\";\n \tclean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree,\n \t\t\t    &mrtree);\n+\tif (clean < 0)\n+\t\treturn clean;\n \n \tif (o->call_depth) {\n \t\t*result = make_virtual_commit(mrtree, \"merged tree\");\n@@ -2015,6 +2018,9 @@ int merge_recursive_generic(struct merge_options *o,\n \thold_locked_index(lock, 1);\n \tclean = merge_recursive(o, head_commit, next_commit, ca,\n \t\t\tresult);\n+\tif (clean < 0)\n+\t\treturn clean;\n+\n \tif (active_cache_changed &&\n \t    write_locked_index(&the_index, lock, COMMIT_LOCK))\n \t\treturn error(_(\"Unable to write index.\"));\n-- \n2.9.0.280.g32e2a70\n\n\n"},{"id":"290821","messageId":"435b2f32f52f6bec73ce702f71510eb9363e99ef.1467717730.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467717729.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 07/17] merge-recursive: avoid returning a wholesale struct","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-05T11:23:38Z","receivedAt":"2016-07-05T11:24:17Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It is technically allowed, as per C89, for functions' return type to\nbe complete structs (i.e. *not* just pointers to structs).\n\nHowever, it was just an oversight of this developer when converting\nPython code to C code in 6d297f8 (Status update on merge-recursive in\nC, 2006-07-08) which introduced such a return type.\n\nBesides, by converting this construct to pass in the struct, we can now\nstart returning a value that can indicate errors in future patches. This\nwill help the current effort to libify merge-recursive.c.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 93 ++++++++++++++++++++++++++++++-------------------------\n 1 file changed, 50 insertions(+), 43 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 716488b..3e3667f 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -886,47 +886,47 @@ static int merge_3way(struct merge_options *o,\n \treturn merge_status;\n }\n \n-static struct merge_file_info merge_file_1(struct merge_options *o,\n+static int merge_file_1(struct merge_options *o,\n \t\t\t\t\t   const struct diff_filespec *one,\n \t\t\t\t\t   const struct diff_filespec *a,\n \t\t\t\t\t   const struct diff_filespec *b,\n \t\t\t\t\t   const char *branch1,\n-\t\t\t\t\t   const char *branch2)\n+\t\t\t\t\t   const char *branch2,\n+\t\t\t\t\t   struct merge_file_info *result)\n {\n-\tstruct merge_file_info result;\n-\tresult.merge = 0;\n-\tresult.clean = 1;\n+\tresult->merge = 0;\n+\tresult->clean = 1;\n \n \tif ((S_IFMT & a->mode) != (S_IFMT & b->mode)) {\n-\t\tresult.clean = 0;\n+\t\tresult->clean = 0;\n \t\tif (S_ISREG(a->mode)) {\n-\t\t\tresult.mode = a->mode;\n-\t\t\thashcpy(result.sha, a->sha1);\n+\t\t\tresult->mode = a->mode;\n+\t\t\thashcpy(result->sha, a->sha1);\n \t\t} else {\n-\t\t\tresult.mode = b->mode;\n-\t\t\thashcpy(result.sha, b->sha1);\n+\t\t\tresult->mode = b->mode;\n+\t\t\thashcpy(result->sha, b->sha1);\n \t\t}\n \t} else {\n \t\tif (!sha_eq(a->sha1, one->sha1) && !sha_eq(b->sha1, one->sha1))\n-\t\t\tresult.merge = 1;\n+\t\t\tresult->merge = 1;\n \n \t\t/*\n \t\t * Merge modes\n \t\t */\n \t\tif (a->mode == b->mode || a->mode == one->mode)\n-\t\t\tresult.mode = b->mode;\n+\t\t\tresult->mode = b->mode;\n \t\telse {\n-\t\t\tresult.mode = a->mode;\n+\t\t\tresult->mode = a->mode;\n \t\t\tif (b->mode != one->mode) {\n-\t\t\t\tresult.clean = 0;\n-\t\t\t\tresult.merge = 1;\n+\t\t\t\tresult->clean = 0;\n+\t\t\t\tresult->merge = 1;\n \t\t\t}\n \t\t}\n \n \t\tif (sha_eq(a->sha1, b->sha1) || sha_eq(a->sha1, one->sha1))\n-\t\t\thashcpy(result.sha, b->sha1);\n+\t\t\thashcpy(result->sha, b->sha1);\n \t\telse if (sha_eq(b->sha1, one->sha1))\n-\t\t\thashcpy(result.sha, a->sha1);\n+\t\t\thashcpy(result->sha, a->sha1);\n \t\telse if (S_ISREG(a->mode)) {\n \t\t\tmmbuffer_t result_buf;\n \t\t\tint merge_status;\n@@ -938,62 +938,63 @@ static struct merge_file_info merge_file_1(struct merge_options *o,\n \t\t\t\tdie(_(\"Failed to execute internal merge\"));\n \n \t\t\tif (write_sha1_file(result_buf.ptr, result_buf.size,\n-\t\t\t\t\t    blob_type, result.sha))\n+\t\t\t\t\t    blob_type, result->sha))\n \t\t\t\tdie(_(\"Unable to add %s to database\"),\n \t\t\t\t    a->path);\n \n \t\t\tfree(result_buf.ptr);\n-\t\t\tresult.clean = (merge_status == 0);\n+\t\t\tresult->clean = (merge_status == 0);\n \t\t} else if (S_ISGITLINK(a->mode)) {\n-\t\t\tresult.clean = merge_submodule(result.sha,\n+\t\t\tresult->clean = merge_submodule(result->sha,\n \t\t\t\t\t\t       one->path, one->sha1,\n \t\t\t\t\t\t       a->sha1, b->sha1,\n \t\t\t\t\t\t       !o->call_depth);\n \t\t} else if (S_ISLNK(a->mode)) {\n-\t\t\thashcpy(result.sha, a->sha1);\n+\t\t\thashcpy(result->sha, a->sha1);\n \n \t\t\tif (!sha_eq(a->sha1, b->sha1))\n-\t\t\t\tresult.clean = 0;\n+\t\t\t\tresult->clean = 0;\n \t\t} else\n \t\t\tdie(\"BUG: unsupported object type in the tree\");\n \t}\n \n-\treturn result;\n+\treturn 0;\n }\n \n-static struct merge_file_info\n-merge_file_special_markers(struct merge_options *o,\n+static int merge_file_special_markers(struct merge_options *o,\n \t\t\t   const struct diff_filespec *one,\n \t\t\t   const struct diff_filespec *a,\n \t\t\t   const struct diff_filespec *b,\n \t\t\t   const char *branch1,\n \t\t\t   const char *filename1,\n \t\t\t   const char *branch2,\n-\t\t\t   const char *filename2)\n+\t\t\t   const char *filename2,\n+\t\t\t   struct merge_file_info *mfi)\n {\n \tchar *side1 = NULL;\n \tchar *side2 = NULL;\n-\tstruct merge_file_info mfi;\n+\tint ret;\n \n \tif (filename1)\n \t\tside1 = xstrfmt(\"%s:%s\", branch1, filename1);\n \tif (filename2)\n \t\tside2 = xstrfmt(\"%s:%s\", branch2, filename2);\n \n-\tmfi = merge_file_1(o, one, a, b,\n-\t\t\t   side1 ? side1 : branch1, side2 ? side2 : branch2);\n+\tret = merge_file_1(o, one, a, b,\n+\t\tside1 ? side1 : branch1, side2 ? side2 : branch2, mfi);\n \tfree(side1);\n \tfree(side2);\n-\treturn mfi;\n+\treturn ret;\n }\n \n-static struct merge_file_info merge_file_one(struct merge_options *o,\n+static int merge_file_one(struct merge_options *o,\n \t\t\t\t\t const char *path,\n \t\t\t\t\t const unsigned char *o_sha, int o_mode,\n \t\t\t\t\t const unsigned char *a_sha, int a_mode,\n \t\t\t\t\t const unsigned char *b_sha, int b_mode,\n \t\t\t\t\t const char *branch1,\n-\t\t\t\t\t const char *branch2)\n+\t\t\t\t\t const char *branch2,\n+\t\t\t\t\t struct merge_file_info *mfi)\n {\n \tstruct diff_filespec one, a, b;\n \n@@ -1004,7 +1005,7 @@ static struct merge_file_info merge_file_one(struct merge_options *o,\n \ta.mode = a_mode;\n \thashcpy(b.sha1, b_sha);\n \tb.mode = b_mode;\n-\treturn merge_file_1(o, &one, &a, &b, branch1, branch2);\n+\treturn merge_file_1(o, &one, &a, &b, branch1, branch2, mfi);\n }\n \n static void handle_change_delete(struct merge_options *o,\n@@ -1177,11 +1178,14 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n \t\tstruct merge_file_info mfi;\n \t\tstruct diff_filespec other;\n \t\tstruct diff_filespec *add;\n-\t\tmfi = merge_file_one(o, one->path,\n+\n+\t\tif (merge_file_one(o, one->path,\n \t\t\t\t one->sha1, one->mode,\n \t\t\t\t a->sha1, a->mode,\n \t\t\t\t b->sha1, b->mode,\n-\t\t\t\t ci->branch1, ci->branch2);\n+\t\t\t\t ci->branch1, ci->branch2, &mfi))\n+\t\t\treturn;\n+\n \t\t/*\n \t\t * FIXME: For rename/add-source conflicts (if we could detect\n \t\t * such), this is wrong.  We should instead find a unique\n@@ -1235,12 +1239,13 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \tremove_file(o, 1, a->path, o->call_depth || would_lose_untracked(a->path));\n \tremove_file(o, 1, b->path, o->call_depth || would_lose_untracked(b->path));\n \n-\tmfi_c1 = merge_file_special_markers(o, a, c1, &ci->ren1_other,\n+\tif (merge_file_special_markers(o, a, c1, &ci->ren1_other,\n \t\t\t\t\t    o->branch1, c1->path,\n-\t\t\t\t\t    o->branch2, ci->ren1_other.path);\n-\tmfi_c2 = merge_file_special_markers(o, b, &ci->ren2_other, c2,\n+\t\t\t\t\t    o->branch2, ci->ren1_other.path, &mfi_c1) ||\n+\t    merge_file_special_markers(o, b, &ci->ren2_other, c2,\n \t\t\t\t\t    o->branch1, ci->ren2_other.path,\n-\t\t\t\t\t    o->branch2, c2->path);\n+\t\t\t\t\t    o->branch2, c2->path, &mfi_c2))\n+\t\treturn;\n \n \tif (o->call_depth) {\n \t\t/*\n@@ -1461,10 +1466,11 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t       ren1_dst, branch2);\n \t\t\t\tif (o->call_depth) {\n \t\t\t\t\tstruct merge_file_info mfi;\n-\t\t\t\t\tmfi = merge_file_one(o, ren1_dst, null_sha1, 0,\n+\t\t\t\t\tif (merge_file_one(o, ren1_dst, null_sha1, 0,\n \t\t\t\t\t\t\t ren1->pair->two->sha1, ren1->pair->two->mode,\n \t\t\t\t\t\t\t dst_other.sha1, dst_other.mode,\n-\t\t\t\t\t\t\t branch1, branch2);\n+\t\t\t\t\t\t\t branch1, branch2, &mfi))\n+\t\t\t\t\t\treturn -1;\n \t\t\t\t\toutput(o, 1, _(\"Adding merged %s\"), ren1_dst);\n \t\t\t\t\tupdate_file(o, 0, mfi.sha, mfi.mode, ren1_dst);\n \t\t\t\t\ttry_merge = 0;\n@@ -1620,9 +1626,10 @@ static int merge_content(struct merge_options *o,\n \t\tif (dir_in_way(path, !o->call_depth))\n \t\t\tdf_conflict_remains = 1;\n \t}\n-\tmfi = merge_file_special_markers(o, &one, &a, &b,\n+\tif (merge_file_special_markers(o, &one, &a, &b,\n \t\t\t\t\t o->branch1, path1,\n-\t\t\t\t\t o->branch2, path2);\n+\t\t\t\t\t o->branch2, path2, &mfi))\n+\t\treturn -1;\n \n \tif (mfi.clean && !df_conflict_remains &&\n \t    sha_eq(mfi.sha, a_sha) && mfi.mode == a_mode) {\n-- \n2.9.0.280.g32e2a70\n\n\n"},{"id":"290822","messageId":"d57b8b684c45b8ee9c7736f4f273345b1d50a2d4.1467717730.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467717729.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 05/17] Prepare the builtins for a libified merge_recursive()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-05T11:23:28Z","receivedAt":"2016-07-05T11:24:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Previously, callers of merge_trees() or merge_recursive() expected that\ncode to die() with an error message. This used to be okay because we\ncalled those commands from scripts, and had a chance to print out a\nmessage in case the command failed fatally (read: with exit code 128).\n\nAs scripting incurs its own set of problems (portability, speed,\nidiosynchracies of different shells, limited data structures leading to\ninefficient code), we are converting more and more of these scripts into\nbuiltins, using library functions directly.\n\nWe already tried to use merge_recursive() directly in the builtin\ngit-am, for example. Unfortunately, we had to roll it back temporarily\nbecause some of the code in merge-recursive.c still deemed it okay to\ncall die(), when the builtin am code really wanted to print out a useful\nadvice after the merge failed fatally. In the next commits, we want to\nfix that.\n\nThe code touched by this commit expected merge_trees() to die() with\nsome useful message when there is an error condition, but merge_trees()\nis going to be improved by converting all die() calls to return error()\ninstead (i.e. return value -1 after printing out the message as before),\nso that the caller can react more flexibly.\n\nThis is a step to prepare for the version of merge_trees() that no\nlonger dies,  even if we just imitate the previous behavior by calling\nexit(128): this is what callers of e.g. `git merge` have come to expect.\n\nNote that the callers of the sequencer (revert and cherry-pick) already\nfail fast even for the return value -1; The only difference is that they\nnow get a chance to say \"<command> failed\".\n\nA caller of merge_trees() might want handle error messages themselves\n(or even suppress them). As this patch is already complex enough, we\nleave that change for a later patch.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/checkout.c | 4 +++-\n builtin/merge.c    | 2 ++\n sequencer.c        | 4 ++++\n 3 files changed, 9 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex c3486bd..14312f7 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -567,8 +567,10 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\to.ancestor = old->name;\n \t\t\to.branch1 = new->name;\n \t\t\to.branch2 = \"local\";\n-\t\t\tmerge_trees(&o, new->commit->tree, work,\n+\t\t\tret = merge_trees(&o, new->commit->tree, work,\n \t\t\t\told->commit->tree, &result);\n+\t\t\tif (ret < 0)\n+\t\t\t\texit(128);\n \t\t\tret = reset_tree(new->commit->tree, opts, 0,\n \t\t\t\t\t writeout_error);\n \t\t\tif (ret)\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex b555a1b..7b898db 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -682,6 +682,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,\n \t\thold_locked_index(&lock, 1);\n \t\tclean = merge_recursive(&o, head,\n \t\t\t\tremoteheads->item, reversed, &result);\n+\t\tif (clean < 0)\n+\t\t\texit(128);\n \t\tif (active_cache_changed &&\n \t\t    write_locked_index(&the_index, &lock, COMMIT_LOCK))\n \t\t\tdie (_(\"unable to write %s\"), get_index_file());\ndiff --git a/sequencer.c b/sequencer.c\nindex c6362d6..13b794a 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -293,6 +293,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \tclean = merge_trees(&o,\n \t\t\t    head_tree,\n \t\t\t    next_tree, base_tree, &result);\n+\tif (clean < 0)\n+\t\treturn clean;\n \n \tif (active_cache_changed &&\n \t    write_locked_index(&the_index, &index_lock, COMMIT_LOCK))\n@@ -561,6 +563,8 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || opts->action == REPLAY_REVERT) {\n \t\tres = do_recursive_merge(base, next, base_label, next_label,\n \t\t\t\t\t head, &msgbuf, opts);\n+\t\tif (res < 0)\n+\t\t\treturn res;\n \t\twrite_message(&msgbuf, git_path_merge_msg());\n \t} else {\n \t\tstruct commit_list *common = NULL;\n-- \n2.9.0.280.g32e2a70\n\n\n"},{"id":"290823","messageId":"aff644a7766787d3538eeec55b8165004403f860.1467717730.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467717729.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 09/17] merge-recursive: handle return values indicating errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-05T11:23:47Z","receivedAt":"2016-07-05T11:24:21Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We are about to libify the recursive merge machinery, where we only\ndie() in case of a bug or memory contention. To that end, we must heed\nnegative return values as indicating errors.\n\nThis requires our functions to be careful to pass through error\nconditions in call chains, and for quite a few functions this means\nthat they have to return values to begin with.\n\nThe next step will be to convert the places where we currently die() to\nreturn negative values (read: -1) instead.\n\nNote that we ignore errors reported by make_room_for_path(), consistent\nwith the previous behavior (update_file_flags() used the return value of\nmake_room_for_path() only to indicate an early return, but not a fatal\nerror): if the error is really a fatal error, we will notice later; If\nnot, it was not that serious a problem to begin with. (Witnesses in\nfavor of this reasoning are t4151-am-abort and t7610-mergetool, which\nwould start failing if we stopped on errors reported by\nmake_room_for_path()).\n\nNote: while this patch makes the code slightly less readable in\nupdate_file_flags() (we introduce a new \"goto free_buf;\" instead of\nan explicit \"free(buf); return;\"), it is a preparatory change for\nthe next patch where we will convert all of the die() calls in the same\nfunction to go through the free_buf return path instead.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 196 +++++++++++++++++++++++++++++++++---------------------\n 1 file changed, 121 insertions(+), 75 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 99f4202..209427c 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -734,7 +734,7 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n }\n \n-static void update_file_flags(struct merge_options *o,\n+static int update_file_flags(struct merge_options *o,\n \t\t\t      const unsigned char *sha,\n \t\t\t      unsigned mode,\n \t\t\t      const char *path,\n@@ -775,8 +775,7 @@ static void update_file_flags(struct merge_options *o,\n \n \t\tif (make_room_for_path(o, path) < 0) {\n \t\t\tupdate_wd = 0;\n-\t\t\tfree(buf);\n-\t\t\tgoto update_index;\n+\t\t\tgoto free_buf;\n \t\t}\n \t\tif (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) {\n \t\t\tint fd;\n@@ -799,20 +798,22 @@ static void update_file_flags(struct merge_options *o,\n \t\t} else\n \t\t\tdie(_(\"do not know what to do with %06o %s '%s'\"),\n \t\t\t    mode, sha1_to_hex(sha), path);\n+ free_buf:\n \t\tfree(buf);\n \t}\n  update_index:\n \tif (update_cache)\n \t\tadd_cacheinfo(mode, sha, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n+\treturn 0;\n }\n \n-static void update_file(struct merge_options *o,\n+static int update_file(struct merge_options *o,\n \t\t\tint clean,\n \t\t\tconst unsigned char *sha,\n \t\t\tunsigned mode,\n \t\t\tconst char *path)\n {\n-\tupdate_file_flags(o, sha, mode, path, o->call_depth || clean, !o->call_depth);\n+\treturn update_file_flags(o, sha, mode, path, o->call_depth || clean, !o->call_depth);\n }\n \n /* Low level file merging, update and removal */\n@@ -1008,7 +1009,7 @@ static int merge_file_one(struct merge_options *o,\n \treturn merge_file_1(o, &one, &a, &b, branch1, branch2, mfi);\n }\n \n-static void handle_change_delete(struct merge_options *o,\n+static int handle_change_delete(struct merge_options *o,\n \t\t\t\t const char *path,\n \t\t\t\t const unsigned char *o_sha, int o_mode,\n \t\t\t\t const unsigned char *a_sha, int a_mode,\n@@ -1016,6 +1017,7 @@ static void handle_change_delete(struct merge_options *o,\n \t\t\t\t const char *change, const char *change_past)\n {\n \tchar *renamed = NULL;\n+\tint ret = 0;\n \tif (dir_in_way(path, !o->call_depth)) {\n \t\trenamed = unique_path(o, path, a_sha ? o->branch1 : o->branch2);\n \t}\n@@ -1026,21 +1028,23 @@ static void handle_change_delete(struct merge_options *o,\n \t\t * correct; since there is no true \"middle point\" between\n \t\t * them, simply reuse the base version for virtual merge base.\n \t\t */\n-\t\tremove_file_from_cache(path);\n-\t\tupdate_file(o, 0, o_sha, o_mode, renamed ? renamed : path);\n+\t\tret = remove_file_from_cache(path);\n+\t\tif (!ret)\n+\t\t\tret = update_file(o, 0, o_sha, o_mode,\n+\t\t\t\t\t  renamed ? renamed : path);\n \t} else if (!a_sha) {\n \t\tif (!renamed) {\n \t\t\toutput(o, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n \t\t\t       \"and %s in %s. Version %s of %s left in tree.\"),\n \t\t\t       change, path, o->branch1, change_past,\n \t\t\t       o->branch2, o->branch2, path);\n-\t\t\tupdate_file(o, 0, b_sha, b_mode, path);\n+\t\t\tret = update_file(o, 0, b_sha, b_mode, path);\n \t\t} else {\n \t\t\toutput(o, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n \t\t\t       \"and %s in %s. Version %s of %s left in tree at %s.\"),\n \t\t\t       change, path, o->branch1, change_past,\n \t\t\t       o->branch2, o->branch2, path, renamed);\n-\t\t\tupdate_file(o, 0, b_sha, b_mode, renamed);\n+\t\t\tret = update_file(o, 0, b_sha, b_mode, renamed);\n \t\t}\n \t} else {\n \t\tif (!renamed) {\n@@ -1053,7 +1057,7 @@ static void handle_change_delete(struct merge_options *o,\n \t\t\t       \"and %s in %s. Version %s of %s left in tree at %s.\"),\n \t\t\t       change, path, o->branch2, change_past,\n \t\t\t       o->branch1, o->branch1, path, renamed);\n-\t\t\tupdate_file(o, 0, a_sha, a_mode, renamed);\n+\t\t\tret = update_file(o, 0, a_sha, a_mode, renamed);\n \t\t}\n \t\t/*\n \t\t * No need to call update_file() on path when !renamed, since\n@@ -1063,9 +1067,11 @@ static void handle_change_delete(struct merge_options *o,\n \t\t */\n \t}\n \tfree(renamed);\n+\n+\treturn ret;\n }\n \n-static void conflict_rename_delete(struct merge_options *o,\n+static int conflict_rename_delete(struct merge_options *o,\n \t\t\t\t   struct diff_filepair *pair,\n \t\t\t\t   const char *rename_branch,\n \t\t\t\t   const char *other_branch)\n@@ -1085,21 +1091,19 @@ static void conflict_rename_delete(struct merge_options *o,\n \t\tb_mode = dest->mode;\n \t}\n \n-\thandle_change_delete(o,\n+\tif (handle_change_delete(o,\n \t\t\t     o->call_depth ? orig->path : dest->path,\n \t\t\t     orig->sha1, orig->mode,\n \t\t\t     a_sha, a_mode,\n \t\t\t     b_sha, b_mode,\n-\t\t\t     _(\"rename\"), _(\"renamed\"));\n-\n-\tif (o->call_depth) {\n-\t\tremove_file_from_cache(dest->path);\n-\t} else {\n-\t\tupdate_stages(dest->path, NULL,\n+\t\t\t     _(\"rename\"), _(\"renamed\")))\n+\t\treturn -1;\n+\tif (o->call_depth)\n+\t\treturn remove_file_from_cache(dest->path);\n+\telse\n+\t\treturn update_stages(dest->path, NULL,\n \t\t\t      rename_branch == o->branch1 ? dest : NULL,\n \t\t\t      rename_branch == o->branch1 ? NULL : dest);\n-\t}\n-\n }\n \n static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,\n@@ -1115,7 +1119,7 @@ static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,\n \treturn target;\n }\n \n-static void handle_file(struct merge_options *o,\n+static int handle_file(struct merge_options *o,\n \t\t\tstruct diff_filespec *rename,\n \t\t\tint stage,\n \t\t\tstruct rename_conflict_info *ci)\n@@ -1125,6 +1129,7 @@ static void handle_file(struct merge_options *o,\n \tconst char *cur_branch, *other_branch;\n \tstruct diff_filespec other;\n \tstruct diff_filespec *add;\n+\tint ret;\n \n \tif (stage == 2) {\n \t\tdst_entry = ci->dst_entry1;\n@@ -1139,7 +1144,8 @@ static void handle_file(struct merge_options *o,\n \tadd = filespec_from_entry(&other, dst_entry, stage ^ 1);\n \tif (add) {\n \t\tchar *add_name = unique_path(o, rename->path, other_branch);\n-\t\tupdate_file(o, 0, add->sha1, add->mode, add_name);\n+\t\tif (update_file(o, 0, add->sha1, add->mode, add_name))\n+\t\t\treturn -1;\n \n \t\tremove_file(o, 0, rename->path, 0);\n \t\tdst_name = unique_path(o, rename->path, cur_branch);\n@@ -1150,17 +1156,20 @@ static void handle_file(struct merge_options *o,\n \t\t\t       rename->path, other_branch, dst_name);\n \t\t}\n \t}\n-\tupdate_file(o, 0, rename->sha1, rename->mode, dst_name);\n-\tif (stage == 2)\n-\t\tupdate_stages(rename->path, NULL, rename, add);\n+\tif ((ret = update_file(o, 0, rename->sha1, rename->mode, dst_name)))\n+\t\t; /* fall through, do allow dst_name to be released */\n+\telse if (stage == 2)\n+\t\tret = update_stages(rename->path, NULL, rename, add);\n \telse\n-\t\tupdate_stages(rename->path, NULL, add, rename);\n+\t\tret = update_stages(rename->path, NULL, add, rename);\n \n \tif (dst_name != rename->path)\n \t\tfree(dst_name);\n+\n+\treturn ret;\n }\n \n-static void conflict_rename_rename_1to2(struct merge_options *o,\n+static int conflict_rename_rename_1to2(struct merge_options *o,\n \t\t\t\t\tstruct rename_conflict_info *ci)\n {\n \t/* One file was renamed in both branches, but to different names. */\n@@ -1184,7 +1193,7 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n \t\t\t\t a->sha1, a->mode,\n \t\t\t\t b->sha1, b->mode,\n \t\t\t\t ci->branch1, ci->branch2, &mfi))\n-\t\t\treturn;\n+\t\t\treturn -1;\n \n \t\t/*\n \t\t * FIXME: For rename/add-source conflicts (if we could detect\n@@ -1192,7 +1201,8 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n \t\t * pathname and then either rename the add-source file to that\n \t\t * unique path, or use that unique path instead of src here.\n \t\t */\n-\t\tupdate_file(o, 0, mfi.sha, mfi.mode, one->path);\n+\t\tif (update_file(o, 0, mfi.sha, mfi.mode, one->path))\n+\t\t\treturn -1;\n \n \t\t/*\n \t\t * Above, we put the merged content at the merge-base's\n@@ -1203,22 +1213,26 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n \t\t * resolving the conflict at that path in its favor.\n \t\t */\n \t\tadd = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);\n-\t\tif (add)\n-\t\t\tupdate_file(o, 0, add->sha1, add->mode, a->path);\n+\t\tif (add) {\n+\t\t\tif (update_file(o, 0, add->sha1, add->mode, a->path))\n+\t\t\t\treturn -1;\n+\t\t}\n \t\telse\n \t\t\tremove_file_from_cache(a->path);\n \t\tadd = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);\n-\t\tif (add)\n-\t\t\tupdate_file(o, 0, add->sha1, add->mode, b->path);\n+\t\tif (add) {\n+\t\t\tif (update_file(o, 0, add->sha1, add->mode, b->path))\n+\t\t\t\treturn -1;\n+\t\t}\n \t\telse\n \t\t\tremove_file_from_cache(b->path);\n-\t} else {\n-\t\thandle_file(o, a, 2, ci);\n-\t\thandle_file(o, b, 3, ci);\n-\t}\n+\t} else if (handle_file(o, a, 2, ci) || handle_file(o, b, 3, ci))\n+\t\treturn -1;\n+\n+\treturn 0;\n }\n \n-static void conflict_rename_rename_2to1(struct merge_options *o,\n+static int conflict_rename_rename_2to1(struct merge_options *o,\n \t\t\t\t\tstruct rename_conflict_info *ci)\n {\n \t/* Two files, a & b, were renamed to the same thing, c. */\n@@ -1229,6 +1243,7 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \tchar *path = c1->path; /* == c2->path */\n \tstruct merge_file_info mfi_c1;\n \tstruct merge_file_info mfi_c2;\n+\tint ret;\n \n \toutput(o, 1, _(\"CONFLICT (rename/rename): \"\n \t       \"Rename %s->%s in %s. \"\n@@ -1245,7 +1260,7 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \t    merge_file_special_markers(o, b, &ci->ren2_other, c2,\n \t\t\t\t\t    o->branch1, ci->ren2_other.path,\n \t\t\t\t\t    o->branch2, c2->path, &mfi_c2))\n-\t\treturn;\n+\t\treturn -1;\n \n \tif (o->call_depth) {\n \t\t/*\n@@ -1256,19 +1271,25 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \t\t * again later for the non-recursive merge.\n \t\t */\n \t\tremove_file(o, 0, path, 0);\n-\t\tupdate_file(o, 0, mfi_c1.sha, mfi_c1.mode, a->path);\n-\t\tupdate_file(o, 0, mfi_c2.sha, mfi_c2.mode, b->path);\n+\t\tret = update_file(o, 0, mfi_c1.sha, mfi_c1.mode, a->path);\n+\t\tif (!ret)\n+\t\t\tret = update_file(o, 0, mfi_c2.sha, mfi_c2.mode,\n+\t\t\t\tb->path);\n \t} else {\n \t\tchar *new_path1 = unique_path(o, path, ci->branch1);\n \t\tchar *new_path2 = unique_path(o, path, ci->branch2);\n \t\toutput(o, 1, _(\"Renaming %s to %s and %s to %s instead\"),\n \t\t       a->path, new_path1, b->path, new_path2);\n \t\tremove_file(o, 0, path, 0);\n-\t\tupdate_file(o, 0, mfi_c1.sha, mfi_c1.mode, new_path1);\n-\t\tupdate_file(o, 0, mfi_c2.sha, mfi_c2.mode, new_path2);\n+\t\tret = update_file(o, 0, mfi_c1.sha, mfi_c1.mode, new_path1);\n+\t\tif (!ret)\n+\t\t\tret = update_file(o, 0, mfi_c2.sha, mfi_c2.mode,\n+\t\t\t\tnew_path2);\n \t\tfree(new_path2);\n \t\tfree(new_path1);\n \t}\n+\n+\treturn ret;\n }\n \n static int process_renames(struct merge_options *o,\n@@ -1451,12 +1472,13 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t * update_file_flags() instead of\n \t\t\t\t * update_file().\n \t\t\t\t */\n-\t\t\t\tupdate_file_flags(o,\n+\t\t\t\tif (update_file_flags(o,\n \t\t\t\t\t\t  ren1->pair->two->sha1,\n \t\t\t\t\t\t  ren1->pair->two->mode,\n \t\t\t\t\t\t  ren1_dst,\n \t\t\t\t\t\t  1, /* update_cache */\n-\t\t\t\t\t\t  0  /* update_wd    */);\n+\t\t\t\t\t\t  0  /* update_wd    */))\n+\t\t\t\t\tclean_merge = -1;\n \t\t\t} else if (!sha_eq(dst_other.sha1, null_sha1)) {\n \t\t\t\tclean_merge = 0;\n \t\t\t\ttry_merge = 1;\n@@ -1469,20 +1491,26 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t\tif (merge_file_one(o, ren1_dst, null_sha1, 0,\n \t\t\t\t\t\t\t ren1->pair->two->sha1, ren1->pair->two->mode,\n \t\t\t\t\t\t\t dst_other.sha1, dst_other.mode,\n-\t\t\t\t\t\t\t branch1, branch2, &mfi))\n-\t\t\t\t\t\treturn -1;\n+\t\t\t\t\t\t\t branch1, branch2, &mfi)) {\n+\t\t\t\t\t\tclean_merge = -1;\n+\t\t\t\t\t\tgoto cleanup_and_return;\n+\t\t\t\t\t}\n \t\t\t\t\toutput(o, 1, _(\"Adding merged %s\"), ren1_dst);\n-\t\t\t\t\tupdate_file(o, 0, mfi.sha, mfi.mode, ren1_dst);\n+\t\t\t\t\tif (update_file(o, 0, mfi.sha, mfi.mode, ren1_dst))\n+\t\t\t\t\t\tclean_merge = -1;\n \t\t\t\t\ttry_merge = 0;\n \t\t\t\t} else {\n \t\t\t\t\tchar *new_path = unique_path(o, ren1_dst, branch2);\n \t\t\t\t\toutput(o, 1, _(\"Adding as %s instead\"), new_path);\n-\t\t\t\t\tupdate_file(o, 0, dst_other.sha1, dst_other.mode, new_path);\n+\t\t\t\t\tif (update_file(o, 0, dst_other.sha1, dst_other.mode, new_path))\n+\t\t\t\t\t\tclean_merge = -1;\n \t\t\t\t\tfree(new_path);\n \t\t\t\t}\n \t\t\t} else\n \t\t\t\ttry_merge = 1;\n \n+\t\t\tif (clean_merge < 0)\n+\t\t\t\tgoto cleanup_and_return;\n \t\t\tif (try_merge) {\n \t\t\t\tstruct diff_filespec *one, *a, *b;\n \t\t\t\tsrc_other.path = (char *)ren1_src;\n@@ -1509,6 +1537,7 @@ static int process_renames(struct merge_options *o,\n \t\t\t}\n \t\t}\n \t}\n+cleanup_and_return:\n \tstring_list_clear(&a_by_dst, 0);\n \tstring_list_clear(&b_by_dst, 0);\n \n@@ -1571,13 +1600,13 @@ error_return:\n \treturn ret;\n }\n \n-static void handle_modify_delete(struct merge_options *o,\n+static int handle_modify_delete(struct merge_options *o,\n \t\t\t\t const char *path,\n \t\t\t\t unsigned char *o_sha, int o_mode,\n \t\t\t\t unsigned char *a_sha, int a_mode,\n \t\t\t\t unsigned char *b_sha, int b_mode)\n {\n-\thandle_change_delete(o,\n+\treturn handle_change_delete(o,\n \t\t\t     path,\n \t\t\t     o_sha, o_mode,\n \t\t\t     a_sha, a_mode,\n@@ -1656,7 +1685,8 @@ static int merge_content(struct merge_options *o,\n \t\toutput(o, 1, _(\"CONFLICT (%s): Merge conflict in %s\"),\n \t\t\t\treason, path);\n \t\tif (rename_conflict_info && !df_conflict_remains)\n-\t\t\tupdate_stages(path, &one, &a, &b);\n+\t\t\tif (update_stages(path, &one, &a, &b))\n+\t\t\t\treturn -1;\n \t}\n \n \tif (df_conflict_remains) {\n@@ -1664,30 +1694,33 @@ static int merge_content(struct merge_options *o,\n \t\tif (o->call_depth) {\n \t\t\tremove_file_from_cache(path);\n \t\t} else {\n-\t\t\tif (!mfi.clean)\n-\t\t\t\tupdate_stages(path, &one, &a, &b);\n-\t\t\telse {\n+\t\t\tif (!mfi.clean) {\n+\t\t\t\tif (update_stages(path, &one, &a, &b))\n+\t\t\t\t\treturn -1;\n+\t\t\t} else {\n \t\t\t\tint file_from_stage2 = was_tracked(path);\n \t\t\t\tstruct diff_filespec merged;\n \t\t\t\thashcpy(merged.sha1, mfi.sha);\n \t\t\t\tmerged.mode = mfi.mode;\n \n-\t\t\t\tupdate_stages(path, NULL,\n+\t\t\t\tif (update_stages(path, NULL,\n \t\t\t\t\t      file_from_stage2 ? &merged : NULL,\n-\t\t\t\t\t      file_from_stage2 ? NULL : &merged);\n+\t\t\t\t\t      file_from_stage2 ? NULL : &merged))\n+\t\t\t\t\treturn -1;\n \t\t\t}\n \n \t\t}\n \t\tnew_path = unique_path(o, path, rename_conflict_info->branch1);\n \t\toutput(o, 1, _(\"Adding as %s instead\"), new_path);\n-\t\tupdate_file(o, 0, mfi.sha, mfi.mode, new_path);\n+\t\tif (update_file(o, 0, mfi.sha, mfi.mode, new_path)) {\n+\t\t\tfree(new_path);\n+\t\t\treturn -1;\n+\t\t}\n \t\tfree(new_path);\n \t\tmfi.clean = 0;\n-\t} else {\n-\t\tupdate_file(o, mfi.clean, mfi.sha, mfi.mode, path);\n-\t}\n+\t} else if (update_file(o, mfi.clean, mfi.sha, mfi.mode, path))\n+\t\treturn -1;\n \treturn mfi.clean;\n-\n }\n \n /* Per entry merge function */\n@@ -1715,17 +1748,21 @@ static int process_entry(struct merge_options *o,\n \t\t\tbreak;\n \t\tcase RENAME_DELETE:\n \t\t\tclean_merge = 0;\n-\t\t\tconflict_rename_delete(o, conflict_info->pair1,\n+\t\t\tif (conflict_rename_delete(o,\n+\t\t\t\t\t       conflict_info->pair1,\n \t\t\t\t\t       conflict_info->branch1,\n-\t\t\t\t\t       conflict_info->branch2);\n+\t\t\t\t\t       conflict_info->branch2))\n+\t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_ONE_FILE_TO_TWO:\n \t\t\tclean_merge = 0;\n-\t\t\tconflict_rename_rename_1to2(o, conflict_info);\n+\t\t\tif (conflict_rename_rename_1to2(o, conflict_info))\n+\t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_TWO_FILES_TO_ONE:\n \t\t\tclean_merge = 0;\n-\t\t\tconflict_rename_rename_2to1(o, conflict_info);\n+\t\t\tif (conflict_rename_rename_2to1(o, conflict_info))\n+\t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tdefault:\n \t\t\tentry->processed = 0;\n@@ -1745,8 +1782,9 @@ static int process_entry(struct merge_options *o,\n \t\t} else {\n \t\t\t/* Modify/delete; deleted side may have put a directory in the way */\n \t\t\tclean_merge = 0;\n-\t\t\thandle_modify_delete(o, path, o_sha, o_mode,\n-\t\t\t\t\t     a_sha, a_mode, b_sha, b_mode);\n+\t\t\tif (handle_modify_delete(o, path, o_sha, o_mode,\n+\t\t\t\t\t\t a_sha, a_mode, b_sha, b_mode))\n+\t\t\t\tclean_merge = -1;\n \t\t}\n \t} else if ((!o_sha && a_sha && !b_sha) ||\n \t\t   (!o_sha && !a_sha && b_sha)) {\n@@ -1778,14 +1816,16 @@ static int process_entry(struct merge_options *o,\n \t\t\toutput(o, 1, _(\"CONFLICT (%s): There is a directory with name %s in %s. \"\n \t\t\t       \"Adding %s as %s\"),\n \t\t\t       conf, path, other_branch, path, new_path);\n-\t\t\tupdate_file(o, 0, sha, mode, new_path);\n-\t\t\tif (o->call_depth)\n+\t\t\tif (update_file(o, 0, sha, mode, new_path))\n+\t\t\t\tclean_merge = -1;\n+\t\t\telse if (o->call_depth)\n \t\t\t\tremove_file_from_cache(path);\n \t\t\tfree(new_path);\n \t\t} else {\n \t\t\toutput(o, 2, _(\"Adding %s\"), path);\n \t\t\t/* do not overwrite file if already present */\n-\t\t\tupdate_file_flags(o, sha, mode, path, 1, !a_sha);\n+\t\t\tif (update_file_flags(o, sha, mode, path, 1, !a_sha))\n+\t\t\t\tclean_merge = -1;\n \t\t}\n \t} else if (a_sha && b_sha) {\n \t\t/* Case C: Added in both (check for same permissions) and */\n@@ -1848,12 +1888,18 @@ int merge_trees(struct merge_options *o,\n \t\tre_head  = get_renames(o, head, common, head, merge, entries);\n \t\tre_merge = get_renames(o, merge, common, head, merge, entries);\n \t\tclean = process_renames(o, re_head, re_merge);\n+\t\tif (clean < 0)\n+\t\t\treturn clean;\n \t\tfor (i = entries->nr-1; 0 <= i; i--) {\n \t\t\tconst char *path = entries->items[i].string;\n \t\t\tstruct stage_data *e = entries->items[i].util;\n-\t\t\tif (!e->processed\n-\t\t\t\t&& !process_entry(o, path, e))\n-\t\t\t\tclean = 0;\n+\t\t\tif (!e->processed) {\n+\t\t\t\tint ret = process_entry(o, path, e);\n+\t\t\t\tif (!ret)\n+\t\t\t\t\tclean = 0;\n+\t\t\t\telse if (ret < 0)\n+\t\t\t\t\treturn ret;\n+\t\t\t}\n \t\t}\n \t\tfor (i = 0; i < entries->nr; i++) {\n \t\t\tstruct stage_data *e = entries->items[i].util;\n-- \n2.9.0.280.g32e2a70\n\n\n"},{"id":"290824","messageId":"0e54b56c7cd04e9f58ebf886c4f3d536f989a36c.1467717730.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467717729.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 08/17] merge-recursive: allow write_tree_from_memory() to error out","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-05T11:23:42Z","receivedAt":"2016-07-05T11:24:29Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It is possible that a tree cannot be written (think: disk full). We\nwill want to give the caller a chance to clean up instead of letting\nthe program die() in such a case.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 3e3667f..99f4202 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1873,8 +1873,8 @@ int merge_trees(struct merge_options *o,\n \telse\n \t\tclean = 1;\n \n-\tif (o->call_depth)\n-\t\t*result = write_tree_from_memory(o);\n+\tif (o->call_depth && !(*result = write_tree_from_memory(o)))\n+\t\treturn -1;\n \n \treturn clean;\n }\n-- \n2.9.0.280.g32e2a70\n\n\n"},{"id":"290825","messageId":"109a08b8c31d35cfbb5e82e3016446a2a5f774b3.1467717730.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467717729.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 10/17] merge-recursive: switch to returning errors instead of dying","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-05T11:23:53Z","receivedAt":"2016-07-05T11:24:32Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The recursive merge machinery is supposed to be a library function, i.e.\nit should return an error when it fails. Originally the functions were\npart of the builtin \"merge-recursive\", though, where it was simpler to\ncall die() and be done with error handling.\n\nThe existing callers were already prepared to detect negative return\nvalues to indicate errors and to behave as previously: exit with code 128\n(which is the same thing that die() does, after printing the message).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 59 +++++++++++++++++++++++++++++++------------------------\n 1 file changed, 33 insertions(+), 26 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 209427c..10010a4 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -266,8 +266,10 @@ struct tree *write_tree_from_memory(struct merge_options *o)\n \t\tactive_cache_tree = cache_tree();\n \n \tif (!cache_tree_fully_valid(active_cache_tree) &&\n-\t    cache_tree_update(&the_index, 0) < 0)\n-\t\tdie(_(\"error building trees\"));\n+\t    cache_tree_update(&the_index, 0) < 0) {\n+\t\terror(_(\"error building trees\"));\n+\t\treturn NULL;\n+\t}\n \n \tresult = lookup_tree(active_cache_tree->sha1);\n \n@@ -708,12 +710,10 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \t/* Make sure leading directories are created */\n \tstatus = safe_create_leading_directories_const(path);\n \tif (status) {\n-\t\tif (status == SCLD_EXISTS) {\n+\t\tif (status == SCLD_EXISTS)\n \t\t\t/* something else exists */\n-\t\t\terror(msg, path, _(\": perhaps a D/F conflict?\"));\n-\t\t\treturn -1;\n-\t\t}\n-\t\tdie(msg, path, \"\");\n+\t\t\treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n+\t\treturn error(msg, path, \"\");\n \t}\n \n \t/*\n@@ -741,6 +741,8 @@ static int update_file_flags(struct merge_options *o,\n \t\t\t      int update_cache,\n \t\t\t      int update_wd)\n {\n+\tint ret = 0;\n+\n \tif (o->call_depth)\n \t\tupdate_wd = 0;\n \n@@ -761,9 +763,11 @@ static int update_file_flags(struct merge_options *o,\n \n \t\tbuf = read_sha1_file(sha, &type, &size);\n \t\tif (!buf)\n-\t\t\tdie(_(\"cannot read object %s '%s'\"), sha1_to_hex(sha), path);\n-\t\tif (type != OBJ_BLOB)\n-\t\t\tdie(_(\"blob expected for %s '%s'\"), sha1_to_hex(sha), path);\n+\t\t\treturn error(_(\"cannot read object %s '%s'\"), sha1_to_hex(sha), path);\n+\t\tif (type != OBJ_BLOB) {\n+\t\t\tret = error(_(\"blob expected for %s '%s'\"), sha1_to_hex(sha), path);\n+\t\t\tgoto free_buf;\n+\t\t}\n \t\tif (S_ISREG(mode)) {\n \t\t\tstruct strbuf strbuf = STRBUF_INIT;\n \t\t\tif (convert_to_working_tree(path, buf, size, &strbuf)) {\n@@ -784,8 +788,10 @@ static int update_file_flags(struct merge_options *o,\n \t\t\telse\n \t\t\t\tmode = 0666;\n \t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n-\t\t\tif (fd < 0)\n-\t\t\t\tdie_errno(_(\"failed to open '%s'\"), path);\n+\t\t\tif (fd < 0) {\n+\t\t\t\tret = error_errno(_(\"failed to open '%s'\"), path);\n+\t\t\t\tgoto free_buf;\n+\t\t\t}\n \t\t\twrite_in_full(fd, buf, size);\n \t\t\tclose(fd);\n \t\t} else if (S_ISLNK(mode)) {\n@@ -793,18 +799,18 @@ static int update_file_flags(struct merge_options *o,\n \t\t\tsafe_create_leading_directories_const(path);\n \t\t\tunlink(path);\n \t\t\tif (symlink(lnk, path))\n-\t\t\t\tdie_errno(_(\"failed to symlink '%s'\"), path);\n+\t\t\t\tret = error_errno(_(\"failed to symlink '%s'\"), path);\n \t\t\tfree(lnk);\n \t\t} else\n-\t\t\tdie(_(\"do not know what to do with %06o %s '%s'\"),\n-\t\t\t    mode, sha1_to_hex(sha), path);\n+\t\t\tret = error(_(\"do not know what to do with %06o %s '%s'\"),\n+\t\t\t\tmode, sha1_to_hex(sha), path);\n  free_buf:\n \t\tfree(buf);\n \t}\n  update_index:\n-\tif (update_cache)\n+\tif (!ret && update_cache)\n \t\tadd_cacheinfo(mode, sha, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n-\treturn 0;\n+\treturn ret;\n }\n \n static int update_file(struct merge_options *o,\n@@ -930,20 +936,22 @@ static int merge_file_1(struct merge_options *o,\n \t\t\thashcpy(result->sha, a->sha1);\n \t\telse if (S_ISREG(a->mode)) {\n \t\t\tmmbuffer_t result_buf;\n-\t\t\tint merge_status;\n+\t\t\tint ret = 0, merge_status;\n \n \t\t\tmerge_status = merge_3way(o, &result_buf, one, a, b,\n \t\t\t\t\t\t  branch1, branch2);\n \n \t\t\tif ((merge_status < 0) || !result_buf.ptr)\n-\t\t\t\tdie(_(\"Failed to execute internal merge\"));\n+\t\t\t\tret = error(_(\"Failed to execute internal merge\"));\n \n-\t\t\tif (write_sha1_file(result_buf.ptr, result_buf.size,\n+\t\t\tif (!ret && write_sha1_file(result_buf.ptr, result_buf.size,\n \t\t\t\t\t    blob_type, result->sha))\n-\t\t\t\tdie(_(\"Unable to add %s to database\"),\n-\t\t\t\t    a->path);\n+\t\t\t\tret = error(_(\"Unable to add %s to database\"),\n+\t\t\t\t\ta->path);\n \n \t\t\tfree(result_buf.ptr);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n \t\t\tresult->clean = (merge_status == 0);\n \t\t} else if (S_ISGITLINK(a->mode)) {\n \t\t\tresult->clean = merge_submodule(result->sha,\n@@ -1868,11 +1876,10 @@ int merge_trees(struct merge_options *o,\n \n \tif (code != 0) {\n \t\tif (show(o, 4) || o->call_depth)\n-\t\t\tdie(_(\"merging of trees %s and %s failed\"),\n+\t\t\terror(_(\"merging of trees %s and %s failed\"),\n \t\t\t    oid_to_hex(&head->object.oid),\n \t\t\t    oid_to_hex(&merge->object.oid));\n-\t\telse\n-\t\t\texit(128);\n+\t\treturn -1;\n \t}\n \n \tif (unmerged_cache()) {\n@@ -2003,7 +2010,7 @@ int merge_recursive(struct merge_options *o,\n \t\to->call_depth--;\n \n \t\tif (!merged_common_ancestors)\n-\t\t\tdie(_(\"merge returned no commit\"));\n+\t\t\treturn error(_(\"merge returned no commit\"));\n \t}\n \n \tdiscard_cache();\n-- \n2.9.0.280.g32e2a70\n\n\n"},{"id":"290826","messageId":"6f759e9860cc423e6aff57af58ecd7d4c79e6d0f.1467717730.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467717729.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 12/17] am -3: use merge_recursive() directly again","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-05T11:24:01Z","receivedAt":"2016-07-05T11:24:33Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Last October, we had to change this code to run `git merge-recursive`\nin a child process: git-am wants to print some helpful advice when the\nmerge failed, but the code in question was not prepared to return, it\ndie()d instead.\n\nWe are finally at a point when the code *is* prepared to return errors,\nand can avoid the child process again.\n\nThis reverts commit c63d4b2 (am -3: do not let failed merge from\ncompleting the error codepath, 2015-10-09).\n\nNote: the code now calls merge_recursive_generic() again. Unlike\nmerge_trees() and merge_recursive(), this function returns 0 upon success,\nas most of Git's functions. Therefore, the error value -1 naturally is\nhandled correctly, and we do not have to take care of it specifically.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/am.c | 49 ++++++++++++++++---------------------------------\n 1 file changed, 16 insertions(+), 33 deletions(-)\n\ndiff --git a/builtin/am.c b/builtin/am.c\nindex f07f89a..be652f9 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -1578,44 +1578,16 @@ static int build_fake_ancestor(const struct am_state *state, const char *index_f\n }\n \n /**\n- * Do the three-way merge using fake ancestor, her tree constructed\n- * from the fake ancestor and the postimage of the patch, and our\n- * state.\n- */\n-static int run_fallback_merge_recursive(const struct am_state *state,\n-\t\t\t\t\tunsigned char *orig_tree,\n-\t\t\t\t\tunsigned char *our_tree,\n-\t\t\t\t\tunsigned char *her_tree)\n-{\n-\tstruct child_process cp = CHILD_PROCESS_INIT;\n-\tint status;\n-\n-\tcp.git_cmd = 1;\n-\n-\targv_array_pushf(&cp.env_array, \"GITHEAD_%s=%.*s\",\n-\t\t\t sha1_to_hex(her_tree), linelen(state->msg), state->msg);\n-\tif (state->quiet)\n-\t\targv_array_push(&cp.env_array, \"GIT_MERGE_VERBOSITY=0\");\n-\n-\targv_array_push(&cp.args, \"merge-recursive\");\n-\targv_array_push(&cp.args, sha1_to_hex(orig_tree));\n-\targv_array_push(&cp.args, \"--\");\n-\targv_array_push(&cp.args, sha1_to_hex(our_tree));\n-\targv_array_push(&cp.args, sha1_to_hex(her_tree));\n-\n-\tstatus = run_command(&cp) ? (-1) : 0;\n-\tdiscard_cache();\n-\tread_cache();\n-\treturn status;\n-}\n-\n-/**\n  * Attempt a threeway merge, using index_path as the temporary index.\n  */\n static int fall_back_threeway(const struct am_state *state, const char *index_path)\n {\n \tunsigned char orig_tree[GIT_SHA1_RAWSZ], her_tree[GIT_SHA1_RAWSZ],\n \t\t      our_tree[GIT_SHA1_RAWSZ];\n+\tconst unsigned char *bases[1] = {orig_tree};\n+\tstruct merge_options o;\n+\tstruct commit *result;\n+\tchar *her_tree_name;\n \n \tif (get_sha1(\"HEAD\", our_tree) < 0)\n \t\thashcpy(our_tree, EMPTY_TREE_SHA1_BIN);\n@@ -1667,11 +1639,22 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \t * changes.\n \t */\n \n-\tif (run_fallback_merge_recursive(state, orig_tree, our_tree, her_tree)) {\n+\tinit_merge_options(&o);\n+\n+\to.branch1 = \"HEAD\";\n+\ther_tree_name = xstrfmt(\"%.*s\", linelen(state->msg), state->msg);\n+\to.branch2 = her_tree_name;\n+\n+\tif (state->quiet)\n+\t\to.verbosity = 0;\n+\n+\tif (merge_recursive_generic(&o, our_tree, her_tree, 1, bases, &result)) {\n \t\trerere(state->allow_rerere_autoupdate);\n+\t\tfree(her_tree_name);\n \t\treturn error(_(\"Failed to merge in the changes.\"));\n \t}\n \n+\tfree(her_tree_name);\n \treturn 0;\n }\n \n-- \n2.9.0.280.g32e2a70\n\n\n"},{"id":"290827","messageId":"ea23faf258b6e62e770879362869f49eea4db869.1467717730.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467717729.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 11/17] am: counteract gender bias","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-05T11:23:57Z","receivedAt":"2016-07-05T11:24:34Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Since d1c5f2a (Add git-am, applymbox replacement., 2005-10-07), i.e. for\nalmost 11 years already, we demonstrated our disrespect to the pioneers\nof software development like Ada Lovelace, Grace Hopper and Margaret\nHamilton, by pretending that each and every software developer is male\n(\"his_tree\"). It appears almost as if we weren't fully aware that the\nfirst professional software developers were all female.\n\nWe know our field to have this unfortunate gender bias that has nothing\nto do with qualification or biological reasons, and we are very sad\nabout the current gender imbalance of the Git developer community.\n\nLet's start changing that by using the variable name \"her_tree\" for an\nequal number of years out of fairness, and change to the gender neutral\n\"their_tree\" after that.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/am.c | 16 ++++++++--------\n 1 file changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/am.c b/builtin/am.c\nindex 3dfe70b..f07f89a 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -1578,14 +1578,14 @@ static int build_fake_ancestor(const struct am_state *state, const char *index_f\n }\n \n /**\n- * Do the three-way merge using fake ancestor, his tree constructed\n+ * Do the three-way merge using fake ancestor, her tree constructed\n  * from the fake ancestor and the postimage of the patch, and our\n  * state.\n  */\n static int run_fallback_merge_recursive(const struct am_state *state,\n \t\t\t\t\tunsigned char *orig_tree,\n \t\t\t\t\tunsigned char *our_tree,\n-\t\t\t\t\tunsigned char *his_tree)\n+\t\t\t\t\tunsigned char *her_tree)\n {\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n \tint status;\n@@ -1593,7 +1593,7 @@ static int run_fallback_merge_recursive(const struct am_state *state,\n \tcp.git_cmd = 1;\n \n \targv_array_pushf(&cp.env_array, \"GITHEAD_%s=%.*s\",\n-\t\t\t sha1_to_hex(his_tree), linelen(state->msg), state->msg);\n+\t\t\t sha1_to_hex(her_tree), linelen(state->msg), state->msg);\n \tif (state->quiet)\n \t\targv_array_push(&cp.env_array, \"GIT_MERGE_VERBOSITY=0\");\n \n@@ -1601,7 +1601,7 @@ static int run_fallback_merge_recursive(const struct am_state *state,\n \targv_array_push(&cp.args, sha1_to_hex(orig_tree));\n \targv_array_push(&cp.args, \"--\");\n \targv_array_push(&cp.args, sha1_to_hex(our_tree));\n-\targv_array_push(&cp.args, sha1_to_hex(his_tree));\n+\targv_array_push(&cp.args, sha1_to_hex(her_tree));\n \n \tstatus = run_command(&cp) ? (-1) : 0;\n \tdiscard_cache();\n@@ -1614,7 +1614,7 @@ static int run_fallback_merge_recursive(const struct am_state *state,\n  */\n static int fall_back_threeway(const struct am_state *state, const char *index_path)\n {\n-\tunsigned char orig_tree[GIT_SHA1_RAWSZ], his_tree[GIT_SHA1_RAWSZ],\n+\tunsigned char orig_tree[GIT_SHA1_RAWSZ], her_tree[GIT_SHA1_RAWSZ],\n \t\t      our_tree[GIT_SHA1_RAWSZ];\n \n \tif (get_sha1(\"HEAD\", our_tree) < 0)\n@@ -1651,7 +1651,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \t\treturn error(_(\"Did you hand edit your patch?\\n\"\n \t\t\t\t\"It does not apply to blobs recorded in its index.\"));\n \n-\tif (write_index_as_tree(his_tree, &the_index, index_path, 0, NULL))\n+\tif (write_index_as_tree(her_tree, &the_index, index_path, 0, NULL))\n \t\treturn error(\"could not write tree\");\n \n \tsay(state, stdout, _(\"Falling back to patching base and 3-way merge...\"));\n@@ -1661,13 +1661,13 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \n \t/*\n \t * This is not so wrong. Depending on which base we picked, orig_tree\n-\t * may be wildly different from ours, but his_tree has the same set of\n+\t * may be wildly different from ours, but her_tree has the same set of\n \t * wildly different changes in parts the patch did not touch, so\n \t * recursive ends up canceling them, saying that we reverted all those\n \t * changes.\n \t */\n \n-\tif (run_fallback_merge_recursive(state, orig_tree, our_tree, his_tree)) {\n+\tif (run_fallback_merge_recursive(state, orig_tree, our_tree, her_tree)) {\n \t\trerere(state->allow_rerere_autoupdate);\n \t\treturn error(_(\"Failed to merge in the changes.\"));\n \t}\n-- \n2.9.0.280.g32e2a70\n\n\n"},{"id":"290828","messageId":"83aea04d1fdceccdfd79f30cfb03671eb1303c0b.1467717730.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467717729.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 13/17] merge-recursive: flush output buffer before printing error messages","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-05T11:24:06Z","receivedAt":"2016-07-05T11:24:36Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The data structure passed to the recursive merge machinery has a feature\nwhere the caller can ask for the output to be buffered into a strbuf, by\nsetting the field 'buffer_output'.\n\nPreviously, we simply swallowed the buffered output when showing error\nmessages. With this patch, we show the output first, and only then print\nthe error message.\n\nCurrently, the only user of that buffering is merge_recursive() itself,\nto avoid the progress output to interfere.\n\nIn the next patches, we will introduce a new buffer_output mode that\nforces merge_recursive() to retain the output buffer for further\nprocessing by the caller. If the caller asked for that, we will then\nalso write the error messages into the output buffer. This is necessary\nto give the caller more control not only how to react in case of errors\nbut also control how/if to display the error messages.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 110 ++++++++++++++++++++++++++++++++----------------------\n 1 file changed, 65 insertions(+), 45 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 10010a4..0eb23a6 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -23,6 +23,28 @@\n #include \"dir.h\"\n #include \"submodule.h\"\n \n+static void flush_output(struct merge_options *o)\n+{\n+\tif (o->obuf.len) {\n+\t\tfputs(o->obuf.buf, stdout);\n+\t\tstrbuf_reset(&o->obuf);\n+\t}\n+}\n+\n+static int err(struct merge_options *o, const char *err, ...)\n+{\n+\tva_list params;\n+\n+\tva_start(params, err);\n+\tflush_output(o);\n+\tstrbuf_vaddf(&o->obuf, err, params);\n+\terror(\"%s\", o->obuf.buf);\n+\tstrbuf_reset(&o->obuf);\n+\tva_end(params);\n+\n+\treturn -1;\n+}\n+\n static struct tree *shift_tree_object(struct tree *one, struct tree *two,\n \t\t\t\t      const char *subtree_shift)\n {\n@@ -148,14 +170,6 @@ static int show(struct merge_options *o, int v)\n \treturn (!o->call_depth && o->verbosity >= v) || o->verbosity >= 5;\n }\n \n-static void flush_output(struct merge_options *o)\n-{\n-\tif (o->obuf.len) {\n-\t\tfputs(o->obuf.buf, stdout);\n-\t\tstrbuf_reset(&o->obuf);\n-\t}\n-}\n-\n __attribute__((format (printf, 3, 4)))\n static void output(struct merge_options *o, int v, const char *fmt, ...)\n {\n@@ -198,7 +212,8 @@ static void output_commit_title(struct merge_options *o, struct commit *commit)\n \t}\n }\n \n-static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,\n+static int add_cacheinfo(struct merge_options *o,\n+\t\tunsigned int mode, const unsigned char *sha1,\n \t\tconst char *path, int stage, int refresh, int options)\n {\n \tstruct cache_entry *ce;\n@@ -206,7 +221,7 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,\n \t\t\t      (refresh ? (CE_MATCH_REFRESH |\n \t\t\t\t\t  CE_MATCH_IGNORE_MISSING) : 0 ));\n \tif (!ce)\n-\t\treturn error(_(\"addinfo_cache failed for path '%s'\"), path);\n+\t\treturn err(o, _(\"addinfo_cache failed for path '%s'\"), path);\n \treturn add_cache_entry(ce, options);\n }\n \n@@ -267,7 +282,7 @@ struct tree *write_tree_from_memory(struct merge_options *o)\n \n \tif (!cache_tree_fully_valid(active_cache_tree) &&\n \t    cache_tree_update(&the_index, 0) < 0) {\n-\t\terror(_(\"error building trees\"));\n+\t\terr(o, _(\"error building trees\"));\n \t\treturn NULL;\n \t}\n \n@@ -535,7 +550,8 @@ static struct string_list *get_renames(struct merge_options *o,\n \treturn renames;\n }\n \n-static int update_stages(const char *path, const struct diff_filespec *o,\n+static int update_stages(struct merge_options *opt, const char *path,\n+\t\t\t const struct diff_filespec *o,\n \t\t\t const struct diff_filespec *a,\n \t\t\t const struct diff_filespec *b)\n {\n@@ -554,13 +570,13 @@ static int update_stages(const char *path, const struct diff_filespec *o,\n \t\tif (remove_file_from_cache(path))\n \t\t\treturn -1;\n \tif (o)\n-\t\tif (add_cacheinfo(o->mode, o->sha1, path, 1, 0, options))\n+\t\tif (add_cacheinfo(opt, o->mode, o->sha1, path, 1, 0, options))\n \t\t\treturn -1;\n \tif (a)\n-\t\tif (add_cacheinfo(a->mode, a->sha1, path, 2, 0, options))\n+\t\tif (add_cacheinfo(opt, a->mode, a->sha1, path, 2, 0, options))\n \t\t\treturn -1;\n \tif (b)\n-\t\tif (add_cacheinfo(b->mode, b->sha1, path, 3, 0, options))\n+\t\tif (add_cacheinfo(opt, b->mode, b->sha1, path, 3, 0, options))\n \t\t\treturn -1;\n \treturn 0;\n }\n@@ -712,8 +728,8 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \tif (status) {\n \t\tif (status == SCLD_EXISTS)\n \t\t\t/* something else exists */\n-\t\t\treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n-\t\treturn error(msg, path, \"\");\n+\t\t\treturn err(o, msg, path, _(\": perhaps a D/F conflict?\"));\n+\t\treturn err(o, msg, path, \"\");\n \t}\n \n \t/*\n@@ -721,7 +737,7 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \t * tracking it.\n \t */\n \tif (would_lose_untracked(path))\n-\t\treturn error(_(\"refusing to lose untracked file at '%s'\"),\n+\t\treturn err(o, _(\"refusing to lose untracked file at '%s'\"),\n \t\t\t     path);\n \n \t/* Successful unlink is good.. */\n@@ -731,7 +747,7 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \tif (errno == ENOENT)\n \t\treturn 0;\n \t/* .. but not some other error (who really cares what?) */\n-\treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n+\treturn err(o, msg, path, _(\": perhaps a D/F conflict?\"));\n }\n \n static int update_file_flags(struct merge_options *o,\n@@ -763,9 +779,9 @@ static int update_file_flags(struct merge_options *o,\n \n \t\tbuf = read_sha1_file(sha, &type, &size);\n \t\tif (!buf)\n-\t\t\treturn error(_(\"cannot read object %s '%s'\"), sha1_to_hex(sha), path);\n+\t\t\treturn err(o, _(\"cannot read object %s '%s'\"), sha1_to_hex(sha), path);\n \t\tif (type != OBJ_BLOB) {\n-\t\t\tret = error(_(\"blob expected for %s '%s'\"), sha1_to_hex(sha), path);\n+\t\t\tret = err(o, _(\"blob expected for %s '%s'\"), sha1_to_hex(sha), path);\n \t\t\tgoto free_buf;\n \t\t}\n \t\tif (S_ISREG(mode)) {\n@@ -789,7 +805,8 @@ static int update_file_flags(struct merge_options *o,\n \t\t\t\tmode = 0666;\n \t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n \t\t\tif (fd < 0) {\n-\t\t\t\tret = error_errno(_(\"failed to open '%s'\"), path);\n+\t\t\t\tret = err(o, _(\"failed to open '%s': %s\"),\n+\t\t\t\t\tpath, strerror(errno));\n \t\t\t\tgoto free_buf;\n \t\t\t}\n \t\t\twrite_in_full(fd, buf, size);\n@@ -799,17 +816,18 @@ static int update_file_flags(struct merge_options *o,\n \t\t\tsafe_create_leading_directories_const(path);\n \t\t\tunlink(path);\n \t\t\tif (symlink(lnk, path))\n-\t\t\t\tret = error_errno(_(\"failed to symlink '%s'\"), path);\n+\t\t\t\tret = err(o, _(\"failed to symlink '%s': %s\"),\n+\t\t\t\t\tpath, strerror(errno));\n \t\t\tfree(lnk);\n \t\t} else\n-\t\t\tret = error(_(\"do not know what to do with %06o %s '%s'\"),\n+\t\t\tret = err(o, _(\"do not know what to do with %06o %s '%s'\"),\n \t\t\t\tmode, sha1_to_hex(sha), path);\n  free_buf:\n \t\tfree(buf);\n \t}\n  update_index:\n \tif (!ret && update_cache)\n-\t\tadd_cacheinfo(mode, sha, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n+\t\tadd_cacheinfo(o, mode, sha, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n \treturn ret;\n }\n \n@@ -942,11 +960,11 @@ static int merge_file_1(struct merge_options *o,\n \t\t\t\t\t\t  branch1, branch2);\n \n \t\t\tif ((merge_status < 0) || !result_buf.ptr)\n-\t\t\t\tret = error(_(\"Failed to execute internal merge\"));\n+\t\t\t\tret = err(o, _(\"Failed to execute internal merge\"));\n \n \t\t\tif (!ret && write_sha1_file(result_buf.ptr, result_buf.size,\n \t\t\t\t\t    blob_type, result->sha))\n-\t\t\t\tret = error(_(\"Unable to add %s to database\"),\n+\t\t\t\tret = err(o, _(\"Unable to add %s to database\"),\n \t\t\t\t\ta->path);\n \n \t\t\tfree(result_buf.ptr);\n@@ -1109,7 +1127,7 @@ static int conflict_rename_delete(struct merge_options *o,\n \tif (o->call_depth)\n \t\treturn remove_file_from_cache(dest->path);\n \telse\n-\t\treturn update_stages(dest->path, NULL,\n+\t\treturn update_stages(o, dest->path, NULL,\n \t\t\t      rename_branch == o->branch1 ? dest : NULL,\n \t\t\t      rename_branch == o->branch1 ? NULL : dest);\n }\n@@ -1167,9 +1185,9 @@ static int handle_file(struct merge_options *o,\n \tif ((ret = update_file(o, 0, rename->sha1, rename->mode, dst_name)))\n \t\t; /* fall through, do allow dst_name to be released */\n \telse if (stage == 2)\n-\t\tret = update_stages(rename->path, NULL, rename, add);\n+\t\tret = update_stages(o, rename->path, NULL, rename, add);\n \telse\n-\t\tret = update_stages(rename->path, NULL, add, rename);\n+\t\tret = update_stages(o, rename->path, NULL, add, rename);\n \n \tif (dst_name != rename->path)\n \t\tfree(dst_name);\n@@ -1557,23 +1575,25 @@ static unsigned char *stage_sha(const unsigned char *sha, unsigned mode)\n \treturn (is_null_sha1(sha) || mode == 0) ? NULL: (unsigned char *)sha;\n }\n \n-static int read_sha1_strbuf(const unsigned char *sha1, struct strbuf *dst)\n+static int read_sha1_strbuf(struct merge_options *o,\n+\tconst unsigned char *sha1, struct strbuf *dst)\n {\n \tvoid *buf;\n \tenum object_type type;\n \tunsigned long size;\n \tbuf = read_sha1_file(sha1, &type, &size);\n \tif (!buf)\n-\t\treturn error(_(\"cannot read object %s\"), sha1_to_hex(sha1));\n+\t\treturn err(o, _(\"cannot read object %s\"), sha1_to_hex(sha1));\n \tif (type != OBJ_BLOB) {\n \t\tfree(buf);\n-\t\treturn error(_(\"object %s is not a blob\"), sha1_to_hex(sha1));\n+\t\treturn err(o, _(\"object %s is not a blob\"), sha1_to_hex(sha1));\n \t}\n \tstrbuf_attach(dst, buf, size, size + 1);\n \treturn 0;\n }\n \n-static int blob_unchanged(const unsigned char *o_sha,\n+static int blob_unchanged(struct merge_options *opt,\n+\t\t\t  const unsigned char *o_sha,\n \t\t\t  unsigned o_mode,\n \t\t\t  const unsigned char *a_sha,\n \t\t\t  unsigned a_mode,\n@@ -1591,7 +1611,7 @@ static int blob_unchanged(const unsigned char *o_sha,\n \t\treturn 0;\n \n \tassert(o_sha && a_sha);\n-\tif (read_sha1_strbuf(o_sha, &o) || read_sha1_strbuf(a_sha, &a))\n+\tif (read_sha1_strbuf(opt, o_sha, &o) || read_sha1_strbuf(opt, a_sha, &a))\n \t\tgoto error_return;\n \t/*\n \t * Note: binary | is used so that both renormalizations are\n@@ -1680,7 +1700,7 @@ static int merge_content(struct merge_options *o,\n \t\t */\n \t\tpath_renamed_outside_HEAD = !path2 || !strcmp(path, path2);\n \t\tif (!path_renamed_outside_HEAD) {\n-\t\t\tadd_cacheinfo(mfi.mode, mfi.sha, path,\n+\t\t\tadd_cacheinfo(o, mfi.mode, mfi.sha, path,\n \t\t\t\t      0, (!o->call_depth), 0);\n \t\t\treturn mfi.clean;\n \t\t}\n@@ -1693,7 +1713,7 @@ static int merge_content(struct merge_options *o,\n \t\toutput(o, 1, _(\"CONFLICT (%s): Merge conflict in %s\"),\n \t\t\t\treason, path);\n \t\tif (rename_conflict_info && !df_conflict_remains)\n-\t\t\tif (update_stages(path, &one, &a, &b))\n+\t\t\tif (update_stages(o, path, &one, &a, &b))\n \t\t\t\treturn -1;\n \t}\n \n@@ -1703,7 +1723,7 @@ static int merge_content(struct merge_options *o,\n \t\t\tremove_file_from_cache(path);\n \t\t} else {\n \t\t\tif (!mfi.clean) {\n-\t\t\t\tif (update_stages(path, &one, &a, &b))\n+\t\t\t\tif (update_stages(o, path, &one, &a, &b))\n \t\t\t\t\treturn -1;\n \t\t\t} else {\n \t\t\t\tint file_from_stage2 = was_tracked(path);\n@@ -1711,7 +1731,7 @@ static int merge_content(struct merge_options *o,\n \t\t\t\thashcpy(merged.sha1, mfi.sha);\n \t\t\t\tmerged.mode = mfi.mode;\n \n-\t\t\t\tif (update_stages(path, NULL,\n+\t\t\t\tif (update_stages(o, path, NULL,\n \t\t\t\t\t      file_from_stage2 ? &merged : NULL,\n \t\t\t\t\t      file_from_stage2 ? NULL : &merged))\n \t\t\t\t\treturn -1;\n@@ -1779,8 +1799,8 @@ static int process_entry(struct merge_options *o,\n \t} else if (o_sha && (!a_sha || !b_sha)) {\n \t\t/* Case A: Deleted in one */\n \t\tif ((!a_sha && !b_sha) ||\n-\t\t    (!b_sha && blob_unchanged(o_sha, o_mode, a_sha, a_mode, normalize, path)) ||\n-\t\t    (!a_sha && blob_unchanged(o_sha, o_mode, b_sha, b_mode, normalize, path))) {\n+\t\t    (!b_sha && blob_unchanged(o, o_sha, o_mode, a_sha, a_mode, normalize, path)) ||\n+\t\t    (!a_sha && blob_unchanged(o, o_sha, o_mode, b_sha, b_mode, normalize, path))) {\n \t\t\t/* Deleted in both or deleted in one and\n \t\t\t * unchanged in the other */\n \t\t\tif (a_sha)\n@@ -1876,7 +1896,7 @@ int merge_trees(struct merge_options *o,\n \n \tif (code != 0) {\n \t\tif (show(o, 4) || o->call_depth)\n-\t\t\terror(_(\"merging of trees %s and %s failed\"),\n+\t\t\terr(o, _(\"merging of trees %s and %s failed\"),\n \t\t\t    oid_to_hex(&head->object.oid),\n \t\t\t    oid_to_hex(&merge->object.oid));\n \t\treturn -1;\n@@ -2010,7 +2030,7 @@ int merge_recursive(struct merge_options *o,\n \t\to->call_depth--;\n \n \t\tif (!merged_common_ancestors)\n-\t\t\treturn error(_(\"merge returned no commit\"));\n+\t\t\treturn err(o, _(\"merge returned no commit\"));\n \t}\n \n \tdiscard_cache();\n@@ -2069,7 +2089,7 @@ int merge_recursive_generic(struct merge_options *o,\n \t\tfor (i = 0; i < num_base_list; ++i) {\n \t\t\tstruct commit *base;\n \t\t\tif (!(base = get_ref(base_list[i], sha1_to_hex(base_list[i]))))\n-\t\t\t\treturn error(_(\"Could not parse object '%s'\"),\n+\t\t\t\treturn err(o, _(\"Could not parse object '%s'\"),\n \t\t\t\t\tsha1_to_hex(base_list[i]));\n \t\t\tcommit_list_insert(base, &ca);\n \t\t}\n@@ -2083,7 +2103,7 @@ int merge_recursive_generic(struct merge_options *o,\n \n \tif (active_cache_changed &&\n \t    write_locked_index(&the_index, lock, COMMIT_LOCK))\n-\t\treturn error(_(\"Unable to write index.\"));\n+\t\treturn err(o, _(\"Unable to write index.\"));\n \n \treturn clean ? 0 : 1;\n }\n-- \n2.9.0.280.g32e2a70\n\n\n"},{"id":"290829","messageId":"0bc1729b4098c3d5b97be5cf7cd09a0c03352edd.1467717730.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467717729.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 14/17] merge-recursive: write the commit title in one go","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-05T11:24:10Z","receivedAt":"2016-07-05T11:24:40Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"In 66a155b (Enable output buffering in merge-recursive., 2007-01-14), we\nchanged the code such that it prints the output in one go, to avoid\ninterfering with the progress output.\n\nLet's make sure that the same holds true when outputting the commit\ntitle: previously, we used several printf() statements to stdout and\nspeculated that stdout's buffer is large enough to hold the entire\ncommit title.\n\nApart from making that speculation unnecessary, we change the code to\nadd the message to the output buffer before flushing for another reason:\nthe next commit will introduce a new level of output buffering, where\nthe caller can request the output not to be flushed, but to be retained\nfor further processing.\n\nThis latter feature will be needed when teaching the sequencer to do\nrebase -i's brunt work: it wants to control the output of the\ncherry-picks (i.e. recursive merges).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 17 +++++++++--------\n 1 file changed, 9 insertions(+), 8 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 0eb23a6..81836f2 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -191,25 +191,26 @@ static void output(struct merge_options *o, int v, const char *fmt, ...)\n \n static void output_commit_title(struct merge_options *o, struct commit *commit)\n {\n-\tint i;\n-\tflush_output(o);\n-\tfor (i = o->call_depth; i--;)\n-\t\tfputs(\"  \", stdout);\n+\tstrbuf_addchars(&o->obuf, ' ', o->call_depth * 2);\n \tif (commit->util)\n-\t\tprintf(\"virtual %s\\n\", merge_remote_util(commit)->name);\n+\t\tstrbuf_addf(&o->obuf, \"virtual %s\\n\",\n+\t\t\tmerge_remote_util(commit)->name);\n \telse {\n-\t\tprintf(\"%s \", find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV));\n+\t\tstrbuf_addf(&o->obuf, \"%s \",\n+\t\t\tfind_unique_abbrev(commit->object.oid.hash,\n+\t\t\t\tDEFAULT_ABBREV));\n \t\tif (parse_commit(commit) != 0)\n-\t\t\tprintf(_(\"(bad commit)\\n\"));\n+\t\t\tstrbuf_addf(&o->obuf, _(\"(bad commit)\\n\"));\n \t\telse {\n \t\t\tconst char *title;\n \t\t\tconst char *msg = get_commit_buffer(commit, NULL);\n \t\t\tint len = find_commit_subject(msg, &title);\n \t\t\tif (len)\n-\t\t\t\tprintf(\"%.*s\\n\", len, title);\n+\t\t\t\tstrbuf_addf(&o->obuf, \"%.*s\\n\", len, title);\n \t\t\tunuse_commit_buffer(commit, msg);\n \t\t}\n \t}\n+\tflush_output(o);\n }\n \n static int add_cacheinfo(struct merge_options *o,\n-- \n2.9.0.280.g32e2a70\n\n\n"},{"id":"290830","messageId":"d4dba1424f5c4c4c735975606d041043c1997b37.1467717730.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467717729.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 16/17] Ensure that the output buffer is released after calling merge_trees()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-05T11:24:17Z","receivedAt":"2016-07-05T11:24:42Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The recursive merge machinery accumulates its output in an output\nbuffer, to be flushed at the end of merge_recursive(). At this point,\nwe forgot to release the output buffer.\n\nWhen calling merge_trees() (i.e. the non-recursive part of the recursive\nmerge) directly, the output buffer is never flushed because the caller\nmay be merge_recursive() which wants to flush the output itself.\n\nFor the same reason, merge_trees() cannot release the output buffer: it\nmay still be needed.\n\nForgetting to release the output buffer did not matter much when running\ngit-checkout, or git-merge-recursive, because we exited after the\noperation anyway. Ever since cherry-pick learned to pick a commit range,\nhowever, this memory leak had the potential of becoming a problem.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/checkout.c | 1 +\n merge-recursive.c  | 2 ++\n sequencer.c        | 1 +\n 3 files changed, 4 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 14312f7..ced4ac4 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -573,6 +573,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\t\texit(128);\n \t\t\tret = reset_tree(new->commit->tree, opts, 0,\n \t\t\t\t\t writeout_error);\n+\t\t\tstrbuf_release(&o.obuf);\n \t\t\tif (ret)\n \t\t\t\treturn ret;\n \t\t}\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 29cbdac..fdc624a 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -2059,6 +2059,8 @@ int merge_recursive(struct merge_options *o,\n \t\tcommit_list_insert(h2, &(*result)->parents->next);\n \t}\n \tflush_output(o);\n+\tif (o->buffer_output < 2)\n+\t\tstrbuf_release(&o->obuf);\n \tif (show(o, 2))\n \t\tdiff_warn_rename_limit(\"merge.renamelimit\",\n \t\t\t\t       o->needed_rename_limit, 0);\ndiff --git a/sequencer.c b/sequencer.c\nindex 13b794a..8ceeb1b 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -293,6 +293,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \tclean = merge_trees(&o,\n \t\t\t    head_tree,\n \t\t\t    next_tree, base_tree, &result);\n+\tstrbuf_release(&o.obuf);\n \tif (clean < 0)\n \t\treturn clean;\n \n-- \n2.9.0.280.g32e2a70\n\n\n"},{"id":"290831","messageId":"8e821762433961dad0794c39f90adf30baea7d62.1467717730.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467717729.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 15/17] merge-recursive: offer an option to retain the output in 'obuf'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-05T11:24:13Z","receivedAt":"2016-07-05T11:24:44Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Since 66a155b (Enable output buffering in merge-recursive., 2007-01-14),\nwe already accumulate the output in a buffer. The idea was to avoid\ninterfering with the progress output that goes to stderr, which is\nunbuffered, when we write to stdout, which is buffered.\n\nWe extend that buffering to allow the caller to handle the output\n(possibly suppressing it). This will help us when extending the\nsequencer to do rebase -i's brunt work: it does not want the picks to\nprint anything by default but instead determine itself whether to print\nthe output or not.\n\nNote that we also redirect the error messages into the output buffer\nwhen the caller asked not to flush the output buffer, for two reasons:\n1) to retain the correct output order, and 2) to allow the caller to\nsuppress *all* output.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 17 +++++++++++++----\n merge-recursive.h |  2 +-\n 2 files changed, 14 insertions(+), 5 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 81836f2..29cbdac 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -25,7 +25,7 @@\n \n static void flush_output(struct merge_options *o)\n {\n-\tif (o->obuf.len) {\n+\tif (o->buffer_output < 2 && o->obuf.len) {\n \t\tfputs(o->obuf.buf, stdout);\n \t\tstrbuf_reset(&o->obuf);\n \t}\n@@ -36,10 +36,19 @@ static int err(struct merge_options *o, const char *err, ...)\n \tva_list params;\n \n \tva_start(params, err);\n-\tflush_output(o);\n+\tif (o->buffer_output < 2)\n+\t\tflush_output(o);\n+\telse {\n+\t\tstrbuf_complete(&o->obuf, '\\n');\n+\t\tstrbuf_addstr(&o->obuf, \"error: \");\n+\t}\n \tstrbuf_vaddf(&o->obuf, err, params);\n-\terror(\"%s\", o->obuf.buf);\n-\tstrbuf_reset(&o->obuf);\n+\tif (o->buffer_output > 1)\n+\t\tstrbuf_addch(&o->obuf, '\\n');\n+\telse {\n+\t\terror(\"%s\", o->obuf.buf);\n+\t\tstrbuf_reset(&o->obuf);\n+\t}\n \tva_end(params);\n \n \treturn -1;\ndiff --git a/merge-recursive.h b/merge-recursive.h\nindex 52f0201..407d4fc 100644\n--- a/merge-recursive.h\n+++ b/merge-recursive.h\n@@ -13,7 +13,7 @@ struct merge_options {\n \t\tMERGE_RECURSIVE_THEIRS\n \t} recursive_variant;\n \tconst char *subtree_shift;\n-\tunsigned buffer_output : 1;\n+\tunsigned buffer_output : 2; /* 1: output at end, 2: keep buffered */\n \tunsigned renormalize : 1;\n \tlong xdl_opts;\n \tint verbosity;\n-- \n2.9.0.280.g32e2a70\n\n\n"},{"id":"290832","messageId":"aa1ceae751000820b6f2f577646bb3db9f165bb0.1467717730.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467717729.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 17/17] merge-recursive: flush output buffer even when erroring out","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-05T11:24:25Z","receivedAt":"2016-07-05T11:24:47Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Ever since 66a155b (Enable output buffering in merge-recursive.,\n2007-01-14), we had a problem: When the merge failed in a fatal way, all\nregular output was swallowed because we called die() and did not get a\nchance to drain the output buffers.\n\nTo fix this, several modifications were necessary:\n\n- we needed to stop die()ing, to give callers a chance to do something\n  when an error occurred (in this case, flush the output buffers),\n\n- we needed to delay printing the error message so that the caller can\n  print the buffered output before that, and\n\n- we needed to make sure that the output buffers are flushed even when\n  the return value indicates an error.\n\nThe first two changes were introduced through earlier commits in this\npatch series, and this commit addresses the third one.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex fdc624a..d94f853 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -2050,6 +2050,7 @@ int merge_recursive(struct merge_options *o,\n \to->ancestor = \"merged common ancestors\";\n \tclean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree,\n \t\t\t    &mrtree);\n+\tflush_output(o);\n \tif (clean < 0)\n \t\treturn clean;\n \n@@ -2058,7 +2059,6 @@ int merge_recursive(struct merge_options *o,\n \t\tcommit_list_insert(h1, &(*result)->parents);\n \t\tcommit_list_insert(h2, &(*result)->parents->next);\n \t}\n-\tflush_output(o);\n \tif (o->buffer_output < 2)\n \t\tstrbuf_release(&o->obuf);\n \tif (show(o, 2))\n-- \n2.9.0.280.g32e2a70\n"},{"id":"290834","messageId":"alpine.DEB.2.20.1607051330220.8378@virtualbox","threadId":"42743","inReplyTo":"CACsJy8CobWYjpjkkaG=wFK+zUyF3Z9CtFku7eprnX=_08y6KpA@mail.gmail.com","subject":"Re: [PATCH 1/9] Report bugs consistently","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-05T11:32:00Z","receivedAt":"2016-07-05T11:32:28Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Duy,\n\nOn Sat, 2 Jul 2016, Duy Nguyen wrote:\n\n> You're changing the string and adding more work to translators. So\n> either leave the string untouched, or drop _().\n\nThanks. I addressed that concern in v2. Could you please now have a look at\nthe parts of the patch series which could possibly regress Git's\nfunctionality? I am quite a bit more interested in having extra pairs of\neyes look over those.\n\nThanks,\nDscho\n"},{"id":"290840","messageId":"577BB09E.5070801@gmail.com","threadId":"42743","inReplyTo":"fdb0efbeb0b41c0d9976b2d66df90d2366f81ca1.1467717729.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 02/17] Report bugs consistently","fromName":"Jakub Narębski","fromEmail":"jnareb@gmail.com","sentAt":"2016-07-05T13:05:34Z","receivedAt":"2016-07-05T13:06:23Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"W dniu 2016-07-05 o 13:23, Johannes Schindelin pisze:\n> diff --git a/builtin/ls-files.c b/builtin/ls-files.c\n> index f02e3d2..00ea91a 100644\n> --- a/builtin/ls-files.c\n> +++ b/builtin/ls-files.c\n> @@ -118,7 +118,8 @@ static void show_killed_files(struct dir_struct *dir)\n>  \t\t\t\t */\n>  \t\t\t\tpos = cache_name_pos(ent->name, ent->len);\n>  \t\t\t\tif (0 <= pos)\n> -\t\t\t\t\tdie(\"bug in show-killed-files\");\n> +\t\t\t\t\tdie(\"BUG: killed-file %.*s not found\",\n> +\t\t\t\t\t\tent->len, ent->name);\n>  \t\t\t\tpos = -pos - 1;\n>  \t\t\t\twhile (pos < active_nr &&\n>  \t\t\t\t       ce_stage(active_cache[pos]))\n\nThis has an additional improvement (not mentioned in the commit\nmessage, but probably not worth it) in that it shows which file\nwas not found, not only that there was some bug, isn't it?\n\n-- \nJakub Narębski\n\n"},{"id":"290843","messageId":"alpine.DEB.2.20.1607051535550.8378@virtualbox","threadId":"42743","inReplyTo":"577BB09E.5070801@gmail.com","subject":"Re: [PATCH v2 02/17] Report bugs consistently","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-05T13:38:31Z","receivedAt":"2016-07-05T13:57:55Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Kuba,\n\nOn Tue, 5 Jul 2016, Jakub Narębski wrote:\n\n> W dniu 2016-07-05 o 13:23, Johannes Schindelin pisze:\n> > diff --git a/builtin/ls-files.c b/builtin/ls-files.c\n> > index f02e3d2..00ea91a 100644\n> > --- a/builtin/ls-files.c\n> > +++ b/builtin/ls-files.c\n> > @@ -118,7 +118,8 @@ static void show_killed_files(struct dir_struct *dir)\n> >  \t\t\t\t */\n> >  \t\t\t\tpos = cache_name_pos(ent->name, ent->len);\n> >  \t\t\t\tif (0 <= pos)\n> > -\t\t\t\t\tdie(\"bug in show-killed-files\");\n> > +\t\t\t\t\tdie(\"BUG: killed-file %.*s not found\",\n> > +\t\t\t\t\t\tent->len, ent->name);\n> >  \t\t\t\tpos = -pos - 1;\n> >  \t\t\t\twhile (pos < active_nr &&\n> >  \t\t\t\t       ce_stage(active_cache[pos]))\n> \n> This has an additional improvement (not mentioned in the commit\n> message, but probably not worth it) in that it shows which file\n> was not found, not only that there was some bug, isn't it?\n\nSure, it improves that report. In the unlikely event that a bug is\nencountered :-)\n\nIs it really worth mentioning in the commit message?\n\nLooking at it again, however, I think there is a bug in my patch. It says\nthat the file was not found, but pos was non-negative, so it was found\nunexpectedly. So I think I should strike the \"not\" part. Would you concur?\n\nCiao,\nDscho"},{"id":"290880","messageId":"xmqqr3b6u6mb.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1607020906560.12947@virtualbox","subject":"Re: [PATCH 2/9] merge-recursive: clarify code in was_tracked()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-06T15:30:04Z","receivedAt":"2016-07-06T15:30:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> To understand why we're not done yet, the crucial point is *not* that the\n> return value encodes the insert position. The crucial point is that\n> despite asking for an index entry matching a specific name, we might not\n> find one, *even if there is one*.\n\nI've been wondering why you keep saying \"even though we didn't ask,\nwe look for stage#0\", and now I see why.  The cache_pos() interface\n*is* about finding the stage#0 entry for the given path.\n\nWhen it finds none, it indicates where a stage#0 entry of that path\nwould be inserted, which by the sort-order would give us where\nhigher stage entries for the path would be found (if there is any).\nThere is no parameter for you to tell it to find stage#2, and \"even\nthough we didn't ask\" is showing (and being the source of) the\nconfusion.\n\nAnd I did not want a misleading comment to spread the confusion;\nthat is why I was reacting strongly.\n\nAs you pointed out, we can return early without falling into the\ngeneric \"we are still looking at the same path\" codepath when we\nfind thestage#0 entry, so I wouldn't mind doing something like the\nfollowing.\n\nstatic int was_tracked(const char *path)\n{\n\tint pos = cache_name_pos(path, strlen(path));\n\n        if (0 <= pos)\n\t        /* we have been tracking this path */\n        \treturn 1;\n\n\t/*\n         * Look for an unmerged entry for the path,\n         * specifically stage #2, which would indicate\n         * that \"our\" side before the merge started\n         * had the path tracked (and resulted in a conflict).\n         */\n\tfor (pos = -1 - pos;\n             pos < active_nr && !strcmp(path, active_cache[pos]->name);\n\t     pos++)\n\t\tif (ce_stage(active_cache[pos]) == 2)\n\t\t\treturn 1;\n\treturn 0;\n}\n"},{"id":"290881","messageId":"CACsJy8DY=wRfMBZn75fqjM7i4JzRbr70OCykHU_KdjSXnLY6Pg@mail.gmail.com","threadId":"42743","inReplyTo":"fdb0efbeb0b41c0d9976b2d66df90d2366f81ca1.1467717729.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 02/17] Report bugs consistently","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-07-06T15:30:37Z","receivedAt":"2016-07-06T15:31:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Jul 5, 2016 at 1:23 PM, Johannes Schindelin\n<johannes.schindelin@gmx.de> wrote:\n> The vast majority of error messages in Git's source code which report a\n> bug use the convention to prefix the message with \"BUG:\".\n>\n> As part of cleaning up merge-recursive to stop die()ing except in case of\n> detected bugs, let's just make the remainder of the bug reports consistent\n> with the de facto rule.\n\nIf you search 'die(_(\"bug:' in this patch,  you'll find 5 instances\nwhere _() should be gone too.\n-- \nDuy\n"},{"id":"290917","messageId":"xmqq1t36sbqt.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"ea23faf258b6e62e770879362869f49eea4db869.1467717730.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 11/17] am: counteract gender bias","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-06T21:22:18Z","receivedAt":"2016-07-06T21:22:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> Since d1c5f2a (Add git-am, applymbox replacement., 2005-10-07), i.e. for\n> almost 11 years already,...\n> ...Let's start changing that by using the variable name \"her_tree\" for an\n> equal number of years out of fairness, and change to the gender neutral\n> \"their_tree\" after that.\n\nI doubt this kind fo distraction is desirable in the middle of a\nseriously heavy series like this one.  As a standalone clean-up to\nturn these directly to \"their\" that everybody would agree on and can\nbe merged down quickly to 'master' that does not have to keep the\nbody of the main topic waiting for the dust to settle might be a\nbetter approach.\n\nUnless you are trying to discourage the reviewers, that is ;-).\n"},{"id":"290918","messageId":"xmqqwpkyqwzh.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"cover.1467717729.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 00/17] Use merge_recursive() directly in the builtin am","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-06T21:26:26Z","receivedAt":"2016-07-06T21:26:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> This is the second iteration of the long-awaited re-roll of the attempt to\n> avoid spawning merge-recursive from the builtin am and use merge_recursive()\n> directly instead.\n\nI wanted to queue this in 'pu', but an unfortunate series that\nchanges the convert_to_git() infrastructure has serious conflicts\nwith the changes in this series.  I am still unsure if these\nchanges cannot be done without butchering the calling convention\nof what leads to convert_to_git(), but in the meantime, this cannot\nyet be merged to 'pu'.\n\n"},{"id":"290927","messageId":"alpine.DEB.2.20.1607071313560.6426@virtualbox","threadId":"42743","inReplyTo":"xmqqwpkyqwzh.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 00/17] Use merge_recursive() directly in the builtin am","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-07T11:16:25Z","receivedAt":"2016-07-07T11:17:02Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 6 Jul 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > This is the second iteration of the long-awaited re-roll of the attempt to\n> > avoid spawning merge-recursive from the builtin am and use merge_recursive()\n> > directly instead.\n> \n> I wanted to queue this in 'pu', but an unfortunate series that\n> changes the convert_to_git() infrastructure has serious conflicts\n> with the changes in this series.  I am still unsure if these\n> changes cannot be done without butchering the calling convention\n> of what leads to convert_to_git(), but in the meantime, this cannot\n> yet be merged to 'pu'.\n\nThere is nothing butchered there. The secret to the merge conflicts is the\nsha1 -> oid conversion paired with Vasco's untranslating of BUG: reports\nwithout upcasing the \"bug:\" prefix.\n\nI will rebase to `pu` and re-send.\n\nCiao,\nDscho\n"},{"id":"290928","messageId":"alpine.DEB.2.20.1607071316390.6426@virtualbox","threadId":"42743","inReplyTo":"xmqqr3b6u6mb.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 2/9] merge-recursive: clarify code in was_tracked()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-07T11:17:49Z","receivedAt":"2016-07-07T11:18:14Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 6 Jul 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > To understand why we're not done yet, the crucial point is *not* that the\n> > return value encodes the insert position. The crucial point is that\n> > despite asking for an index entry matching a specific name, we might not\n> > find one, *even if there is one*.\n> \n> I've been wondering why you keep saying \"even though we didn't ask,\n> we look for stage#0\", and now I see why.  The cache_pos() interface\n> *is* about finding the stage#0 entry for the given path.\n\nGood that this is clarified now.\n\n> [...]\n> As you pointed out, we can return early without falling into the\n> generic \"we are still looking at the same path\" codepath when we\n> find thestage#0 entry, so I wouldn't mind doing something like the\n> following.\n> [...]\n\nAs this is essentially what I wrote with some minor touch-ups, I just\nreplaced my version with yours.\n\nWill resend,\nDscho\n"},{"id":"290929","messageId":"alpine.DEB.2.20.1607071319070.6426@virtualbox","threadId":"42743","inReplyTo":"CACsJy8DY=wRfMBZn75fqjM7i4JzRbr70OCykHU_KdjSXnLY6Pg@mail.gmail.com","subject":"Re: [PATCH v2 02/17] Report bugs consistently","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-07T11:23:21Z","receivedAt":"2016-07-07T11:23:46Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Duy,\n\nOn Wed, 6 Jul 2016, Duy Nguyen wrote:\n\n> On Tue, Jul 5, 2016 at 1:23 PM, Johannes Schindelin\n> <johannes.schindelin@gmx.de> wrote:\n> > The vast majority of error messages in Git's source code which report a\n> > bug use the convention to prefix the message with \"BUG:\".\n> >\n> > As part of cleaning up merge-recursive to stop die()ing except in case of\n> > detected bugs, let's just make the remainder of the bug reports consistent\n> > with the de facto rule.\n> \n> If you search 'die(_(\"bug:' in this patch,  you'll find 5 instances\n> where _() should be gone too.\n\nI should have known better. Vasco's i18n work already conflicts seriously\nwith what I have in this patch series.\n\nThis proves once again, beyond any doubt, that one should refrain from\nintroducing patches that have little to do with the purpose of the patch\nseries.\n\nSo as far as this here patch series goes, let's re-focus on the recursive\nmerge code again, okay?\n\nCiao,\nDscho\n"},{"id":"290930","messageId":"alpine.DEB.2.20.1607071323440.6426@virtualbox","threadId":"42743","inReplyTo":"xmqq1t36sbqt.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 11/17] am: counteract gender bias","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-07T11:30:36Z","receivedAt":"2016-07-07T11:30:57Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 6 Jul 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > Since d1c5f2a (Add git-am, applymbox replacement., 2005-10-07), i.e. for\n> > almost 11 years already,...\n> > ...Let's start changing that by using the variable name \"her_tree\" for an\n> > equal number of years out of fairness, and change to the gender neutral\n> > \"their_tree\" after that.\n> \n> I doubt this kind fo distraction is desirable in the middle of a\n> seriously heavy series like this one.  As a standalone clean-up to\n> turn these directly to \"their\" that everybody would agree on and can\n> be merged down quickly to 'master' that does not have to keep the\n> body of the main topic waiting for the dust to settle might be a\n> better approach.\n> \n> Unless you are trying to discourage the reviewers, that is ;-).\n\nFunny. In other comments, I am asked to patch things that are truly\nunrelated to the patch series' intent, and here I am asked to refrain from\ncleaning up the code before I touch it.\n\nI am really curious, though. Has it not been our practice to encourage\npreparatory patches like white-space or const fixes as part of patch\nseries that touch a certain part of the code that needed fixing? I deem\nthis here patch to be much, much more important than a mere white-space or\nconst fix.\n\nSince you asked so nicely, I will break out this patch from the patch\nseries, of course, but please note that it will now look as if I willfully\nsnuck in an unrelated change in the next patch, just because I was not\nallowed to prepare the code properly.\n\nCiao,\nDscho\n"},{"id":"290937","messageId":"cover.1467902082.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467717729.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 00/16] Use merge_recursive() directly in the builtin am","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-07T14:35:06Z","receivedAt":"2016-07-07T14:35:43Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This is the second iteration of the long-awaited re-roll of the attempt to\navoid spawning merge-recursive from the builtin am and use merge_recursive()\ndirectly instead.\n\nThe *real* reason for the reroll is that I need a libified recursive\nmerge to accelerate the interactive rebase by teaching the sequencer to\ndo rebase -i's grunt work.\n\nIn this endeavor, we need to be extra careful to retain backwards\ncompatibility. The test script t6022-merge-rename.sh, for example, verifies\nthat `git pull` exits with status 128 in case of a fatal error. To that end,\nwe need to make sure that fatal errors are handled by existing (builtin)\nusers via exit(128) (or die(), which calls exit(128) at the end).  New users\n(such as a builtin helper doing rebase -i's grunt work) may want to print\nsome helpful advice what happened and how to get out of this mess before\nerroring out.\n\nThe changes relative to the second iteration of this patch series:\n\n- the was_tracked() function was adjusted as per Junio's suggestions\n\n- the \"counter gender bias\" patch was submitted separately, in\n  $gmane/299009 (please note that the \"am -3: use merge_recursive()\n  directly again\" patch is now slightly awkward as a consequence)\n\n- this patch series is on top of 'pu', to address the conflicts with\n  the 'jh/clean-smudge-annex' and the 'bc/cocci' branches\n\n- please note that the interdiff does not show the full picture: I\n  generated it relative to v2 rebased on top of pu (resolving many\n  merge conflicts in the process that are hidden from the interdiff)\n\nThis patch series touches rather important code. Now that I addressed\nconcerns such as fixing translated bug reports, I would appreciate thorough\nreviews with a focus on the critical parts of the code, those that could\nresult in regressions.\n\n\nJohannes Schindelin (16):\n  Verify that `git pull --rebase` shows the helpful advice when failing\n  Report bugs consistently\n  Avoid translating bug messages\n  merge-recursive: clarify code in was_tracked()\n  Prepare the builtins for a libified merge_recursive()\n  merge_recursive: abort properly upon errors\n  merge-recursive: avoid returning a wholesale struct\n  merge-recursive: allow write_tree_from_memory() to error out\n  merge-recursive: handle return values indicating errors\n  merge-recursive: switch to returning errors instead of dying\n  am -3: use merge_recursive() directly again\n  merge-recursive: flush output buffer before printing error messages\n  merge-recursive: write the commit title in one go\n  merge-recursive: offer an option to retain the output in 'obuf'\n  Ensure that the output buffer is released after calling merge_trees()\n  merge-recursive: flush output buffer even when erroring out\n\n builtin/am.c           |  63 +++---\n builtin/checkout.c     |   5 +-\n builtin/ls-files.c     |   3 +-\n builtin/merge.c        |   2 +\n builtin/update-index.c |   2 +-\n grep.c                 |   8 +-\n imap-send.c            |   4 +-\n merge-recursive.c      | 508 +++++++++++++++++++++++++++++--------------------\n merge-recursive.h      |   2 +-\n sequencer.c            |   5 +\n sha1_file.c            |   4 +-\n t/t5520-pull.sh        |  30 +++\n trailer.c              |   2 +-\n transport.c            |   2 +-\n wt-status.c            |   4 +-\n 15 files changed, 382 insertions(+), 262 deletions(-)\n\nPublished-As: https://github.com/dscho/git/releases/tag/am-3-merge-recursive-direct-v3\nInterdiff vs v2:\n\n diff --git a/builtin/am.c b/builtin/am.c\n index d626532..8dc4239 100644\n --- a/builtin/am.c\n +++ b/builtin/am.c\n @@ -29,6 +29,7 @@\n  #include \"prompt.h\"\n  #include \"mailinfo.h\"\n  #include \"apply.h\"\n +#include \"object.h\"\n  \n  /**\n   * Returns the length of the first line of msg.\n @@ -1627,15 +1628,14 @@ static int build_fake_ancestor(const struct am_state *state, const char *index_f\n   */\n  static int fall_back_threeway(const struct am_state *state, const char *index_path)\n  {\n -\tunsigned char orig_tree[GIT_SHA1_RAWSZ], her_tree[GIT_SHA1_RAWSZ],\n -\t\t      our_tree[GIT_SHA1_RAWSZ];\n -\tconst unsigned char *bases[1] = {orig_tree};\n +\tstruct object_id orig_tree, her_tree, our_tree;\n +\tconst struct object_id *bases[1] = { &orig_tree };\n  \tstruct merge_options o;\n  \tstruct commit *result;\n  \tchar *her_tree_name;\n  \n -\tif (get_sha1(\"HEAD\", our_tree) < 0)\n -\t\thashcpy(our_tree, EMPTY_TREE_SHA1_BIN);\n +\tif (get_oid(\"HEAD\", &our_tree) < 0)\n +\t\thashcpy(our_tree.hash, EMPTY_TREE_SHA1_BIN);\n  \n  \tif (build_fake_ancestor(state, index_path))\n  \t\treturn error(\"could not build fake ancestor\");\n @@ -1643,7 +1643,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n  \tdiscard_cache();\n  \tread_cache_from(index_path);\n  \n -\tif (write_index_as_tree(orig_tree, &the_index, index_path, 0, NULL))\n +\tif (write_index_as_tree(orig_tree.hash, &the_index, index_path, 0, NULL))\n  \t\treturn error(_(\"Repository lacks necessary blobs to fall back on 3-way merge.\"));\n  \n  \tsay(state, stdout, _(\"Using index info to reconstruct a base tree...\"));\n @@ -1659,7 +1659,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n  \t\tinit_revisions(&rev_info, NULL);\n  \t\trev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;\n  \t\tdiff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix);\n -\t\tadd_pending_sha1(&rev_info, \"HEAD\", our_tree, 0);\n +\t\tadd_pending_sha1(&rev_info, \"HEAD\", our_tree.hash, 0);\n  \t\tdiff_setup_done(&rev_info.diffopt);\n  \t\trun_diff_index(&rev_info, 1);\n  \t}\n @@ -1668,7 +1668,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n  \t\treturn error(_(\"Did you hand edit your patch?\\n\"\n  \t\t\t\t\"It does not apply to blobs recorded in its index.\"));\n  \n -\tif (write_index_as_tree(her_tree, &the_index, index_path, 0, NULL))\n +\tif (write_index_as_tree(her_tree.hash, &the_index, index_path, 0, NULL))\n  \t\treturn error(\"could not write tree\");\n  \n  \tsay(state, stdout, _(\"Falling back to patching base and 3-way merge...\"));\n @@ -1678,7 +1678,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n  \n  \t/*\n  \t * This is not so wrong. Depending on which base we picked, orig_tree\n -\t * may be wildly different from ours, but her_tree has the same set of\n +\t * may be wildly different from ours, but his_tree has the same set of\n  \t * wildly different changes in parts the patch did not touch, so\n  \t * recursive ends up canceling them, saying that we reverted all those\n  \t * changes.\n @@ -1693,7 +1693,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n  \tif (state->quiet)\n  \t\to.verbosity = 0;\n  \n -\tif (merge_recursive_generic(&o, our_tree, her_tree, 1, bases, &result)) {\n +\tif (merge_recursive_generic(&o, &our_tree, &her_tree, 1, bases, &result)) {\n  \t\trerere(state->allow_rerere_autoupdate);\n  \t\tfree(her_tree_name);\n  \t\treturn error(_(\"Failed to merge in the changes.\"));\n diff --git a/merge-recursive.c b/merge-recursive.c\n index f80ad35..cb701ee 100644\n --- a/merge-recursive.c\n +++ b/merge-recursive.c\n @@ -686,20 +686,19 @@ static int was_tracked(const char *path)\n  {\n  \tint pos = cache_name_pos(path, strlen(path));\n  \n -\tif (pos >= 0)\n -\t\treturn pos < active_nr;\n +\tif (0 <= pos)\n +\t\t/* we have been tracking this path */\n +\t\treturn 1;\n +\n  \t/*\n -\t * cache_name_pos() looks for stage == 0, even if we did not ask for\n -\t * it. Let's look for stage == 2 now.\n +\t * Look for an unmerged entry for the path,\n +\t * specifically stage #2, which would indicate\n +\t * that \"our\" side before the merge started\n +\t * had the path tracked (and resulted in a conflict).\n  \t */\n -\tfor (pos = -1 - pos; pos < active_nr &&\n -\t     !strcmp(path, active_cache[pos]->name); pos++)\n -\t\t/*\n -\t\t * If stage #0, it is definitely tracked.\n -\t\t * If it has stage #2 then it was tracked\n -\t\t * before this merge started.  All other\n -\t\t * cases the path was not tracked.\n -\t\t */\n +\tfor (pos = -1 - pos;\n +\t     pos < active_nr && !strcmp(path, active_cache[pos]->name);\n +\t     pos++)\n  \t\tif (ce_stage(active_cache[pos]) == 2)\n  \t\t\treturn 1;\n  \treturn 0;\n\n-- \n2.9.0.278.g1caae67\n\nbase-commit: 6addd022ce5331ee7dc41781ded714e5d5f01206\n"},{"id":"290938","messageId":"6ed923d5172ec43d9ec8a8d622398e5666eff505.1467902082.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467902082.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 02/16] Report bugs consistently","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-07T14:35:21Z","receivedAt":"2016-07-07T14:35:47Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The vast majority of error messages in Git's source code which report a\nbug use the convention to prefix the message with \"BUG:\".\n\nAs part of cleaning up merge-recursive to stop die()ing except in case of\ndetected bugs, let's just make the remainder of the bug reports consistent\nwith the de facto rule.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/ls-files.c     |  3 ++-\n builtin/update-index.c |  2 +-\n grep.c                 |  8 ++++----\n imap-send.c            |  4 ++--\n merge-recursive.c      | 15 +++++++--------\n sha1_file.c            |  4 ++--\n trailer.c              |  2 +-\n transport.c            |  2 +-\n wt-status.c            |  4 ++--\n 9 files changed, 22 insertions(+), 22 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex f02e3d2..00ea91a 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -118,7 +118,8 @@ static void show_killed_files(struct dir_struct *dir)\n \t\t\t\t */\n \t\t\t\tpos = cache_name_pos(ent->name, ent->len);\n \t\t\t\tif (0 <= pos)\n-\t\t\t\t\tdie(\"bug in show-killed-files\");\n+\t\t\t\t\tdie(\"BUG: killed-file %.*s not found\",\n+\t\t\t\t\t\tent->len, ent->name);\n \t\t\t\tpos = -pos - 1;\n \t\t\t\twhile (pos < active_nr &&\n \t\t\t\t       ce_stage(active_cache[pos]))\ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex 25af040..881a4b0 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -1149,7 +1149,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)\n \t\treport(_(\"Untracked cache enabled for '%s'\"), get_git_work_tree());\n \t\tbreak;\n \tdefault:\n-\t\tdie(\"Bug: bad untracked_cache value: %d\", untracked_cache);\n+\t\tdie(\"BUG: bad untracked_cache value: %d\", untracked_cache);\n \t}\n \n \tif (use_watchman > 0) {\ndiff --git a/grep.c b/grep.c\nindex 394c856..22cbb73 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -693,10 +693,10 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)\n \n \tfor (p = opt->header_list; p; p = p->next) {\n \t\tif (p->token != GREP_PATTERN_HEAD)\n-\t\t\tdie(\"bug: a non-header pattern in grep header list.\");\n+\t\t\tdie(\"BUG: a non-header pattern in grep header list.\");\n \t\tif (p->field < GREP_HEADER_FIELD_MIN ||\n \t\t    GREP_HEADER_FIELD_MAX <= p->field)\n-\t\t\tdie(\"bug: unknown header field %d\", p->field);\n+\t\t\tdie(\"BUG: unknown header field %d\", p->field);\n \t\tcompile_regexp(p, opt);\n \t}\n \n@@ -709,7 +709,7 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)\n \n \t\th = compile_pattern_atom(&pp);\n \t\tif (!h || pp != p->next)\n-\t\t\tdie(\"bug: malformed header expr\");\n+\t\t\tdie(\"BUG: malformed header expr\");\n \t\tif (!header_group[p->field]) {\n \t\t\theader_group[p->field] = h;\n \t\t\tcontinue;\n@@ -1514,7 +1514,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle\n \t\tcase GREP_BINARY_TEXT:\n \t\t\tbreak;\n \t\tdefault:\n-\t\t\tdie(\"bug: unknown binary handling mode\");\n+\t\t\tdie(\"BUG: unknown binary handling mode\");\n \t\t}\n \t}\n \ndiff --git a/imap-send.c b/imap-send.c\nindex db0fafe..67d67f8 100644\n--- a/imap-send.c\n+++ b/imap-send.c\n@@ -506,12 +506,12 @@ static char *next_arg(char **s)\n \n static int nfsnprintf(char *buf, int blen, const char *fmt, ...)\n {\n-\tint ret;\n+\tint ret = -1;\n \tva_list va;\n \n \tva_start(va, fmt);\n \tif (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)\n-\t\tdie(\"Fatal: buffer too small. Please report a bug.\");\n+\t\tdie(\"BUG: buffer too small (%d < %d)\", ret, blen);\n \tva_end(va);\n \treturn ret;\n }\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 067f656..05b9789 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -259,7 +259,7 @@ struct tree *write_tree_from_memory(struct merge_options *o)\n \t\t\t\tfprintf(stderr, \"BUG: %d %.*s\\n\", ce_stage(ce),\n \t\t\t\t\t(int)ce_namelen(ce), ce->name);\n \t\t}\n-\t\tdie(\"Bug in merge-recursive.c\");\n+\t\tdie(\"BUG: unmerged index entries in merge-recursive.c\");\n \t}\n \n \tif (!active_cache_tree)\n@@ -982,9 +982,8 @@ static struct merge_file_info merge_file_1(struct merge_options *o,\n \n \t\t\tif (!oid_eq(&a->oid, &b->oid))\n \t\t\t\tresult.clean = 0;\n-\t\t} else {\n-\t\t\tdie(_(\"unsupported object type in the tree\"));\n-\t\t}\n+\t\t} else\n+\t\t\tdie(_(\"BUG: unsupported object type in the tree\"));\n \t}\n \n \treturn result;\n@@ -1370,7 +1369,7 @@ static int process_renames(struct merge_options *o,\n \t\t\tconst char *ren2_dst = ren2->pair->two->path;\n \t\t\tenum rename_type rename_type;\n \t\t\tif (strcmp(ren1_src, ren2_src) != 0)\n-\t\t\t\tdie(\"ren1_src != ren2_src\");\n+\t\t\t\tdie(\"BUG: ren1_src != ren2_src\");\n \t\t\tren2->dst_entry->processed = 1;\n \t\t\tren2->processed = 1;\n \t\t\tif (strcmp(ren1_dst, ren2_dst) != 0) {\n@@ -1404,7 +1403,7 @@ static int process_renames(struct merge_options *o,\n \t\t\tren2 = lookup->util;\n \t\t\tren2_dst = ren2->pair->two->path;\n \t\t\tif (strcmp(ren1_dst, ren2_dst) != 0)\n-\t\t\t\tdie(\"ren1_dst != ren2_dst\");\n+\t\t\t\tdie(\"BUG: ren1_dst != ren2_dst\");\n \n \t\t\tclean_merge = 0;\n \t\t\tren2->processed = 1;\n@@ -1828,7 +1827,7 @@ static int process_entry(struct merge_options *o,\n \t\t */\n \t\tremove_file(o, 1, path, !a_mode);\n \t} else\n-\t\tdie(_(\"Fatal merge failure, shouldn't happen.\"));\n+\t\tdie(_(\"BUG: fatal merge failure, shouldn't happen.\"));\n \n \treturn clean_merge;\n }\n@@ -1886,7 +1885,7 @@ int merge_trees(struct merge_options *o,\n \t\tfor (i = 0; i < entries->nr; i++) {\n \t\t\tstruct stage_data *e = entries->items[i].util;\n \t\t\tif (!e->processed)\n-\t\t\t\tdie(_(\"Unprocessed path??? %s\"),\n+\t\t\t\tdie(_(\"BUG: unprocessed path??? %s\"),\n \t\t\t\t    entries->items[i].string);\n \t\t}\n \ndiff --git a/sha1_file.c b/sha1_file.c\nindex df62eaf..27dadff 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -795,7 +795,7 @@ void close_all_packs(void)\n \n \tfor (p = packed_git; p; p = p->next)\n \t\tif (p->do_not_close)\n-\t\t\tdie(\"BUG! Want to close pack marked 'do-not-close'\");\n+\t\t\tdie(\"BUG: want to close pack marked 'do-not-close'\");\n \t\telse\n \t\t\tclose_pack(p);\n }\n@@ -2336,7 +2336,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,\n \tcase OBJ_OFS_DELTA:\n \tcase OBJ_REF_DELTA:\n \t\tif (data)\n-\t\t\tdie(\"BUG in unpack_entry: left loop at a valid delta\");\n+\t\t\tdie(\"BUG: unpack_entry: left loop at a valid delta\");\n \t\tbreak;\n \tcase OBJ_COMMIT:\n \tcase OBJ_TREE:\ndiff --git a/trailer.c b/trailer.c\nindex 8e48a5c..c6ea9ac 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -562,7 +562,7 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)\n \t\t\twarning(_(\"unknown value '%s' for key '%s'\"), value, conf_key);\n \t\tbreak;\n \tdefault:\n-\t\tdie(\"internal bug in trailer.c\");\n+\t\tdie(\"BUG: trailer.c: unhandled type %d\", type);\n \t}\n \treturn 0;\n }\ndiff --git a/transport.c b/transport.c\nindex ad9ac15..d1dd29a 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -577,7 +577,7 @@ void transport_take_over(struct transport *transport,\n \tstruct git_transport_data *data;\n \n \tif (!transport->smart_options)\n-\t\tdie(\"Bug detected: Taking over transport requires non-NULL \"\n+\t\tdie(\"BUG: taking over transport requires non-NULL \"\n \t\t    \"smart_options field.\");\n \n \tdata = xcalloc(1, sizeof(*data));\ndiff --git a/wt-status.c b/wt-status.c\nindex de62ab2..3fb86a4 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -263,7 +263,7 @@ static const char *wt_status_unmerged_status_string(int stagemask)\n \tcase 7:\n \t\treturn _(\"both modified:\");\n \tdefault:\n-\t\tdie(\"bug: unhandled unmerged status %x\", stagemask);\n+\t\tdie(\"BUG: unhandled unmerged status %x\", stagemask);\n \t}\n }\n \n@@ -388,7 +388,7 @@ static void wt_status_print_change_data(struct wt_status *s,\n \tstatus_printf(s, color(WT_STATUS_HEADER, s), \"\\t\");\n \twhat = wt_status_diff_status_string(status);\n \tif (!what)\n-\t\tdie(\"bug: unhandled diff status %c\", status);\n+\t\tdie(\"BUG: unhandled diff status %c\", status);\n \tlen = label_width - utf8_strwidth(what);\n \tassert(len >= 0);\n \tif (status == DIFF_STATUS_COPIED || status == DIFF_STATUS_RENAMED)\n-- \n2.9.0.278.g1caae67\n\n\n"},{"id":"290939","messageId":"b5c530404c8bb71e513e9e79a09d8ef7d259b580.1467902083.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467902082.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 03/16] Avoid translating bug messages","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-07T14:35:27Z","receivedAt":"2016-07-07T14:35:55Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"While working on the patch series that avoids die()ing in recursive\nmerges, the issue came up that bug reports (i.e. die(\"BUG: ...\")\nconstructs) should never be translated, as the target audience is the\nGit developer community, not necessarily the current user, and hence\na translated message would make it *harder* to address the problem.\n\nSo let's stop translating the obvious ones. As it is really, really\noutside the purview of this patch series to see whether there are more\ndie() statements that report bugs and are currently translated, that\ntask is left for another day and patch.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 05b9789..ea0df22 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -983,7 +983,7 @@ static struct merge_file_info merge_file_1(struct merge_options *o,\n \t\t\tif (!oid_eq(&a->oid, &b->oid))\n \t\t\t\tresult.clean = 0;\n \t\t} else\n-\t\t\tdie(_(\"BUG: unsupported object type in the tree\"));\n+\t\t\tdie(\"BUG: unsupported object type in the tree\");\n \t}\n \n \treturn result;\n@@ -1827,7 +1827,7 @@ static int process_entry(struct merge_options *o,\n \t\t */\n \t\tremove_file(o, 1, path, !a_mode);\n \t} else\n-\t\tdie(_(\"BUG: fatal merge failure, shouldn't happen.\"));\n+\t\tdie(\"BUG: fatal merge failure, shouldn't happen.\");\n \n \treturn clean_merge;\n }\n@@ -1885,7 +1885,7 @@ int merge_trees(struct merge_options *o,\n \t\tfor (i = 0; i < entries->nr; i++) {\n \t\t\tstruct stage_data *e = entries->items[i].util;\n \t\t\tif (!e->processed)\n-\t\t\t\tdie(_(\"BUG: unprocessed path??? %s\"),\n+\t\t\t\tdie(\"BUG: unprocessed path??? %s\",\n \t\t\t\t    entries->items[i].string);\n \t\t}\n \n-- \n2.9.0.278.g1caae67\n\n\n"},{"id":"290940","messageId":"61992e3afb8b9073bc78410c95c6c21a5abfd02b.1467902083.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467902082.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 05/16] Prepare the builtins for a libified merge_recursive()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-07T14:35:35Z","receivedAt":"2016-07-07T14:35:58Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Previously, callers of merge_trees() or merge_recursive() expected that\ncode to die() with an error message. This used to be okay because we\ncalled those commands from scripts, and had a chance to print out a\nmessage in case the command failed fatally (read: with exit code 128).\n\nAs scripting incurs its own set of problems (portability, speed,\nidiosynchracies of different shells, limited data structures leading to\ninefficient code), we are converting more and more of these scripts into\nbuiltins, using library functions directly.\n\nWe already tried to use merge_recursive() directly in the builtin\ngit-am, for example. Unfortunately, we had to roll it back temporarily\nbecause some of the code in merge-recursive.c still deemed it okay to\ncall die(), when the builtin am code really wanted to print out a useful\nadvice after the merge failed fatally. In the next commits, we want to\nfix that.\n\nThe code touched by this commit expected merge_trees() to die() with\nsome useful message when there is an error condition, but merge_trees()\nis going to be improved by converting all die() calls to return error()\ninstead (i.e. return value -1 after printing out the message as before),\nso that the caller can react more flexibly.\n\nThis is a step to prepare for the version of merge_trees() that no\nlonger dies,  even if we just imitate the previous behavior by calling\nexit(128): this is what callers of e.g. `git merge` have come to expect.\n\nNote that the callers of the sequencer (revert and cherry-pick) already\nfail fast even for the return value -1; The only difference is that they\nnow get a chance to say \"<command> failed\".\n\nA caller of merge_trees() might want handle error messages themselves\n(or even suppress them). As this patch is already complex enough, we\nleave that change for a later patch.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/checkout.c | 4 +++-\n builtin/merge.c    | 2 ++\n sequencer.c        | 4 ++++\n 3 files changed, 9 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 27c1a05..07dea3b 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -567,8 +567,10 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\to.ancestor = old->name;\n \t\t\to.branch1 = new->name;\n \t\t\to.branch2 = \"local\";\n-\t\t\tmerge_trees(&o, new->commit->tree, work,\n+\t\t\tret = merge_trees(&o, new->commit->tree, work,\n \t\t\t\told->commit->tree, &result);\n+\t\t\tif (ret < 0)\n+\t\t\t\texit(128);\n \t\t\tret = reset_tree(new->commit->tree, opts, 0,\n \t\t\t\t\t writeout_error);\n \t\t\tif (ret)\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 46b88ad..6837e15 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -682,6 +682,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,\n \t\thold_locked_index(&lock, 1);\n \t\tclean = merge_recursive(&o, head,\n \t\t\t\tremoteheads->item, reversed, &result);\n+\t\tif (clean < 0)\n+\t\t\texit(128);\n \t\tif (active_cache_changed &&\n \t\t    write_locked_index(&the_index, &lock, COMMIT_LOCK))\n \t\t\tdie (_(\"unable to write %s\"), get_index_file());\ndiff --git a/sequencer.c b/sequencer.c\nindex cdfac82..286a435 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -293,6 +293,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \tclean = merge_trees(&o,\n \t\t\t    head_tree,\n \t\t\t    next_tree, base_tree, &result);\n+\tif (clean < 0)\n+\t\treturn clean;\n \n \tif (active_cache_changed &&\n \t    write_locked_index(&the_index, &index_lock, COMMIT_LOCK))\n@@ -559,6 +561,8 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || opts->action == REPLAY_REVERT) {\n \t\tres = do_recursive_merge(base, next, base_label, next_label,\n \t\t\t\t\t head, &msgbuf, opts);\n+\t\tif (res < 0)\n+\t\t\treturn res;\n \t\twrite_message(&msgbuf, git_path_merge_msg());\n \t} else {\n \t\tstruct commit_list *common = NULL;\n-- \n2.9.0.278.g1caae67\n\n\n"},{"id":"290941","messageId":"2bb0b37527d05e4220c6c9d2d705c1dac20dd41a.1467902083.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467902082.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 06/16] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-07T14:35:40Z","receivedAt":"2016-07-07T14:35:59Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"There are a couple of places where return values indicating errors\nare ignored. Let's teach them manners.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 10 ++++++++--\n 1 file changed, 8 insertions(+), 2 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 469741d..37c181a 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1974,8 +1974,9 @@ int merge_recursive(struct merge_options *o,\n \t\tsaved_b2 = o->branch2;\n \t\to->branch1 = \"Temporary merge branch 1\";\n \t\to->branch2 = \"Temporary merge branch 2\";\n-\t\tmerge_recursive(o, merged_common_ancestors, iter->item,\n-\t\t\t\tNULL, &merged_common_ancestors);\n+\t\tif (merge_recursive(o, merged_common_ancestors, iter->item,\n+\t\t\t\tNULL, &merged_common_ancestors) < 0)\n+\t\t\treturn -1;\n \t\to->branch1 = saved_b1;\n \t\to->branch2 = saved_b2;\n \t\to->call_depth--;\n@@ -1991,6 +1992,8 @@ int merge_recursive(struct merge_options *o,\n \to->ancestor = \"merged common ancestors\";\n \tclean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree,\n \t\t\t    &mrtree);\n+\tif (clean < 0)\n+\t\treturn clean;\n \n \tif (o->call_depth) {\n \t\t*result = make_virtual_commit(mrtree, \"merged tree\");\n@@ -2047,6 +2050,9 @@ int merge_recursive_generic(struct merge_options *o,\n \thold_locked_index(lock, 1);\n \tclean = merge_recursive(o, head_commit, next_commit, ca,\n \t\t\tresult);\n+\tif (clean < 0)\n+\t\treturn clean;\n+\n \tif (active_cache_changed &&\n \t    write_locked_index(&the_index, lock, COMMIT_LOCK))\n \t\treturn error(_(\"Unable to write index.\"));\n-- \n2.9.0.278.g1caae67\n\n\n"},{"id":"290942","messageId":"e4b7334e4623e9b654ab1c18c4c9d95a75ddb59b.1467902083.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467902082.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 04/16] merge-recursive: clarify code in was_tracked()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-07T14:35:31Z","receivedAt":"2016-07-07T14:36:07Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It can be puzzling to see that was_tracked() asks to get an index entry\nby name, but does not take a negative return value for an answer.\n\nThe reason we have to do this is that cache_name_pos() only looks for\nentries in stage 0, even if nobody asked for any stage in particular.\n\nLet's rewrite the logic a little bit, to handle the easy case early: if\ncache_name_pos() returned a non-negative position, we know it is a match,\nand we do not even have to compare the name again (cache_name_pos() did\nthat for us already). We can say right away: yes, this file was tracked.\n\nOnly if there was no exact match do we need to look harder for any\nmatching entry in stage 2.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 30 ++++++++++++++----------------\n 1 file changed, 14 insertions(+), 16 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex ea0df22..469741d 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -658,23 +658,21 @@ static int was_tracked(const char *path)\n {\n \tint pos = cache_name_pos(path, strlen(path));\n \n-\tif (pos < 0)\n-\t\tpos = -1 - pos;\n-\twhile (pos < active_nr &&\n-\t       !strcmp(path, active_cache[pos]->name)) {\n-\t\t/*\n-\t\t * If stage #0, it is definitely tracked.\n-\t\t * If it has stage #2 then it was tracked\n-\t\t * before this merge started.  All other\n-\t\t * cases the path was not tracked.\n-\t\t */\n-\t\tswitch (ce_stage(active_cache[pos])) {\n-\t\tcase 0:\n-\t\tcase 2:\n+\tif (0 <= pos)\n+\t\t/* we have been tracking this path */\n+\t\treturn 1;\n+\n+\t/*\n+\t * Look for an unmerged entry for the path,\n+\t * specifically stage #2, which would indicate\n+\t * that \"our\" side before the merge started\n+\t * had the path tracked (and resulted in a conflict).\n+\t */\n+\tfor (pos = -1 - pos;\n+\t     pos < active_nr && !strcmp(path, active_cache[pos]->name);\n+\t     pos++)\n+\t\tif (ce_stage(active_cache[pos]) == 2)\n \t\t\treturn 1;\n-\t\t}\n-\t\tpos++;\n-\t}\n \treturn 0;\n }\n \n-- \n2.9.0.278.g1caae67\n\n\n"},{"id":"290943","messageId":"baa7e840b71e14aa69544ae09d3027453602a539.1467902082.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467902082.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 01/16] Verify that `git pull --rebase` shows the helpful advice when failing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-07T14:35:15Z","receivedAt":"2016-07-07T14:36:09Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n t/t5520-pull.sh | 30 ++++++++++++++++++++++++++++++\n 1 file changed, 30 insertions(+)\n\ndiff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\nindex 5d4880e..217b416 100755\n--- a/t/t5520-pull.sh\n+++ b/t/t5520-pull.sh\n@@ -255,6 +255,36 @@ test_expect_success '--rebase' '\n \ttest new = \"$(git show HEAD:file2)\"\n '\n \n+test_expect_success '--rebase with conflicts shows advice' '\n+\ttest_when_finished \"git rebase --abort; git checkout -f to-rebase\" &&\n+\tgit checkout -b seq &&\n+\tprintf \"1\\\\n2\\\\n3\\\\n4\\\\n5\\\\n\" >seq.txt &&\n+\tgit add seq.txt &&\n+\ttest_tick &&\n+\tgit commit -m \"Add seq.txt\" &&\n+\tprintf \"6\\\\n\" >>seq.txt &&\n+\ttest_tick &&\n+\tgit commit -m \"Append to seq.txt\" seq.txt &&\n+\tgit checkout -b with-conflicts HEAD^ &&\n+\tprintf \"conflicting\\\\n\" >>seq.txt &&\n+\ttest_tick &&\n+\tgit commit -m \"Create conflict\" seq.txt &&\n+\ttest_must_fail git pull --rebase . seq 2>err >out &&\n+\tgrep \"When you have resolved this problem\" out\n+'\n+test_expect_success 'failed --rebase shows advice' '\n+\ttest_when_finished \"git rebase --abort; git checkout -f to-rebase\" &&\n+\tgit checkout -b diverging &&\n+\ttest_commit attributes .gitattributes \"* text=auto\" attrs &&\n+\tsha1=\"$(printf \"1\\\\r\\\\n\" | git hash-object -w --stdin)\" &&\n+\tgit update-index --cacheinfo 0644 $sha1 file &&\n+\tgit commit -m v1-with-cr &&\n+\tgit checkout -f -b fails-to-rebase HEAD^ &&\n+\ttest_commit v2-without-cr file \"2\" file2-lf &&\n+\ttest_must_fail git pull --rebase . diverging 2>err >out &&\n+\tgrep \"When you have resolved this problem\" out\n+'\n+\n test_expect_success '--rebase fails with multiple branches' '\n \tgit reset --hard before-rebase &&\n \ttest_must_fail git pull --rebase . copy master 2>err &&\n-- \n2.9.0.278.g1caae67\n\n\n"},{"id":"290944","messageId":"d47bec8d5e711ca346f3f50551ca0765f715f26c.1467902083.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467902082.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 07/16] merge-recursive: avoid returning a wholesale struct","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-07T14:35:43Z","receivedAt":"2016-07-07T14:36:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It is technically allowed, as per C89, for functions' return type to\nbe complete structs (i.e. *not* just pointers to structs).\n\nHowever, it was just an oversight of this developer when converting\nPython code to C code in 6d297f8 (Status update on merge-recursive in\nC, 2006-07-08) which introduced such a return type.\n\nBesides, by converting this construct to pass in the struct, we can now\nstart returning a value that can indicate errors in future patches. This\nwill help the current effort to libify merge-recursive.c.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 91 +++++++++++++++++++++++++++++--------------------------\n 1 file changed, 48 insertions(+), 43 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 37c181a..d9221ce 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -910,47 +910,47 @@ static int merge_3way(struct merge_options *o,\n \treturn merge_status;\n }\n \n-static struct merge_file_info merge_file_1(struct merge_options *o,\n+static int merge_file_1(struct merge_options *o,\n \t\t\t\t\t   const struct diff_filespec *one,\n \t\t\t\t\t   const struct diff_filespec *a,\n \t\t\t\t\t   const struct diff_filespec *b,\n \t\t\t\t\t   const char *branch1,\n-\t\t\t\t\t   const char *branch2)\n+\t\t\t\t\t   const char *branch2,\n+\t\t\t\t\t   struct merge_file_info *result)\n {\n-\tstruct merge_file_info result;\n-\tresult.merge = 0;\n-\tresult.clean = 1;\n+\tresult->merge = 0;\n+\tresult->clean = 1;\n \n \tif ((S_IFMT & a->mode) != (S_IFMT & b->mode)) {\n-\t\tresult.clean = 0;\n+\t\tresult->clean = 0;\n \t\tif (S_ISREG(a->mode)) {\n-\t\t\tresult.mode = a->mode;\n-\t\t\toidcpy(&result.oid, &a->oid);\n+\t\t\tresult->mode = a->mode;\n+\t\t\toidcpy(&result->oid, &a->oid);\n \t\t} else {\n-\t\t\tresult.mode = b->mode;\n-\t\t\toidcpy(&result.oid, &b->oid);\n+\t\t\tresult->mode = b->mode;\n+\t\t\toidcpy(&result->oid, &b->oid);\n \t\t}\n \t} else {\n \t\tif (!oid_eq(&a->oid, &one->oid) && !oid_eq(&b->oid, &one->oid))\n-\t\t\tresult.merge = 1;\n+\t\t\tresult->merge = 1;\n \n \t\t/*\n \t\t * Merge modes\n \t\t */\n \t\tif (a->mode == b->mode || a->mode == one->mode)\n-\t\t\tresult.mode = b->mode;\n+\t\t\tresult->mode = b->mode;\n \t\telse {\n-\t\t\tresult.mode = a->mode;\n+\t\t\tresult->mode = a->mode;\n \t\t\tif (b->mode != one->mode) {\n-\t\t\t\tresult.clean = 0;\n-\t\t\t\tresult.merge = 1;\n+\t\t\t\tresult->clean = 0;\n+\t\t\t\tresult->merge = 1;\n \t\t\t}\n \t\t}\n \n \t\tif (oid_eq(&a->oid, &b->oid) || oid_eq(&a->oid, &one->oid))\n-\t\t\toidcpy(&result.oid, &b->oid);\n+\t\t\toidcpy(&result->oid, &b->oid);\n \t\telse if (oid_eq(&b->oid, &one->oid))\n-\t\t\toidcpy(&result.oid, &a->oid);\n+\t\t\toidcpy(&result->oid, &a->oid);\n \t\telse if (S_ISREG(a->mode)) {\n \t\t\tmmbuffer_t result_buf;\n \t\t\tint merge_status;\n@@ -962,64 +962,65 @@ static struct merge_file_info merge_file_1(struct merge_options *o,\n \t\t\t\tdie(_(\"Failed to execute internal merge\"));\n \n \t\t\tif (write_sha1_file(result_buf.ptr, result_buf.size,\n-\t\t\t\t\t    blob_type, result.oid.hash))\n+\t\t\t\t\t    blob_type, result->oid.hash))\n \t\t\t\tdie(_(\"Unable to add %s to database\"),\n \t\t\t\t    a->path);\n \n \t\t\tfree(result_buf.ptr);\n-\t\t\tresult.clean = (merge_status == 0);\n+\t\t\tresult->clean = (merge_status == 0);\n \t\t} else if (S_ISGITLINK(a->mode)) {\n-\t\t\tresult.clean = merge_submodule(result.oid.hash,\n+\t\t\tresult->clean = merge_submodule(result->oid.hash,\n \t\t\t\t\t\t       one->path,\n \t\t\t\t\t\t       one->oid.hash,\n \t\t\t\t\t\t       a->oid.hash,\n \t\t\t\t\t\t       b->oid.hash,\n \t\t\t\t\t\t       !o->call_depth);\n \t\t} else if (S_ISLNK(a->mode)) {\n-\t\t\toidcpy(&result.oid, &a->oid);\n+\t\t\toidcpy(&result->oid, &a->oid);\n \n \t\t\tif (!oid_eq(&a->oid, &b->oid))\n-\t\t\t\tresult.clean = 0;\n+\t\t\t\tresult->clean = 0;\n \t\t} else\n \t\t\tdie(\"BUG: unsupported object type in the tree\");\n \t}\n \n-\treturn result;\n+\treturn 0;\n }\n \n-static struct merge_file_info\n-merge_file_special_markers(struct merge_options *o,\n+static int merge_file_special_markers(struct merge_options *o,\n \t\t\t   const struct diff_filespec *one,\n \t\t\t   const struct diff_filespec *a,\n \t\t\t   const struct diff_filespec *b,\n \t\t\t   const char *branch1,\n \t\t\t   const char *filename1,\n \t\t\t   const char *branch2,\n-\t\t\t   const char *filename2)\n+\t\t\t   const char *filename2,\n+\t\t\t   struct merge_file_info *mfi)\n {\n \tchar *side1 = NULL;\n \tchar *side2 = NULL;\n-\tstruct merge_file_info mfi;\n+\tint ret;\n \n \tif (filename1)\n \t\tside1 = xstrfmt(\"%s:%s\", branch1, filename1);\n \tif (filename2)\n \t\tside2 = xstrfmt(\"%s:%s\", branch2, filename2);\n \n-\tmfi = merge_file_1(o, one, a, b,\n-\t\t\t   side1 ? side1 : branch1, side2 ? side2 : branch2);\n+\tret = merge_file_1(o, one, a, b,\n+\t\tside1 ? side1 : branch1, side2 ? side2 : branch2, mfi);\n \tfree(side1);\n \tfree(side2);\n-\treturn mfi;\n+\treturn ret;\n }\n \n-static struct merge_file_info merge_file_one(struct merge_options *o,\n+static int merge_file_one(struct merge_options *o,\n \t\t\t\t\t const char *path,\n \t\t\t\t\t const struct object_id *o_oid, int o_mode,\n \t\t\t\t\t const struct object_id *a_oid, int a_mode,\n \t\t\t\t\t const struct object_id *b_oid, int b_mode,\n \t\t\t\t\t const char *branch1,\n-\t\t\t\t\t const char *branch2)\n+\t\t\t\t\t const char *branch2,\n+\t\t\t\t\t struct merge_file_info *mfi)\n {\n \tstruct diff_filespec one, a, b;\n \n@@ -1030,7 +1031,7 @@ static struct merge_file_info merge_file_one(struct merge_options *o,\n \ta.mode = a_mode;\n \toidcpy(&b.oid, b_oid);\n \tb.mode = b_mode;\n-\treturn merge_file_1(o, &one, &a, &b, branch1, branch2);\n+\treturn merge_file_1(o, &one, &a, &b, branch1, branch2, mfi);\n }\n \n static void handle_change_delete(struct merge_options *o,\n@@ -1203,11 +1204,12 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n \t\tstruct merge_file_info mfi;\n \t\tstruct diff_filespec other;\n \t\tstruct diff_filespec *add;\n-\t\tmfi = merge_file_one(o, one->path,\n+\t\tif (merge_file_one(o, one->path,\n \t\t\t\t &one->oid, one->mode,\n \t\t\t\t &a->oid, a->mode,\n \t\t\t\t &b->oid, b->mode,\n-\t\t\t\t ci->branch1, ci->branch2);\n+\t\t\t\t ci->branch1, ci->branch2, &mfi))\n+\t\t\treturn;\n \t\t/*\n \t\t * FIXME: For rename/add-source conflicts (if we could detect\n \t\t * such), this is wrong.  We should instead find a unique\n@@ -1261,12 +1263,13 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \tremove_file(o, 1, a->path, o->call_depth || would_lose_untracked(a->path));\n \tremove_file(o, 1, b->path, o->call_depth || would_lose_untracked(b->path));\n \n-\tmfi_c1 = merge_file_special_markers(o, a, c1, &ci->ren1_other,\n+\tif (merge_file_special_markers(o, a, c1, &ci->ren1_other,\n \t\t\t\t\t    o->branch1, c1->path,\n-\t\t\t\t\t    o->branch2, ci->ren1_other.path);\n-\tmfi_c2 = merge_file_special_markers(o, b, &ci->ren2_other, c2,\n+\t\t\t\t\t    o->branch2, ci->ren1_other.path, &mfi_c1) ||\n+\t    merge_file_special_markers(o, b, &ci->ren2_other, c2,\n \t\t\t\t\t    o->branch1, ci->ren2_other.path,\n-\t\t\t\t\t    o->branch2, c2->path);\n+\t\t\t\t\t    o->branch2, c2->path, &mfi_c2))\n+\t\treturn;\n \n \tif (o->call_depth) {\n \t\t/*\n@@ -1489,12 +1492,13 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t       ren1_dst, branch2);\n \t\t\t\tif (o->call_depth) {\n \t\t\t\t\tstruct merge_file_info mfi;\n-\t\t\t\t\tmfi = merge_file_one(o, ren1_dst, &null_oid, 0,\n+\t\t\t\t\tif (merge_file_one(o, ren1_dst, &null_oid, 0,\n \t\t\t\t\t\t\t &ren1->pair->two->oid,\n \t\t\t\t\t\t\t ren1->pair->two->mode,\n \t\t\t\t\t\t\t &dst_other.oid,\n \t\t\t\t\t\t\t dst_other.mode,\n-\t\t\t\t\t\t\t branch1, branch2);\n+\t\t\t\t\t\t\t branch1, branch2, &mfi))\n+\t\t\t\t\t\treturn -1;\n \t\t\t\t\toutput(o, 1, _(\"Adding merged %s\"), ren1_dst);\n \t\t\t\t\tupdate_file(o, 0, &mfi.oid,\n \t\t\t\t\t\t    mfi.mode, ren1_dst);\n@@ -1652,9 +1656,10 @@ static int merge_content(struct merge_options *o,\n \t\tif (dir_in_way(path, !o->call_depth))\n \t\t\tdf_conflict_remains = 1;\n \t}\n-\tmfi = merge_file_special_markers(o, &one, &a, &b,\n+\tif (merge_file_special_markers(o, &one, &a, &b,\n \t\t\t\t\t o->branch1, path1,\n-\t\t\t\t\t o->branch2, path2);\n+\t\t\t\t\t o->branch2, path2, &mfi))\n+\t\treturn -1;\n \n \tif (mfi.clean && !df_conflict_remains &&\n \t    oid_eq(&mfi.oid, a_oid) && mfi.mode == a_mode) {\n-- \n2.9.0.278.g1caae67\n\n\n"},{"id":"290945","messageId":"fc331704b5d9fdf58bded97eaf4ac866998180d8.1467902083.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467902082.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 08/16] merge-recursive: allow write_tree_from_memory() to error out","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-07T14:35:47Z","receivedAt":"2016-07-07T14:36:14Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It is possible that a tree cannot be written (think: disk full). We\nwill want to give the caller a chance to clean up instead of letting\nthe program die() in such a case.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex d9221ce..09f4e27 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1903,8 +1903,8 @@ int merge_trees(struct merge_options *o,\n \telse\n \t\tclean = 1;\n \n-\tif (o->call_depth)\n-\t\t*result = write_tree_from_memory(o);\n+\tif (o->call_depth && !(*result = write_tree_from_memory(o)))\n+\t\treturn -1;\n \n \treturn clean;\n }\n-- \n2.9.0.278.g1caae67\n\n\n"},{"id":"290946","messageId":"0561daa9da77b6ee0fcfabc0fa8a7c77e511f1e5.1467902083.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467902082.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 09/16] merge-recursive: handle return values indicating errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-07T14:35:50Z","receivedAt":"2016-07-07T14:36:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We are about to libify the recursive merge machinery, where we only\ndie() in case of a bug or memory contention. To that end, we must heed\nnegative return values as indicating errors.\n\nThis requires our functions to be careful to pass through error\nconditions in call chains, and for quite a few functions this means\nthat they have to return values to begin with.\n\nThe next step will be to convert the places where we currently die() to\nreturn negative values (read: -1) instead.\n\nNote that we ignore errors reported by make_room_for_path(), consistent\nwith the previous behavior (update_file_flags() used the return value of\nmake_room_for_path() only to indicate an early return, but not a fatal\nerror): if the error is really a fatal error, we will notice later; If\nnot, it was not that serious a problem to begin with. (Witnesses in\nfavor of this reasoning are t4151-am-abort and t7610-mergetool, which\nwould start failing if we stopped on errors reported by\nmake_room_for_path()).\n\nNote: while this patch makes the code slightly less readable in\nupdate_file_flags() (we introduce a new \"goto free_buf;\" instead of\nan explicit \"free(buf); return;\"), it is a preparatory change for\nthe next patch where we will convert all of the die() calls in the same\nfunction to go through the free_buf return path instead.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 199 +++++++++++++++++++++++++++++++++---------------------\n 1 file changed, 123 insertions(+), 76 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 09f4e27..89eb937 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -733,7 +733,7 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n }\n \n-static void update_file_flags(struct merge_options *o,\n+static int update_file_flags(struct merge_options *o,\n \t\t\t      const struct object_id *oid,\n \t\t\t      unsigned mode,\n \t\t\t      const char *path,\n@@ -766,8 +766,7 @@ static void update_file_flags(struct merge_options *o,\n \n \t\tif (make_room_for_path(o, path) < 0) {\n \t\t\tupdate_wd = 0;\n-\t\t\tfree(buf);\n-\t\t\tgoto update_index;\n+\t\t\tgoto free_buf;\n \t\t}\n \t\tif (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) {\n \t\t\tint fd;\n@@ -823,20 +822,22 @@ static void update_file_flags(struct merge_options *o,\n \t\t} else\n \t\t\tdie(_(\"do not know what to do with %06o %s '%s'\"),\n \t\t\t    mode, oid_to_hex(oid), path);\n+ free_buf:\n \t\tfree(buf);\n \t}\n  update_index:\n \tif (update_cache)\n \t\tadd_cacheinfo(mode, oid, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n+\treturn 0;\n }\n \n-static void update_file(struct merge_options *o,\n+static int update_file(struct merge_options *o,\n \t\t\tint clean,\n \t\t\tconst struct object_id *oid,\n \t\t\tunsigned mode,\n \t\t\tconst char *path)\n {\n-\tupdate_file_flags(o, oid, mode, path, o->call_depth || clean, !o->call_depth);\n+\treturn update_file_flags(o, oid, mode, path, o->call_depth || clean, !o->call_depth);\n }\n \n /* Low level file merging, update and removal */\n@@ -1034,7 +1035,7 @@ static int merge_file_one(struct merge_options *o,\n \treturn merge_file_1(o, &one, &a, &b, branch1, branch2, mfi);\n }\n \n-static void handle_change_delete(struct merge_options *o,\n+static int handle_change_delete(struct merge_options *o,\n \t\t\t\t const char *path,\n \t\t\t\t const struct object_id *o_oid, int o_mode,\n \t\t\t\t const struct object_id *a_oid, int a_mode,\n@@ -1042,6 +1043,7 @@ static void handle_change_delete(struct merge_options *o,\n \t\t\t\t const char *change, const char *change_past)\n {\n \tchar *renamed = NULL;\n+\tint ret = 0;\n \tif (dir_in_way(path, !o->call_depth)) {\n \t\trenamed = unique_path(o, path, a_oid ? o->branch1 : o->branch2);\n \t}\n@@ -1052,21 +1054,22 @@ static void handle_change_delete(struct merge_options *o,\n \t\t * correct; since there is no true \"middle point\" between\n \t\t * them, simply reuse the base version for virtual merge base.\n \t\t */\n-\t\tremove_file_from_cache(path);\n-\t\tupdate_file(o, 0, o_oid, o_mode, renamed ? renamed : path);\n+\t\tret = remove_file_from_cache(path);\n+\t\tif (!ret)\n+\t\t\tret = update_file(o, 0, o_oid, o_mode, renamed ? renamed : path);\n \t} else if (!a_oid) {\n \t\tif (!renamed) {\n \t\t\toutput(o, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n \t\t\t       \"and %s in %s. Version %s of %s left in tree.\"),\n \t\t\t       change, path, o->branch1, change_past,\n \t\t\t       o->branch2, o->branch2, path);\n-\t\t\tupdate_file(o, 0, b_oid, b_mode, path);\n+\t\t\tret = update_file(o, 0, b_oid, b_mode, path);\n \t\t} else {\n \t\t\toutput(o, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n \t\t\t       \"and %s in %s. Version %s of %s left in tree at %s.\"),\n \t\t\t       change, path, o->branch1, change_past,\n \t\t\t       o->branch2, o->branch2, path, renamed);\n-\t\t\tupdate_file(o, 0, b_oid, b_mode, renamed);\n+\t\t\tret = update_file(o, 0, b_oid, b_mode, renamed);\n \t\t}\n \t} else {\n \t\tif (!renamed) {\n@@ -1079,7 +1082,7 @@ static void handle_change_delete(struct merge_options *o,\n \t\t\t       \"and %s in %s. Version %s of %s left in tree at %s.\"),\n \t\t\t       change, path, o->branch2, change_past,\n \t\t\t       o->branch1, o->branch1, path, renamed);\n-\t\t\tupdate_file(o, 0, a_oid, a_mode, renamed);\n+\t\t\tret = update_file(o, 0, a_oid, a_mode, renamed);\n \t\t}\n \t\t/*\n \t\t * No need to call update_file() on path when !renamed, since\n@@ -1089,9 +1092,11 @@ static void handle_change_delete(struct merge_options *o,\n \t\t */\n \t}\n \tfree(renamed);\n+\n+\treturn ret;\n }\n \n-static void conflict_rename_delete(struct merge_options *o,\n+static int conflict_rename_delete(struct merge_options *o,\n \t\t\t\t   struct diff_filepair *pair,\n \t\t\t\t   const char *rename_branch,\n \t\t\t\t   const char *other_branch)\n@@ -1111,21 +1116,20 @@ static void conflict_rename_delete(struct merge_options *o,\n \t\tb_mode = dest->mode;\n \t}\n \n-\thandle_change_delete(o,\n+\tif (handle_change_delete(o,\n \t\t\t     o->call_depth ? orig->path : dest->path,\n \t\t\t     &orig->oid, orig->mode,\n \t\t\t     a_oid, a_mode,\n \t\t\t     b_oid, b_mode,\n-\t\t\t     _(\"rename\"), _(\"renamed\"));\n+\t\t\t     _(\"rename\"), _(\"renamed\")))\n+\t\treturn -1;\n \n-\tif (o->call_depth) {\n-\t\tremove_file_from_cache(dest->path);\n-\t} else {\n-\t\tupdate_stages(dest->path, NULL,\n+\tif (o->call_depth)\n+\t\treturn remove_file_from_cache(dest->path);\n+\telse\n+\t\treturn update_stages(dest->path, NULL,\n \t\t\t      rename_branch == o->branch1 ? dest : NULL,\n \t\t\t      rename_branch == o->branch1 ? NULL : dest);\n-\t}\n-\n }\n \n static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,\n@@ -1141,7 +1145,7 @@ static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,\n \treturn target;\n }\n \n-static void handle_file(struct merge_options *o,\n+static int handle_file(struct merge_options *o,\n \t\t\tstruct diff_filespec *rename,\n \t\t\tint stage,\n \t\t\tstruct rename_conflict_info *ci)\n@@ -1151,6 +1155,7 @@ static void handle_file(struct merge_options *o,\n \tconst char *cur_branch, *other_branch;\n \tstruct diff_filespec other;\n \tstruct diff_filespec *add;\n+\tint ret;\n \n \tif (stage == 2) {\n \t\tdst_entry = ci->dst_entry1;\n@@ -1165,7 +1170,8 @@ static void handle_file(struct merge_options *o,\n \tadd = filespec_from_entry(&other, dst_entry, stage ^ 1);\n \tif (add) {\n \t\tchar *add_name = unique_path(o, rename->path, other_branch);\n-\t\tupdate_file(o, 0, &add->oid, add->mode, add_name);\n+\t\tif (update_file(o, 0, &add->oid, add->mode, add_name))\n+\t\t\treturn -1;\n \n \t\tremove_file(o, 0, rename->path, 0);\n \t\tdst_name = unique_path(o, rename->path, cur_branch);\n@@ -1176,17 +1182,20 @@ static void handle_file(struct merge_options *o,\n \t\t\t       rename->path, other_branch, dst_name);\n \t\t}\n \t}\n-\tupdate_file(o, 0, &rename->oid, rename->mode, dst_name);\n-\tif (stage == 2)\n-\t\tupdate_stages(rename->path, NULL, rename, add);\n+\tif ((ret = update_file(o, 0, &rename->oid, rename->mode, dst_name)))\n+\t\t; /* fall through, do allow dst_name to be released */\n+\telse if (stage == 2)\n+\t\tret = update_stages(rename->path, NULL, rename, add);\n \telse\n-\t\tupdate_stages(rename->path, NULL, add, rename);\n+\t\tret = update_stages(rename->path, NULL, add, rename);\n \n \tif (dst_name != rename->path)\n \t\tfree(dst_name);\n+\n+\treturn ret;\n }\n \n-static void conflict_rename_rename_1to2(struct merge_options *o,\n+static int conflict_rename_rename_1to2(struct merge_options *o,\n \t\t\t\t\tstruct rename_conflict_info *ci)\n {\n \t/* One file was renamed in both branches, but to different names. */\n@@ -1209,14 +1218,16 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n \t\t\t\t &a->oid, a->mode,\n \t\t\t\t &b->oid, b->mode,\n \t\t\t\t ci->branch1, ci->branch2, &mfi))\n-\t\t\treturn;\n+\t\t\treturn -1;\n+\n \t\t/*\n \t\t * FIXME: For rename/add-source conflicts (if we could detect\n \t\t * such), this is wrong.  We should instead find a unique\n \t\t * pathname and then either rename the add-source file to that\n \t\t * unique path, or use that unique path instead of src here.\n \t\t */\n-\t\tupdate_file(o, 0, &mfi.oid, mfi.mode, one->path);\n+\t\tif (update_file(o, 0, &mfi.oid, mfi.mode, one->path))\n+\t\t\treturn -1;\n \n \t\t/*\n \t\t * Above, we put the merged content at the merge-base's\n@@ -1227,22 +1238,26 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n \t\t * resolving the conflict at that path in its favor.\n \t\t */\n \t\tadd = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);\n-\t\tif (add)\n-\t\t\tupdate_file(o, 0, &add->oid, add->mode, a->path);\n+\t\tif (add) {\n+\t\t\tif (update_file(o, 0, &add->oid, add->mode, a->path))\n+\t\t\t\treturn -1;\n+\t\t}\n \t\telse\n \t\t\tremove_file_from_cache(a->path);\n \t\tadd = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);\n-\t\tif (add)\n-\t\t\tupdate_file(o, 0, &add->oid, add->mode, b->path);\n+\t\tif (add) {\n+\t\t\tif (update_file(o, 0, &add->oid, add->mode, b->path))\n+\t\t\t\treturn -1;\n+\t\t}\n \t\telse\n \t\t\tremove_file_from_cache(b->path);\n-\t} else {\n-\t\thandle_file(o, a, 2, ci);\n-\t\thandle_file(o, b, 3, ci);\n-\t}\n+\t} else if (handle_file(o, a, 2, ci) || handle_file(o, b, 3, ci))\n+\t\treturn -1;\n+\n+\treturn 0;\n }\n \n-static void conflict_rename_rename_2to1(struct merge_options *o,\n+static int conflict_rename_rename_2to1(struct merge_options *o,\n \t\t\t\t\tstruct rename_conflict_info *ci)\n {\n \t/* Two files, a & b, were renamed to the same thing, c. */\n@@ -1253,6 +1268,7 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \tchar *path = c1->path; /* == c2->path */\n \tstruct merge_file_info mfi_c1;\n \tstruct merge_file_info mfi_c2;\n+\tint ret;\n \n \toutput(o, 1, _(\"CONFLICT (rename/rename): \"\n \t       \"Rename %s->%s in %s. \"\n@@ -1269,7 +1285,7 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \t    merge_file_special_markers(o, b, &ci->ren2_other, c2,\n \t\t\t\t\t    o->branch1, ci->ren2_other.path,\n \t\t\t\t\t    o->branch2, c2->path, &mfi_c2))\n-\t\treturn;\n+\t\treturn -1;\n \n \tif (o->call_depth) {\n \t\t/*\n@@ -1280,19 +1296,25 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \t\t * again later for the non-recursive merge.\n \t\t */\n \t\tremove_file(o, 0, path, 0);\n-\t\tupdate_file(o, 0, &mfi_c1.oid, mfi_c1.mode, a->path);\n-\t\tupdate_file(o, 0, &mfi_c2.oid, mfi_c2.mode, b->path);\n+\t\tret = update_file(o, 0, &mfi_c1.oid, mfi_c1.mode, a->path);\n+\t\tif (!ret)\n+\t\t\tret = update_file(o, 0, &mfi_c2.oid, mfi_c2.mode,\n+\t\t\t\tb->path);\n \t} else {\n \t\tchar *new_path1 = unique_path(o, path, ci->branch1);\n \t\tchar *new_path2 = unique_path(o, path, ci->branch2);\n \t\toutput(o, 1, _(\"Renaming %s to %s and %s to %s instead\"),\n \t\t       a->path, new_path1, b->path, new_path2);\n \t\tremove_file(o, 0, path, 0);\n-\t\tupdate_file(o, 0, &mfi_c1.oid, mfi_c1.mode, new_path1);\n-\t\tupdate_file(o, 0, &mfi_c2.oid, mfi_c2.mode, new_path2);\n+\t\tret = update_file(o, 0, &mfi_c1.oid, mfi_c1.mode, new_path1);\n+\t\tif (!ret)\n+\t\t\tret = update_file(o, 0, &mfi_c2.oid, mfi_c2.mode,\n+\t\t\t\tnew_path2);\n \t\tfree(new_path2);\n \t\tfree(new_path1);\n \t}\n+\n+\treturn ret;\n }\n \n static int process_renames(struct merge_options *o,\n@@ -1477,12 +1499,13 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t * update_file_flags() instead of\n \t\t\t\t * update_file().\n \t\t\t\t */\n-\t\t\t\tupdate_file_flags(o,\n+\t\t\t\tif (update_file_flags(o,\n \t\t\t\t\t\t  &ren1->pair->two->oid,\n \t\t\t\t\t\t  ren1->pair->two->mode,\n \t\t\t\t\t\t  ren1_dst,\n \t\t\t\t\t\t  1, /* update_cache */\n-\t\t\t\t\t\t  0  /* update_wd    */);\n+\t\t\t\t\t\t  0  /* update_wd    */))\n+\t\t\t\t\tclean_merge = -1;\n \t\t\t} else if (!oid_eq(&dst_other.oid, &null_oid)) {\n \t\t\t\tclean_merge = 0;\n \t\t\t\ttry_merge = 1;\n@@ -1497,22 +1520,28 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t\t\t\t ren1->pair->two->mode,\n \t\t\t\t\t\t\t &dst_other.oid,\n \t\t\t\t\t\t\t dst_other.mode,\n-\t\t\t\t\t\t\t branch1, branch2, &mfi))\n-\t\t\t\t\t\treturn -1;\n+\t\t\t\t\t\t\t branch1, branch2, &mfi)) {\n+\t\t\t\t\t\tclean_merge = -1;\n+\t\t\t\t\t\tgoto cleanup_and_return;\n+\t\t\t\t\t}\n \t\t\t\t\toutput(o, 1, _(\"Adding merged %s\"), ren1_dst);\n-\t\t\t\t\tupdate_file(o, 0, &mfi.oid,\n-\t\t\t\t\t\t    mfi.mode, ren1_dst);\n+\t\t\t\t\tif (update_file(o, 0, &mfi.oid,\n+\t\t\t\t\t\t\tmfi.mode, ren1_dst))\n+\t\t\t\t\t\tclean_merge = -1;\n \t\t\t\t\ttry_merge = 0;\n \t\t\t\t} else {\n \t\t\t\t\tchar *new_path = unique_path(o, ren1_dst, branch2);\n \t\t\t\t\toutput(o, 1, _(\"Adding as %s instead\"), new_path);\n-\t\t\t\t\tupdate_file(o, 0, &dst_other.oid,\n-\t\t\t\t\t\t    dst_other.mode, new_path);\n+\t\t\t\t\tif (update_file(o, 0, &dst_other.oid,\n+\t\t\t\t\t\t    dst_other.mode, new_path))\n+\t\t\t\t\t\tclean_merge = -1;\n \t\t\t\t\tfree(new_path);\n \t\t\t\t}\n \t\t\t} else\n \t\t\t\ttry_merge = 1;\n \n+\t\t\tif (clean_merge < 0)\n+\t\t\t\tgoto cleanup_and_return;\n \t\t\tif (try_merge) {\n \t\t\t\tstruct diff_filespec *one, *a, *b;\n \t\t\t\tsrc_other.path = (char *)ren1_src;\n@@ -1539,6 +1568,7 @@ static int process_renames(struct merge_options *o,\n \t\t\t}\n \t\t}\n \t}\n+cleanup_and_return:\n \tstring_list_clear(&a_by_dst, 0);\n \tstring_list_clear(&b_by_dst, 0);\n \n@@ -1601,13 +1631,13 @@ error_return:\n \treturn ret;\n }\n \n-static void handle_modify_delete(struct merge_options *o,\n+static int handle_modify_delete(struct merge_options *o,\n \t\t\t\t const char *path,\n \t\t\t\t struct object_id *o_oid, int o_mode,\n \t\t\t\t struct object_id *a_oid, int a_mode,\n \t\t\t\t struct object_id *b_oid, int b_mode)\n {\n-\thandle_change_delete(o,\n+\treturn handle_change_delete(o,\n \t\t\t     path,\n \t\t\t     o_oid, o_mode,\n \t\t\t     a_oid, a_mode,\n@@ -1686,7 +1716,8 @@ static int merge_content(struct merge_options *o,\n \t\toutput(o, 1, _(\"CONFLICT (%s): Merge conflict in %s\"),\n \t\t\t\treason, path);\n \t\tif (rename_conflict_info && !df_conflict_remains)\n-\t\t\tupdate_stages(path, &one, &a, &b);\n+\t\t\tif (update_stages(path, &one, &a, &b))\n+\t\t\t\treturn -1;\n \t}\n \n \tif (df_conflict_remains) {\n@@ -1694,30 +1725,33 @@ static int merge_content(struct merge_options *o,\n \t\tif (o->call_depth) {\n \t\t\tremove_file_from_cache(path);\n \t\t} else {\n-\t\t\tif (!mfi.clean)\n-\t\t\t\tupdate_stages(path, &one, &a, &b);\n-\t\t\telse {\n+\t\t\tif (!mfi.clean) {\n+\t\t\t\tif (update_stages(path, &one, &a, &b))\n+\t\t\t\t\treturn -1;\n+\t\t\t} else {\n \t\t\t\tint file_from_stage2 = was_tracked(path);\n \t\t\t\tstruct diff_filespec merged;\n \t\t\t\toidcpy(&merged.oid, &mfi.oid);\n \t\t\t\tmerged.mode = mfi.mode;\n \n-\t\t\t\tupdate_stages(path, NULL,\n+\t\t\t\tif (update_stages(path, NULL,\n \t\t\t\t\t      file_from_stage2 ? &merged : NULL,\n-\t\t\t\t\t      file_from_stage2 ? NULL : &merged);\n+\t\t\t\t\t      file_from_stage2 ? NULL : &merged))\n+\t\t\t\t\treturn -1;\n \t\t\t}\n \n \t\t}\n \t\tnew_path = unique_path(o, path, rename_conflict_info->branch1);\n \t\toutput(o, 1, _(\"Adding as %s instead\"), new_path);\n-\t\tupdate_file(o, 0, &mfi.oid, mfi.mode, new_path);\n+\t\tif (update_file(o, 0, &mfi.oid, mfi.mode, new_path)) {\n+\t\t\tfree(new_path);\n+\t\t\treturn -1;\n+\t\t}\n \t\tfree(new_path);\n \t\tmfi.clean = 0;\n-\t} else {\n-\t\tupdate_file(o, mfi.clean, &mfi.oid, mfi.mode, path);\n-\t}\n+\t} else if (update_file(o, mfi.clean, &mfi.oid, mfi.mode, path))\n+\t\treturn -1;\n \treturn mfi.clean;\n-\n }\n \n /* Per entry merge function */\n@@ -1745,17 +1779,21 @@ static int process_entry(struct merge_options *o,\n \t\t\tbreak;\n \t\tcase RENAME_DELETE:\n \t\t\tclean_merge = 0;\n-\t\t\tconflict_rename_delete(o, conflict_info->pair1,\n+\t\t\tif (conflict_rename_delete(o,\n+\t\t\t\t\t       conflict_info->pair1,\n \t\t\t\t\t       conflict_info->branch1,\n-\t\t\t\t\t       conflict_info->branch2);\n+\t\t\t\t\t       conflict_info->branch2))\n+\t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_ONE_FILE_TO_TWO:\n \t\t\tclean_merge = 0;\n-\t\t\tconflict_rename_rename_1to2(o, conflict_info);\n+\t\t\tif (conflict_rename_rename_1to2(o, conflict_info))\n+\t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_TWO_FILES_TO_ONE:\n \t\t\tclean_merge = 0;\n-\t\t\tconflict_rename_rename_2to1(o, conflict_info);\n+\t\t\tif (conflict_rename_rename_2to1(o, conflict_info))\n+\t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tdefault:\n \t\t\tentry->processed = 0;\n@@ -1775,8 +1813,9 @@ static int process_entry(struct merge_options *o,\n \t\t} else {\n \t\t\t/* Modify/delete; deleted side may have put a directory in the way */\n \t\t\tclean_merge = 0;\n-\t\t\thandle_modify_delete(o, path, o_oid, o_mode,\n-\t\t\t\t\t     a_oid, a_mode, b_oid, b_mode);\n+\t\t\tif (handle_modify_delete(o, path, o_oid, o_mode,\n+\t\t\t\t\t     a_oid, a_mode, b_oid, b_mode))\n+\t\t\t\tclean_merge = -1;\n \t\t}\n \t} else if ((!o_oid && a_oid && !b_oid) ||\n \t\t   (!o_oid && !a_oid && b_oid)) {\n@@ -1808,14 +1847,16 @@ static int process_entry(struct merge_options *o,\n \t\t\toutput(o, 1, _(\"CONFLICT (%s): There is a directory with name %s in %s. \"\n \t\t\t       \"Adding %s as %s\"),\n \t\t\t       conf, path, other_branch, path, new_path);\n-\t\t\tupdate_file(o, 0, oid, mode, new_path);\n-\t\t\tif (o->call_depth)\n+\t\t\tif (update_file(o, 0, oid, mode, new_path))\n+\t\t\t\tclean_merge = -1;\n+\t\t\telse if (o->call_depth)\n \t\t\t\tremove_file_from_cache(path);\n \t\t\tfree(new_path);\n \t\t} else {\n \t\t\toutput(o, 2, _(\"Adding %s\"), path);\n \t\t\t/* do not overwrite file if already present */\n-\t\t\tupdate_file_flags(o, oid, mode, path, 1, !a_oid);\n+\t\t\tif (update_file_flags(o, oid, mode, path, 1, !a_oid))\n+\t\t\t\tclean_merge = -1;\n \t\t}\n \t} else if (a_oid && b_oid) {\n \t\t/* Case C: Added in both (check for same permissions) and */\n@@ -1878,12 +1919,18 @@ int merge_trees(struct merge_options *o,\n \t\tre_head  = get_renames(o, head, common, head, merge, entries);\n \t\tre_merge = get_renames(o, merge, common, head, merge, entries);\n \t\tclean = process_renames(o, re_head, re_merge);\n+\t\tif (clean < 0)\n+\t\t\treturn clean;\n \t\tfor (i = entries->nr-1; 0 <= i; i--) {\n \t\t\tconst char *path = entries->items[i].string;\n \t\t\tstruct stage_data *e = entries->items[i].util;\n-\t\t\tif (!e->processed\n-\t\t\t\t&& !process_entry(o, path, e))\n-\t\t\t\tclean = 0;\n+\t\t\tif (!e->processed) {\n+\t\t\t\tint ret = process_entry(o, path, e);\n+\t\t\t\tif (!ret)\n+\t\t\t\t\tclean = 0;\n+\t\t\t\telse if (ret < 0)\n+\t\t\t\t\treturn ret;\n+\t\t\t}\n \t\t}\n \t\tfor (i = 0; i < entries->nr; i++) {\n \t\t\tstruct stage_data *e = entries->items[i].util;\n-- \n2.9.0.278.g1caae67\n\n\n"},{"id":"290947","messageId":"4d4d65e3f207c10ffa941a547e6394d3b51b2eaa.1467902083.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467902082.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 11/16] am -3: use merge_recursive() directly again","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-07T14:35:58Z","receivedAt":"2016-07-07T14:36:20Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Last October, we had to change this code to run `git merge-recursive`\nin a child process: git-am wants to print some helpful advice when the\nmerge failed, but the code in question was not prepared to return, it\ndie()d instead.\n\nWe are finally at a point when the code *is* prepared to return errors,\nand can avoid the child process again.\n\nThis reverts commit c63d4b2 (am -3: do not let failed merge from\ncompleting the error codepath, 2015-10-09), with the necessary changes\nto adjust for the fact that Git's source code changed in the meantime\n(such as: using OIDs instead of hashes in the recursive merge). While at\nit, the same undesired gender bias is addressed in the same spirit as in\nthe separately submitted patch in $gmane/299009.\n\nNote: the code now calls merge_recursive_generic() again. Unlike\nmerge_trees() and merge_recursive(), this function returns 0 upon success,\nas most of Git's functions. Therefore, the error value -1 naturally is\nhandled correctly, and we do not have to take care of it specifically.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/am.c | 63 ++++++++++++++++++++++--------------------------------------\n 1 file changed, 23 insertions(+), 40 deletions(-)\n\ndiff --git a/builtin/am.c b/builtin/am.c\nindex f9a724e..8dc4239 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -29,6 +29,7 @@\n #include \"prompt.h\"\n #include \"mailinfo.h\"\n #include \"apply.h\"\n+#include \"object.h\"\n \n /**\n  * Returns the length of the first line of msg.\n@@ -1623,47 +1624,18 @@ static int build_fake_ancestor(const struct am_state *state, const char *index_f\n }\n \n /**\n- * Do the three-way merge using fake ancestor, his tree constructed\n- * from the fake ancestor and the postimage of the patch, and our\n- * state.\n- */\n-static int run_fallback_merge_recursive(const struct am_state *state,\n-\t\t\t\t\tunsigned char *orig_tree,\n-\t\t\t\t\tunsigned char *our_tree,\n-\t\t\t\t\tunsigned char *his_tree)\n-{\n-\tstruct child_process cp = CHILD_PROCESS_INIT;\n-\tint status;\n-\n-\tcp.git_cmd = 1;\n-\n-\targv_array_pushf(&cp.env_array, \"GITHEAD_%s=%.*s\",\n-\t\t\t sha1_to_hex(his_tree), linelen(state->msg), state->msg);\n-\tif (state->quiet)\n-\t\targv_array_push(&cp.env_array, \"GIT_MERGE_VERBOSITY=0\");\n-\n-\targv_array_push(&cp.args, \"merge-recursive\");\n-\targv_array_push(&cp.args, sha1_to_hex(orig_tree));\n-\targv_array_push(&cp.args, \"--\");\n-\targv_array_push(&cp.args, sha1_to_hex(our_tree));\n-\targv_array_push(&cp.args, sha1_to_hex(his_tree));\n-\n-\tstatus = run_command(&cp) ? (-1) : 0;\n-\tdiscard_cache();\n-\tread_cache();\n-\treturn status;\n-}\n-\n-/**\n  * Attempt a threeway merge, using index_path as the temporary index.\n  */\n static int fall_back_threeway(const struct am_state *state, const char *index_path)\n {\n-\tunsigned char orig_tree[GIT_SHA1_RAWSZ], his_tree[GIT_SHA1_RAWSZ],\n-\t\t      our_tree[GIT_SHA1_RAWSZ];\n+\tstruct object_id orig_tree, her_tree, our_tree;\n+\tconst struct object_id *bases[1] = { &orig_tree };\n+\tstruct merge_options o;\n+\tstruct commit *result;\n+\tchar *her_tree_name;\n \n-\tif (get_sha1(\"HEAD\", our_tree) < 0)\n-\t\thashcpy(our_tree, EMPTY_TREE_SHA1_BIN);\n+\tif (get_oid(\"HEAD\", &our_tree) < 0)\n+\t\thashcpy(our_tree.hash, EMPTY_TREE_SHA1_BIN);\n \n \tif (build_fake_ancestor(state, index_path))\n \t\treturn error(\"could not build fake ancestor\");\n@@ -1671,7 +1643,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \tdiscard_cache();\n \tread_cache_from(index_path);\n \n-\tif (write_index_as_tree(orig_tree, &the_index, index_path, 0, NULL))\n+\tif (write_index_as_tree(orig_tree.hash, &the_index, index_path, 0, NULL))\n \t\treturn error(_(\"Repository lacks necessary blobs to fall back on 3-way merge.\"));\n \n \tsay(state, stdout, _(\"Using index info to reconstruct a base tree...\"));\n@@ -1687,7 +1659,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \t\tinit_revisions(&rev_info, NULL);\n \t\trev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;\n \t\tdiff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix);\n-\t\tadd_pending_sha1(&rev_info, \"HEAD\", our_tree, 0);\n+\t\tadd_pending_sha1(&rev_info, \"HEAD\", our_tree.hash, 0);\n \t\tdiff_setup_done(&rev_info.diffopt);\n \t\trun_diff_index(&rev_info, 1);\n \t}\n@@ -1696,7 +1668,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \t\treturn error(_(\"Did you hand edit your patch?\\n\"\n \t\t\t\t\"It does not apply to blobs recorded in its index.\"));\n \n-\tif (write_index_as_tree(his_tree, &the_index, index_path, 0, NULL))\n+\tif (write_index_as_tree(her_tree.hash, &the_index, index_path, 0, NULL))\n \t\treturn error(\"could not write tree\");\n \n \tsay(state, stdout, _(\"Falling back to patching base and 3-way merge...\"));\n@@ -1712,11 +1684,22 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \t * changes.\n \t */\n \n-\tif (run_fallback_merge_recursive(state, orig_tree, our_tree, his_tree)) {\n+\tinit_merge_options(&o);\n+\n+\to.branch1 = \"HEAD\";\n+\ther_tree_name = xstrfmt(\"%.*s\", linelen(state->msg), state->msg);\n+\to.branch2 = her_tree_name;\n+\n+\tif (state->quiet)\n+\t\to.verbosity = 0;\n+\n+\tif (merge_recursive_generic(&o, &our_tree, &her_tree, 1, bases, &result)) {\n \t\trerere(state->allow_rerere_autoupdate);\n+\t\tfree(her_tree_name);\n \t\treturn error(_(\"Failed to merge in the changes.\"));\n \t}\n \n+\tfree(her_tree_name);\n \treturn 0;\n }\n \n-- \n2.9.0.278.g1caae67\n\n\n"},{"id":"290948","messageId":"c0b4356c3469231c7b6ebe5a031acc98937f6746.1467902083.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467902082.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 10/16] merge-recursive: switch to returning errors instead of dying","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-07T14:35:53Z","receivedAt":"2016-07-07T14:36:23Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The recursive merge machinery is supposed to be a library function, i.e.\nit should return an error when it fails. Originally the functions were\npart of the builtin \"merge-recursive\", though, where it was simpler to\ncall die() and be done with error handling.\n\nThe existing callers were already prepared to detect negative return\nvalues to indicate errors and to behave as previously: exit with code 128\n(which is the same thing that die() does, after printing the message).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 63 +++++++++++++++++++++++++++++++------------------------\n 1 file changed, 36 insertions(+), 27 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 89eb937..7c9f22c 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -266,8 +266,10 @@ struct tree *write_tree_from_memory(struct merge_options *o)\n \t\tactive_cache_tree = cache_tree();\n \n \tif (!cache_tree_fully_valid(active_cache_tree) &&\n-\t    cache_tree_update(&the_index, 0) < 0)\n-\t\tdie(_(\"error building trees\"));\n+\t    cache_tree_update(&the_index, 0) < 0) {\n+\t\terror(_(\"error building trees\"));\n+\t\treturn NULL;\n+\t}\n \n \tresult = lookup_tree(active_cache_tree->sha1);\n \n@@ -707,12 +709,10 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \t/* Make sure leading directories are created */\n \tstatus = safe_create_leading_directories_const(path);\n \tif (status) {\n-\t\tif (status == SCLD_EXISTS) {\n+\t\tif (status == SCLD_EXISTS)\n \t\t\t/* something else exists */\n-\t\t\terror(msg, path, _(\": perhaps a D/F conflict?\"));\n-\t\t\treturn -1;\n-\t\t}\n-\t\tdie(msg, path, \"\");\n+\t\t\treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n+\t\treturn error(msg, path, \"\");\n \t}\n \n \t/*\n@@ -740,6 +740,8 @@ static int update_file_flags(struct merge_options *o,\n \t\t\t      int update_cache,\n \t\t\t      int update_wd)\n {\n+\tint ret = 0;\n+\n \tif (o->call_depth)\n \t\tupdate_wd = 0;\n \n@@ -760,9 +762,11 @@ static int update_file_flags(struct merge_options *o,\n \n \t\tbuf = read_sha1_file(oid->hash, &type, &size);\n \t\tif (!buf)\n-\t\t\tdie(_(\"cannot read object %s '%s'\"), oid_to_hex(oid), path);\n-\t\tif (type != OBJ_BLOB)\n-\t\t\tdie(_(\"blob expected for %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\treturn error(_(\"cannot read object %s '%s'\"), oid_to_hex(oid), path);\n+\t\tif (type != OBJ_BLOB) {\n+\t\t\tret = error(_(\"blob expected for %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\tgoto free_buf;\n+\t\t}\n \n \t\tif (make_room_for_path(o, path) < 0) {\n \t\t\tupdate_wd = 0;\n@@ -778,8 +782,10 @@ static int update_file_flags(struct merge_options *o,\n \t\t\telse\n \t\t\t\tmode = 0666;\n \t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n-\t\t\tif (fd < 0)\n-\t\t\t\tdie_errno(_(\"failed to open '%s'\"), path);\n+\t\t\tif (fd < 0) {\n+\t\t\t\tret = error_errno(_(\"failed to open '%s'\"), path);\n+\t\t\t\tgoto free_buf;\n+\t\t\t}\n \n \t\t\tsmudge_to_file = can_smudge_to_file(path);\n \n@@ -792,8 +798,10 @@ static int update_file_flags(struct merge_options *o,\n \t\t\t\t\t * creation. */\n \t\t\t\t\tsmudge_to_file = 0;\n \t\t\t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n-\t\t\t\t\tif (fd < 0)\n-\t\t\t\t\t\tdie_errno(_(\"failed to open '%s'\"), path);\n+\t\t\t\t\tif (fd < 0) {\n+\t\t\t\t\t\tret = error_errno(_(\"failed to open '%s'\"), path);\n+\t\t\t\t\t\tgoto free_buf;\n+\t\t\t\t\t}\n \t\t\t\t}\n \t\t\t\telse {\n \t\t\t\t\tclose(fd);\n@@ -817,18 +825,18 @@ static int update_file_flags(struct merge_options *o,\n \t\t\tsafe_create_leading_directories_const(path);\n \t\t\tunlink(path);\n \t\t\tif (symlink(lnk, path))\n-\t\t\t\tdie_errno(_(\"failed to symlink '%s'\"), path);\n+\t\t\t\tret = error_errno(_(\"failed to symlink '%s'\"), path);\n \t\t\tfree(lnk);\n \t\t} else\n-\t\t\tdie(_(\"do not know what to do with %06o %s '%s'\"),\n+\t\t\tret = error(_(\"do not know what to do with %06o %s '%s'\"),\n \t\t\t    mode, oid_to_hex(oid), path);\n  free_buf:\n \t\tfree(buf);\n \t}\n  update_index:\n-\tif (update_cache)\n+\tif (!ret && update_cache)\n \t\tadd_cacheinfo(mode, oid, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n-\treturn 0;\n+\treturn ret;\n }\n \n static int update_file(struct merge_options *o,\n@@ -954,20 +962,22 @@ static int merge_file_1(struct merge_options *o,\n \t\t\toidcpy(&result->oid, &a->oid);\n \t\telse if (S_ISREG(a->mode)) {\n \t\t\tmmbuffer_t result_buf;\n-\t\t\tint merge_status;\n+\t\t\tint ret = 0, merge_status;\n \n \t\t\tmerge_status = merge_3way(o, &result_buf, one, a, b,\n \t\t\t\t\t\t  branch1, branch2);\n \n \t\t\tif ((merge_status < 0) || !result_buf.ptr)\n-\t\t\t\tdie(_(\"Failed to execute internal merge\"));\n+\t\t\t\tret = error(_(\"Failed to execute internal merge\"));\n \n-\t\t\tif (write_sha1_file(result_buf.ptr, result_buf.size,\n+\t\t\tif (!ret && write_sha1_file(result_buf.ptr, result_buf.size,\n \t\t\t\t\t    blob_type, result->oid.hash))\n-\t\t\t\tdie(_(\"Unable to add %s to database\"),\n-\t\t\t\t    a->path);\n+\t\t\t\tret = error(_(\"Unable to add %s to database\"),\n+\t\t\t\t\ta->path);\n \n \t\t\tfree(result_buf.ptr);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n \t\t\tresult->clean = (merge_status == 0);\n \t\t} else if (S_ISGITLINK(a->mode)) {\n \t\t\tresult->clean = merge_submodule(result->oid.hash,\n@@ -1899,11 +1909,10 @@ int merge_trees(struct merge_options *o,\n \n \tif (code != 0) {\n \t\tif (show(o, 4) || o->call_depth)\n-\t\t\tdie(_(\"merging of trees %s and %s failed\"),\n+\t\t\terror(_(\"merging of trees %s and %s failed\"),\n \t\t\t    oid_to_hex(&head->object.oid),\n \t\t\t    oid_to_hex(&merge->object.oid));\n-\t\telse\n-\t\t\texit(128);\n+\t\treturn -1;\n \t}\n \n \tif (unmerged_cache()) {\n@@ -2034,7 +2043,7 @@ int merge_recursive(struct merge_options *o,\n \t\to->call_depth--;\n \n \t\tif (!merged_common_ancestors)\n-\t\t\tdie(_(\"merge returned no commit\"));\n+\t\t\treturn error(_(\"merge returned no commit\"));\n \t}\n \n \tdiscard_cache();\n-- \n2.9.0.278.g1caae67\n\n\n"},{"id":"290949","messageId":"75978451dbdd80bda88140b471e5918d39ec4e91.1467902083.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467902082.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 12/16] merge-recursive: flush output buffer before printing error messages","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-07T14:36:01Z","receivedAt":"2016-07-07T14:36:27Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The data structure passed to the recursive merge machinery has a feature\nwhere the caller can ask for the output to be buffered into a strbuf, by\nsetting the field 'buffer_output'.\n\nPreviously, we simply swallowed the buffered output when showing error\nmessages. With this patch, we show the output first, and only then print\nthe error message.\n\nCurrently, the only user of that buffering is merge_recursive() itself,\nto avoid the progress output to interfere.\n\nIn the next patches, we will introduce a new buffer_output mode that\nforces merge_recursive() to retain the output buffer for further\nprocessing by the caller. If the caller asked for that, we will then\nalso write the error messages into the output buffer. This is necessary\nto give the caller more control not only how to react in case of errors\nbut also control how/if to display the error messages.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 112 ++++++++++++++++++++++++++++++++----------------------\n 1 file changed, 66 insertions(+), 46 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 7c9f22c..205ea04 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -23,6 +23,28 @@\n #include \"dir.h\"\n #include \"submodule.h\"\n \n+static void flush_output(struct merge_options *o)\n+{\n+\tif (o->obuf.len) {\n+\t\tfputs(o->obuf.buf, stdout);\n+\t\tstrbuf_reset(&o->obuf);\n+\t}\n+}\n+\n+static int err(struct merge_options *o, const char *err, ...)\n+{\n+\tva_list params;\n+\n+\tva_start(params, err);\n+\tflush_output(o);\n+\tstrbuf_vaddf(&o->obuf, err, params);\n+\terror(\"%s\", o->obuf.buf);\n+\tstrbuf_reset(&o->obuf);\n+\tva_end(params);\n+\n+\treturn -1;\n+}\n+\n static struct tree *shift_tree_object(struct tree *one, struct tree *two,\n \t\t\t\t      const char *subtree_shift)\n {\n@@ -148,14 +170,6 @@ static int show(struct merge_options *o, int v)\n \treturn (!o->call_depth && o->verbosity >= v) || o->verbosity >= 5;\n }\n \n-static void flush_output(struct merge_options *o)\n-{\n-\tif (o->obuf.len) {\n-\t\tfputs(o->obuf.buf, stdout);\n-\t\tstrbuf_reset(&o->obuf);\n-\t}\n-}\n-\n __attribute__((format (printf, 3, 4)))\n static void output(struct merge_options *o, int v, const char *fmt, ...)\n {\n@@ -198,7 +212,8 @@ static void output_commit_title(struct merge_options *o, struct commit *commit)\n \t}\n }\n \n-static int add_cacheinfo(unsigned int mode, const struct object_id *oid,\n+static int add_cacheinfo(struct merge_options *o,\n+\t\tunsigned int mode, const struct object_id *oid,\n \t\tconst char *path, int stage, int refresh, int options)\n {\n \tstruct cache_entry *ce;\n@@ -206,7 +221,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid,\n \t\t\t      (refresh ? (CE_MATCH_REFRESH |\n \t\t\t\t\t  CE_MATCH_IGNORE_MISSING) : 0 ));\n \tif (!ce)\n-\t\treturn error(_(\"addinfo_cache failed for path '%s'\"), path);\n+\t\treturn err(o, _(\"addinfo_cache failed for path '%s'\"), path);\n \treturn add_cache_entry(ce, options);\n }\n \n@@ -267,7 +282,7 @@ struct tree *write_tree_from_memory(struct merge_options *o)\n \n \tif (!cache_tree_fully_valid(active_cache_tree) &&\n \t    cache_tree_update(&the_index, 0) < 0) {\n-\t\terror(_(\"error building trees\"));\n+\t\terr(o, _(\"error building trees\"));\n \t\treturn NULL;\n \t}\n \n@@ -535,7 +550,8 @@ static struct string_list *get_renames(struct merge_options *o,\n \treturn renames;\n }\n \n-static int update_stages(const char *path, const struct diff_filespec *o,\n+static int update_stages(struct merge_options *opt, const char *path,\n+\t\t\t const struct diff_filespec *o,\n \t\t\t const struct diff_filespec *a,\n \t\t\t const struct diff_filespec *b)\n {\n@@ -554,13 +570,13 @@ static int update_stages(const char *path, const struct diff_filespec *o,\n \t\tif (remove_file_from_cache(path))\n \t\t\treturn -1;\n \tif (o)\n-\t\tif (add_cacheinfo(o->mode, &o->oid, path, 1, 0, options))\n+\t\tif (add_cacheinfo(opt, o->mode, &o->oid, path, 1, 0, options))\n \t\t\treturn -1;\n \tif (a)\n-\t\tif (add_cacheinfo(a->mode, &a->oid, path, 2, 0, options))\n+\t\tif (add_cacheinfo(opt, a->mode, &a->oid, path, 2, 0, options))\n \t\t\treturn -1;\n \tif (b)\n-\t\tif (add_cacheinfo(b->mode, &b->oid, path, 3, 0, options))\n+\t\tif (add_cacheinfo(opt, b->mode, &b->oid, path, 3, 0, options))\n \t\t\treturn -1;\n \treturn 0;\n }\n@@ -711,8 +727,8 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \tif (status) {\n \t\tif (status == SCLD_EXISTS)\n \t\t\t/* something else exists */\n-\t\t\treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n-\t\treturn error(msg, path, \"\");\n+\t\t\treturn err(o, msg, path, _(\": perhaps a D/F conflict?\"));\n+\t\treturn err(o, msg, path, \"\");\n \t}\n \n \t/*\n@@ -720,7 +736,7 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \t * tracking it.\n \t */\n \tif (would_lose_untracked(path))\n-\t\treturn error(_(\"refusing to lose untracked file at '%s'\"),\n+\t\treturn err(o, _(\"refusing to lose untracked file at '%s'\"),\n \t\t\t     path);\n \n \t/* Successful unlink is good.. */\n@@ -730,7 +746,7 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \tif (errno == ENOENT)\n \t\treturn 0;\n \t/* .. but not some other error (who really cares what?) */\n-\treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n+\treturn err(o, msg, path, _(\": perhaps a D/F conflict?\"));\n }\n \n static int update_file_flags(struct merge_options *o,\n@@ -762,9 +778,9 @@ static int update_file_flags(struct merge_options *o,\n \n \t\tbuf = read_sha1_file(oid->hash, &type, &size);\n \t\tif (!buf)\n-\t\t\treturn error(_(\"cannot read object %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\treturn err(o, _(\"cannot read object %s '%s'\"), oid_to_hex(oid), path);\n \t\tif (type != OBJ_BLOB) {\n-\t\t\tret = error(_(\"blob expected for %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\tret = err(o, _(\"blob expected for %s '%s'\"), oid_to_hex(oid), path);\n \t\t\tgoto free_buf;\n \t\t}\n \n@@ -783,7 +799,8 @@ static int update_file_flags(struct merge_options *o,\n \t\t\t\tmode = 0666;\n \t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n \t\t\tif (fd < 0) {\n-\t\t\t\tret = error_errno(_(\"failed to open '%s'\"), path);\n+\t\t\t\tret = err(o, _(\"failed to open '%s': %s\"),\n+\t\t\t\t\tpath, strerror(errno));\n \t\t\t\tgoto free_buf;\n \t\t\t}\n \n@@ -825,17 +842,18 @@ static int update_file_flags(struct merge_options *o,\n \t\t\tsafe_create_leading_directories_const(path);\n \t\t\tunlink(path);\n \t\t\tif (symlink(lnk, path))\n-\t\t\t\tret = error_errno(_(\"failed to symlink '%s'\"), path);\n+\t\t\t\tret = err(o, _(\"failed to symlink '%s': %s\"),\n+\t\t\t\t\tpath, strerror(errno));\n \t\t\tfree(lnk);\n \t\t} else\n-\t\t\tret = error(_(\"do not know what to do with %06o %s '%s'\"),\n-\t\t\t    mode, oid_to_hex(oid), path);\n+\t\t\tret = err(o, _(\"do not know what to do with %06o %s '%s'\"),\n+\t\t\t\tmode, oid_to_hex(oid), path);\n  free_buf:\n \t\tfree(buf);\n \t}\n  update_index:\n \tif (!ret && update_cache)\n-\t\tadd_cacheinfo(mode, oid, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n+\t\tadd_cacheinfo(o, mode, oid, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n \treturn ret;\n }\n \n@@ -968,11 +986,11 @@ static int merge_file_1(struct merge_options *o,\n \t\t\t\t\t\t  branch1, branch2);\n \n \t\t\tif ((merge_status < 0) || !result_buf.ptr)\n-\t\t\t\tret = error(_(\"Failed to execute internal merge\"));\n+\t\t\t\tret = err(o, _(\"Failed to execute internal merge\"));\n \n \t\t\tif (!ret && write_sha1_file(result_buf.ptr, result_buf.size,\n \t\t\t\t\t    blob_type, result->oid.hash))\n-\t\t\t\tret = error(_(\"Unable to add %s to database\"),\n+\t\t\t\tret = err(o, _(\"Unable to add %s to database\"),\n \t\t\t\t\ta->path);\n \n \t\t\tfree(result_buf.ptr);\n@@ -1137,7 +1155,7 @@ static int conflict_rename_delete(struct merge_options *o,\n \tif (o->call_depth)\n \t\treturn remove_file_from_cache(dest->path);\n \telse\n-\t\treturn update_stages(dest->path, NULL,\n+\t\treturn update_stages(o, dest->path, NULL,\n \t\t\t      rename_branch == o->branch1 ? dest : NULL,\n \t\t\t      rename_branch == o->branch1 ? NULL : dest);\n }\n@@ -1195,9 +1213,9 @@ static int handle_file(struct merge_options *o,\n \tif ((ret = update_file(o, 0, &rename->oid, rename->mode, dst_name)))\n \t\t; /* fall through, do allow dst_name to be released */\n \telse if (stage == 2)\n-\t\tret = update_stages(rename->path, NULL, rename, add);\n+\t\tret = update_stages(o, rename->path, NULL, rename, add);\n \telse\n-\t\tret = update_stages(rename->path, NULL, add, rename);\n+\t\tret = update_stages(o, rename->path, NULL, add, rename);\n \n \tif (dst_name != rename->path)\n \t\tfree(dst_name);\n@@ -1590,23 +1608,25 @@ static struct object_id *stage_oid(const struct object_id *oid, unsigned mode)\n \treturn (is_null_oid(oid) || mode == 0) ? NULL: (struct object_id *)oid;\n }\n \n-static int read_oid_strbuf(const struct object_id *oid, struct strbuf *dst)\n+static int read_oid_strbuf(struct merge_options *o,\n+\tconst struct object_id *oid, struct strbuf *dst)\n {\n \tvoid *buf;\n \tenum object_type type;\n \tunsigned long size;\n \tbuf = read_sha1_file(oid->hash, &type, &size);\n \tif (!buf)\n-\t\treturn error(_(\"cannot read object %s\"), oid_to_hex(oid));\n+\t\treturn err(o, _(\"cannot read object %s\"), oid_to_hex(oid));\n \tif (type != OBJ_BLOB) {\n \t\tfree(buf);\n-\t\treturn error(_(\"object %s is not a blob\"), oid_to_hex(oid));\n+\t\treturn err(o, _(\"object %s is not a blob\"), oid_to_hex(oid));\n \t}\n \tstrbuf_attach(dst, buf, size, size + 1);\n \treturn 0;\n }\n \n-static int blob_unchanged(const struct object_id *o_oid,\n+static int blob_unchanged(struct merge_options *opt,\n+\t\t\t  const struct object_id *o_oid,\n \t\t\t  unsigned o_mode,\n \t\t\t  const struct object_id *a_oid,\n \t\t\t  unsigned a_mode,\n@@ -1624,7 +1644,7 @@ static int blob_unchanged(const struct object_id *o_oid,\n \t\treturn 0;\n \n \tassert(o_oid && a_oid);\n-\tif (read_oid_strbuf(o_oid, &o) || read_oid_strbuf(a_oid, &a))\n+\tif (read_oid_strbuf(opt, o_oid, &o) || read_oid_strbuf(opt, a_oid, &a))\n \t\tgoto error_return;\n \t/*\n \t * Note: binary | is used so that both renormalizations are\n@@ -1713,7 +1733,7 @@ static int merge_content(struct merge_options *o,\n \t\t */\n \t\tpath_renamed_outside_HEAD = !path2 || !strcmp(path, path2);\n \t\tif (!path_renamed_outside_HEAD) {\n-\t\t\tadd_cacheinfo(mfi.mode, &mfi.oid, path,\n+\t\t\tadd_cacheinfo(o, mfi.mode, &mfi.oid, path,\n \t\t\t\t      0, (!o->call_depth), 0);\n \t\t\treturn mfi.clean;\n \t\t}\n@@ -1726,7 +1746,7 @@ static int merge_content(struct merge_options *o,\n \t\toutput(o, 1, _(\"CONFLICT (%s): Merge conflict in %s\"),\n \t\t\t\treason, path);\n \t\tif (rename_conflict_info && !df_conflict_remains)\n-\t\t\tif (update_stages(path, &one, &a, &b))\n+\t\t\tif (update_stages(o, path, &one, &a, &b))\n \t\t\t\treturn -1;\n \t}\n \n@@ -1736,7 +1756,7 @@ static int merge_content(struct merge_options *o,\n \t\t\tremove_file_from_cache(path);\n \t\t} else {\n \t\t\tif (!mfi.clean) {\n-\t\t\t\tif (update_stages(path, &one, &a, &b))\n+\t\t\t\tif (update_stages(o, path, &one, &a, &b))\n \t\t\t\t\treturn -1;\n \t\t\t} else {\n \t\t\t\tint file_from_stage2 = was_tracked(path);\n@@ -1744,7 +1764,7 @@ static int merge_content(struct merge_options *o,\n \t\t\t\toidcpy(&merged.oid, &mfi.oid);\n \t\t\t\tmerged.mode = mfi.mode;\n \n-\t\t\t\tif (update_stages(path, NULL,\n+\t\t\t\tif (update_stages(o, path, NULL,\n \t\t\t\t\t      file_from_stage2 ? &merged : NULL,\n \t\t\t\t\t      file_from_stage2 ? NULL : &merged))\n \t\t\t\t\treturn -1;\n@@ -1812,8 +1832,8 @@ static int process_entry(struct merge_options *o,\n \t} else if (o_oid && (!a_oid || !b_oid)) {\n \t\t/* Case A: Deleted in one */\n \t\tif ((!a_oid && !b_oid) ||\n-\t\t    (!b_oid && blob_unchanged(o_oid, o_mode, a_oid, a_mode, normalize, path)) ||\n-\t\t    (!a_oid && blob_unchanged(o_oid, o_mode, b_oid, b_mode, normalize, path))) {\n+\t\t    (!b_oid && blob_unchanged(o, o_oid, o_mode, a_oid, a_mode, normalize, path)) ||\n+\t\t    (!a_oid && blob_unchanged(o, o_oid, o_mode, b_oid, b_mode, normalize, path))) {\n \t\t\t/* Deleted in both or deleted in one and\n \t\t\t * unchanged in the other */\n \t\t\tif (a_oid)\n@@ -1909,7 +1929,7 @@ int merge_trees(struct merge_options *o,\n \n \tif (code != 0) {\n \t\tif (show(o, 4) || o->call_depth)\n-\t\t\terror(_(\"merging of trees %s and %s failed\"),\n+\t\t\terr(o, _(\"merging of trees %s and %s failed\"),\n \t\t\t    oid_to_hex(&head->object.oid),\n \t\t\t    oid_to_hex(&merge->object.oid));\n \t\treturn -1;\n@@ -2043,7 +2063,7 @@ int merge_recursive(struct merge_options *o,\n \t\to->call_depth--;\n \n \t\tif (!merged_common_ancestors)\n-\t\t\treturn error(_(\"merge returned no commit\"));\n+\t\t\treturn err(o, _(\"merge returned no commit\"));\n \t}\n \n \tdiscard_cache();\n@@ -2102,7 +2122,7 @@ int merge_recursive_generic(struct merge_options *o,\n \t\tfor (i = 0; i < num_base_list; ++i) {\n \t\t\tstruct commit *base;\n \t\t\tif (!(base = get_ref(base_list[i], oid_to_hex(base_list[i]))))\n-\t\t\t\treturn error(_(\"Could not parse object '%s'\"),\n+\t\t\t\treturn err(o, _(\"Could not parse object '%s'\"),\n \t\t\t\t\toid_to_hex(base_list[i]));\n \t\t\tcommit_list_insert(base, &ca);\n \t\t}\n@@ -2116,7 +2136,7 @@ int merge_recursive_generic(struct merge_options *o,\n \n \tif (active_cache_changed &&\n \t    write_locked_index(&the_index, lock, COMMIT_LOCK))\n-\t\treturn error(_(\"Unable to write index.\"));\n+\t\treturn err(o, _(\"Unable to write index.\"));\n \n \treturn clean ? 0 : 1;\n }\n-- \n2.9.0.278.g1caae67\n\n\n"},{"id":"290950","messageId":"c22a956c2b9669f3cf61638fd269c82afd294eb5.1467902083.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467902082.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 14/16] merge-recursive: offer an option to retain the output in 'obuf'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-07T14:36:09Z","receivedAt":"2016-07-07T14:36:28Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Since 66a155b (Enable output buffering in merge-recursive., 2007-01-14),\nwe already accumulate the output in a buffer. The idea was to avoid\ninterfering with the progress output that goes to stderr, which is\nunbuffered, when we write to stdout, which is buffered.\n\nWe extend that buffering to allow the caller to handle the output\n(possibly suppressing it). This will help us when extending the\nsequencer to do rebase -i's brunt work: it does not want the picks to\nprint anything by default but instead determine itself whether to print\nthe output or not.\n\nNote that we also redirect the error messages into the output buffer\nwhen the caller asked not to flush the output buffer, for two reasons:\n1) to retain the correct output order, and 2) to allow the caller to\nsuppress *all* output.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 17 +++++++++++++----\n merge-recursive.h |  2 +-\n 2 files changed, 14 insertions(+), 5 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex ad5b961..10d3355 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -25,7 +25,7 @@\n \n static void flush_output(struct merge_options *o)\n {\n-\tif (o->obuf.len) {\n+\tif (o->buffer_output < 2 && o->obuf.len) {\n \t\tfputs(o->obuf.buf, stdout);\n \t\tstrbuf_reset(&o->obuf);\n \t}\n@@ -36,10 +36,19 @@ static int err(struct merge_options *o, const char *err, ...)\n \tva_list params;\n \n \tva_start(params, err);\n-\tflush_output(o);\n+\tif (o->buffer_output < 2)\n+\t\tflush_output(o);\n+\telse {\n+\t\tstrbuf_complete(&o->obuf, '\\n');\n+\t\tstrbuf_addstr(&o->obuf, \"error: \");\n+\t}\n \tstrbuf_vaddf(&o->obuf, err, params);\n-\terror(\"%s\", o->obuf.buf);\n-\tstrbuf_reset(&o->obuf);\n+\tif (o->buffer_output > 1)\n+\t\tstrbuf_addch(&o->obuf, '\\n');\n+\telse {\n+\t\terror(\"%s\", o->obuf.buf);\n+\t\tstrbuf_reset(&o->obuf);\n+\t}\n \tva_end(params);\n \n \treturn -1;\ndiff --git a/merge-recursive.h b/merge-recursive.h\nindex d415724..340704c 100644\n--- a/merge-recursive.h\n+++ b/merge-recursive.h\n@@ -13,7 +13,7 @@ struct merge_options {\n \t\tMERGE_RECURSIVE_THEIRS\n \t} recursive_variant;\n \tconst char *subtree_shift;\n-\tunsigned buffer_output : 1;\n+\tunsigned buffer_output : 2; /* 1: output at end, 2: keep buffered */\n \tunsigned renormalize : 1;\n \tlong xdl_opts;\n \tint verbosity;\n-- \n2.9.0.278.g1caae67\n\n\n"},{"id":"290951","messageId":"12170348e4a9f44cdbcb060ea9505bb7b6c830e8.1467902083.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467902082.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 13/16] merge-recursive: write the commit title in one go","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-07T14:36:06Z","receivedAt":"2016-07-07T14:36:33Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"In 66a155b (Enable output buffering in merge-recursive., 2007-01-14), we\nchanged the code such that it prints the output in one go, to avoid\ninterfering with the progress output.\n\nLet's make sure that the same holds true when outputting the commit\ntitle: previously, we used several printf() statements to stdout and\nspeculated that stdout's buffer is large enough to hold the entire\ncommit title.\n\nApart from making that speculation unnecessary, we change the code to\nadd the message to the output buffer before flushing for another reason:\nthe next commit will introduce a new level of output buffering, where\nthe caller can request the output not to be flushed, but to be retained\nfor further processing.\n\nThis latter feature will be needed when teaching the sequencer to do\nrebase -i's brunt work: it wants to control the output of the\ncherry-picks (i.e. recursive merges).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 17 +++++++++--------\n 1 file changed, 9 insertions(+), 8 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 205ea04..ad5b961 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -191,25 +191,26 @@ static void output(struct merge_options *o, int v, const char *fmt, ...)\n \n static void output_commit_title(struct merge_options *o, struct commit *commit)\n {\n-\tint i;\n-\tflush_output(o);\n-\tfor (i = o->call_depth; i--;)\n-\t\tfputs(\"  \", stdout);\n+\tstrbuf_addchars(&o->obuf, ' ', o->call_depth * 2);\n \tif (commit->util)\n-\t\tprintf(\"virtual %s\\n\", merge_remote_util(commit)->name);\n+\t\tstrbuf_addf(&o->obuf, \"virtual %s\\n\",\n+\t\t\tmerge_remote_util(commit)->name);\n \telse {\n-\t\tprintf(\"%s \", find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV));\n+\t\tstrbuf_addf(&o->obuf, \"%s \",\n+\t\t\tfind_unique_abbrev(commit->object.oid.hash,\n+\t\t\t\tDEFAULT_ABBREV));\n \t\tif (parse_commit(commit) != 0)\n-\t\t\tprintf(_(\"(bad commit)\\n\"));\n+\t\t\tstrbuf_addf(&o->obuf, _(\"(bad commit)\\n\"));\n \t\telse {\n \t\t\tconst char *title;\n \t\t\tconst char *msg = get_commit_buffer(commit, NULL);\n \t\t\tint len = find_commit_subject(msg, &title);\n \t\t\tif (len)\n-\t\t\t\tprintf(\"%.*s\\n\", len, title);\n+\t\t\t\tstrbuf_addf(&o->obuf, \"%.*s\\n\", len, title);\n \t\t\tunuse_commit_buffer(commit, msg);\n \t\t}\n \t}\n+\tflush_output(o);\n }\n \n static int add_cacheinfo(struct merge_options *o,\n-- \n2.9.0.278.g1caae67\n\n\n"},{"id":"290952","messageId":"9565962533ef1b88cfe33e9ab668ee2c5c531adb.1467902083.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467902082.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 15/16] Ensure that the output buffer is released after calling merge_trees()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-07T14:36:13Z","receivedAt":"2016-07-07T14:36:36Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The recursive merge machinery accumulates its output in an output\nbuffer, to be flushed at the end of merge_recursive(). At this point,\nwe forgot to release the output buffer.\n\nWhen calling merge_trees() (i.e. the non-recursive part of the recursive\nmerge) directly, the output buffer is never flushed because the caller\nmay be merge_recursive() which wants to flush the output itself.\n\nFor the same reason, merge_trees() cannot release the output buffer: it\nmay still be needed.\n\nForgetting to release the output buffer did not matter much when running\ngit-checkout, or git-merge-recursive, because we exited after the\noperation anyway. Ever since cherry-pick learned to pick a commit range,\nhowever, this memory leak had the potential of becoming a problem.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/checkout.c | 1 +\n merge-recursive.c  | 2 ++\n sequencer.c        | 1 +\n 3 files changed, 4 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 07dea3b..8d852d4 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -573,6 +573,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\t\texit(128);\n \t\t\tret = reset_tree(new->commit->tree, opts, 0,\n \t\t\t\t\t writeout_error);\n+\t\t\tstrbuf_release(&o.obuf);\n \t\t\tif (ret)\n \t\t\t\treturn ret;\n \t\t}\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 10d3355..c0be0c5 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -2092,6 +2092,8 @@ int merge_recursive(struct merge_options *o,\n \t\tcommit_list_insert(h2, &(*result)->parents->next);\n \t}\n \tflush_output(o);\n+\tif (o->buffer_output < 2)\n+\t\tstrbuf_release(&o->obuf);\n \tif (show(o, 2))\n \t\tdiff_warn_rename_limit(\"merge.renamelimit\",\n \t\t\t\t       o->needed_rename_limit, 0);\ndiff --git a/sequencer.c b/sequencer.c\nindex 286a435..ec50519 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -293,6 +293,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \tclean = merge_trees(&o,\n \t\t\t    head_tree,\n \t\t\t    next_tree, base_tree, &result);\n+\tstrbuf_release(&o.obuf);\n \tif (clean < 0)\n \t\treturn clean;\n \n-- \n2.9.0.278.g1caae67\n\n\n"},{"id":"290953","messageId":"bdf1cccf96cd3b9908cf170d6b71f10411ab65bb.1467902083.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467902082.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 16/16] merge-recursive: flush output buffer even when erroring out","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-07T14:36:18Z","receivedAt":"2016-07-07T14:36:40Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Ever since 66a155b (Enable output buffering in merge-recursive.,\n2007-01-14), we had a problem: When the merge failed in a fatal way, all\nregular output was swallowed because we called die() and did not get a\nchance to drain the output buffers.\n\nTo fix this, several modifications were necessary:\n\n- we needed to stop die()ing, to give callers a chance to do something\n  when an error occurred (in this case, flush the output buffers),\n\n- we needed to delay printing the error message so that the caller can\n  print the buffered output before that, and\n\n- we needed to make sure that the output buffers are flushed even when\n  the return value indicates an error.\n\nThe first two changes were introduced through earlier commits in this\npatch series, and this commit addresses the third one.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex c0be0c5..cb701ee 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -2083,6 +2083,7 @@ int merge_recursive(struct merge_options *o,\n \to->ancestor = \"merged common ancestors\";\n \tclean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree,\n \t\t\t    &mrtree);\n+\tflush_output(o);\n \tif (clean < 0)\n \t\treturn clean;\n \n@@ -2091,7 +2092,6 @@ int merge_recursive(struct merge_options *o,\n \t\tcommit_list_insert(h1, &(*result)->parents);\n \t\tcommit_list_insert(h2, &(*result)->parents->next);\n \t}\n-\tflush_output(o);\n \tif (o->buffer_output < 2)\n \t\tstrbuf_release(&o->obuf);\n \tif (show(o, 2))\n-- \n2.9.0.278.g1caae67\n"},{"id":"290956","messageId":"xmqqmvltqxjs.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1607071323440.6426@virtualbox","subject":"Re: [PATCH v2 11/17] am: counteract gender bias","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-07T15:26:31Z","receivedAt":"2016-07-07T15:26:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> I doubt this kind fo distraction is desirable in the middle of a\n>> seriously heavy series like this one.  As a standalone clean-up to\n>> turn these directly to \"their\" that everybody would agree on and can\n>> be merged down quickly to 'master' that does not have to keep the\n>> body of the main topic waiting for the dust to settle might be a\n>> better approach.\n>>  ...\n> I am really curious, though. Has it not been our practice to encourage\n> preparatory patches like white-space or const fixes as part of patch\n> series that touch a certain part of the code that needed fixing?\n\nYes, isn't that \"preparatory clean-up\" what I said \"might be a\nbetter approach\"?\n"},{"id":"290961","messageId":"alpine.DEB.2.20.1607071753370.6426@virtualbox","threadId":"42743","inReplyTo":"xmqqmvltqxjs.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 11/17] am: counteract gender bias","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-07T15:54:21Z","receivedAt":"2016-07-07T15:54:44Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Thu, 7 Jul 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> >> I doubt this kind fo distraction is desirable in the middle of a\n> >> seriously heavy series like this one.  As a standalone clean-up to\n> >> turn these directly to \"their\" that everybody would agree on and can\n> >> be merged down quickly to 'master' that does not have to keep the\n> >> body of the main topic waiting for the dust to settle might be a\n> >> better approach.\n> >>  ...\n> > I am really curious, though. Has it not been our practice to encourage\n> > preparatory patches like white-space or const fixes as part of patch\n> > series that touch a certain part of the code that needed fixing?\n> \n> Yes, isn't that \"preparatory clean-up\" what I said \"might be a\n> better approach\"?\n\nWhat I meant by \"preparatory clean-up\" is a patch in the patch series,\njust as I had it.\n\nNow it is a separate patch \"series\".\n\nCiao,\nDscho\n"},{"id":"290962","messageId":"xmqqa8htqvuo.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1607071753370.6426@virtualbox","subject":"Re: [PATCH v2 11/17] am: counteract gender bias","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-07T16:03:11Z","receivedAt":"2016-07-07T16:03:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Hi Junio,\n>\n> On Thu, 7 Jul 2016, Junio C Hamano wrote:\n>\n>> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>> \n>> >> I doubt this kind fo distraction is desirable in the middle of a\n>> >> seriously heavy series like this one.  As a standalone clean-up to\n>> >> turn these directly to \"their\" that everybody would agree on and can\n>> >> be merged down quickly to 'master' that does not have to keep the\n>> >> body of the main topic waiting for the dust to settle might be a\n>> >> better approach.\n>> >>  ...\n>> > I am really curious, though. Has it not been our practice to encourage\n>> > preparatory patches like white-space or const fixes as part of patch\n>> > series that touch a certain part of the code that needed fixing?\n>> \n>> Yes, isn't that \"preparatory clean-up\" what I said \"might be a\n>> better approach\"?\n>\n> What I meant by \"preparatory clean-up\" is a patch in the patch series,\n> just as I had it.\n>\n> Now it is a separate patch \"series\".\n\nThe main thing I had trouble with was to have it in the middle at\n11/17.  Since I do not have time to reorder other people's patches,\nevery time you reroll the main series, this step will need to be\nlooked at together with others.  Having it near the beginning as\npreparatory clean-up, or even better a separate series that is an\nuncontroversial preparatory clean-up that the main topic depends on,\nis what I meant.\n\nYou build the main \"series\" on top of what 'master' would have when\nthe separate one that \"does not have to keep the body of the main\ntopic waiting\" gets merged.  Because the separate one would be\nsomething \"everybody would agree on and can be merged down quickly\",\nthat will not slow the main topic down.\n\n\n"},{"id":"291305","messageId":"xmqqpoqi35u3.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"cover.1467902082.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v3 00/16] Use merge_recursive() directly in the builtin am","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-12T21:27:16Z","receivedAt":"2016-07-12T21:27:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> This is the second iteration of the long-awaited re-roll of the attempt to\n> avoid spawning merge-recursive from the builtin am and use merge_recursive()\n> directly instead.\n\nThis is actually the third iteration.\n\nI am trying to tease dependencies apart and apply this on a more\nreasonable base than a commit that happened to be at 'pu' on one\nday, but this would probably take some time, and I may give up\nmerging it anywhere for today's integration cycle.  We'll see.\n\n\n\n\n\n"},{"id":"291415","messageId":"alpine.DEB.2.20.1607141414180.6426@virtualbox","threadId":"42743","inReplyTo":"xmqqpoqi35u3.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 00/16] Use merge_recursive() directly in the builtin am","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-14T14:03:33Z","receivedAt":"2016-07-14T14:04:02Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 12 Jul 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > This is the second iteration of the long-awaited re-roll of the attempt to\n> > avoid spawning merge-recursive from the builtin am and use merge_recursive()\n> > directly instead.\n> \n> This is actually the third iteration.\n\nIt is.\n\n> I am trying to tease dependencies apart and apply this on a more\n> reasonable base than a commit that happened to be at 'pu' on one\n> day, but this would probably take some time, and I may give up\n> merging it anywhere for today's integration cycle.  We'll see.\n\nThe two topics that are in 'pu' and conflict with this series are\n'jh/clean-smudge-annex' and 'bc/cocci'.\n\nIt also conflicted with 'va/i18n-even-more', but that one was merged to\n'master'.\n\nNow, I think it would be okay to wait for 'bc/cocci' to go to 'master'\nbefore integrating the 'am-3-merge-recursive-direct' branch, but I would\nwant to avoid waiting for 'jh/clean-smudge-annex'.\n\nDo you concur? If so, I will rebase onto 'master' as soon as 'bc/cocci'\nlands in there.\n\nCiao,\nDscho\n"},{"id":"291465","messageId":"xmqq1t2wqaa3.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1607141414180.6426@virtualbox","subject":"Re: [PATCH v3 00/16] Use merge_recursive() directly in the builtin am","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-14T19:39:32Z","receivedAt":"2016-07-14T19:39:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> The two topics that are in 'pu' and conflict with this series are\n> 'jh/clean-smudge-annex' and 'bc/cocci'.\n>\n> It also conflicted with 'va/i18n-even-more', but that one was merged to\n> 'master'.\n>\n> Now, I think it would be okay to wait for 'bc/cocci' to go to 'master'\n> before integrating the 'am-3-merge-recursive-direct' branch, but I would\n> want to avoid waiting for 'jh/clean-smudge-annex'.\n>\n> Do you concur? If so, I will rebase onto 'master' as soon as 'bc/cocci'\n> lands in there.\n\nI do not have a strong preference myself.  As you are not proposing\nto eject jh/clean-smudge-annex from my tree, I'd have to resolve the\nconflicts when the second topic is merged after one topic, whichever\nhappens to be more ready.  IOW, such a rebase adds to my workload.\n\nLike it or not, it is normal for different topics to want to touch\nthe overlapping area or one topic to invalidate an assumption the\nother topic is based on, and working well together does not have to\nmean leaving the conflict resolution to the sole responsibility of\nthe integrator.  A clean way forward may be for everybody involved\n(and I see you forgot to add Joey to this thread) to agree that this\ntopic is more ready than jh/clean-smudge-annex and you and Joey to\nwork together to rebase jh/clean-smudge-annex on top of this topic\n(or possibly the other way around), making him wait for you.\n\n"},{"id":"291699","messageId":"xmqqshv6h460.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"xmqq1t2wqaa3.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 00/16] Use merge_recursive() directly in the builtin am","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-19T00:17:43Z","receivedAt":"2016-07-19T00:17:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>\n>> The two topics that are in 'pu' and conflict with this series are\n>> 'jh/clean-smudge-annex' and 'bc/cocci'.\n>>\n>> It also conflicted with 'va/i18n-even-more', but that one was merged to\n>> 'master'.\n>>\n>> Now, I think it would be okay to wait for 'bc/cocci' to go to 'master'\n>> before integrating the 'am-3-merge-recursive-direct' branch, but I would\n>> want to avoid waiting for 'jh/clean-smudge-annex'.\n\nNothing seems to be happening on jh/clean-smudge-annex front, so\nunless we hear otherwise from Joey in the coming couple of days,\nlet's get js/am-3-merge-recursive-direct untangled from its\ndependencies and get it into a shape to hit 'next'.  You can assume\nthat I'll merge bc/cocci and js/am-call-theirs-theirs-in-fallback-3way\nto 'master' during that time, so an appropriate base to use would be\nthe result of\n\n    git checkout master^0\n    git merge bc/cocci\n    git merge js/am-call-theirs-theirs-in-fallback-3way\n    git merge cc/apply-am\n\nif you want a semi-solid base to rebuild am-3-merge-recursive-direct\non.  I am not 100% sure about the doneness of cc/apply-am yet,\nthough but it could be that am-3-merge-recursive-direct does not\nhave to depend on it at all.  You'd know better than I do ;-)\n\nAfter that, I'll see if the conflict resolution is manageable when\nremerging jh/clean-smudge-annex to 'pu', and if it is not, I'll\nworry about it at that point.\n\nThanks.\n\n"},{"id":"291718","messageId":"alpine.DEB.2.20.1607191427400.3472@virtualbox","threadId":"42743","inReplyTo":"xmqqshv6h460.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 00/16] Use merge_recursive() directly in the builtin am","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-19T12:31:43Z","receivedAt":"2016-07-19T12:32:23Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Mon, 18 Jul 2016, Junio C Hamano wrote:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n> \n> > Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> >\n> >> The two topics that are in 'pu' and conflict with this series are\n> >> 'jh/clean-smudge-annex' and 'bc/cocci'.\n> >>\n> >> It also conflicted with 'va/i18n-even-more', but that one was merged to\n> >> 'master'.\n> >>\n> >> Now, I think it would be okay to wait for 'bc/cocci' to go to 'master'\n> >> before integrating the 'am-3-merge-recursive-direct' branch, but I would\n> >> want to avoid waiting for 'jh/clean-smudge-annex'.\n> \n> Nothing seems to be happening on jh/clean-smudge-annex front, so\n> unless we hear otherwise from Joey in the coming couple of days,\n> let's get js/am-3-merge-recursive-direct untangled from its\n> dependencies and get it into a shape to hit 'next'.\n\nOkay, I will rebase and re-submit.\n\n> You can assume that I'll merge bc/cocci and\n> js/am-call-theirs-theirs-in-fallback-3way to 'master' during that time,\n> so an appropriate base to use would be the result of\n> \n>     git checkout master^0\n>     git merge bc/cocci\n>     git merge js/am-call-theirs-theirs-in-fallback-3way\n>     git merge cc/apply-am\n> \n> if you want a semi-solid base to rebuild am-3-merge-recursive-direct\n> on.\n\nOkay. The way my `mail-patch-series.sh` script works, however, is that it\ninfers the base commit by testing which tip among pu, next and master is\nthe most appropriate.\n\nSo I guess I will have to hack it up a bit to allow basing my patch series\non something custom.\n\n> I am not 100% sure about the doneness of cc/apply-am yet, though but it\n> could be that am-3-merge-recursive-direct does not have to depend on it\n> at all.  You'd know better than I do ;-)\n\nI agree on the doneness of cc/apply-am (as you know, I tried to help it\nalong but my comments had an adverse effect).\n\nAnd no: nothing in my entire rebase--helper patches relies on cc/apply-am\n(I do not even believe that anything conflicts with it, either).\n\n> After that, I'll see if the conflict resolution is manageable when\n> remerging jh/clean-smudge-annex to 'pu', and if it is not, I'll worry\n> about it at that point.\n\nI can help with that, too.\n\nCiao,\nDscho\n"},{"id":"291720","messageId":"alpine.DEB.2.20.1607191602520.3472@virtualbox","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1607191427400.3472@virtualbox","subject":"Re: [PATCH v3 00/16] Use merge_recursive() directly in the builtin am","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-19T14:28:30Z","receivedAt":"2016-07-19T14:28:58Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 19 Jul 2016, Johannes Schindelin wrote:\n\n> On Mon, 18 Jul 2016, Junio C Hamano wrote:\n> \n> > Junio C Hamano <gitster@pobox.com> writes:\n> > \n> > You can assume that I'll merge bc/cocci and\n> > js/am-call-theirs-theirs-in-fallback-3way to 'master' during that time,\n> > so an appropriate base to use would be the result of\n> > \n> >     git checkout master^0\n> >     git merge bc/cocci\n> >     git merge js/am-call-theirs-theirs-in-fallback-3way\n> >     git merge cc/apply-am\n> > \n> > if you want a semi-solid base to rebuild am-3-merge-recursive-direct\n> > on.\n\nI do not need cc/apply-am at all, it turns out, but my patch series has a\nminor conflict with 'jc/renormalize-merge-kill-safer-crlf'.\n\nSince you indicated that you want to cook that branch a bit in 'next'\nfirst, I will rebase to master+bc/cocci+js/am-call-theirs-theirs and\nre-submit.\n\nCiao,\nDscho\n"},{"id":"291763","messageId":"xmqq4m7lfmn0.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1607191602520.3472@virtualbox","subject":"Re: [PATCH v3 00/16] Use merge_recursive() directly in the builtin am","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-19T19:33:55Z","receivedAt":"2016-07-19T19:34:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> I do not need cc/apply-am at all, it turns out, but my patch series has a\n> minor conflict with 'jc/renormalize-merge-kill-safer-crlf'.\n>\n> Since you indicated that you want to cook that branch a bit in 'next'\n> first, I will rebase to master+bc/cocci+js/am-call-theirs-theirs and\n> re-submit.\n\nThanks.  I suspect the renomalization would graduate earlier than\nthe topic in question, but leaving dependency to the minimum and\nhave rerere take care of minor conflicts has proved to be a better\napproach in general over time; the base you chose above sounds\nappropriate.\n\nThanks.\n"},{"id":"291944","messageId":"cover.1469187652.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1467902082.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 00/16] Use merge_recursive() directly in the builtin am","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-22T12:23:52Z","receivedAt":"2016-07-22T12:24:39Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This is the fourth iteration of the long-awaited re-roll of the attempt to\navoid spawning merge-recursive from the builtin am and use merge_recursive()\ndirectly instead.\n\nThe *real* reason for the reroll is that I need a libified recursive\nmerge to accelerate the interactive rebase by teaching the sequencer to\ndo rebase -i's grunt work. Coming with a very nice 3x-5x speedup of\n`rebase -i`.\n\nIn this endeavor, we need to be extra careful to retain backwards\ncompatibility. The test script t6022-merge-rename.sh, for example, verifies\nthat `git pull` exits with status 128 in case of a fatal error. To that end,\nwe need to make sure that fatal errors are handled by existing (builtin)\nusers via exit(128) (or die(), which calls exit(128) at the end).  New users\n(such as a builtin helper doing rebase -i's grunt work) may want to print\nsome helpful advice what happened and how to get out of this mess before\nerroring out.\n\nThe changes relative to the second iteration of this patch series (I\nhave a feeling that nobody reviewed the 3rd iteration because it was\nbased on `pu`):\n\n- the was_tracked() function was adjusted as per Junio's suggestions\n\n- the \"counter gender bias\" patch was submitted, and accepted,\n  separately, even if the version we settled on sends a much weaker\n  message than I would have preferred\n\n- this patch series is on top of 'master' again\n\n- a test was introduced to verify that we do not reintroduce the bug\n  which required our hot fix to spawn the recursive merge again\n\n- while at it, I fixed a ton of incorrect indentations that I missed in\n  the first three iterations\n\n- as the bc/cocci branch was merged, a couple of fixups were necessary\n  to the patch that avoids spawning the recursive merge\n\n- the interdiff is relative to v2 rebased onto master. That means that\n  I had to simulate the \"her_tree\" change for the sake of the interdiff.\n\nThis patch series touches rather important code. Now that I addressed\nconcerns such as fixing translated bug reports, I would appreciate thorough\nreviews with a focus on the critical parts of the code, those that could\nresult in regressions.\n\n\nJohannes Schindelin (16):\n  Verify that `git pull --rebase` shows the helpful advice when failing\n  Report bugs consistently\n  Avoid translating bug messages\n  merge-recursive: clarify code in was_tracked()\n  Prepare the builtins for a libified merge_recursive()\n  merge_recursive: abort properly upon errors\n  merge-recursive: avoid returning a wholesale struct\n  merge-recursive: allow write_tree_from_memory() to error out\n  merge-recursive: handle return values indicating errors\n  merge-recursive: switch to returning errors instead of dying\n  am -3: use merge_recursive() directly again\n  merge-recursive: flush output buffer before printing error messages\n  merge-recursive: write the commit title in one go\n  merge-recursive: offer an option to retain the output in 'obuf'\n  Ensure that the output buffer is released after calling merge_trees()\n  merge-recursive: flush output buffer even when erroring out\n\n builtin/am.c           |  62 ++----\n builtin/checkout.c     |   5 +-\n builtin/ls-files.c     |   3 +-\n builtin/merge.c        |   2 +\n builtin/update-index.c |   2 +-\n grep.c                 |   8 +-\n imap-send.c            |   4 +-\n merge-recursive.c      | 571 +++++++++++++++++++++++++++++--------------------\n merge-recursive.h      |   2 +-\n sequencer.c            |   5 +\n sha1_file.c            |   4 +-\n t/t5520-pull.sh        |  30 +++\n trailer.c              |   2 +-\n transport.c            |   2 +-\n wt-status.c            |   4 +-\n 15 files changed, 413 insertions(+), 293 deletions(-)\n\nPublished-As: https://github.com/dscho/git/releases/tag/am-3-merge-recursive-direct-v4\nInterdiff vs v3:\n\n diff --git a/builtin/am.c b/builtin/am.c\n index bf4f79b..cfb79ea 100644\n --- a/builtin/am.c\n +++ b/builtin/am.c\n @@ -1583,15 +1583,14 @@ static int build_fake_ancestor(const struct am_state *state, const char *index_f\n   */\n  static int fall_back_threeway(const struct am_state *state, const char *index_path)\n  {\n -\tunsigned char orig_tree[GIT_SHA1_RAWSZ], her_tree[GIT_SHA1_RAWSZ],\n -\t\t      our_tree[GIT_SHA1_RAWSZ];\n -\tconst unsigned char *bases[1] = {orig_tree};\n +\tstruct object_id orig_tree, their_tree, our_tree;\n +\tconst struct object_id *bases[1] = { &orig_tree };\n  \tstruct merge_options o;\n  \tstruct commit *result;\n -\tchar *her_tree_name;\n +\tchar *their_tree_name;\n  \n -\tif (get_sha1(\"HEAD\", our_tree) < 0)\n -\t\thashcpy(our_tree, EMPTY_TREE_SHA1_BIN);\n +\tif (get_oid(\"HEAD\", &our_tree) < 0)\n +\t\thashcpy(our_tree.hash, EMPTY_TREE_SHA1_BIN);\n  \n  \tif (build_fake_ancestor(state, index_path))\n  \t\treturn error(\"could not build fake ancestor\");\n @@ -1599,7 +1598,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n  \tdiscard_cache();\n  \tread_cache_from(index_path);\n  \n -\tif (write_index_as_tree(orig_tree, &the_index, index_path, 0, NULL))\n +\tif (write_index_as_tree(orig_tree.hash, &the_index, index_path, 0, NULL))\n  \t\treturn error(_(\"Repository lacks necessary blobs to fall back on 3-way merge.\"));\n  \n  \tsay(state, stdout, _(\"Using index info to reconstruct a base tree...\"));\n @@ -1615,7 +1614,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n  \t\tinit_revisions(&rev_info, NULL);\n  \t\trev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;\n  \t\tdiff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix);\n -\t\tadd_pending_sha1(&rev_info, \"HEAD\", our_tree, 0);\n +\t\tadd_pending_sha1(&rev_info, \"HEAD\", our_tree.hash, 0);\n  \t\tdiff_setup_done(&rev_info.diffopt);\n  \t\trun_diff_index(&rev_info, 1);\n  \t}\n @@ -1624,7 +1623,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n  \t\treturn error(_(\"Did you hand edit your patch?\\n\"\n  \t\t\t\t\"It does not apply to blobs recorded in its index.\"));\n  \n -\tif (write_index_as_tree(her_tree, &the_index, index_path, 0, NULL))\n +\tif (write_index_as_tree(their_tree.hash, &the_index, index_path, 0, NULL))\n  \t\treturn error(\"could not write tree\");\n  \n  \tsay(state, stdout, _(\"Falling back to patching base and 3-way merge...\"));\n @@ -1634,7 +1633,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n  \n  \t/*\n  \t * This is not so wrong. Depending on which base we picked, orig_tree\n -\t * may be wildly different from ours, but her_tree has the same set of\n +\t * may be wildly different from ours, but their_tree has the same set of\n  \t * wildly different changes in parts the patch did not touch, so\n  \t * recursive ends up canceling them, saying that we reverted all those\n  \t * changes.\n @@ -1643,19 +1642,19 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n  \tinit_merge_options(&o);\n  \n  \to.branch1 = \"HEAD\";\n -\ther_tree_name = xstrfmt(\"%.*s\", linelen(state->msg), state->msg);\n -\to.branch2 = her_tree_name;\n +\ttheir_tree_name = xstrfmt(\"%.*s\", linelen(state->msg), state->msg);\n +\to.branch2 = their_tree_name;\n  \n  \tif (state->quiet)\n  \t\to.verbosity = 0;\n  \n -\tif (merge_recursive_generic(&o, our_tree, her_tree, 1, bases, &result)) {\n +\tif (merge_recursive_generic(&o, &our_tree, &their_tree, 1, bases, &result)) {\n  \t\trerere(state->allow_rerere_autoupdate);\n -\t\tfree(her_tree_name);\n +\t\tfree(their_tree_name);\n  \t\treturn error(_(\"Failed to merge in the changes.\"));\n  \t}\n  \n -\tfree(her_tree_name);\n +\tfree(their_tree_name);\n  \treturn 0;\n  }\n  \n diff --git a/merge-recursive.c b/merge-recursive.c\n index cd2a533..501cfb5 100644\n --- a/merge-recursive.c\n +++ b/merge-recursive.c\n @@ -686,20 +686,19 @@ static int was_tracked(const char *path)\n  {\n  \tint pos = cache_name_pos(path, strlen(path));\n  \n -\tif (pos >= 0)\n -\t\treturn pos < active_nr;\n +\tif (0 <= pos)\n +\t\t/* we have been tracking this path */\n +\t\treturn 1;\n +\n  \t/*\n -\t * cache_name_pos() looks for stage == 0, even if we did not ask for\n -\t * it. Let's look for stage == 2 now.\n +\t * Look for an unmerged entry for the path,\n +\t * specifically stage #2, which would indicate\n +\t * that \"our\" side before the merge started\n +\t * had the path tracked (and resulted in a conflict).\n  \t */\n -\tfor (pos = -1 - pos; pos < active_nr &&\n -\t     !strcmp(path, active_cache[pos]->name); pos++)\n -\t\t/*\n -\t\t * If stage #0, it is definitely tracked.\n -\t\t * If it has stage #2 then it was tracked\n -\t\t * before this merge started.  All other\n -\t\t * cases the path was not tracked.\n -\t\t */\n +\tfor (pos = -1 - pos;\n +\t     pos < active_nr && !strcmp(path, active_cache[pos]->name);\n +\t     pos++)\n  \t\tif (ce_stage(active_cache[pos]) == 2)\n  \t\t\treturn 1;\n  \treturn 0;\n @@ -761,11 +760,11 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n  }\n  \n  static int update_file_flags(struct merge_options *o,\n -\t\t\t      const struct object_id *oid,\n -\t\t\t      unsigned mode,\n -\t\t\t      const char *path,\n -\t\t\t      int update_cache,\n -\t\t\t      int update_wd)\n +\t\t\t     const struct object_id *oid,\n +\t\t\t     unsigned mode,\n +\t\t\t     const char *path,\n +\t\t\t     int update_cache,\n +\t\t\t     int update_wd)\n  {\n  \tint ret = 0;\n  \n @@ -816,7 +815,7 @@ static int update_file_flags(struct merge_options *o,\n  \t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n  \t\t\tif (fd < 0) {\n  \t\t\t\tret = err(o, _(\"failed to open '%s': %s\"),\n -\t\t\t\t\tpath, strerror(errno));\n +\t\t\t\t\t  path, strerror(errno));\n  \t\t\t\tgoto free_buf;\n  \t\t\t}\n  \t\t\twrite_in_full(fd, buf, size);\n @@ -830,8 +829,9 @@ static int update_file_flags(struct merge_options *o,\n  \t\t\t\t\tpath, strerror(errno));\n  \t\t\tfree(lnk);\n  \t\t} else\n -\t\t\tret = err(o, _(\"do not know what to do with %06o %s '%s'\"),\n -\t\t\t\tmode, oid_to_hex(oid), path);\n +\t\t\tret = err(o,\n +\t\t\t\t  _(\"do not know what to do with %06o %s '%s'\"),\n +\t\t\t\t  mode, oid_to_hex(oid), path);\n   free_buf:\n  \t\tfree(buf);\n  \t}\n @@ -842,10 +842,10 @@ static int update_file_flags(struct merge_options *o,\n  }\n  \n  static int update_file(struct merge_options *o,\n -\t\t\tint clean,\n -\t\t\tconst struct object_id *oid,\n -\t\t\tunsigned mode,\n -\t\t\tconst char *path)\n +\t\t       int clean,\n +\t\t       const struct object_id *oid,\n +\t\t       unsigned mode,\n +\t\t       const char *path)\n  {\n  \treturn update_file_flags(o, oid, mode, path, o->call_depth || clean, !o->call_depth);\n  }\n @@ -973,9 +973,9 @@ static int merge_file_1(struct merge_options *o,\n  \t\t\t\tret = err(o, _(\"Failed to execute internal merge\"));\n  \n  \t\t\tif (!ret && write_sha1_file(result_buf.ptr, result_buf.size,\n -\t\t\t\t\t    blob_type, result->oid.hash))\n +\t\t\t\t\t\t    blob_type, result->oid.hash))\n  \t\t\t\tret = err(o, _(\"Unable to add %s to database\"),\n -\t\t\t\t\ta->path);\n +\t\t\t\t\t  a->path);\n  \n  \t\t\tfree(result_buf.ptr);\n  \t\t\tif (ret)\n @@ -1020,7 +1020,8 @@ static int merge_file_special_markers(struct merge_options *o,\n  \t\tside2 = xstrfmt(\"%s:%s\", branch2, filename2);\n  \n  \tret = merge_file_1(o, one, a, b,\n -\t\tside1 ? side1 : branch1, side2 ? side2 : branch2, mfi);\n +\t\t\t   side1 ? side1 : branch1,\n +\t\t\t   side2 ? side2 : branch2, mfi);\n  \tfree(side1);\n  \tfree(side2);\n  \treturn ret;\n @@ -1068,7 +1069,8 @@ static int handle_change_delete(struct merge_options *o,\n  \t\t */\n  \t\tret = remove_file_from_cache(path);\n  \t\tif (!ret)\n -\t\t\tret = update_file(o, 0, o_oid, o_mode, renamed ? renamed : path);\n +\t\t\tret = update_file(o, 0, o_oid, o_mode,\n +\t\t\t\t\t  renamed ? renamed : path);\n  \t} else if (!a_oid) {\n  \t\tif (!renamed) {\n  \t\t\toutput(o, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n @@ -1129,19 +1131,19 @@ static int conflict_rename_delete(struct merge_options *o,\n  \t}\n  \n  \tif (handle_change_delete(o,\n -\t\t\t     o->call_depth ? orig->path : dest->path,\n -\t\t\t     &orig->oid, orig->mode,\n -\t\t\t     a_oid, a_mode,\n -\t\t\t     b_oid, b_mode,\n -\t\t\t     _(\"rename\"), _(\"renamed\")))\n +\t\t\t\t o->call_depth ? orig->path : dest->path,\n +\t\t\t\t &orig->oid, orig->mode,\n +\t\t\t\t a_oid, a_mode,\n +\t\t\t\t b_oid, b_mode,\n +\t\t\t\t _(\"rename\"), _(\"renamed\")))\n  \t\treturn -1;\n  \n  \tif (o->call_depth)\n  \t\treturn remove_file_from_cache(dest->path);\n  \telse\n  \t\treturn update_stages(o, dest->path, NULL,\n -\t\t\t      rename_branch == o->branch1 ? dest : NULL,\n -\t\t\t      rename_branch == o->branch1 ? NULL : dest);\n +\t\t\t\t     rename_branch == o->branch1 ? dest : NULL,\n +\t\t\t\t     rename_branch == o->branch1 ? NULL : dest);\n  }\n  \n  static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,\n @@ -1292,11 +1294,11 @@ static int conflict_rename_rename_2to1(struct merge_options *o,\n  \tremove_file(o, 1, b->path, o->call_depth || would_lose_untracked(b->path));\n  \n  \tif (merge_file_special_markers(o, a, c1, &ci->ren1_other,\n -\t\t\t\t\t    o->branch1, c1->path,\n -\t\t\t\t\t    o->branch2, ci->ren1_other.path, &mfi_c1) ||\n +\t\t\t\t       o->branch1, c1->path,\n +\t\t\t\t       o->branch2, ci->ren1_other.path, &mfi_c1) ||\n  \t    merge_file_special_markers(o, b, &ci->ren2_other, c2,\n -\t\t\t\t\t    o->branch1, ci->ren2_other.path,\n -\t\t\t\t\t    o->branch2, c2->path, &mfi_c2))\n +\t\t\t\t       o->branch1, ci->ren2_other.path,\n +\t\t\t\t       o->branch2, c2->path, &mfi_c2))\n  \t\treturn -1;\n  \n  \tif (o->call_depth) {\n @@ -1311,7 +1313,7 @@ static int conflict_rename_rename_2to1(struct merge_options *o,\n  \t\tret = update_file(o, 0, &mfi_c1.oid, mfi_c1.mode, a->path);\n  \t\tif (!ret)\n  \t\t\tret = update_file(o, 0, &mfi_c2.oid, mfi_c2.mode,\n -\t\t\t\tb->path);\n +\t\t\t\t\t  b->path);\n  \t} else {\n  \t\tchar *new_path1 = unique_path(o, path, ci->branch1);\n  \t\tchar *new_path2 = unique_path(o, path, ci->branch2);\n @@ -1321,7 +1323,7 @@ static int conflict_rename_rename_2to1(struct merge_options *o,\n  \t\tret = update_file(o, 0, &mfi_c1.oid, mfi_c1.mode, new_path1);\n  \t\tif (!ret)\n  \t\t\tret = update_file(o, 0, &mfi_c2.oid, mfi_c2.mode,\n -\t\t\t\tnew_path2);\n +\t\t\t\t\t  new_path2);\n  \t\tfree(new_path2);\n  \t\tfree(new_path1);\n  \t}\n @@ -1512,11 +1514,11 @@ static int process_renames(struct merge_options *o,\n  \t\t\t\t * update_file().\n  \t\t\t\t */\n  \t\t\t\tif (update_file_flags(o,\n -\t\t\t\t\t\t  &ren1->pair->two->oid,\n -\t\t\t\t\t\t  ren1->pair->two->mode,\n -\t\t\t\t\t\t  ren1_dst,\n -\t\t\t\t\t\t  1, /* update_cache */\n -\t\t\t\t\t\t  0  /* update_wd    */))\n +\t\t\t\t\t\t      &ren1->pair->two->oid,\n +\t\t\t\t\t\t      ren1->pair->two->mode,\n +\t\t\t\t\t\t      ren1_dst,\n +\t\t\t\t\t\t      1, /* update_cache */\n +\t\t\t\t\t\t      0  /* update_wd    */))\n  \t\t\t\t\tclean_merge = -1;\n  \t\t\t} else if (!oid_eq(&dst_other.oid, &null_oid)) {\n  \t\t\t\tclean_merge = 0;\n @@ -1528,11 +1530,11 @@ static int process_renames(struct merge_options *o,\n  \t\t\t\tif (o->call_depth) {\n  \t\t\t\t\tstruct merge_file_info mfi;\n  \t\t\t\t\tif (merge_file_one(o, ren1_dst, &null_oid, 0,\n -\t\t\t\t\t\t\t &ren1->pair->two->oid,\n -\t\t\t\t\t\t\t ren1->pair->two->mode,\n -\t\t\t\t\t\t\t &dst_other.oid,\n -\t\t\t\t\t\t\t dst_other.mode,\n -\t\t\t\t\t\t\t branch1, branch2, &mfi)) {\n +\t\t\t\t\t\t\t   &ren1->pair->two->oid,\n +\t\t\t\t\t\t\t   ren1->pair->two->mode,\n +\t\t\t\t\t\t\t   &dst_other.oid,\n +\t\t\t\t\t\t\t   dst_other.mode,\n +\t\t\t\t\t\t\t   branch1, branch2, &mfi)) {\n  \t\t\t\t\t\tclean_merge = -1;\n  \t\t\t\t\t\tgoto cleanup_and_return;\n  \t\t\t\t\t}\n @@ -1545,7 +1547,7 @@ static int process_renames(struct merge_options *o,\n  \t\t\t\t\tchar *new_path = unique_path(o, ren1_dst, branch2);\n  \t\t\t\t\toutput(o, 1, _(\"Adding as %s instead\"), new_path);\n  \t\t\t\t\tif (update_file(o, 0, &dst_other.oid,\n -\t\t\t\t\t\t    dst_other.mode, new_path))\n +\t\t\t\t\t\t\tdst_other.mode, new_path))\n  \t\t\t\t\t\tclean_merge = -1;\n  \t\t\t\t\tfree(new_path);\n  \t\t\t\t}\n @@ -1652,11 +1654,11 @@ static int handle_modify_delete(struct merge_options *o,\n  \t\t\t\t struct object_id *b_oid, int b_mode)\n  {\n  \treturn handle_change_delete(o,\n -\t\t\t     path,\n -\t\t\t     o_oid, o_mode,\n -\t\t\t     a_oid, a_mode,\n -\t\t\t     b_oid, b_mode,\n -\t\t\t     _(\"modify\"), _(\"modified\"));\n +\t\t\t\t    path,\n +\t\t\t\t    o_oid, o_mode,\n +\t\t\t\t    a_oid, a_mode,\n +\t\t\t\t    b_oid, b_mode,\n +\t\t\t\t    _(\"modify\"), _(\"modified\"));\n  }\n  \n  static int merge_content(struct merge_options *o,\n @@ -1701,8 +1703,8 @@ static int merge_content(struct merge_options *o,\n  \t\t\tdf_conflict_remains = 1;\n  \t}\n  \tif (merge_file_special_markers(o, &one, &a, &b,\n -\t\t\t\t\t o->branch1, path1,\n -\t\t\t\t\t o->branch2, path2, &mfi))\n +\t\t\t\t       o->branch1, path1,\n +\t\t\t\t       o->branch2, path2, &mfi))\n  \t\treturn -1;\n  \n  \tif (mfi.clean && !df_conflict_remains &&\n @@ -1749,8 +1751,8 @@ static int merge_content(struct merge_options *o,\n  \t\t\t\tmerged.mode = mfi.mode;\n  \n  \t\t\t\tif (update_stages(o, path, NULL,\n -\t\t\t\t\t      file_from_stage2 ? &merged : NULL,\n -\t\t\t\t\t      file_from_stage2 ? NULL : &merged))\n +\t\t\t\t\t\t  file_from_stage2 ? &merged : NULL,\n +\t\t\t\t\t\t  file_from_stage2 ? NULL : &merged))\n  \t\t\t\t\treturn -1;\n  \t\t\t}\n  \n @@ -1794,9 +1796,9 @@ static int process_entry(struct merge_options *o,\n  \t\tcase RENAME_DELETE:\n  \t\t\tclean_merge = 0;\n  \t\t\tif (conflict_rename_delete(o,\n -\t\t\t\t\t       conflict_info->pair1,\n -\t\t\t\t\t       conflict_info->branch1,\n -\t\t\t\t\t       conflict_info->branch2))\n +\t\t\t\t\t\t   conflict_info->pair1,\n +\t\t\t\t\t\t   conflict_info->branch1,\n +\t\t\t\t\t\t   conflict_info->branch2))\n  \t\t\t\tclean_merge = -1;\n  \t\t\tbreak;\n  \t\tcase RENAME_ONE_FILE_TO_TWO:\n @@ -1828,7 +1830,7 @@ static int process_entry(struct merge_options *o,\n  \t\t\t/* Modify/delete; deleted side may have put a directory in the way */\n  \t\t\tclean_merge = 0;\n  \t\t\tif (handle_modify_delete(o, path, o_oid, o_mode,\n -\t\t\t\t\t     a_oid, a_mode, b_oid, b_mode))\n +\t\t\t\t\t\t a_oid, a_mode, b_oid, b_mode))\n  \t\t\t\tclean_merge = -1;\n  \t\t}\n  \t} else if ((!o_oid && a_oid && !b_oid) ||\n @@ -2040,7 +2042,7 @@ int merge_recursive(struct merge_options *o,\n  \t\to->branch1 = \"Temporary merge branch 1\";\n  \t\to->branch2 = \"Temporary merge branch 2\";\n  \t\tif (merge_recursive(o, merged_common_ancestors, iter->item,\n -\t\t\t\tNULL, &merged_common_ancestors) < 0)\n +\t\t\t\t    NULL, &merged_common_ancestors) < 0)\n  \t\t\treturn -1;\n  \t\to->branch1 = saved_b1;\n  \t\to->branch2 = saved_b2;\n\n-- \n2.9.0.281.g286a8d9\n\nbase-commit: 08bb3500a2a718c3c78b0547c68601cafa7a8fd9\n"},{"id":"291945","messageId":"37e2f36e4f982261a741e327f1b534cb67b65149.1469187652.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469187652.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 01/16] Verify that `git pull --rebase` shows the helpful advice when failing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-22T12:24:48Z","receivedAt":"2016-07-22T12:25:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It was noticed by Brendan Forster last October that the builtin `git am`\nregressed on that. Our hot fix reverted to spawning the recursive merge\ninstead of using it as a library function.\n\nAs we are about to revert that hot fix, after making the recursive merge a\ntrue library function (i.e. a function that does not die() in case of\n\"normal\" errors), let's add a test that verifies that we do not regress on\nthe same problem which made the hot fix necessary in the first place.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n t/t5520-pull.sh | 30 ++++++++++++++++++++++++++++++\n 1 file changed, 30 insertions(+)\n\ndiff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\nindex 37ebbcf..d289056 100755\n--- a/t/t5520-pull.sh\n+++ b/t/t5520-pull.sh\n@@ -255,6 +255,36 @@ test_expect_success '--rebase' '\n \ttest new = \"$(git show HEAD:file2)\"\n '\n \n+test_expect_success '--rebase with conflicts shows advice' '\n+\ttest_when_finished \"git rebase --abort; git checkout -f to-rebase\" &&\n+\tgit checkout -b seq &&\n+\tprintf \"1\\\\n2\\\\n3\\\\n4\\\\n5\\\\n\" >seq.txt &&\n+\tgit add seq.txt &&\n+\ttest_tick &&\n+\tgit commit -m \"Add seq.txt\" &&\n+\tprintf \"6\\\\n\" >>seq.txt &&\n+\ttest_tick &&\n+\tgit commit -m \"Append to seq.txt\" seq.txt &&\n+\tgit checkout -b with-conflicts HEAD^ &&\n+\tprintf \"conflicting\\\\n\" >>seq.txt &&\n+\ttest_tick &&\n+\tgit commit -m \"Create conflict\" seq.txt &&\n+\ttest_must_fail git pull --rebase . seq 2>err >out &&\n+\tgrep \"When you have resolved this problem\" out\n+'\n+test_expect_success 'failed --rebase shows advice' '\n+\ttest_when_finished \"git rebase --abort; git checkout -f to-rebase\" &&\n+\tgit checkout -b diverging &&\n+\ttest_commit attributes .gitattributes \"* text=auto\" attrs &&\n+\tsha1=\"$(printf \"1\\\\r\\\\n\" | git hash-object -w --stdin)\" &&\n+\tgit update-index --cacheinfo 0644 $sha1 file &&\n+\tgit commit -m v1-with-cr &&\n+\tgit checkout -f -b fails-to-rebase HEAD^ &&\n+\ttest_commit v2-without-cr file \"2\" file2-lf &&\n+\ttest_must_fail git pull --rebase . diverging 2>err >out &&\n+\tgrep \"When you have resolved this problem\" out\n+'\n+\n test_expect_success '--rebase fails with multiple branches' '\n \tgit reset --hard before-rebase &&\n \ttest_must_fail git pull --rebase . copy master 2>err &&\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"291946","messageId":"72d1d530bb0e3c96d3affd6679cb7c26026d8321.1469187652.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469187652.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 02/16] Report bugs consistently","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-22T12:24:55Z","receivedAt":"2016-07-22T12:25:19Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The vast majority of error messages in Git's source code which report a\nbug use the convention to prefix the message with \"BUG:\".\n\nAs part of cleaning up merge-recursive to stop die()ing except in case of\ndetected bugs, let's just make the remainder of the bug reports consistent\nwith the de facto rule.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/ls-files.c     |  3 ++-\n builtin/update-index.c |  2 +-\n grep.c                 |  8 ++++----\n imap-send.c            |  4 ++--\n merge-recursive.c      | 15 +++++++--------\n sha1_file.c            |  4 ++--\n trailer.c              |  2 +-\n transport.c            |  2 +-\n wt-status.c            |  4 ++--\n 9 files changed, 22 insertions(+), 22 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex f02e3d2..00ea91a 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -118,7 +118,8 @@ static void show_killed_files(struct dir_struct *dir)\n \t\t\t\t */\n \t\t\t\tpos = cache_name_pos(ent->name, ent->len);\n \t\t\t\tif (0 <= pos)\n-\t\t\t\t\tdie(\"bug in show-killed-files\");\n+\t\t\t\t\tdie(\"BUG: killed-file %.*s not found\",\n+\t\t\t\t\t\tent->len, ent->name);\n \t\t\t\tpos = -pos - 1;\n \t\t\t\twhile (pos < active_nr &&\n \t\t\t\t       ce_stage(active_cache[pos]))\ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex 6cdfd5f..ba04b19 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -1146,7 +1146,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)\n \t\treport(_(\"Untracked cache enabled for '%s'\"), get_git_work_tree());\n \t\tbreak;\n \tdefault:\n-\t\tdie(\"Bug: bad untracked_cache value: %d\", untracked_cache);\n+\t\tdie(\"BUG: bad untracked_cache value: %d\", untracked_cache);\n \t}\n \n \tif (active_cache_changed) {\ndiff --git a/grep.c b/grep.c\nindex 394c856..22cbb73 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -693,10 +693,10 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)\n \n \tfor (p = opt->header_list; p; p = p->next) {\n \t\tif (p->token != GREP_PATTERN_HEAD)\n-\t\t\tdie(\"bug: a non-header pattern in grep header list.\");\n+\t\t\tdie(\"BUG: a non-header pattern in grep header list.\");\n \t\tif (p->field < GREP_HEADER_FIELD_MIN ||\n \t\t    GREP_HEADER_FIELD_MAX <= p->field)\n-\t\t\tdie(\"bug: unknown header field %d\", p->field);\n+\t\t\tdie(\"BUG: unknown header field %d\", p->field);\n \t\tcompile_regexp(p, opt);\n \t}\n \n@@ -709,7 +709,7 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)\n \n \t\th = compile_pattern_atom(&pp);\n \t\tif (!h || pp != p->next)\n-\t\t\tdie(\"bug: malformed header expr\");\n+\t\t\tdie(\"BUG: malformed header expr\");\n \t\tif (!header_group[p->field]) {\n \t\t\theader_group[p->field] = h;\n \t\t\tcontinue;\n@@ -1514,7 +1514,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle\n \t\tcase GREP_BINARY_TEXT:\n \t\t\tbreak;\n \t\tdefault:\n-\t\t\tdie(\"bug: unknown binary handling mode\");\n+\t\t\tdie(\"BUG: unknown binary handling mode\");\n \t\t}\n \t}\n \ndiff --git a/imap-send.c b/imap-send.c\nindex db0fafe..67d67f8 100644\n--- a/imap-send.c\n+++ b/imap-send.c\n@@ -506,12 +506,12 @@ static char *next_arg(char **s)\n \n static int nfsnprintf(char *buf, int blen, const char *fmt, ...)\n {\n-\tint ret;\n+\tint ret = -1;\n \tva_list va;\n \n \tva_start(va, fmt);\n \tif (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)\n-\t\tdie(\"Fatal: buffer too small. Please report a bug.\");\n+\t\tdie(\"BUG: buffer too small (%d < %d)\", ret, blen);\n \tva_end(va);\n \treturn ret;\n }\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 48fe7e7..7400034 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -259,7 +259,7 @@ struct tree *write_tree_from_memory(struct merge_options *o)\n \t\t\t\tfprintf(stderr, \"BUG: %d %.*s\\n\", ce_stage(ce),\n \t\t\t\t\t(int)ce_namelen(ce), ce->name);\n \t\t}\n-\t\tdie(\"Bug in merge-recursive.c\");\n+\t\tdie(\"BUG: unmerged index entries in merge-recursive.c\");\n \t}\n \n \tif (!active_cache_tree)\n@@ -957,9 +957,8 @@ static struct merge_file_info merge_file_1(struct merge_options *o,\n \n \t\t\tif (!oid_eq(&a->oid, &b->oid))\n \t\t\t\tresult.clean = 0;\n-\t\t} else {\n-\t\t\tdie(_(\"unsupported object type in the tree\"));\n-\t\t}\n+\t\t} else\n+\t\t\tdie(_(\"BUG: unsupported object type in the tree\"));\n \t}\n \n \treturn result;\n@@ -1345,7 +1344,7 @@ static int process_renames(struct merge_options *o,\n \t\t\tconst char *ren2_dst = ren2->pair->two->path;\n \t\t\tenum rename_type rename_type;\n \t\t\tif (strcmp(ren1_src, ren2_src) != 0)\n-\t\t\t\tdie(\"ren1_src != ren2_src\");\n+\t\t\t\tdie(\"BUG: ren1_src != ren2_src\");\n \t\t\tren2->dst_entry->processed = 1;\n \t\t\tren2->processed = 1;\n \t\t\tif (strcmp(ren1_dst, ren2_dst) != 0) {\n@@ -1379,7 +1378,7 @@ static int process_renames(struct merge_options *o,\n \t\t\tren2 = lookup->util;\n \t\t\tren2_dst = ren2->pair->two->path;\n \t\t\tif (strcmp(ren1_dst, ren2_dst) != 0)\n-\t\t\t\tdie(\"ren1_dst != ren2_dst\");\n+\t\t\t\tdie(\"BUG: ren1_dst != ren2_dst\");\n \n \t\t\tclean_merge = 0;\n \t\t\tren2->processed = 1;\n@@ -1803,7 +1802,7 @@ static int process_entry(struct merge_options *o,\n \t\t */\n \t\tremove_file(o, 1, path, !a_mode);\n \t} else\n-\t\tdie(_(\"Fatal merge failure, shouldn't happen.\"));\n+\t\tdie(_(\"BUG: fatal merge failure, shouldn't happen.\"));\n \n \treturn clean_merge;\n }\n@@ -1861,7 +1860,7 @@ int merge_trees(struct merge_options *o,\n \t\tfor (i = 0; i < entries->nr; i++) {\n \t\t\tstruct stage_data *e = entries->items[i].util;\n \t\t\tif (!e->processed)\n-\t\t\t\tdie(_(\"Unprocessed path??? %s\"),\n+\t\t\t\tdie(_(\"BUG: unprocessed path??? %s\"),\n \t\t\t\t    entries->items[i].string);\n \t\t}\n \ndiff --git a/sha1_file.c b/sha1_file.c\nindex d5e1121..5085fe0 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -795,7 +795,7 @@ void close_all_packs(void)\n \n \tfor (p = packed_git; p; p = p->next)\n \t\tif (p->do_not_close)\n-\t\t\tdie(\"BUG! Want to close pack marked 'do-not-close'\");\n+\t\t\tdie(\"BUG: want to close pack marked 'do-not-close'\");\n \t\telse\n \t\t\tclose_pack(p);\n }\n@@ -2330,7 +2330,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,\n \tcase OBJ_OFS_DELTA:\n \tcase OBJ_REF_DELTA:\n \t\tif (data)\n-\t\t\tdie(\"BUG in unpack_entry: left loop at a valid delta\");\n+\t\t\tdie(\"BUG: unpack_entry: left loop at a valid delta\");\n \t\tbreak;\n \tcase OBJ_COMMIT:\n \tcase OBJ_TREE:\ndiff --git a/trailer.c b/trailer.c\nindex 8e48a5c..c6ea9ac 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -562,7 +562,7 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)\n \t\t\twarning(_(\"unknown value '%s' for key '%s'\"), value, conf_key);\n \t\tbreak;\n \tdefault:\n-\t\tdie(\"internal bug in trailer.c\");\n+\t\tdie(\"BUG: trailer.c: unhandled type %d\", type);\n \t}\n \treturn 0;\n }\ndiff --git a/transport.c b/transport.c\nindex 59b911e..5d446da 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -563,7 +563,7 @@ void transport_take_over(struct transport *transport,\n \tstruct git_transport_data *data;\n \n \tif (!transport->smart_options)\n-\t\tdie(\"Bug detected: Taking over transport requires non-NULL \"\n+\t\tdie(\"BUG: taking over transport requires non-NULL \"\n \t\t    \"smart_options field.\");\n \n \tdata = xcalloc(1, sizeof(*data));\ndiff --git a/wt-status.c b/wt-status.c\nindex de62ab2..3fb86a4 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -263,7 +263,7 @@ static const char *wt_status_unmerged_status_string(int stagemask)\n \tcase 7:\n \t\treturn _(\"both modified:\");\n \tdefault:\n-\t\tdie(\"bug: unhandled unmerged status %x\", stagemask);\n+\t\tdie(\"BUG: unhandled unmerged status %x\", stagemask);\n \t}\n }\n \n@@ -388,7 +388,7 @@ static void wt_status_print_change_data(struct wt_status *s,\n \tstatus_printf(s, color(WT_STATUS_HEADER, s), \"\\t\");\n \twhat = wt_status_diff_status_string(status);\n \tif (!what)\n-\t\tdie(\"bug: unhandled diff status %c\", status);\n+\t\tdie(\"BUG: unhandled diff status %c\", status);\n \tlen = label_width - utf8_strwidth(what);\n \tassert(len >= 0);\n \tif (status == DIFF_STATUS_COPIED || status == DIFF_STATUS_RENAMED)\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"291947","messageId":"20433a69119e46ee6816ef3028f939399c2e5cf9.1469187653.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469187652.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 03/16] Avoid translating bug messages","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-22T12:24:59Z","receivedAt":"2016-07-22T12:25:22Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"While working on the patch series that avoids die()ing in recursive\nmerges, the issue came up that bug reports (i.e. die(\"BUG: ...\")\nconstructs) should never be translated, as the target audience is the\nGit developer community, not necessarily the current user, and hence\na translated message would make it *harder* to address the problem.\n\nSo let's stop translating the obvious ones. As it is really, really\noutside the purview of this patch series to see whether there are more\ndie() statements that report bugs and are currently translated, that\ntask is left for another day and patch.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 7400034..e0d5a57 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -958,7 +958,7 @@ static struct merge_file_info merge_file_1(struct merge_options *o,\n \t\t\tif (!oid_eq(&a->oid, &b->oid))\n \t\t\t\tresult.clean = 0;\n \t\t} else\n-\t\t\tdie(_(\"BUG: unsupported object type in the tree\"));\n+\t\t\tdie(\"BUG: unsupported object type in the tree\");\n \t}\n \n \treturn result;\n@@ -1802,7 +1802,7 @@ static int process_entry(struct merge_options *o,\n \t\t */\n \t\tremove_file(o, 1, path, !a_mode);\n \t} else\n-\t\tdie(_(\"BUG: fatal merge failure, shouldn't happen.\"));\n+\t\tdie(\"BUG: fatal merge failure, shouldn't happen.\");\n \n \treturn clean_merge;\n }\n@@ -1860,7 +1860,7 @@ int merge_trees(struct merge_options *o,\n \t\tfor (i = 0; i < entries->nr; i++) {\n \t\t\tstruct stage_data *e = entries->items[i].util;\n \t\t\tif (!e->processed)\n-\t\t\t\tdie(_(\"BUG: unprocessed path??? %s\"),\n+\t\t\t\tdie(\"BUG: unprocessed path??? %s\",\n \t\t\t\t    entries->items[i].string);\n \t\t}\n \n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"291948","messageId":"79c0cf5a5d842b31f7393e335c52cd3c061381c2.1469187653.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469187652.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 04/16] merge-recursive: clarify code in was_tracked()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-22T12:25:05Z","receivedAt":"2016-07-22T12:25:26Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It can be puzzling to see that was_tracked() asks to get an index entry\nby name, but does not take a negative return value for an answer.\n\nThe reason we have to do this is that cache_name_pos() only looks for\nentries in stage 0, even if nobody asked for any stage in particular.\n\nLet's rewrite the logic a little bit, to handle the easy case early: if\ncache_name_pos() returned a non-negative position, we know it is a match,\nand we do not even have to compare the name again (cache_name_pos() did\nthat for us already). We can say right away: yes, this file was tracked.\n\nOnly if there was no exact match do we need to look harder for any\nmatching entry in stage 2.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 30 ++++++++++++++----------------\n 1 file changed, 14 insertions(+), 16 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex e0d5a57..dc3182b 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -658,23 +658,21 @@ static int was_tracked(const char *path)\n {\n \tint pos = cache_name_pos(path, strlen(path));\n \n-\tif (pos < 0)\n-\t\tpos = -1 - pos;\n-\twhile (pos < active_nr &&\n-\t       !strcmp(path, active_cache[pos]->name)) {\n-\t\t/*\n-\t\t * If stage #0, it is definitely tracked.\n-\t\t * If it has stage #2 then it was tracked\n-\t\t * before this merge started.  All other\n-\t\t * cases the path was not tracked.\n-\t\t */\n-\t\tswitch (ce_stage(active_cache[pos])) {\n-\t\tcase 0:\n-\t\tcase 2:\n+\tif (0 <= pos)\n+\t\t/* we have been tracking this path */\n+\t\treturn 1;\n+\n+\t/*\n+\t * Look for an unmerged entry for the path,\n+\t * specifically stage #2, which would indicate\n+\t * that \"our\" side before the merge started\n+\t * had the path tracked (and resulted in a conflict).\n+\t */\n+\tfor (pos = -1 - pos;\n+\t     pos < active_nr && !strcmp(path, active_cache[pos]->name);\n+\t     pos++)\n+\t\tif (ce_stage(active_cache[pos]) == 2)\n \t\t\treturn 1;\n-\t\t}\n-\t\tpos++;\n-\t}\n \treturn 0;\n }\n \n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"291949","messageId":"9f3ff7ff08496247b86791f2087ea447b0e110aa.1469187653.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469187652.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 05/16] Prepare the builtins for a libified merge_recursive()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-22T12:25:11Z","receivedAt":"2016-07-22T12:25:30Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Previously, callers of merge_trees() or merge_recursive() expected that\ncode to die() with an error message. This used to be okay because we\ncalled those commands from scripts, and had a chance to print out a\nmessage in case the command failed fatally (read: with exit code 128).\n\nAs scripting incurs its own set of problems (portability, speed,\nidiosynchracies of different shells, limited data structures leading to\ninefficient code), we are converting more and more of these scripts into\nbuiltins, using library functions directly.\n\nWe already tried to use merge_recursive() directly in the builtin\ngit-am, for example. Unfortunately, we had to roll it back temporarily\nbecause some of the code in merge-recursive.c still deemed it okay to\ncall die(), when the builtin am code really wanted to print out a useful\nadvice after the merge failed fatally. In the next commits, we want to\nfix that.\n\nThe code touched by this commit expected merge_trees() to die() with\nsome useful message when there is an error condition, but merge_trees()\nis going to be improved by converting all die() calls to return error()\ninstead (i.e. return value -1 after printing out the message as before),\nso that the caller can react more flexibly.\n\nThis is a step to prepare for the version of merge_trees() that no\nlonger dies,  even if we just imitate the previous behavior by calling\nexit(128): this is what callers of e.g. `git merge` have come to expect.\n\nNote that the callers of the sequencer (revert and cherry-pick) already\nfail fast even for the return value -1; The only difference is that they\nnow get a chance to say \"<command> failed\".\n\nA caller of merge_trees() might want handle error messages themselves\n(or even suppress them). As this patch is already complex enough, we\nleave that change for a later patch.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/checkout.c | 4 +++-\n builtin/merge.c    | 2 ++\n sequencer.c        | 4 ++++\n 3 files changed, 9 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 27c1a05..07dea3b 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -567,8 +567,10 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\to.ancestor = old->name;\n \t\t\to.branch1 = new->name;\n \t\t\to.branch2 = \"local\";\n-\t\t\tmerge_trees(&o, new->commit->tree, work,\n+\t\t\tret = merge_trees(&o, new->commit->tree, work,\n \t\t\t\told->commit->tree, &result);\n+\t\t\tif (ret < 0)\n+\t\t\t\texit(128);\n \t\t\tret = reset_tree(new->commit->tree, opts, 0,\n \t\t\t\t\t writeout_error);\n \t\t\tif (ret)\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 19b3bc2..148a9a5 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -673,6 +673,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,\n \t\thold_locked_index(&lock, 1);\n \t\tclean = merge_recursive(&o, head,\n \t\t\t\tremoteheads->item, reversed, &result);\n+\t\tif (clean < 0)\n+\t\t\texit(128);\n \t\tif (active_cache_changed &&\n \t\t    write_locked_index(&the_index, &lock, COMMIT_LOCK))\n \t\t\tdie (_(\"unable to write %s\"), get_index_file());\ndiff --git a/sequencer.c b/sequencer.c\nindex cdfac82..286a435 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -293,6 +293,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \tclean = merge_trees(&o,\n \t\t\t    head_tree,\n \t\t\t    next_tree, base_tree, &result);\n+\tif (clean < 0)\n+\t\treturn clean;\n \n \tif (active_cache_changed &&\n \t    write_locked_index(&the_index, &index_lock, COMMIT_LOCK))\n@@ -559,6 +561,8 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || opts->action == REPLAY_REVERT) {\n \t\tres = do_recursive_merge(base, next, base_label, next_label,\n \t\t\t\t\t head, &msgbuf, opts);\n+\t\tif (res < 0)\n+\t\t\treturn res;\n \t\twrite_message(&msgbuf, git_path_merge_msg());\n \t} else {\n \t\tstruct commit_list *common = NULL;\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"291950","messageId":"26f12ac5a5b8e722d81c782b32585531521c98d4.1469187653.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469187652.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 06/16] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-22T12:25:16Z","receivedAt":"2016-07-22T12:25:34Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"There are a couple of places where return values indicating errors\nare ignored. Let's teach them manners.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 10 ++++++++--\n 1 file changed, 8 insertions(+), 2 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex dc3182b..2d4cb80 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1949,8 +1949,9 @@ int merge_recursive(struct merge_options *o,\n \t\tsaved_b2 = o->branch2;\n \t\to->branch1 = \"Temporary merge branch 1\";\n \t\to->branch2 = \"Temporary merge branch 2\";\n-\t\tmerge_recursive(o, merged_common_ancestors, iter->item,\n-\t\t\t\tNULL, &merged_common_ancestors);\n+\t\tif (merge_recursive(o, merged_common_ancestors, iter->item,\n+\t\t\t\t    NULL, &merged_common_ancestors) < 0)\n+\t\t\treturn -1;\n \t\to->branch1 = saved_b1;\n \t\to->branch2 = saved_b2;\n \t\to->call_depth--;\n@@ -1966,6 +1967,8 @@ int merge_recursive(struct merge_options *o,\n \to->ancestor = \"merged common ancestors\";\n \tclean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree,\n \t\t\t    &mrtree);\n+\tif (clean < 0)\n+\t\treturn clean;\n \n \tif (o->call_depth) {\n \t\t*result = make_virtual_commit(mrtree, \"merged tree\");\n@@ -2022,6 +2025,9 @@ int merge_recursive_generic(struct merge_options *o,\n \thold_locked_index(lock, 1);\n \tclean = merge_recursive(o, head_commit, next_commit, ca,\n \t\t\tresult);\n+\tif (clean < 0)\n+\t\treturn clean;\n+\n \tif (active_cache_changed &&\n \t    write_locked_index(&the_index, lock, COMMIT_LOCK))\n \t\treturn error(_(\"Unable to write index.\"));\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"291951","messageId":"6a6b5421c8c95177b96b451abc972745e16585c9.1469187653.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469187652.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 07/16] merge-recursive: avoid returning a wholesale struct","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-22T12:25:22Z","receivedAt":"2016-07-22T12:25:38Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It is technically allowed, as per C89, for functions' return type to\nbe complete structs (i.e. *not* just pointers to structs).\n\nHowever, it was just an oversight of this developer when converting\nPython code to C code in 6d297f8 (Status update on merge-recursive in\nC, 2006-07-08) which introduced such a return type.\n\nBesides, by converting this construct to pass in the struct, we can now\nstart returning a value that can indicate errors in future patches. This\nwill help the current effort to libify merge-recursive.c.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 106 ++++++++++++++++++++++++++++--------------------------\n 1 file changed, 56 insertions(+), 50 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 2d4cb80..e1cea96 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -885,47 +885,47 @@ static int merge_3way(struct merge_options *o,\n \treturn merge_status;\n }\n \n-static struct merge_file_info merge_file_1(struct merge_options *o,\n+static int merge_file_1(struct merge_options *o,\n \t\t\t\t\t   const struct diff_filespec *one,\n \t\t\t\t\t   const struct diff_filespec *a,\n \t\t\t\t\t   const struct diff_filespec *b,\n \t\t\t\t\t   const char *branch1,\n-\t\t\t\t\t   const char *branch2)\n+\t\t\t\t\t   const char *branch2,\n+\t\t\t\t\t   struct merge_file_info *result)\n {\n-\tstruct merge_file_info result;\n-\tresult.merge = 0;\n-\tresult.clean = 1;\n+\tresult->merge = 0;\n+\tresult->clean = 1;\n \n \tif ((S_IFMT & a->mode) != (S_IFMT & b->mode)) {\n-\t\tresult.clean = 0;\n+\t\tresult->clean = 0;\n \t\tif (S_ISREG(a->mode)) {\n-\t\t\tresult.mode = a->mode;\n-\t\t\toidcpy(&result.oid, &a->oid);\n+\t\t\tresult->mode = a->mode;\n+\t\t\toidcpy(&result->oid, &a->oid);\n \t\t} else {\n-\t\t\tresult.mode = b->mode;\n-\t\t\toidcpy(&result.oid, &b->oid);\n+\t\t\tresult->mode = b->mode;\n+\t\t\toidcpy(&result->oid, &b->oid);\n \t\t}\n \t} else {\n \t\tif (!oid_eq(&a->oid, &one->oid) && !oid_eq(&b->oid, &one->oid))\n-\t\t\tresult.merge = 1;\n+\t\t\tresult->merge = 1;\n \n \t\t/*\n \t\t * Merge modes\n \t\t */\n \t\tif (a->mode == b->mode || a->mode == one->mode)\n-\t\t\tresult.mode = b->mode;\n+\t\t\tresult->mode = b->mode;\n \t\telse {\n-\t\t\tresult.mode = a->mode;\n+\t\t\tresult->mode = a->mode;\n \t\t\tif (b->mode != one->mode) {\n-\t\t\t\tresult.clean = 0;\n-\t\t\t\tresult.merge = 1;\n+\t\t\t\tresult->clean = 0;\n+\t\t\t\tresult->merge = 1;\n \t\t\t}\n \t\t}\n \n \t\tif (oid_eq(&a->oid, &b->oid) || oid_eq(&a->oid, &one->oid))\n-\t\t\toidcpy(&result.oid, &b->oid);\n+\t\t\toidcpy(&result->oid, &b->oid);\n \t\telse if (oid_eq(&b->oid, &one->oid))\n-\t\t\toidcpy(&result.oid, &a->oid);\n+\t\t\toidcpy(&result->oid, &a->oid);\n \t\telse if (S_ISREG(a->mode)) {\n \t\t\tmmbuffer_t result_buf;\n \t\t\tint merge_status;\n@@ -937,64 +937,66 @@ static struct merge_file_info merge_file_1(struct merge_options *o,\n \t\t\t\tdie(_(\"Failed to execute internal merge\"));\n \n \t\t\tif (write_sha1_file(result_buf.ptr, result_buf.size,\n-\t\t\t\t\t    blob_type, result.oid.hash))\n+\t\t\t\t\t    blob_type, result->oid.hash))\n \t\t\t\tdie(_(\"Unable to add %s to database\"),\n \t\t\t\t    a->path);\n \n \t\t\tfree(result_buf.ptr);\n-\t\t\tresult.clean = (merge_status == 0);\n+\t\t\tresult->clean = (merge_status == 0);\n \t\t} else if (S_ISGITLINK(a->mode)) {\n-\t\t\tresult.clean = merge_submodule(result.oid.hash,\n+\t\t\tresult->clean = merge_submodule(result->oid.hash,\n \t\t\t\t\t\t       one->path,\n \t\t\t\t\t\t       one->oid.hash,\n \t\t\t\t\t\t       a->oid.hash,\n \t\t\t\t\t\t       b->oid.hash,\n \t\t\t\t\t\t       !o->call_depth);\n \t\t} else if (S_ISLNK(a->mode)) {\n-\t\t\toidcpy(&result.oid, &a->oid);\n+\t\t\toidcpy(&result->oid, &a->oid);\n \n \t\t\tif (!oid_eq(&a->oid, &b->oid))\n-\t\t\t\tresult.clean = 0;\n+\t\t\t\tresult->clean = 0;\n \t\t} else\n \t\t\tdie(\"BUG: unsupported object type in the tree\");\n \t}\n \n-\treturn result;\n+\treturn 0;\n }\n \n-static struct merge_file_info\n-merge_file_special_markers(struct merge_options *o,\n+static int merge_file_special_markers(struct merge_options *o,\n \t\t\t   const struct diff_filespec *one,\n \t\t\t   const struct diff_filespec *a,\n \t\t\t   const struct diff_filespec *b,\n \t\t\t   const char *branch1,\n \t\t\t   const char *filename1,\n \t\t\t   const char *branch2,\n-\t\t\t   const char *filename2)\n+\t\t\t   const char *filename2,\n+\t\t\t   struct merge_file_info *mfi)\n {\n \tchar *side1 = NULL;\n \tchar *side2 = NULL;\n-\tstruct merge_file_info mfi;\n+\tint ret;\n \n \tif (filename1)\n \t\tside1 = xstrfmt(\"%s:%s\", branch1, filename1);\n \tif (filename2)\n \t\tside2 = xstrfmt(\"%s:%s\", branch2, filename2);\n \n-\tmfi = merge_file_1(o, one, a, b,\n-\t\t\t   side1 ? side1 : branch1, side2 ? side2 : branch2);\n+\tret = merge_file_1(o, one, a, b,\n+\t\t\t   side1 ? side1 : branch1,\n+\t\t\t   side2 ? side2 : branch2, mfi);\n \tfree(side1);\n \tfree(side2);\n-\treturn mfi;\n+\treturn ret;\n }\n \n-static struct merge_file_info merge_file_one(struct merge_options *o,\n+static int merge_file_one(struct merge_options *o,\n \t\t\t\t\t const char *path,\n \t\t\t\t\t const struct object_id *o_oid, int o_mode,\n \t\t\t\t\t const struct object_id *a_oid, int a_mode,\n \t\t\t\t\t const struct object_id *b_oid, int b_mode,\n \t\t\t\t\t const char *branch1,\n-\t\t\t\t\t const char *branch2)\n+\t\t\t\t\t const char *branch2,\n+\t\t\t\t\t struct merge_file_info *mfi)\n {\n \tstruct diff_filespec one, a, b;\n \n@@ -1005,7 +1007,7 @@ static struct merge_file_info merge_file_one(struct merge_options *o,\n \ta.mode = a_mode;\n \toidcpy(&b.oid, b_oid);\n \tb.mode = b_mode;\n-\treturn merge_file_1(o, &one, &a, &b, branch1, branch2);\n+\treturn merge_file_1(o, &one, &a, &b, branch1, branch2, mfi);\n }\n \n static void handle_change_delete(struct merge_options *o,\n@@ -1178,11 +1180,12 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n \t\tstruct merge_file_info mfi;\n \t\tstruct diff_filespec other;\n \t\tstruct diff_filespec *add;\n-\t\tmfi = merge_file_one(o, one->path,\n+\t\tif (merge_file_one(o, one->path,\n \t\t\t\t &one->oid, one->mode,\n \t\t\t\t &a->oid, a->mode,\n \t\t\t\t &b->oid, b->mode,\n-\t\t\t\t ci->branch1, ci->branch2);\n+\t\t\t\t ci->branch1, ci->branch2, &mfi))\n+\t\t\treturn;\n \t\t/*\n \t\t * FIXME: For rename/add-source conflicts (if we could detect\n \t\t * such), this is wrong.  We should instead find a unique\n@@ -1236,12 +1239,13 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \tremove_file(o, 1, a->path, o->call_depth || would_lose_untracked(a->path));\n \tremove_file(o, 1, b->path, o->call_depth || would_lose_untracked(b->path));\n \n-\tmfi_c1 = merge_file_special_markers(o, a, c1, &ci->ren1_other,\n-\t\t\t\t\t    o->branch1, c1->path,\n-\t\t\t\t\t    o->branch2, ci->ren1_other.path);\n-\tmfi_c2 = merge_file_special_markers(o, b, &ci->ren2_other, c2,\n-\t\t\t\t\t    o->branch1, ci->ren2_other.path,\n-\t\t\t\t\t    o->branch2, c2->path);\n+\tif (merge_file_special_markers(o, a, c1, &ci->ren1_other,\n+\t\t\t\t       o->branch1, c1->path,\n+\t\t\t\t       o->branch2, ci->ren1_other.path, &mfi_c1) ||\n+\t    merge_file_special_markers(o, b, &ci->ren2_other, c2,\n+\t\t\t\t       o->branch1, ci->ren2_other.path,\n+\t\t\t\t       o->branch2, c2->path, &mfi_c2))\n+\t\treturn;\n \n \tif (o->call_depth) {\n \t\t/*\n@@ -1464,12 +1468,13 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t       ren1_dst, branch2);\n \t\t\t\tif (o->call_depth) {\n \t\t\t\t\tstruct merge_file_info mfi;\n-\t\t\t\t\tmfi = merge_file_one(o, ren1_dst, &null_oid, 0,\n-\t\t\t\t\t\t\t &ren1->pair->two->oid,\n-\t\t\t\t\t\t\t ren1->pair->two->mode,\n-\t\t\t\t\t\t\t &dst_other.oid,\n-\t\t\t\t\t\t\t dst_other.mode,\n-\t\t\t\t\t\t\t branch1, branch2);\n+\t\t\t\t\tif (merge_file_one(o, ren1_dst, &null_oid, 0,\n+\t\t\t\t\t\t\t   &ren1->pair->two->oid,\n+\t\t\t\t\t\t\t   ren1->pair->two->mode,\n+\t\t\t\t\t\t\t   &dst_other.oid,\n+\t\t\t\t\t\t\t   dst_other.mode,\n+\t\t\t\t\t\t\t   branch1, branch2, &mfi))\n+\t\t\t\t\t\treturn -1;\n \t\t\t\t\toutput(o, 1, _(\"Adding merged %s\"), ren1_dst);\n \t\t\t\t\tupdate_file(o, 0, &mfi.oid,\n \t\t\t\t\t\t    mfi.mode, ren1_dst);\n@@ -1627,9 +1632,10 @@ static int merge_content(struct merge_options *o,\n \t\tif (dir_in_way(path, !o->call_depth))\n \t\t\tdf_conflict_remains = 1;\n \t}\n-\tmfi = merge_file_special_markers(o, &one, &a, &b,\n-\t\t\t\t\t o->branch1, path1,\n-\t\t\t\t\t o->branch2, path2);\n+\tif (merge_file_special_markers(o, &one, &a, &b,\n+\t\t\t\t       o->branch1, path1,\n+\t\t\t\t       o->branch2, path2, &mfi))\n+\t\treturn -1;\n \n \tif (mfi.clean && !df_conflict_remains &&\n \t    oid_eq(&mfi.oid, a_oid) && mfi.mode == a_mode) {\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"291952","messageId":"2d80e8c9fce38821fa95646951f7d8351fec7db8.1469187653.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469187652.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 08/16] merge-recursive: allow write_tree_from_memory() to error out","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-22T12:25:28Z","receivedAt":"2016-07-22T12:25:43Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It is possible that a tree cannot be written (think: disk full). We\nwill want to give the caller a chance to clean up instead of letting\nthe program die() in such a case.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex e1cea96..d5b9b1c 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1879,8 +1879,8 @@ int merge_trees(struct merge_options *o,\n \telse\n \t\tclean = 1;\n \n-\tif (o->call_depth)\n-\t\t*result = write_tree_from_memory(o);\n+\tif (o->call_depth && !(*result = write_tree_from_memory(o)))\n+\t\treturn -1;\n \n \treturn clean;\n }\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"291953","messageId":"43886300ac93c43b2e23b5324a3c871a6f6ddd7e.1469187653.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469187652.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 09/16] merge-recursive: handle return values indicating errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-22T12:25:32Z","receivedAt":"2016-07-22T12:25:50Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We are about to libify the recursive merge machinery, where we only\ndie() in case of a bug or memory contention. To that end, we must heed\nnegative return values as indicating errors.\n\nThis requires our functions to be careful to pass through error\nconditions in call chains, and for quite a few functions this means\nthat they have to return values to begin with.\n\nThe next step will be to convert the places where we currently die() to\nreturn negative values (read: -1) instead.\n\nNote that we ignore errors reported by make_room_for_path(), consistent\nwith the previous behavior (update_file_flags() used the return value of\nmake_room_for_path() only to indicate an early return, but not a fatal\nerror): if the error is really a fatal error, we will notice later; If\nnot, it was not that serious a problem to begin with. (Witnesses in\nfavor of this reasoning are t4151-am-abort and t7610-mergetool, which\nwould start failing if we stopped on errors reported by\nmake_room_for_path()).\n\nAlso note: while this patch makes the code slightly less readable in\nupdate_file_flags() (we introduce a new \"goto free_buf;\" instead of\nan explicit \"free(buf); return;\"), it is a preparatory change for\nthe next patch where we will convert all of the die() calls in the same\nfunction to go through the free_buf return path instead.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 252 ++++++++++++++++++++++++++++++++----------------------\n 1 file changed, 150 insertions(+), 102 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex d5b9b1c..697ba03 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -733,12 +733,12 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n }\n \n-static void update_file_flags(struct merge_options *o,\n-\t\t\t      const struct object_id *oid,\n-\t\t\t      unsigned mode,\n-\t\t\t      const char *path,\n-\t\t\t      int update_cache,\n-\t\t\t      int update_wd)\n+static int update_file_flags(struct merge_options *o,\n+\t\t\t     const struct object_id *oid,\n+\t\t\t     unsigned mode,\n+\t\t\t     const char *path,\n+\t\t\t     int update_cache,\n+\t\t\t     int update_wd)\n {\n \tif (o->call_depth)\n \t\tupdate_wd = 0;\n@@ -774,8 +774,7 @@ static void update_file_flags(struct merge_options *o,\n \n \t\tif (make_room_for_path(o, path) < 0) {\n \t\t\tupdate_wd = 0;\n-\t\t\tfree(buf);\n-\t\t\tgoto update_index;\n+\t\t\tgoto free_buf;\n \t\t}\n \t\tif (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) {\n \t\t\tint fd;\n@@ -798,20 +797,22 @@ static void update_file_flags(struct merge_options *o,\n \t\t} else\n \t\t\tdie(_(\"do not know what to do with %06o %s '%s'\"),\n \t\t\t    mode, oid_to_hex(oid), path);\n+ free_buf:\n \t\tfree(buf);\n \t}\n  update_index:\n \tif (update_cache)\n \t\tadd_cacheinfo(mode, oid, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n+\treturn 0;\n }\n \n-static void update_file(struct merge_options *o,\n-\t\t\tint clean,\n-\t\t\tconst struct object_id *oid,\n-\t\t\tunsigned mode,\n-\t\t\tconst char *path)\n+static int update_file(struct merge_options *o,\n+\t\t       int clean,\n+\t\t       const struct object_id *oid,\n+\t\t       unsigned mode,\n+\t\t       const char *path)\n {\n-\tupdate_file_flags(o, oid, mode, path, o->call_depth || clean, !o->call_depth);\n+\treturn update_file_flags(o, oid, mode, path, o->call_depth || clean, !o->call_depth);\n }\n \n /* Low level file merging, update and removal */\n@@ -1010,7 +1011,7 @@ static int merge_file_one(struct merge_options *o,\n \treturn merge_file_1(o, &one, &a, &b, branch1, branch2, mfi);\n }\n \n-static void handle_change_delete(struct merge_options *o,\n+static int handle_change_delete(struct merge_options *o,\n \t\t\t\t const char *path,\n \t\t\t\t const struct object_id *o_oid, int o_mode,\n \t\t\t\t const struct object_id *a_oid, int a_mode,\n@@ -1018,6 +1019,7 @@ static void handle_change_delete(struct merge_options *o,\n \t\t\t\t const char *change, const char *change_past)\n {\n \tchar *renamed = NULL;\n+\tint ret = 0;\n \tif (dir_in_way(path, !o->call_depth)) {\n \t\trenamed = unique_path(o, path, a_oid ? o->branch1 : o->branch2);\n \t}\n@@ -1028,21 +1030,23 @@ static void handle_change_delete(struct merge_options *o,\n \t\t * correct; since there is no true \"middle point\" between\n \t\t * them, simply reuse the base version for virtual merge base.\n \t\t */\n-\t\tremove_file_from_cache(path);\n-\t\tupdate_file(o, 0, o_oid, o_mode, renamed ? renamed : path);\n+\t\tret = remove_file_from_cache(path);\n+\t\tif (!ret)\n+\t\t\tret = update_file(o, 0, o_oid, o_mode,\n+\t\t\t\t\t  renamed ? renamed : path);\n \t} else if (!a_oid) {\n \t\tif (!renamed) {\n \t\t\toutput(o, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n \t\t\t       \"and %s in %s. Version %s of %s left in tree.\"),\n \t\t\t       change, path, o->branch1, change_past,\n \t\t\t       o->branch2, o->branch2, path);\n-\t\t\tupdate_file(o, 0, b_oid, b_mode, path);\n+\t\t\tret = update_file(o, 0, b_oid, b_mode, path);\n \t\t} else {\n \t\t\toutput(o, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n \t\t\t       \"and %s in %s. Version %s of %s left in tree at %s.\"),\n \t\t\t       change, path, o->branch1, change_past,\n \t\t\t       o->branch2, o->branch2, path, renamed);\n-\t\t\tupdate_file(o, 0, b_oid, b_mode, renamed);\n+\t\t\tret = update_file(o, 0, b_oid, b_mode, renamed);\n \t\t}\n \t} else {\n \t\tif (!renamed) {\n@@ -1055,7 +1059,7 @@ static void handle_change_delete(struct merge_options *o,\n \t\t\t       \"and %s in %s. Version %s of %s left in tree at %s.\"),\n \t\t\t       change, path, o->branch2, change_past,\n \t\t\t       o->branch1, o->branch1, path, renamed);\n-\t\t\tupdate_file(o, 0, a_oid, a_mode, renamed);\n+\t\t\tret = update_file(o, 0, a_oid, a_mode, renamed);\n \t\t}\n \t\t/*\n \t\t * No need to call update_file() on path when !renamed, since\n@@ -1065,9 +1069,11 @@ static void handle_change_delete(struct merge_options *o,\n \t\t */\n \t}\n \tfree(renamed);\n+\n+\treturn ret;\n }\n \n-static void conflict_rename_delete(struct merge_options *o,\n+static int conflict_rename_delete(struct merge_options *o,\n \t\t\t\t   struct diff_filepair *pair,\n \t\t\t\t   const char *rename_branch,\n \t\t\t\t   const char *other_branch)\n@@ -1087,21 +1093,20 @@ static void conflict_rename_delete(struct merge_options *o,\n \t\tb_mode = dest->mode;\n \t}\n \n-\thandle_change_delete(o,\n-\t\t\t     o->call_depth ? orig->path : dest->path,\n-\t\t\t     &orig->oid, orig->mode,\n-\t\t\t     a_oid, a_mode,\n-\t\t\t     b_oid, b_mode,\n-\t\t\t     _(\"rename\"), _(\"renamed\"));\n-\n-\tif (o->call_depth) {\n-\t\tremove_file_from_cache(dest->path);\n-\t} else {\n-\t\tupdate_stages(dest->path, NULL,\n-\t\t\t      rename_branch == o->branch1 ? dest : NULL,\n-\t\t\t      rename_branch == o->branch1 ? NULL : dest);\n-\t}\n+\tif (handle_change_delete(o,\n+\t\t\t\t o->call_depth ? orig->path : dest->path,\n+\t\t\t\t &orig->oid, orig->mode,\n+\t\t\t\t a_oid, a_mode,\n+\t\t\t\t b_oid, b_mode,\n+\t\t\t\t _(\"rename\"), _(\"renamed\")))\n+\t\treturn -1;\n \n+\tif (o->call_depth)\n+\t\treturn remove_file_from_cache(dest->path);\n+\telse\n+\t\treturn update_stages(dest->path, NULL,\n+\t\t\t\t     rename_branch == o->branch1 ? dest : NULL,\n+\t\t\t\t     rename_branch == o->branch1 ? NULL : dest);\n }\n \n static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,\n@@ -1117,7 +1122,7 @@ static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,\n \treturn target;\n }\n \n-static void handle_file(struct merge_options *o,\n+static int handle_file(struct merge_options *o,\n \t\t\tstruct diff_filespec *rename,\n \t\t\tint stage,\n \t\t\tstruct rename_conflict_info *ci)\n@@ -1127,6 +1132,7 @@ static void handle_file(struct merge_options *o,\n \tconst char *cur_branch, *other_branch;\n \tstruct diff_filespec other;\n \tstruct diff_filespec *add;\n+\tint ret;\n \n \tif (stage == 2) {\n \t\tdst_entry = ci->dst_entry1;\n@@ -1141,7 +1147,8 @@ static void handle_file(struct merge_options *o,\n \tadd = filespec_from_entry(&other, dst_entry, stage ^ 1);\n \tif (add) {\n \t\tchar *add_name = unique_path(o, rename->path, other_branch);\n-\t\tupdate_file(o, 0, &add->oid, add->mode, add_name);\n+\t\tif (update_file(o, 0, &add->oid, add->mode, add_name))\n+\t\t\treturn -1;\n \n \t\tremove_file(o, 0, rename->path, 0);\n \t\tdst_name = unique_path(o, rename->path, cur_branch);\n@@ -1152,17 +1159,20 @@ static void handle_file(struct merge_options *o,\n \t\t\t       rename->path, other_branch, dst_name);\n \t\t}\n \t}\n-\tupdate_file(o, 0, &rename->oid, rename->mode, dst_name);\n-\tif (stage == 2)\n-\t\tupdate_stages(rename->path, NULL, rename, add);\n+\tif ((ret = update_file(o, 0, &rename->oid, rename->mode, dst_name)))\n+\t\t; /* fall through, do allow dst_name to be released */\n+\telse if (stage == 2)\n+\t\tret = update_stages(rename->path, NULL, rename, add);\n \telse\n-\t\tupdate_stages(rename->path, NULL, add, rename);\n+\t\tret = update_stages(rename->path, NULL, add, rename);\n \n \tif (dst_name != rename->path)\n \t\tfree(dst_name);\n+\n+\treturn ret;\n }\n \n-static void conflict_rename_rename_1to2(struct merge_options *o,\n+static int conflict_rename_rename_1to2(struct merge_options *o,\n \t\t\t\t\tstruct rename_conflict_info *ci)\n {\n \t/* One file was renamed in both branches, but to different names. */\n@@ -1185,14 +1195,16 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n \t\t\t\t &a->oid, a->mode,\n \t\t\t\t &b->oid, b->mode,\n \t\t\t\t ci->branch1, ci->branch2, &mfi))\n-\t\t\treturn;\n+\t\t\treturn -1;\n+\n \t\t/*\n \t\t * FIXME: For rename/add-source conflicts (if we could detect\n \t\t * such), this is wrong.  We should instead find a unique\n \t\t * pathname and then either rename the add-source file to that\n \t\t * unique path, or use that unique path instead of src here.\n \t\t */\n-\t\tupdate_file(o, 0, &mfi.oid, mfi.mode, one->path);\n+\t\tif (update_file(o, 0, &mfi.oid, mfi.mode, one->path))\n+\t\t\treturn -1;\n \n \t\t/*\n \t\t * Above, we put the merged content at the merge-base's\n@@ -1203,22 +1215,26 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n \t\t * resolving the conflict at that path in its favor.\n \t\t */\n \t\tadd = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);\n-\t\tif (add)\n-\t\t\tupdate_file(o, 0, &add->oid, add->mode, a->path);\n+\t\tif (add) {\n+\t\t\tif (update_file(o, 0, &add->oid, add->mode, a->path))\n+\t\t\t\treturn -1;\n+\t\t}\n \t\telse\n \t\t\tremove_file_from_cache(a->path);\n \t\tadd = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);\n-\t\tif (add)\n-\t\t\tupdate_file(o, 0, &add->oid, add->mode, b->path);\n+\t\tif (add) {\n+\t\t\tif (update_file(o, 0, &add->oid, add->mode, b->path))\n+\t\t\t\treturn -1;\n+\t\t}\n \t\telse\n \t\t\tremove_file_from_cache(b->path);\n-\t} else {\n-\t\thandle_file(o, a, 2, ci);\n-\t\thandle_file(o, b, 3, ci);\n-\t}\n+\t} else if (handle_file(o, a, 2, ci) || handle_file(o, b, 3, ci))\n+\t\treturn -1;\n+\n+\treturn 0;\n }\n \n-static void conflict_rename_rename_2to1(struct merge_options *o,\n+static int conflict_rename_rename_2to1(struct merge_options *o,\n \t\t\t\t\tstruct rename_conflict_info *ci)\n {\n \t/* Two files, a & b, were renamed to the same thing, c. */\n@@ -1229,6 +1245,7 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \tchar *path = c1->path; /* == c2->path */\n \tstruct merge_file_info mfi_c1;\n \tstruct merge_file_info mfi_c2;\n+\tint ret;\n \n \toutput(o, 1, _(\"CONFLICT (rename/rename): \"\n \t       \"Rename %s->%s in %s. \"\n@@ -1245,7 +1262,7 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \t    merge_file_special_markers(o, b, &ci->ren2_other, c2,\n \t\t\t\t       o->branch1, ci->ren2_other.path,\n \t\t\t\t       o->branch2, c2->path, &mfi_c2))\n-\t\treturn;\n+\t\treturn -1;\n \n \tif (o->call_depth) {\n \t\t/*\n@@ -1256,19 +1273,25 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \t\t * again later for the non-recursive merge.\n \t\t */\n \t\tremove_file(o, 0, path, 0);\n-\t\tupdate_file(o, 0, &mfi_c1.oid, mfi_c1.mode, a->path);\n-\t\tupdate_file(o, 0, &mfi_c2.oid, mfi_c2.mode, b->path);\n+\t\tret = update_file(o, 0, &mfi_c1.oid, mfi_c1.mode, a->path);\n+\t\tif (!ret)\n+\t\t\tret = update_file(o, 0, &mfi_c2.oid, mfi_c2.mode,\n+\t\t\t\t\t  b->path);\n \t} else {\n \t\tchar *new_path1 = unique_path(o, path, ci->branch1);\n \t\tchar *new_path2 = unique_path(o, path, ci->branch2);\n \t\toutput(o, 1, _(\"Renaming %s to %s and %s to %s instead\"),\n \t\t       a->path, new_path1, b->path, new_path2);\n \t\tremove_file(o, 0, path, 0);\n-\t\tupdate_file(o, 0, &mfi_c1.oid, mfi_c1.mode, new_path1);\n-\t\tupdate_file(o, 0, &mfi_c2.oid, mfi_c2.mode, new_path2);\n+\t\tret = update_file(o, 0, &mfi_c1.oid, mfi_c1.mode, new_path1);\n+\t\tif (!ret)\n+\t\t\tret = update_file(o, 0, &mfi_c2.oid, mfi_c2.mode,\n+\t\t\t\t\t  new_path2);\n \t\tfree(new_path2);\n \t\tfree(new_path1);\n \t}\n+\n+\treturn ret;\n }\n \n static int process_renames(struct merge_options *o,\n@@ -1453,12 +1476,13 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t * update_file_flags() instead of\n \t\t\t\t * update_file().\n \t\t\t\t */\n-\t\t\t\tupdate_file_flags(o,\n-\t\t\t\t\t\t  &ren1->pair->two->oid,\n-\t\t\t\t\t\t  ren1->pair->two->mode,\n-\t\t\t\t\t\t  ren1_dst,\n-\t\t\t\t\t\t  1, /* update_cache */\n-\t\t\t\t\t\t  0  /* update_wd    */);\n+\t\t\t\tif (update_file_flags(o,\n+\t\t\t\t\t\t      &ren1->pair->two->oid,\n+\t\t\t\t\t\t      ren1->pair->two->mode,\n+\t\t\t\t\t\t      ren1_dst,\n+\t\t\t\t\t\t      1, /* update_cache */\n+\t\t\t\t\t\t      0  /* update_wd    */))\n+\t\t\t\t\tclean_merge = -1;\n \t\t\t} else if (!oid_eq(&dst_other.oid, &null_oid)) {\n \t\t\t\tclean_merge = 0;\n \t\t\t\ttry_merge = 1;\n@@ -1473,22 +1497,28 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t\t\t\t   ren1->pair->two->mode,\n \t\t\t\t\t\t\t   &dst_other.oid,\n \t\t\t\t\t\t\t   dst_other.mode,\n-\t\t\t\t\t\t\t   branch1, branch2, &mfi))\n-\t\t\t\t\t\treturn -1;\n+\t\t\t\t\t\t\t   branch1, branch2, &mfi)) {\n+\t\t\t\t\t\tclean_merge = -1;\n+\t\t\t\t\t\tgoto cleanup_and_return;\n+\t\t\t\t\t}\n \t\t\t\t\toutput(o, 1, _(\"Adding merged %s\"), ren1_dst);\n-\t\t\t\t\tupdate_file(o, 0, &mfi.oid,\n-\t\t\t\t\t\t    mfi.mode, ren1_dst);\n+\t\t\t\t\tif (update_file(o, 0, &mfi.oid,\n+\t\t\t\t\t\t\tmfi.mode, ren1_dst))\n+\t\t\t\t\t\tclean_merge = -1;\n \t\t\t\t\ttry_merge = 0;\n \t\t\t\t} else {\n \t\t\t\t\tchar *new_path = unique_path(o, ren1_dst, branch2);\n \t\t\t\t\toutput(o, 1, _(\"Adding as %s instead\"), new_path);\n-\t\t\t\t\tupdate_file(o, 0, &dst_other.oid,\n-\t\t\t\t\t\t    dst_other.mode, new_path);\n+\t\t\t\t\tif (update_file(o, 0, &dst_other.oid,\n+\t\t\t\t\t\t\tdst_other.mode, new_path))\n+\t\t\t\t\t\tclean_merge = -1;\n \t\t\t\t\tfree(new_path);\n \t\t\t\t}\n \t\t\t} else\n \t\t\t\ttry_merge = 1;\n \n+\t\t\tif (clean_merge < 0)\n+\t\t\t\tgoto cleanup_and_return;\n \t\t\tif (try_merge) {\n \t\t\t\tstruct diff_filespec *one, *a, *b;\n \t\t\t\tsrc_other.path = (char *)ren1_src;\n@@ -1515,6 +1545,7 @@ static int process_renames(struct merge_options *o,\n \t\t\t}\n \t\t}\n \t}\n+cleanup_and_return:\n \tstring_list_clear(&a_by_dst, 0);\n \tstring_list_clear(&b_by_dst, 0);\n \n@@ -1577,18 +1608,18 @@ static int blob_unchanged(const struct object_id *o_oid,\n \treturn ret;\n }\n \n-static void handle_modify_delete(struct merge_options *o,\n+static int handle_modify_delete(struct merge_options *o,\n \t\t\t\t const char *path,\n \t\t\t\t struct object_id *o_oid, int o_mode,\n \t\t\t\t struct object_id *a_oid, int a_mode,\n \t\t\t\t struct object_id *b_oid, int b_mode)\n {\n-\thandle_change_delete(o,\n-\t\t\t     path,\n-\t\t\t     o_oid, o_mode,\n-\t\t\t     a_oid, a_mode,\n-\t\t\t     b_oid, b_mode,\n-\t\t\t     _(\"modify\"), _(\"modified\"));\n+\treturn handle_change_delete(o,\n+\t\t\t\t    path,\n+\t\t\t\t    o_oid, o_mode,\n+\t\t\t\t    a_oid, a_mode,\n+\t\t\t\t    b_oid, b_mode,\n+\t\t\t\t    _(\"modify\"), _(\"modified\"));\n }\n \n static int merge_content(struct merge_options *o,\n@@ -1662,7 +1693,8 @@ static int merge_content(struct merge_options *o,\n \t\toutput(o, 1, _(\"CONFLICT (%s): Merge conflict in %s\"),\n \t\t\t\treason, path);\n \t\tif (rename_conflict_info && !df_conflict_remains)\n-\t\t\tupdate_stages(path, &one, &a, &b);\n+\t\t\tif (update_stages(path, &one, &a, &b))\n+\t\t\t\treturn -1;\n \t}\n \n \tif (df_conflict_remains) {\n@@ -1670,30 +1702,33 @@ static int merge_content(struct merge_options *o,\n \t\tif (o->call_depth) {\n \t\t\tremove_file_from_cache(path);\n \t\t} else {\n-\t\t\tif (!mfi.clean)\n-\t\t\t\tupdate_stages(path, &one, &a, &b);\n-\t\t\telse {\n+\t\t\tif (!mfi.clean) {\n+\t\t\t\tif (update_stages(path, &one, &a, &b))\n+\t\t\t\t\treturn -1;\n+\t\t\t} else {\n \t\t\t\tint file_from_stage2 = was_tracked(path);\n \t\t\t\tstruct diff_filespec merged;\n \t\t\t\toidcpy(&merged.oid, &mfi.oid);\n \t\t\t\tmerged.mode = mfi.mode;\n \n-\t\t\t\tupdate_stages(path, NULL,\n-\t\t\t\t\t      file_from_stage2 ? &merged : NULL,\n-\t\t\t\t\t      file_from_stage2 ? NULL : &merged);\n+\t\t\t\tif (update_stages(path, NULL,\n+\t\t\t\t\t\t  file_from_stage2 ? &merged : NULL,\n+\t\t\t\t\t\t  file_from_stage2 ? NULL : &merged))\n+\t\t\t\t\treturn -1;\n \t\t\t}\n \n \t\t}\n \t\tnew_path = unique_path(o, path, rename_conflict_info->branch1);\n \t\toutput(o, 1, _(\"Adding as %s instead\"), new_path);\n-\t\tupdate_file(o, 0, &mfi.oid, mfi.mode, new_path);\n+\t\tif (update_file(o, 0, &mfi.oid, mfi.mode, new_path)) {\n+\t\t\tfree(new_path);\n+\t\t\treturn -1;\n+\t\t}\n \t\tfree(new_path);\n \t\tmfi.clean = 0;\n-\t} else {\n-\t\tupdate_file(o, mfi.clean, &mfi.oid, mfi.mode, path);\n-\t}\n+\t} else if (update_file(o, mfi.clean, &mfi.oid, mfi.mode, path))\n+\t\treturn -1;\n \treturn mfi.clean;\n-\n }\n \n /* Per entry merge function */\n@@ -1721,17 +1756,21 @@ static int process_entry(struct merge_options *o,\n \t\t\tbreak;\n \t\tcase RENAME_DELETE:\n \t\t\tclean_merge = 0;\n-\t\t\tconflict_rename_delete(o, conflict_info->pair1,\n-\t\t\t\t\t       conflict_info->branch1,\n-\t\t\t\t\t       conflict_info->branch2);\n+\t\t\tif (conflict_rename_delete(o,\n+\t\t\t\t\t\t   conflict_info->pair1,\n+\t\t\t\t\t\t   conflict_info->branch1,\n+\t\t\t\t\t\t   conflict_info->branch2))\n+\t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_ONE_FILE_TO_TWO:\n \t\t\tclean_merge = 0;\n-\t\t\tconflict_rename_rename_1to2(o, conflict_info);\n+\t\t\tif (conflict_rename_rename_1to2(o, conflict_info))\n+\t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_TWO_FILES_TO_ONE:\n \t\t\tclean_merge = 0;\n-\t\t\tconflict_rename_rename_2to1(o, conflict_info);\n+\t\t\tif (conflict_rename_rename_2to1(o, conflict_info))\n+\t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tdefault:\n \t\t\tentry->processed = 0;\n@@ -1751,8 +1790,9 @@ static int process_entry(struct merge_options *o,\n \t\t} else {\n \t\t\t/* Modify/delete; deleted side may have put a directory in the way */\n \t\t\tclean_merge = 0;\n-\t\t\thandle_modify_delete(o, path, o_oid, o_mode,\n-\t\t\t\t\t     a_oid, a_mode, b_oid, b_mode);\n+\t\t\tif (handle_modify_delete(o, path, o_oid, o_mode,\n+\t\t\t\t\t\t a_oid, a_mode, b_oid, b_mode))\n+\t\t\t\tclean_merge = -1;\n \t\t}\n \t} else if ((!o_oid && a_oid && !b_oid) ||\n \t\t   (!o_oid && !a_oid && b_oid)) {\n@@ -1784,14 +1824,16 @@ static int process_entry(struct merge_options *o,\n \t\t\toutput(o, 1, _(\"CONFLICT (%s): There is a directory with name %s in %s. \"\n \t\t\t       \"Adding %s as %s\"),\n \t\t\t       conf, path, other_branch, path, new_path);\n-\t\t\tupdate_file(o, 0, oid, mode, new_path);\n-\t\t\tif (o->call_depth)\n+\t\t\tif (update_file(o, 0, oid, mode, new_path))\n+\t\t\t\tclean_merge = -1;\n+\t\t\telse if (o->call_depth)\n \t\t\t\tremove_file_from_cache(path);\n \t\t\tfree(new_path);\n \t\t} else {\n \t\t\toutput(o, 2, _(\"Adding %s\"), path);\n \t\t\t/* do not overwrite file if already present */\n-\t\t\tupdate_file_flags(o, oid, mode, path, 1, !a_oid);\n+\t\t\tif (update_file_flags(o, oid, mode, path, 1, !a_oid))\n+\t\t\t\tclean_merge = -1;\n \t\t}\n \t} else if (a_oid && b_oid) {\n \t\t/* Case C: Added in both (check for same permissions) and */\n@@ -1854,12 +1896,18 @@ int merge_trees(struct merge_options *o,\n \t\tre_head  = get_renames(o, head, common, head, merge, entries);\n \t\tre_merge = get_renames(o, merge, common, head, merge, entries);\n \t\tclean = process_renames(o, re_head, re_merge);\n+\t\tif (clean < 0)\n+\t\t\treturn clean;\n \t\tfor (i = entries->nr-1; 0 <= i; i--) {\n \t\t\tconst char *path = entries->items[i].string;\n \t\t\tstruct stage_data *e = entries->items[i].util;\n-\t\t\tif (!e->processed\n-\t\t\t\t&& !process_entry(o, path, e))\n-\t\t\t\tclean = 0;\n+\t\t\tif (!e->processed) {\n+\t\t\t\tint ret = process_entry(o, path, e);\n+\t\t\t\tif (!ret)\n+\t\t\t\t\tclean = 0;\n+\t\t\t\telse if (ret < 0)\n+\t\t\t\t\treturn ret;\n+\t\t\t}\n \t\t}\n \t\tfor (i = 0; i < entries->nr; i++) {\n \t\t\tstruct stage_data *e = entries->items[i].util;\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"291954","messageId":"41bf328361a52df72c268fc37cffe6806b2447dd.1469187653.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469187652.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 10/16] merge-recursive: switch to returning errors instead of dying","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-22T12:25:36Z","receivedAt":"2016-07-22T12:25:53Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The recursive merge machinery is supposed to be a library function, i.e.\nit should return an error when it fails. Originally the functions were\npart of the builtin \"merge-recursive\", though, where it was simpler to\ncall die() and be done with error handling.\n\nThe existing callers were already prepared to detect negative return\nvalues to indicate errors and to behave as previously: exit with code 128\n(which is the same thing that die() does, after printing the message).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 62 +++++++++++++++++++++++++++++++------------------------\n 1 file changed, 35 insertions(+), 27 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 697ba03..24b42d6 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -266,8 +266,10 @@ struct tree *write_tree_from_memory(struct merge_options *o)\n \t\tactive_cache_tree = cache_tree();\n \n \tif (!cache_tree_fully_valid(active_cache_tree) &&\n-\t    cache_tree_update(&the_index, 0) < 0)\n-\t\tdie(_(\"error building trees\"));\n+\t    cache_tree_update(&the_index, 0) < 0) {\n+\t\terror(_(\"error building trees\"));\n+\t\treturn NULL;\n+\t}\n \n \tresult = lookup_tree(active_cache_tree->sha1);\n \n@@ -707,12 +709,10 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \t/* Make sure leading directories are created */\n \tstatus = safe_create_leading_directories_const(path);\n \tif (status) {\n-\t\tif (status == SCLD_EXISTS) {\n+\t\tif (status == SCLD_EXISTS)\n \t\t\t/* something else exists */\n-\t\t\terror(msg, path, _(\": perhaps a D/F conflict?\"));\n-\t\t\treturn -1;\n-\t\t}\n-\t\tdie(msg, path, \"\");\n+\t\t\treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n+\t\treturn error(msg, path, \"\");\n \t}\n \n \t/*\n@@ -740,6 +740,8 @@ static int update_file_flags(struct merge_options *o,\n \t\t\t     int update_cache,\n \t\t\t     int update_wd)\n {\n+\tint ret = 0;\n+\n \tif (o->call_depth)\n \t\tupdate_wd = 0;\n \n@@ -760,9 +762,11 @@ static int update_file_flags(struct merge_options *o,\n \n \t\tbuf = read_sha1_file(oid->hash, &type, &size);\n \t\tif (!buf)\n-\t\t\tdie(_(\"cannot read object %s '%s'\"), oid_to_hex(oid), path);\n-\t\tif (type != OBJ_BLOB)\n-\t\t\tdie(_(\"blob expected for %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\treturn error(_(\"cannot read object %s '%s'\"), oid_to_hex(oid), path);\n+\t\tif (type != OBJ_BLOB) {\n+\t\t\tret = error(_(\"blob expected for %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\tgoto free_buf;\n+\t\t}\n \t\tif (S_ISREG(mode)) {\n \t\t\tstruct strbuf strbuf = STRBUF_INIT;\n \t\t\tif (convert_to_working_tree(path, buf, size, &strbuf)) {\n@@ -783,8 +787,11 @@ static int update_file_flags(struct merge_options *o,\n \t\t\telse\n \t\t\t\tmode = 0666;\n \t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n-\t\t\tif (fd < 0)\n-\t\t\t\tdie_errno(_(\"failed to open '%s'\"), path);\n+\t\t\tif (fd < 0) {\n+\t\t\t\tret = error_errno(_(\"failed to open '%s'\"),\n+\t\t\t\t\t\t  path);\n+\t\t\t\tgoto free_buf;\n+\t\t\t}\n \t\t\twrite_in_full(fd, buf, size);\n \t\t\tclose(fd);\n \t\t} else if (S_ISLNK(mode)) {\n@@ -792,18 +799,18 @@ static int update_file_flags(struct merge_options *o,\n \t\t\tsafe_create_leading_directories_const(path);\n \t\t\tunlink(path);\n \t\t\tif (symlink(lnk, path))\n-\t\t\t\tdie_errno(_(\"failed to symlink '%s'\"), path);\n+\t\t\t\tret = error_errno(_(\"failed to symlink '%s'\"), path);\n \t\t\tfree(lnk);\n \t\t} else\n-\t\t\tdie(_(\"do not know what to do with %06o %s '%s'\"),\n-\t\t\t    mode, oid_to_hex(oid), path);\n+\t\t\tret = error(_(\"do not know what to do with %06o %s '%s'\"),\n+\t\t\t\t    mode, oid_to_hex(oid), path);\n  free_buf:\n \t\tfree(buf);\n \t}\n  update_index:\n-\tif (update_cache)\n+\tif (!ret && update_cache)\n \t\tadd_cacheinfo(mode, oid, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n-\treturn 0;\n+\treturn ret;\n }\n \n static int update_file(struct merge_options *o,\n@@ -929,20 +936,22 @@ static int merge_file_1(struct merge_options *o,\n \t\t\toidcpy(&result->oid, &a->oid);\n \t\telse if (S_ISREG(a->mode)) {\n \t\t\tmmbuffer_t result_buf;\n-\t\t\tint merge_status;\n+\t\t\tint ret = 0, merge_status;\n \n \t\t\tmerge_status = merge_3way(o, &result_buf, one, a, b,\n \t\t\t\t\t\t  branch1, branch2);\n \n \t\t\tif ((merge_status < 0) || !result_buf.ptr)\n-\t\t\t\tdie(_(\"Failed to execute internal merge\"));\n+\t\t\t\tret = error(_(\"Failed to execute internal merge\"));\n \n-\t\t\tif (write_sha1_file(result_buf.ptr, result_buf.size,\n-\t\t\t\t\t    blob_type, result->oid.hash))\n-\t\t\t\tdie(_(\"Unable to add %s to database\"),\n-\t\t\t\t    a->path);\n+\t\t\tif (!ret && write_sha1_file(result_buf.ptr, result_buf.size,\n+\t\t\t\t\t\t    blob_type, result->oid.hash))\n+\t\t\t\tret = error(_(\"Unable to add %s to database\"),\n+\t\t\t\t\t    a->path);\n \n \t\t\tfree(result_buf.ptr);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n \t\t\tresult->clean = (merge_status == 0);\n \t\t} else if (S_ISGITLINK(a->mode)) {\n \t\t\tresult->clean = merge_submodule(result->oid.hash,\n@@ -1876,11 +1885,10 @@ int merge_trees(struct merge_options *o,\n \n \tif (code != 0) {\n \t\tif (show(o, 4) || o->call_depth)\n-\t\t\tdie(_(\"merging of trees %s and %s failed\"),\n+\t\t\terror(_(\"merging of trees %s and %s failed\"),\n \t\t\t    oid_to_hex(&head->object.oid),\n \t\t\t    oid_to_hex(&merge->object.oid));\n-\t\telse\n-\t\t\texit(128);\n+\t\treturn -1;\n \t}\n \n \tif (unmerged_cache()) {\n@@ -2011,7 +2019,7 @@ int merge_recursive(struct merge_options *o,\n \t\to->call_depth--;\n \n \t\tif (!merged_common_ancestors)\n-\t\t\tdie(_(\"merge returned no commit\"));\n+\t\t\treturn error(_(\"merge returned no commit\"));\n \t}\n \n \tdiscard_cache();\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"291955","messageId":"667d2f991f1423b138a746f4c685b13c5b572a83.1469187653.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469187652.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 11/16] am -3: use merge_recursive() directly again","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-22T12:25:40Z","receivedAt":"2016-07-22T12:26:08Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Last October, we had to change this code to run `git merge-recursive`\nin a child process: git-am wants to print some helpful advice when the\nmerge failed, but the code in question was not prepared to return, it\ndie()d instead.\n\nWe are finally at a point when the code *is* prepared to return errors,\nand can avoid the child process again.\n\nThis reverts commit c63d4b2 (am -3: do not let failed merge from\ncompleting the error codepath, 2015-10-09), with the necessary changes\nto adjust for the fact that Git's source code changed in the meantime\n(such as: using OIDs instead of hashes in the recursive merge, and a\nremoved gender bias).\n\nNote: the code now calls merge_recursive_generic() again. Unlike\nmerge_trees() and merge_recursive(), this function returns 0 upon success,\nas most of Git's functions. Therefore, the error value -1 naturally is\nhandled correctly, and we do not have to take care of it specifically.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/am.c | 62 +++++++++++++++++++++---------------------------------------\n 1 file changed, 22 insertions(+), 40 deletions(-)\n\ndiff --git a/builtin/am.c b/builtin/am.c\nindex b77bf11..cfb79ea 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -1579,47 +1579,18 @@ static int build_fake_ancestor(const struct am_state *state, const char *index_f\n }\n \n /**\n- * Do the three-way merge using fake ancestor, their tree constructed\n- * from the fake ancestor and the postimage of the patch, and our\n- * state.\n- */\n-static int run_fallback_merge_recursive(const struct am_state *state,\n-\t\t\t\t\tunsigned char *orig_tree,\n-\t\t\t\t\tunsigned char *our_tree,\n-\t\t\t\t\tunsigned char *their_tree)\n-{\n-\tstruct child_process cp = CHILD_PROCESS_INIT;\n-\tint status;\n-\n-\tcp.git_cmd = 1;\n-\n-\targv_array_pushf(&cp.env_array, \"GITHEAD_%s=%.*s\",\n-\t\t\t sha1_to_hex(their_tree), linelen(state->msg), state->msg);\n-\tif (state->quiet)\n-\t\targv_array_push(&cp.env_array, \"GIT_MERGE_VERBOSITY=0\");\n-\n-\targv_array_push(&cp.args, \"merge-recursive\");\n-\targv_array_push(&cp.args, sha1_to_hex(orig_tree));\n-\targv_array_push(&cp.args, \"--\");\n-\targv_array_push(&cp.args, sha1_to_hex(our_tree));\n-\targv_array_push(&cp.args, sha1_to_hex(their_tree));\n-\n-\tstatus = run_command(&cp) ? (-1) : 0;\n-\tdiscard_cache();\n-\tread_cache();\n-\treturn status;\n-}\n-\n-/**\n  * Attempt a threeway merge, using index_path as the temporary index.\n  */\n static int fall_back_threeway(const struct am_state *state, const char *index_path)\n {\n-\tunsigned char orig_tree[GIT_SHA1_RAWSZ], their_tree[GIT_SHA1_RAWSZ],\n-\t\t      our_tree[GIT_SHA1_RAWSZ];\n+\tstruct object_id orig_tree, their_tree, our_tree;\n+\tconst struct object_id *bases[1] = { &orig_tree };\n+\tstruct merge_options o;\n+\tstruct commit *result;\n+\tchar *their_tree_name;\n \n-\tif (get_sha1(\"HEAD\", our_tree) < 0)\n-\t\thashcpy(our_tree, EMPTY_TREE_SHA1_BIN);\n+\tif (get_oid(\"HEAD\", &our_tree) < 0)\n+\t\thashcpy(our_tree.hash, EMPTY_TREE_SHA1_BIN);\n \n \tif (build_fake_ancestor(state, index_path))\n \t\treturn error(\"could not build fake ancestor\");\n@@ -1627,7 +1598,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \tdiscard_cache();\n \tread_cache_from(index_path);\n \n-\tif (write_index_as_tree(orig_tree, &the_index, index_path, 0, NULL))\n+\tif (write_index_as_tree(orig_tree.hash, &the_index, index_path, 0, NULL))\n \t\treturn error(_(\"Repository lacks necessary blobs to fall back on 3-way merge.\"));\n \n \tsay(state, stdout, _(\"Using index info to reconstruct a base tree...\"));\n@@ -1643,7 +1614,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \t\tinit_revisions(&rev_info, NULL);\n \t\trev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;\n \t\tdiff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix);\n-\t\tadd_pending_sha1(&rev_info, \"HEAD\", our_tree, 0);\n+\t\tadd_pending_sha1(&rev_info, \"HEAD\", our_tree.hash, 0);\n \t\tdiff_setup_done(&rev_info.diffopt);\n \t\trun_diff_index(&rev_info, 1);\n \t}\n@@ -1652,7 +1623,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \t\treturn error(_(\"Did you hand edit your patch?\\n\"\n \t\t\t\t\"It does not apply to blobs recorded in its index.\"));\n \n-\tif (write_index_as_tree(their_tree, &the_index, index_path, 0, NULL))\n+\tif (write_index_as_tree(their_tree.hash, &the_index, index_path, 0, NULL))\n \t\treturn error(\"could not write tree\");\n \n \tsay(state, stdout, _(\"Falling back to patching base and 3-way merge...\"));\n@@ -1668,11 +1639,22 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \t * changes.\n \t */\n \n-\tif (run_fallback_merge_recursive(state, orig_tree, our_tree, their_tree)) {\n+\tinit_merge_options(&o);\n+\n+\to.branch1 = \"HEAD\";\n+\ttheir_tree_name = xstrfmt(\"%.*s\", linelen(state->msg), state->msg);\n+\to.branch2 = their_tree_name;\n+\n+\tif (state->quiet)\n+\t\to.verbosity = 0;\n+\n+\tif (merge_recursive_generic(&o, &our_tree, &their_tree, 1, bases, &result)) {\n \t\trerere(state->allow_rerere_autoupdate);\n+\t\tfree(their_tree_name);\n \t\treturn error(_(\"Failed to merge in the changes.\"));\n \t}\n \n+\tfree(their_tree_name);\n \treturn 0;\n }\n \n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"291956","messageId":"3b7c0b465c682cf3f1aba92122fdcd461f0712f9.1469187653.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469187652.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 12/16] merge-recursive: flush output buffer before printing error messages","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-22T12:25:44Z","receivedAt":"2016-07-22T12:26:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The data structure passed to the recursive merge machinery has a feature\nwhere the caller can ask for the output to be buffered into a strbuf, by\nsetting the field 'buffer_output'.\n\nPreviously, we simply swallowed the buffered output when showing error\nmessages. With this patch, we show the output first, and only then print\nthe error message.\n\nCurrently, the only user of that buffering is merge_recursive() itself,\nto avoid the progress output to interfere.\n\nIn the next patches, we will introduce a new buffer_output mode that\nforces merge_recursive() to retain the output buffer for further\nprocessing by the caller. If the caller asked for that, we will then\nalso write the error messages into the output buffer. This is necessary\nto give the caller more control not only how to react in case of errors\nbut also control how/if to display the error messages.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 116 ++++++++++++++++++++++++++++++++----------------------\n 1 file changed, 68 insertions(+), 48 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 24b42d6..3ef1e2f 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -23,6 +23,28 @@\n #include \"dir.h\"\n #include \"submodule.h\"\n \n+static void flush_output(struct merge_options *o)\n+{\n+\tif (o->obuf.len) {\n+\t\tfputs(o->obuf.buf, stdout);\n+\t\tstrbuf_reset(&o->obuf);\n+\t}\n+}\n+\n+static int err(struct merge_options *o, const char *err, ...)\n+{\n+\tva_list params;\n+\n+\tva_start(params, err);\n+\tflush_output(o);\n+\tstrbuf_vaddf(&o->obuf, err, params);\n+\terror(\"%s\", o->obuf.buf);\n+\tstrbuf_reset(&o->obuf);\n+\tva_end(params);\n+\n+\treturn -1;\n+}\n+\n static struct tree *shift_tree_object(struct tree *one, struct tree *two,\n \t\t\t\t      const char *subtree_shift)\n {\n@@ -148,14 +170,6 @@ static int show(struct merge_options *o, int v)\n \treturn (!o->call_depth && o->verbosity >= v) || o->verbosity >= 5;\n }\n \n-static void flush_output(struct merge_options *o)\n-{\n-\tif (o->obuf.len) {\n-\t\tfputs(o->obuf.buf, stdout);\n-\t\tstrbuf_reset(&o->obuf);\n-\t}\n-}\n-\n __attribute__((format (printf, 3, 4)))\n static void output(struct merge_options *o, int v, const char *fmt, ...)\n {\n@@ -198,7 +212,8 @@ static void output_commit_title(struct merge_options *o, struct commit *commit)\n \t}\n }\n \n-static int add_cacheinfo(unsigned int mode, const struct object_id *oid,\n+static int add_cacheinfo(struct merge_options *o,\n+\t\tunsigned int mode, const struct object_id *oid,\n \t\tconst char *path, int stage, int refresh, int options)\n {\n \tstruct cache_entry *ce;\n@@ -206,7 +221,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid,\n \t\t\t      (refresh ? (CE_MATCH_REFRESH |\n \t\t\t\t\t  CE_MATCH_IGNORE_MISSING) : 0 ));\n \tif (!ce)\n-\t\treturn error(_(\"addinfo_cache failed for path '%s'\"), path);\n+\t\treturn err(o, _(\"addinfo_cache failed for path '%s'\"), path);\n \treturn add_cache_entry(ce, options);\n }\n \n@@ -267,7 +282,7 @@ struct tree *write_tree_from_memory(struct merge_options *o)\n \n \tif (!cache_tree_fully_valid(active_cache_tree) &&\n \t    cache_tree_update(&the_index, 0) < 0) {\n-\t\terror(_(\"error building trees\"));\n+\t\terr(o, _(\"error building trees\"));\n \t\treturn NULL;\n \t}\n \n@@ -535,7 +550,8 @@ static struct string_list *get_renames(struct merge_options *o,\n \treturn renames;\n }\n \n-static int update_stages(const char *path, const struct diff_filespec *o,\n+static int update_stages(struct merge_options *opt, const char *path,\n+\t\t\t const struct diff_filespec *o,\n \t\t\t const struct diff_filespec *a,\n \t\t\t const struct diff_filespec *b)\n {\n@@ -554,13 +570,13 @@ static int update_stages(const char *path, const struct diff_filespec *o,\n \t\tif (remove_file_from_cache(path))\n \t\t\treturn -1;\n \tif (o)\n-\t\tif (add_cacheinfo(o->mode, &o->oid, path, 1, 0, options))\n+\t\tif (add_cacheinfo(opt, o->mode, &o->oid, path, 1, 0, options))\n \t\t\treturn -1;\n \tif (a)\n-\t\tif (add_cacheinfo(a->mode, &a->oid, path, 2, 0, options))\n+\t\tif (add_cacheinfo(opt, a->mode, &a->oid, path, 2, 0, options))\n \t\t\treturn -1;\n \tif (b)\n-\t\tif (add_cacheinfo(b->mode, &b->oid, path, 3, 0, options))\n+\t\tif (add_cacheinfo(opt, b->mode, &b->oid, path, 3, 0, options))\n \t\t\treturn -1;\n \treturn 0;\n }\n@@ -711,8 +727,8 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \tif (status) {\n \t\tif (status == SCLD_EXISTS)\n \t\t\t/* something else exists */\n-\t\t\treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n-\t\treturn error(msg, path, \"\");\n+\t\t\treturn err(o, msg, path, _(\": perhaps a D/F conflict?\"));\n+\t\treturn err(o, msg, path, \"\");\n \t}\n \n \t/*\n@@ -720,7 +736,7 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \t * tracking it.\n \t */\n \tif (would_lose_untracked(path))\n-\t\treturn error(_(\"refusing to lose untracked file at '%s'\"),\n+\t\treturn err(o, _(\"refusing to lose untracked file at '%s'\"),\n \t\t\t     path);\n \n \t/* Successful unlink is good.. */\n@@ -730,7 +746,7 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \tif (errno == ENOENT)\n \t\treturn 0;\n \t/* .. but not some other error (who really cares what?) */\n-\treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n+\treturn err(o, msg, path, _(\": perhaps a D/F conflict?\"));\n }\n \n static int update_file_flags(struct merge_options *o,\n@@ -762,9 +778,9 @@ static int update_file_flags(struct merge_options *o,\n \n \t\tbuf = read_sha1_file(oid->hash, &type, &size);\n \t\tif (!buf)\n-\t\t\treturn error(_(\"cannot read object %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\treturn err(o, _(\"cannot read object %s '%s'\"), oid_to_hex(oid), path);\n \t\tif (type != OBJ_BLOB) {\n-\t\t\tret = error(_(\"blob expected for %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\tret = err(o, _(\"blob expected for %s '%s'\"), oid_to_hex(oid), path);\n \t\t\tgoto free_buf;\n \t\t}\n \t\tif (S_ISREG(mode)) {\n@@ -788,8 +804,8 @@ static int update_file_flags(struct merge_options *o,\n \t\t\t\tmode = 0666;\n \t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n \t\t\tif (fd < 0) {\n-\t\t\t\tret = error_errno(_(\"failed to open '%s'\"),\n-\t\t\t\t\t\t  path);\n+\t\t\t\tret = err(o, _(\"failed to open '%s': %s\"),\n+\t\t\t\t\t  path, strerror(errno));\n \t\t\t\tgoto free_buf;\n \t\t\t}\n \t\t\twrite_in_full(fd, buf, size);\n@@ -799,17 +815,19 @@ static int update_file_flags(struct merge_options *o,\n \t\t\tsafe_create_leading_directories_const(path);\n \t\t\tunlink(path);\n \t\t\tif (symlink(lnk, path))\n-\t\t\t\tret = error_errno(_(\"failed to symlink '%s'\"), path);\n+\t\t\t\tret = err(o, _(\"failed to symlink '%s': %s\"),\n+\t\t\t\t\tpath, strerror(errno));\n \t\t\tfree(lnk);\n \t\t} else\n-\t\t\tret = error(_(\"do not know what to do with %06o %s '%s'\"),\n-\t\t\t\t    mode, oid_to_hex(oid), path);\n+\t\t\tret = err(o,\n+\t\t\t\t  _(\"do not know what to do with %06o %s '%s'\"),\n+\t\t\t\t  mode, oid_to_hex(oid), path);\n  free_buf:\n \t\tfree(buf);\n \t}\n  update_index:\n \tif (!ret && update_cache)\n-\t\tadd_cacheinfo(mode, oid, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n+\t\tadd_cacheinfo(o, mode, oid, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n \treturn ret;\n }\n \n@@ -942,12 +960,12 @@ static int merge_file_1(struct merge_options *o,\n \t\t\t\t\t\t  branch1, branch2);\n \n \t\t\tif ((merge_status < 0) || !result_buf.ptr)\n-\t\t\t\tret = error(_(\"Failed to execute internal merge\"));\n+\t\t\t\tret = err(o, _(\"Failed to execute internal merge\"));\n \n \t\t\tif (!ret && write_sha1_file(result_buf.ptr, result_buf.size,\n \t\t\t\t\t\t    blob_type, result->oid.hash))\n-\t\t\t\tret = error(_(\"Unable to add %s to database\"),\n-\t\t\t\t\t    a->path);\n+\t\t\t\tret = err(o, _(\"Unable to add %s to database\"),\n+\t\t\t\t\t  a->path);\n \n \t\t\tfree(result_buf.ptr);\n \t\t\tif (ret)\n@@ -1113,7 +1131,7 @@ static int conflict_rename_delete(struct merge_options *o,\n \tif (o->call_depth)\n \t\treturn remove_file_from_cache(dest->path);\n \telse\n-\t\treturn update_stages(dest->path, NULL,\n+\t\treturn update_stages(o, dest->path, NULL,\n \t\t\t\t     rename_branch == o->branch1 ? dest : NULL,\n \t\t\t\t     rename_branch == o->branch1 ? NULL : dest);\n }\n@@ -1171,9 +1189,9 @@ static int handle_file(struct merge_options *o,\n \tif ((ret = update_file(o, 0, &rename->oid, rename->mode, dst_name)))\n \t\t; /* fall through, do allow dst_name to be released */\n \telse if (stage == 2)\n-\t\tret = update_stages(rename->path, NULL, rename, add);\n+\t\tret = update_stages(o, rename->path, NULL, rename, add);\n \telse\n-\t\tret = update_stages(rename->path, NULL, add, rename);\n+\t\tret = update_stages(o, rename->path, NULL, add, rename);\n \n \tif (dst_name != rename->path)\n \t\tfree(dst_name);\n@@ -1566,23 +1584,25 @@ static struct object_id *stage_oid(const struct object_id *oid, unsigned mode)\n \treturn (is_null_oid(oid) || mode == 0) ? NULL: (struct object_id *)oid;\n }\n \n-static int read_oid_strbuf(const struct object_id *oid, struct strbuf *dst)\n+static int read_oid_strbuf(struct merge_options *o,\n+\tconst struct object_id *oid, struct strbuf *dst)\n {\n \tvoid *buf;\n \tenum object_type type;\n \tunsigned long size;\n \tbuf = read_sha1_file(oid->hash, &type, &size);\n \tif (!buf)\n-\t\treturn error(_(\"cannot read object %s\"), oid_to_hex(oid));\n+\t\treturn err(o, _(\"cannot read object %s\"), oid_to_hex(oid));\n \tif (type != OBJ_BLOB) {\n \t\tfree(buf);\n-\t\treturn error(_(\"object %s is not a blob\"), oid_to_hex(oid));\n+\t\treturn err(o, _(\"object %s is not a blob\"), oid_to_hex(oid));\n \t}\n \tstrbuf_attach(dst, buf, size, size + 1);\n \treturn 0;\n }\n \n-static int blob_unchanged(const struct object_id *o_oid,\n+static int blob_unchanged(struct merge_options *opt,\n+\t\t\t  const struct object_id *o_oid,\n \t\t\t  unsigned o_mode,\n \t\t\t  const struct object_id *a_oid,\n \t\t\t  unsigned a_mode,\n@@ -1600,7 +1620,7 @@ static int blob_unchanged(const struct object_id *o_oid,\n \t\treturn 0;\n \n \tassert(o_oid && a_oid);\n-\tif (read_oid_strbuf(o_oid, &o) || read_oid_strbuf(a_oid, &a))\n+\tif (read_oid_strbuf(opt, o_oid, &o) || read_oid_strbuf(opt, a_oid, &a))\n \t\tgoto error_return;\n \t/*\n \t * Note: binary | is used so that both renormalizations are\n@@ -1689,7 +1709,7 @@ static int merge_content(struct merge_options *o,\n \t\t */\n \t\tpath_renamed_outside_HEAD = !path2 || !strcmp(path, path2);\n \t\tif (!path_renamed_outside_HEAD) {\n-\t\t\tadd_cacheinfo(mfi.mode, &mfi.oid, path,\n+\t\t\tadd_cacheinfo(o, mfi.mode, &mfi.oid, path,\n \t\t\t\t      0, (!o->call_depth), 0);\n \t\t\treturn mfi.clean;\n \t\t}\n@@ -1702,7 +1722,7 @@ static int merge_content(struct merge_options *o,\n \t\toutput(o, 1, _(\"CONFLICT (%s): Merge conflict in %s\"),\n \t\t\t\treason, path);\n \t\tif (rename_conflict_info && !df_conflict_remains)\n-\t\t\tif (update_stages(path, &one, &a, &b))\n+\t\t\tif (update_stages(o, path, &one, &a, &b))\n \t\t\t\treturn -1;\n \t}\n \n@@ -1712,7 +1732,7 @@ static int merge_content(struct merge_options *o,\n \t\t\tremove_file_from_cache(path);\n \t\t} else {\n \t\t\tif (!mfi.clean) {\n-\t\t\t\tif (update_stages(path, &one, &a, &b))\n+\t\t\t\tif (update_stages(o, path, &one, &a, &b))\n \t\t\t\t\treturn -1;\n \t\t\t} else {\n \t\t\t\tint file_from_stage2 = was_tracked(path);\n@@ -1720,7 +1740,7 @@ static int merge_content(struct merge_options *o,\n \t\t\t\toidcpy(&merged.oid, &mfi.oid);\n \t\t\t\tmerged.mode = mfi.mode;\n \n-\t\t\t\tif (update_stages(path, NULL,\n+\t\t\t\tif (update_stages(o, path, NULL,\n \t\t\t\t\t\t  file_from_stage2 ? &merged : NULL,\n \t\t\t\t\t\t  file_from_stage2 ? NULL : &merged))\n \t\t\t\t\treturn -1;\n@@ -1788,8 +1808,8 @@ static int process_entry(struct merge_options *o,\n \t} else if (o_oid && (!a_oid || !b_oid)) {\n \t\t/* Case A: Deleted in one */\n \t\tif ((!a_oid && !b_oid) ||\n-\t\t    (!b_oid && blob_unchanged(o_oid, o_mode, a_oid, a_mode, normalize, path)) ||\n-\t\t    (!a_oid && blob_unchanged(o_oid, o_mode, b_oid, b_mode, normalize, path))) {\n+\t\t    (!b_oid && blob_unchanged(o, o_oid, o_mode, a_oid, a_mode, normalize, path)) ||\n+\t\t    (!a_oid && blob_unchanged(o, o_oid, o_mode, b_oid, b_mode, normalize, path))) {\n \t\t\t/* Deleted in both or deleted in one and\n \t\t\t * unchanged in the other */\n \t\t\tif (a_oid)\n@@ -1885,7 +1905,7 @@ int merge_trees(struct merge_options *o,\n \n \tif (code != 0) {\n \t\tif (show(o, 4) || o->call_depth)\n-\t\t\terror(_(\"merging of trees %s and %s failed\"),\n+\t\t\terr(o, _(\"merging of trees %s and %s failed\"),\n \t\t\t    oid_to_hex(&head->object.oid),\n \t\t\t    oid_to_hex(&merge->object.oid));\n \t\treturn -1;\n@@ -2019,7 +2039,7 @@ int merge_recursive(struct merge_options *o,\n \t\to->call_depth--;\n \n \t\tif (!merged_common_ancestors)\n-\t\t\treturn error(_(\"merge returned no commit\"));\n+\t\t\treturn err(o, _(\"merge returned no commit\"));\n \t}\n \n \tdiscard_cache();\n@@ -2078,7 +2098,7 @@ int merge_recursive_generic(struct merge_options *o,\n \t\tfor (i = 0; i < num_base_list; ++i) {\n \t\t\tstruct commit *base;\n \t\t\tif (!(base = get_ref(base_list[i], oid_to_hex(base_list[i]))))\n-\t\t\t\treturn error(_(\"Could not parse object '%s'\"),\n+\t\t\t\treturn err(o, _(\"Could not parse object '%s'\"),\n \t\t\t\t\toid_to_hex(base_list[i]));\n \t\t\tcommit_list_insert(base, &ca);\n \t\t}\n@@ -2092,7 +2112,7 @@ int merge_recursive_generic(struct merge_options *o,\n \n \tif (active_cache_changed &&\n \t    write_locked_index(&the_index, lock, COMMIT_LOCK))\n-\t\treturn error(_(\"Unable to write index.\"));\n+\t\treturn err(o, _(\"Unable to write index.\"));\n \n \treturn clean ? 0 : 1;\n }\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"291957","messageId":"57d6b5ec8dbf6d43e92eae562d3ba48d481aff67.1469187653.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469187652.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 14/16] merge-recursive: offer an option to retain the output in 'obuf'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-22T12:25:53Z","receivedAt":"2016-07-22T12:26:14Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Since 66a155b (Enable output buffering in merge-recursive., 2007-01-14),\nwe already accumulate the output in a buffer. The idea was to avoid\ninterfering with the progress output that goes to stderr, which is\nunbuffered, when we write to stdout, which is buffered.\n\nWe extend that buffering to allow the caller to handle the output\n(possibly suppressing it). This will help us when extending the\nsequencer to do rebase -i's brunt work: it does not want the picks to\nprint anything by default but instead determine itself whether to print\nthe output or not.\n\nNote that we also redirect the error messages into the output buffer\nwhen the caller asked not to flush the output buffer, for two reasons:\n1) to retain the correct output order, and 2) to allow the caller to\nsuppress *all* output.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 17 +++++++++++++----\n merge-recursive.h |  2 +-\n 2 files changed, 14 insertions(+), 5 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 2bc41fe..1746c38 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -25,7 +25,7 @@\n \n static void flush_output(struct merge_options *o)\n {\n-\tif (o->obuf.len) {\n+\tif (o->buffer_output < 2 && o->obuf.len) {\n \t\tfputs(o->obuf.buf, stdout);\n \t\tstrbuf_reset(&o->obuf);\n \t}\n@@ -36,10 +36,19 @@ static int err(struct merge_options *o, const char *err, ...)\n \tva_list params;\n \n \tva_start(params, err);\n-\tflush_output(o);\n+\tif (o->buffer_output < 2)\n+\t\tflush_output(o);\n+\telse {\n+\t\tstrbuf_complete(&o->obuf, '\\n');\n+\t\tstrbuf_addstr(&o->obuf, \"error: \");\n+\t}\n \tstrbuf_vaddf(&o->obuf, err, params);\n-\terror(\"%s\", o->obuf.buf);\n-\tstrbuf_reset(&o->obuf);\n+\tif (o->buffer_output > 1)\n+\t\tstrbuf_addch(&o->obuf, '\\n');\n+\telse {\n+\t\terror(\"%s\", o->obuf.buf);\n+\t\tstrbuf_reset(&o->obuf);\n+\t}\n \tva_end(params);\n \n \treturn -1;\ndiff --git a/merge-recursive.h b/merge-recursive.h\nindex d415724..340704c 100644\n--- a/merge-recursive.h\n+++ b/merge-recursive.h\n@@ -13,7 +13,7 @@ struct merge_options {\n \t\tMERGE_RECURSIVE_THEIRS\n \t} recursive_variant;\n \tconst char *subtree_shift;\n-\tunsigned buffer_output : 1;\n+\tunsigned buffer_output : 2; /* 1: output at end, 2: keep buffered */\n \tunsigned renormalize : 1;\n \tlong xdl_opts;\n \tint verbosity;\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"291958","messageId":"cd008d60b4b277c42960148d359c2d1465b32734.1469187653.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469187652.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 15/16] Ensure that the output buffer is released after calling merge_trees()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-22T12:25:57Z","receivedAt":"2016-07-22T12:26:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The recursive merge machinery accumulates its output in an output\nbuffer, to be flushed at the end of merge_recursive(). At this point,\nwe forgot to release the output buffer.\n\nWhen calling merge_trees() (i.e. the non-recursive part of the recursive\nmerge) directly, the output buffer is never flushed because the caller\nmay be merge_recursive() which wants to flush the output itself.\n\nFor the same reason, merge_trees() cannot release the output buffer: it\nmay still be needed.\n\nForgetting to release the output buffer did not matter much when running\ngit-checkout, or git-merge-recursive, because we exited after the\noperation anyway. Ever since cherry-pick learned to pick a commit range,\nhowever, this memory leak had the potential of becoming a problem.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/checkout.c | 1 +\n merge-recursive.c  | 2 ++\n sequencer.c        | 1 +\n 3 files changed, 4 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 07dea3b..8d852d4 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -573,6 +573,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\t\texit(128);\n \t\t\tret = reset_tree(new->commit->tree, opts, 0,\n \t\t\t\t\t writeout_error);\n+\t\t\tstrbuf_release(&o.obuf);\n \t\t\tif (ret)\n \t\t\t\treturn ret;\n \t\t}\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 1746c38..dcf1535 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -2068,6 +2068,8 @@ int merge_recursive(struct merge_options *o,\n \t\tcommit_list_insert(h2, &(*result)->parents->next);\n \t}\n \tflush_output(o);\n+\tif (o->buffer_output < 2)\n+\t\tstrbuf_release(&o->obuf);\n \tif (show(o, 2))\n \t\tdiff_warn_rename_limit(\"merge.renamelimit\",\n \t\t\t\t       o->needed_rename_limit, 0);\ndiff --git a/sequencer.c b/sequencer.c\nindex 286a435..ec50519 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -293,6 +293,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \tclean = merge_trees(&o,\n \t\t\t    head_tree,\n \t\t\t    next_tree, base_tree, &result);\n+\tstrbuf_release(&o.obuf);\n \tif (clean < 0)\n \t\treturn clean;\n \n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"291959","messageId":"c0fe32e181c0467ac20fe5a80d78781ccad83591.1469187653.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469187652.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 13/16] merge-recursive: write the commit title in one go","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-22T12:25:49Z","receivedAt":"2016-07-22T12:26:21Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"In 66a155b (Enable output buffering in merge-recursive., 2007-01-14), we\nchanged the code such that it prints the output in one go, to avoid\ninterfering with the progress output.\n\nLet's make sure that the same holds true when outputting the commit\ntitle: previously, we used several printf() statements to stdout and\nspeculated that stdout's buffer is large enough to hold the entire\ncommit title.\n\nApart from making that speculation unnecessary, we change the code to\nadd the message to the output buffer before flushing for another reason:\nthe next commit will introduce a new level of output buffering, where\nthe caller can request the output not to be flushed, but to be retained\nfor further processing.\n\nThis latter feature will be needed when teaching the sequencer to do\nrebase -i's brunt work: it wants to control the output of the\ncherry-picks (i.e. recursive merges).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 17 +++++++++--------\n 1 file changed, 9 insertions(+), 8 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 3ef1e2f..2bc41fe 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -191,25 +191,26 @@ static void output(struct merge_options *o, int v, const char *fmt, ...)\n \n static void output_commit_title(struct merge_options *o, struct commit *commit)\n {\n-\tint i;\n-\tflush_output(o);\n-\tfor (i = o->call_depth; i--;)\n-\t\tfputs(\"  \", stdout);\n+\tstrbuf_addchars(&o->obuf, ' ', o->call_depth * 2);\n \tif (commit->util)\n-\t\tprintf(\"virtual %s\\n\", merge_remote_util(commit)->name);\n+\t\tstrbuf_addf(&o->obuf, \"virtual %s\\n\",\n+\t\t\tmerge_remote_util(commit)->name);\n \telse {\n-\t\tprintf(\"%s \", find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV));\n+\t\tstrbuf_addf(&o->obuf, \"%s \",\n+\t\t\tfind_unique_abbrev(commit->object.oid.hash,\n+\t\t\t\tDEFAULT_ABBREV));\n \t\tif (parse_commit(commit) != 0)\n-\t\t\tprintf(_(\"(bad commit)\\n\"));\n+\t\t\tstrbuf_addf(&o->obuf, _(\"(bad commit)\\n\"));\n \t\telse {\n \t\t\tconst char *title;\n \t\t\tconst char *msg = get_commit_buffer(commit, NULL);\n \t\t\tint len = find_commit_subject(msg, &title);\n \t\t\tif (len)\n-\t\t\t\tprintf(\"%.*s\\n\", len, title);\n+\t\t\t\tstrbuf_addf(&o->obuf, \"%.*s\\n\", len, title);\n \t\t\tunuse_commit_buffer(commit, msg);\n \t\t}\n \t}\n+\tflush_output(o);\n }\n \n static int add_cacheinfo(struct merge_options *o,\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"291960","messageId":"3559f07feb75bcfbdab50c756207eaa44df5f4ad.1469187653.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469187652.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 16/16] merge-recursive: flush output buffer even when erroring out","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-22T12:26:00Z","receivedAt":"2016-07-22T12:26:42Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Ever since 66a155b (Enable output buffering in merge-recursive.,\n2007-01-14), we had a problem: When the merge failed in a fatal way, all\nregular output was swallowed because we called die() and did not get a\nchance to drain the output buffers.\n\nTo fix this, several modifications were necessary:\n\n- we needed to stop die()ing, to give callers a chance to do something\n  when an error occurred (in this case, flush the output buffers),\n\n- we needed to delay printing the error message so that the caller can\n  print the buffered output before that, and\n\n- we needed to make sure that the output buffers are flushed even when\n  the return value indicates an error.\n\nThe first two changes were introduced through earlier commits in this\npatch series, and this commit addresses the third one.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex dcf1535..501cfb5 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -2059,6 +2059,7 @@ int merge_recursive(struct merge_options *o,\n \to->ancestor = \"merged common ancestors\";\n \tclean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree,\n \t\t\t    &mrtree);\n+\tflush_output(o);\n \tif (clean < 0)\n \t\treturn clean;\n \n@@ -2067,7 +2068,6 @@ int merge_recursive(struct merge_options *o,\n \t\tcommit_list_insert(h1, &(*result)->parents);\n \t\tcommit_list_insert(h2, &(*result)->parents->next);\n \t}\n-\tflush_output(o);\n \tif (o->buffer_output < 2)\n \t\tstrbuf_release(&o->obuf);\n \tif (show(o, 2))\n-- \n2.9.0.281.g286a8d9\n"},{"id":"292145","messageId":"xmqqk2g94cti.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"37e2f36e4f982261a741e327f1b534cb67b65149.1469187652.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v4 01/16] Verify that `git pull --rebase` shows the helpful advice when failing","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-25T21:39:37Z","receivedAt":"2016-07-25T21:39:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> +test_expect_success '--rebase with conflicts shows advice' '\n> +\ttest_when_finished \"git rebase --abort; git checkout -f to-rebase\" &&\n> +\tgit checkout -b seq &&\n> +\tprintf \"1\\\\n2\\\\n3\\\\n4\\\\n5\\\\n\" >seq.txt &&\n\nMake this more readble by using test-write-lines, perhaps?\n\n> +\tgit add seq.txt &&\n> +\ttest_tick &&\n> +\tgit commit -m \"Add seq.txt\" &&\n> +\tprintf \"6\\\\n\" >>seq.txt &&\n> +\ttest_tick &&\n> +\tgit commit -m \"Append to seq.txt\" seq.txt &&\n> +\tgit checkout -b with-conflicts HEAD^ &&\n> +\tprintf \"conflicting\\\\n\" >>seq.txt &&\n> +\ttest_tick &&\n> +\tgit commit -m \"Create conflict\" seq.txt &&\n> +\ttest_must_fail git pull --rebase . seq 2>err >out &&\n> +\tgrep \"When you have resolved this problem\" out\n> +'\n> +test_expect_success 'failed --rebase shows advice' '\n\nNeed a blank line before this one.\n\n> +\ttest_when_finished \"git rebase --abort; git checkout -f to-rebase\" &&\n> +\tgit checkout -b diverging &&\n> +\ttest_commit attributes .gitattributes \"* text=auto\" attrs &&\n> +\tsha1=\"$(printf \"1\\\\r\\\\n\" | git hash-object -w --stdin)\" &&\n> +\tgit update-index --cacheinfo 0644 $sha1 file &&\n> +\tgit commit -m v1-with-cr &&\n> +\tgit checkout -f -b fails-to-rebase HEAD^ &&\n\nIt is unclear what the \"-f\" is for; is it attempting to clean up a\npotential mess previous steps might have left?  We didn't have it in\nthe previous test above.\n\n> +\ttest_commit v2-without-cr file \"2\" file2-lf &&\n> +\ttest_must_fail git pull --rebase . diverging 2>err >out &&\n> +\tgrep \"When you have resolved this problem\" out\n> +'\n> +\n>  test_expect_success '--rebase fails with multiple branches' '\n>  \tgit reset --hard before-rebase &&\n>  \ttest_must_fail git pull --rebase . copy master 2>err &&\n\nNot worth a reroll but after this series settles we would probably\nwant to address some of the above up with a follow-up clean-up patch.\n"},{"id":"292146","messageId":"xmqqfuqx4cli.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"72d1d530bb0e3c96d3affd6679cb7c26026d8321.1469187652.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v4 02/16] Report bugs consistently","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-25T21:44:25Z","receivedAt":"2016-07-25T21:44:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> diff --git a/imap-send.c b/imap-send.c\n> index db0fafe..67d67f8 100644\n> --- a/imap-send.c\n> +++ b/imap-send.c\n> @@ -506,12 +506,12 @@ static char *next_arg(char **s)\n>  \n>  static int nfsnprintf(char *buf, int blen, const char *fmt, ...)\n>  {\n> -\tint ret;\n> +\tint ret = -1;\n>  \tva_list va;\n>  \n>  \tva_start(va, fmt);\n>  \tif (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)\n> -\t\tdie(\"Fatal: buffer too small. Please report a bug.\");\n> +\t\tdie(\"BUG: buffer too small (%d < %d)\", ret, blen);\n>  \tva_end(va);\n>  \treturn ret;\n>  }\n\nIf \"you gave me this size but you need at least this much\" is truly\nworth reporting, then this is misleading (ret is shown as -1 but you\ndo not even know how much is necessary).  In any case, this should\nbe done as a separate step anyway.\n\nAll the other hunks looked sensible.\n\nThanks.\n"},{"id":"292152","messageId":"xmqq7fc94bf4.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"26f12ac5a5b8e722d81c782b32585531521c98d4.1469187653.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v4 06/16] merge_recursive: abort properly upon errors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-25T22:09:51Z","receivedAt":"2016-07-25T22:10:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> There are a couple of places where return values indicating errors\n> are ignored. Let's teach them manners.\n\nThat is because the return value never indicated errors before this\nseries, isn't it?  A true error used to be expressed by dying, and\nthe return value indicating \"cleanliness\" of the merge were\ndeliberately ignored.\n\nThe world order changed by previous patches in this series and the\ncallers need to be updated to take the new kind of return values\ninto account.  That is not teaching them manners ;-)\n\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  merge-recursive.c | 10 ++++++++--\n>  1 file changed, 8 insertions(+), 2 deletions(-)\n>\n> diff --git a/merge-recursive.c b/merge-recursive.c\n> index dc3182b..2d4cb80 100644\n> --- a/merge-recursive.c\n> +++ b/merge-recursive.c\n> @@ -1949,8 +1949,9 @@ int merge_recursive(struct merge_options *o,\n>  \t\tsaved_b2 = o->branch2;\n>  \t\to->branch1 = \"Temporary merge branch 1\";\n>  \t\to->branch2 = \"Temporary merge branch 2\";\n> -\t\tmerge_recursive(o, merged_common_ancestors, iter->item,\n> -\t\t\t\tNULL, &merged_common_ancestors);\n> +\t\tif (merge_recursive(o, merged_common_ancestors, iter->item,\n> +\t\t\t\t    NULL, &merged_common_ancestors) < 0)\n> +\t\t\treturn -1;\n>  \t\to->branch1 = saved_b1;\n>  \t\to->branch2 = saved_b2;\n>  \t\to->call_depth--;\n\nThis hunk feels somewhat wrong as-is.\n\nThere is a comment before the pre-context explaining why cleanness\nflag is ignored.  It needs to be updated.  We still do not care\nabout cleanliness, i.e. 0=clean, 1=merged with conflict, but we now\ncan get negative values so we need to reject and return early if\nthis call indicates an error.\n\nThee other two hunks make sense.\n\nThanks.\n\n> @@ -1966,6 +1967,8 @@ int merge_recursive(struct merge_options *o,\n>  \to->ancestor = \"merged common ancestors\";\n>  \tclean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree,\n>  \t\t\t    &mrtree);\n> +\tif (clean < 0)\n> +\t\treturn clean;\n>  \n>  \tif (o->call_depth) {\n>  \t\t*result = make_virtual_commit(mrtree, \"merged tree\");\n> @@ -2022,6 +2025,9 @@ int merge_recursive_generic(struct merge_options *o,\n>  \thold_locked_index(lock, 1);\n>  \tclean = merge_recursive(o, head_commit, next_commit, ca,\n>  \t\t\tresult);\n> +\tif (clean < 0)\n> +\t\treturn clean;\n> +\n>  \tif (active_cache_changed &&\n>  \t    write_locked_index(&the_index, lock, COMMIT_LOCK))\n>  \t\treturn error(_(\"Unable to write index.\"));\n"},{"id":"292156","messageId":"20160725221700.GB14131@sigill.intra.peff.net","threadId":"42743","inReplyTo":"xmqqfuqx4cli.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v4 02/16] Report bugs consistently","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-07-25T22:17:01Z","receivedAt":"2016-07-25T22:17:11Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Jul 25, 2016 at 02:44:25PM -0700, Junio C Hamano wrote:\n\n> > diff --git a/imap-send.c b/imap-send.c\n> > index db0fafe..67d67f8 100644\n> > --- a/imap-send.c\n> > +++ b/imap-send.c\n> > @@ -506,12 +506,12 @@ static char *next_arg(char **s)\n> >  \n> >  static int nfsnprintf(char *buf, int blen, const char *fmt, ...)\n> >  {\n> > -\tint ret;\n> > +\tint ret = -1;\n> >  \tva_list va;\n> >  \n> >  \tva_start(va, fmt);\n> >  \tif (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)\n> > -\t\tdie(\"Fatal: buffer too small. Please report a bug.\");\n> > +\t\tdie(\"BUG: buffer too small (%d < %d)\", ret, blen);\n> >  \tva_end(va);\n> >  \treturn ret;\n> >  }\n> \n> If \"you gave me this size but you need at least this much\" is truly\n> worth reporting, then this is misleading (ret is shown as -1 but you\n> do not even know how much is necessary).  In any case, this should\n> be done as a separate step anyway.\n\nHrm, isn't \"ret\" going to be the necessary size? According to the\nstandard, it should tell us how many bytes were needed, not \"-1\" (this\nis the \"your vsnprintf is broken\" case handled by the strbuf code).\n\nI do think the numbers are reversed, though. It should be \"blen < ret\".\n\n-Peff\n"},{"id":"292157","messageId":"xmqqy44p2wds.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"667d2f991f1423b138a746f4c685b13c5b572a83.1469187653.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v4 11/16] am -3: use merge_recursive() directly again","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-25T22:19:59Z","receivedAt":"2016-07-25T22:20:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> Note: the code now calls merge_recursive_generic() again. Unlike\n> merge_trees() and merge_recursive(), this function returns 0 upon success,\n> as most of Git's functions. Therefore, the error value -1 naturally is\n> handled correctly, and we do not have to take care of it specifically.\n\nI've finished reading through up to this point and I'd stop for\nnow.\n\nSome of the patches I didn't look beyond the context presented in\nthe patches, so it is very possible that I missed leaks caused by\nearly returns and things like that, but I didn't see anything\nglaringly wrong.  Looks very promising.\n\nThanks.\n"},{"id":"292161","messageId":"xmqqlh0p2vmn.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"20160725221700.GB14131@sigill.intra.peff.net","subject":"Re: [PATCH v4 02/16] Report bugs consistently","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-25T22:36:16Z","receivedAt":"2016-07-25T22:36:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Mon, Jul 25, 2016 at 02:44:25PM -0700, Junio C Hamano wrote:\n>\n>> > diff --git a/imap-send.c b/imap-send.c\n>> > index db0fafe..67d67f8 100644\n>> > --- a/imap-send.c\n>> > +++ b/imap-send.c\n>> > @@ -506,12 +506,12 @@ static char *next_arg(char **s)\n>> >  \n>> >  static int nfsnprintf(char *buf, int blen, const char *fmt, ...)\n>> >  {\n>> > -\tint ret;\n>> > +\tint ret = -1;\n>> >  \tva_list va;\n>> >  \n>> >  \tva_start(va, fmt);\n>> >  \tif (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)\n>> > -\t\tdie(\"Fatal: buffer too small. Please report a bug.\");\n>> > +\t\tdie(\"BUG: buffer too small (%d < %d)\", ret, blen);\n>> >  \tva_end(va);\n>> >  \treturn ret;\n>> >  }\n>> \n>> If \"you gave me this size but you need at least this much\" is truly\n>> worth reporting, then this is misleading (ret is shown as -1 but you\n>> do not even know how much is necessary).  In any case, this should\n>> be done as a separate step anyway.\n>\n> Hrm, isn't \"ret\" going to be the necessary size? According to the\n> standard, it should tell us how many bytes were needed, not \"-1\" (this\n> is the \"your vsnprintf is broken\" case handled by the strbuf code).\n\nYes.  If blen <= 0, we do not even do vsnprintf() and that is why\nDscho added \"int ret = -1\" initialization; otherwise his new die()\nwould end up referencing uninitialized ret.\n\n> I do think the numbers are reversed, though. It should be \"blen < ret\".\n\nThat, too ;-)\n"},{"id":"292194","messageId":"alpine.DEB.2.20.1607261421180.14111@virtualbox","threadId":"42743","inReplyTo":"xmqqk2g94cti.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v4 01/16] Verify that `git pull --rebase` shows the helpful advice when failing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-26T12:21:22Z","receivedAt":"2016-07-26T12:22:27Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Mon, 25 Jul 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > +test_expect_success '--rebase with conflicts shows advice' '\n> > +\ttest_when_finished \"git rebase --abort; git checkout -f to-rebase\" &&\n> > +\tgit checkout -b seq &&\n> > +\tprintf \"1\\\\n2\\\\n3\\\\n4\\\\n5\\\\n\" >seq.txt &&\n> \n> Make this more readble by using test-write-lines, perhaps?\n\nOr even test_seq. Thanks for pointing my nose to this.\n\n> > +\tgit add seq.txt &&\n> > +\ttest_tick &&\n> > +\tgit commit -m \"Add seq.txt\" &&\n> > +\tprintf \"6\\\\n\" >>seq.txt &&\n> > +\ttest_tick &&\n> > +\tgit commit -m \"Append to seq.txt\" seq.txt &&\n> > +\tgit checkout -b with-conflicts HEAD^ &&\n> > +\tprintf \"conflicting\\\\n\" >>seq.txt &&\n> > +\ttest_tick &&\n> > +\tgit commit -m \"Create conflict\" seq.txt &&\n> > +\ttest_must_fail git pull --rebase . seq 2>err >out &&\n> > +\tgrep \"When you have resolved this problem\" out\n> > +'\n> > +test_expect_success 'failed --rebase shows advice' '\n> \n> Need a blank line before this one.\n\nYep, sorry.\n\n> > +\ttest_when_finished \"git rebase --abort; git checkout -f to-rebase\" &&\n> > +\tgit checkout -b diverging &&\n> > +\ttest_commit attributes .gitattributes \"* text=auto\" attrs &&\n> > +\tsha1=\"$(printf \"1\\\\r\\\\n\" | git hash-object -w --stdin)\" &&\n> > +\tgit update-index --cacheinfo 0644 $sha1 file &&\n> > +\tgit commit -m v1-with-cr &&\n> > +\tgit checkout -f -b fails-to-rebase HEAD^ &&\n> \n> It is unclear what the \"-f\" is for; is it attempting to clean up a\n> potential mess previous steps might have left?  We didn't have it in\n> the previous test above.\n\nIt is there to clean up a very non-potential mess: forcing a CR/LF into a\nfile marked with `text=auto` makes a royal mess. Neither `git reset\n--hard` nor `git stash` will make that file clean. As a consequence, the\n`git checkout` without an `-f` would *always* fail \"because of uncommitted\nchanges\".\n\nI clarified that in a comment.\n\n> > +\ttest_commit v2-without-cr file \"2\" file2-lf &&\n> > +\ttest_must_fail git pull --rebase . diverging 2>err >out &&\n> > +\tgrep \"When you have resolved this problem\" out\n> > +'\n> > +\n> >  test_expect_success '--rebase fails with multiple branches' '\n> >  \tgit reset --hard before-rebase &&\n> >  \ttest_must_fail git pull --rebase . copy master 2>err &&\n> \n> Not worth a reroll but after this series settles we would probably\n> want to address some of the above up with a follow-up clean-up patch.\n\nAs I re-roll anyway, no big deal.\n\nCiao,\nDscho\n"},{"id":"292196","messageId":"alpine.DEB.2.20.1607261421310.14111@virtualbox","threadId":"42743","inReplyTo":"xmqqlh0p2vmn.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v4 02/16] Report bugs consistently","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-26T12:24:59Z","receivedAt":"2016-07-26T12:25:33Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio & Peff,\n\nOn Mon, 25 Jul 2016, Junio C Hamano wrote:\n\n> Jeff King <peff@peff.net> writes:\n> \n> > On Mon, Jul 25, 2016 at 02:44:25PM -0700, Junio C Hamano wrote:\n> >\n> >> > diff --git a/imap-send.c b/imap-send.c\n> >> > index db0fafe..67d67f8 100644\n> >> > --- a/imap-send.c\n> >> > +++ b/imap-send.c\n> >> > @@ -506,12 +506,12 @@ static char *next_arg(char **s)\n> >> >  \n> >> >  static int nfsnprintf(char *buf, int blen, const char *fmt, ...)\n> >> >  {\n> >> > -\tint ret;\n> >> > +\tint ret = -1;\n> >> >  \tva_list va;\n> >> >  \n> >> >  \tva_start(va, fmt);\n> >> >  \tif (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)\n> >> > -\t\tdie(\"Fatal: buffer too small. Please report a bug.\");\n> >> > +\t\tdie(\"BUG: buffer too small (%d < %d)\", ret, blen);\n> >> >  \tva_end(va);\n> >> >  \treturn ret;\n> >> >  }\n> >> \n> >> If \"you gave me this size but you need at least this much\" is truly\n> >> worth reporting, then this is misleading (ret is shown as -1 but you\n> >> do not even know how much is necessary).  In any case, this should\n> >> be done as a separate step anyway.\n> >\n> > Hrm, isn't \"ret\" going to be the necessary size? According to the\n> > standard, it should tell us how many bytes were needed, not \"-1\" (this\n> > is the \"your vsnprintf is broken\" case handled by the strbuf code).\n> \n> Yes.  If blen <= 0, we do not even do vsnprintf() and that is why\n> Dscho added \"int ret = -1\" initialization; otherwise his new die()\n> would end up referencing uninitialized ret.\n\nExactly. While I was fixing this bug message, it occurred to me that it\nmakes little sense to ask a user to report a bug when it is unknown how\nsmall the buffer was and what would have been the desired buffer size. So\nI did a fly-by fix.\n\nHowever, it is true that this is completely outside the purpose of this\npatch series (in fact, most of this patch is completely outside the\npurpose, and I am regretting that dearly, as the patch series' submission\nalready takes almost a month, and I only now get people to review the\ncritical parts of the changes).\n\nSo I simply backed out the more verbose message and *only* do the obvious\nthing: replace the \"Fatal:\" prefix by a \"BUG:\" one.\n\n> > I do think the numbers are reversed, though. It should be \"blen < ret\".\n> \n> That, too ;-)\n\nTrue. All the better that I reverted that part of the patch ;-)\n\nCiao,\nDscho\n"},{"id":"292197","messageId":"alpine.DEB.2.20.1607261425300.14111@virtualbox","threadId":"42743","inReplyTo":"xmqq7fc94bf4.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v4 06/16] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-26T12:26:19Z","receivedAt":"2016-07-26T12:26:45Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Mon, 25 Jul 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > There are a couple of places where return values indicating errors\n> > are ignored. Let's teach them manners.\n> \n> That is because the return value never indicated errors before this\n> series, isn't it?  A true error used to be expressed by dying, and\n> the return value indicating \"cleanliness\" of the merge were\n> deliberately ignored.\n> \n> The world order changed by previous patches in this series and the\n> callers need to be updated to take the new kind of return values\n> into account.  That is not teaching them manners ;-)\n\nI reworded that commit message.\n\n> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > ---\n> >  merge-recursive.c | 10 ++++++++--\n> >  1 file changed, 8 insertions(+), 2 deletions(-)\n> >\n> > diff --git a/merge-recursive.c b/merge-recursive.c\n> > index dc3182b..2d4cb80 100644\n> > --- a/merge-recursive.c\n> > +++ b/merge-recursive.c\n> > @@ -1949,8 +1949,9 @@ int merge_recursive(struct merge_options *o,\n> >  \t\tsaved_b2 = o->branch2;\n> >  \t\to->branch1 = \"Temporary merge branch 1\";\n> >  \t\to->branch2 = \"Temporary merge branch 2\";\n> > -\t\tmerge_recursive(o, merged_common_ancestors, iter->item,\n> > -\t\t\t\tNULL, &merged_common_ancestors);\n> > +\t\tif (merge_recursive(o, merged_common_ancestors, iter->item,\n> > +\t\t\t\t    NULL, &merged_common_ancestors) < 0)\n> > +\t\t\treturn -1;\n> >  \t\to->branch1 = saved_b1;\n> >  \t\to->branch2 = saved_b2;\n> >  \t\to->call_depth--;\n> \n> This hunk feels somewhat wrong as-is.\n> \n> There is a comment before the pre-context explaining why cleanness\n> flag is ignored.  It needs to be updated.  We still do not care\n> about cleanliness, i.e. 0=clean, 1=merged with conflict, but we now\n> can get negative values so we need to reject and return early if\n> this call indicates an error.\n\nI updated that comment. Hopefully now the hunk makes sense ;-)\n\nCiao,\nDscho\n"},{"id":"292198","messageId":"alpine.DEB.2.20.1607261426340.14111@virtualbox","threadId":"42743","inReplyTo":"xmqqy44p2wds.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v4 11/16] am -3: use merge_recursive() directly again","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-26T12:30:55Z","receivedAt":"2016-07-26T12:31:31Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Mon, 25 Jul 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > Note: the code now calls merge_recursive_generic() again. Unlike\n> > merge_trees() and merge_recursive(), this function returns 0 upon success,\n> > as most of Git's functions. Therefore, the error value -1 naturally is\n> > handled correctly, and we do not have to take care of it specifically.\n> \n> I've finished reading through up to this point and I'd stop for\n> now.\n\nIf you want, I can break out the subsequent patches into a separate\nseries. I just thought that you might want to have them here, as I\nimplemented them in response to the concern you raised in a previous\niteration of the same patch series: you pointed out that returning a\nnegative error value still does not let the caller handle the error\nmessage, and with the subsequent patches that is now possible, too.\n\n> Some of the patches I didn't look beyond the context presented in\n> the patches, so it is very possible that I missed leaks caused by\n> early returns and things like that, but I didn't see anything\n> glaringly wrong.  Looks very promising.\n\nThanks.\n\nI did try my best to catch all resource leaks, but I did stare at those\npatches so often and so long that it is easy for some obvious bug to have\nslipped by. Therefore, I would appreciate it if you (or somebody as\ndiligent as you) could have a careful look in particular at the\n\"merge-recursive: switch to returning errors instead of dying\" patch.\n\nI may have missed something as stupid as an unclosed file handle, after\nall.\n\nThank you,\nDscho\n"},{"id":"292217","messageId":"cf805d9b0f48a475df4dcb98ab3f285d583f6e2a.1469547160.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469547160.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 01/16] t5520: verify that `pull --rebase` shows the helpful advice when failing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-26T16:05:43Z","receivedAt":"2016-07-26T16:06:21Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It was noticed by Brendan Forster last October that the builtin `git am`\nregressed on that. Our hot fix reverted to spawning the recursive merge\ninstead of using it as a library function.\n\nAs we are about to revert that hot fix, after making the recursive merge a\ntrue library function (i.e. a function that does not die() in case of\n\"normal\" errors), let's add a test that verifies that we do not regress on\nthe same problem which made the hot fix necessary in the first place.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n t/t5520-pull.sh | 32 ++++++++++++++++++++++++++++++++\n 1 file changed, 32 insertions(+)\n\ndiff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\nindex 37ebbcf..6ad37b5 100755\n--- a/t/t5520-pull.sh\n+++ b/t/t5520-pull.sh\n@@ -255,6 +255,38 @@ test_expect_success '--rebase' '\n \ttest new = \"$(git show HEAD:file2)\"\n '\n \n+test_expect_success '--rebase with conflicts shows advice' '\n+\ttest_when_finished \"git rebase --abort; git checkout -f to-rebase\" &&\n+\tgit checkout -b seq &&\n+\ttest_seq 5 >seq.txt &&\n+\tgit add seq.txt &&\n+\ttest_tick &&\n+\tgit commit -m \"Add seq.txt\" &&\n+\techo 6 >>seq.txt &&\n+\ttest_tick &&\n+\tgit commit -m \"Append to seq.txt\" seq.txt &&\n+\tgit checkout -b with-conflicts HEAD^ &&\n+\techo conflicting >>seq.txt &&\n+\ttest_tick &&\n+\tgit commit -m \"Create conflict\" seq.txt &&\n+\ttest_must_fail git pull --rebase . seq 2>err >out &&\n+\tgrep \"When you have resolved this problem\" out\n+'\n+\n+test_expect_success 'failed --rebase shows advice' '\n+\ttest_when_finished \"git rebase --abort; git checkout -f to-rebase\" &&\n+\tgit checkout -b diverging &&\n+\ttest_commit attributes .gitattributes \"* text=auto\" attrs &&\n+\tsha1=\"$(printf \"1\\\\r\\\\n\" | git hash-object -w --stdin)\" &&\n+\tgit update-index --cacheinfo 0644 $sha1 file &&\n+\tgit commit -m v1-with-cr &&\n+\t# force checkout because `git reset --hard` will not leave clean `file`\n+\tgit checkout -f -b fails-to-rebase HEAD^ &&\n+\ttest_commit v2-without-cr file \"2\" file2-lf &&\n+\ttest_must_fail git pull --rebase . diverging 2>err >out &&\n+\tgrep \"When you have resolved this problem\" out\n+'\n+\n test_expect_success '--rebase fails with multiple branches' '\n \tgit reset --hard before-rebase &&\n \ttest_must_fail git pull --rebase . copy master 2>err &&\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292218","messageId":"95ea7dc3b4a382c24a6e63844525922a8305464c.1469547160.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469547160.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 02/16] Report bugs consistently","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-26T16:05:50Z","receivedAt":"2016-07-26T16:06:25Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The vast majority of error messages in Git's source code which report a\nbug use the convention to prefix the message with \"BUG:\".\n\nAs part of cleaning up merge-recursive to stop die()ing except in case of\ndetected bugs, let's just make the remainder of the bug reports consistent\nwith the de facto rule.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/ls-files.c     |  3 ++-\n builtin/update-index.c |  2 +-\n grep.c                 |  8 ++++----\n imap-send.c            |  2 +-\n merge-recursive.c      | 15 +++++++--------\n sha1_file.c            |  4 ++--\n trailer.c              |  2 +-\n transport.c            |  2 +-\n wt-status.c            |  4 ++--\n 9 files changed, 21 insertions(+), 21 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex f02e3d2..00ea91a 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -118,7 +118,8 @@ static void show_killed_files(struct dir_struct *dir)\n \t\t\t\t */\n \t\t\t\tpos = cache_name_pos(ent->name, ent->len);\n \t\t\t\tif (0 <= pos)\n-\t\t\t\t\tdie(\"bug in show-killed-files\");\n+\t\t\t\t\tdie(\"BUG: killed-file %.*s not found\",\n+\t\t\t\t\t\tent->len, ent->name);\n \t\t\t\tpos = -pos - 1;\n \t\t\t\twhile (pos < active_nr &&\n \t\t\t\t       ce_stage(active_cache[pos]))\ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex 6cdfd5f..ba04b19 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -1146,7 +1146,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)\n \t\treport(_(\"Untracked cache enabled for '%s'\"), get_git_work_tree());\n \t\tbreak;\n \tdefault:\n-\t\tdie(\"Bug: bad untracked_cache value: %d\", untracked_cache);\n+\t\tdie(\"BUG: bad untracked_cache value: %d\", untracked_cache);\n \t}\n \n \tif (active_cache_changed) {\ndiff --git a/grep.c b/grep.c\nindex 394c856..22cbb73 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -693,10 +693,10 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)\n \n \tfor (p = opt->header_list; p; p = p->next) {\n \t\tif (p->token != GREP_PATTERN_HEAD)\n-\t\t\tdie(\"bug: a non-header pattern in grep header list.\");\n+\t\t\tdie(\"BUG: a non-header pattern in grep header list.\");\n \t\tif (p->field < GREP_HEADER_FIELD_MIN ||\n \t\t    GREP_HEADER_FIELD_MAX <= p->field)\n-\t\t\tdie(\"bug: unknown header field %d\", p->field);\n+\t\t\tdie(\"BUG: unknown header field %d\", p->field);\n \t\tcompile_regexp(p, opt);\n \t}\n \n@@ -709,7 +709,7 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)\n \n \t\th = compile_pattern_atom(&pp);\n \t\tif (!h || pp != p->next)\n-\t\t\tdie(\"bug: malformed header expr\");\n+\t\t\tdie(\"BUG: malformed header expr\");\n \t\tif (!header_group[p->field]) {\n \t\t\theader_group[p->field] = h;\n \t\t\tcontinue;\n@@ -1514,7 +1514,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle\n \t\tcase GREP_BINARY_TEXT:\n \t\t\tbreak;\n \t\tdefault:\n-\t\t\tdie(\"bug: unknown binary handling mode\");\n+\t\t\tdie(\"BUG: unknown binary handling mode\");\n \t\t}\n \t}\n \ndiff --git a/imap-send.c b/imap-send.c\nindex db0fafe..0f5f476 100644\n--- a/imap-send.c\n+++ b/imap-send.c\n@@ -511,7 +511,7 @@ static int nfsnprintf(char *buf, int blen, const char *fmt, ...)\n \n \tva_start(va, fmt);\n \tif (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)\n-\t\tdie(\"Fatal: buffer too small. Please report a bug.\");\n+\t\tdie(\"BUG: buffer too small. Please report a bug.\");\n \tva_end(va);\n \treturn ret;\n }\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex a4a1195..4338b73 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -268,7 +268,7 @@ struct tree *write_tree_from_memory(struct merge_options *o)\n \t\t\t\tfprintf(stderr, \"BUG: %d %.*s\\n\", ce_stage(ce),\n \t\t\t\t\t(int)ce_namelen(ce), ce->name);\n \t\t}\n-\t\tdie(\"Bug in merge-recursive.c\");\n+\t\tdie(\"BUG: unmerged index entries in merge-recursive.c\");\n \t}\n \n \tif (!active_cache_tree)\n@@ -966,9 +966,8 @@ static struct merge_file_info merge_file_1(struct merge_options *o,\n \n \t\t\tif (!oid_eq(&a->oid, &b->oid))\n \t\t\t\tresult.clean = 0;\n-\t\t} else {\n-\t\t\tdie(_(\"unsupported object type in the tree\"));\n-\t\t}\n+\t\t} else\n+\t\t\tdie(_(\"BUG: unsupported object type in the tree\"));\n \t}\n \n \treturn result;\n@@ -1354,7 +1353,7 @@ static int process_renames(struct merge_options *o,\n \t\t\tconst char *ren2_dst = ren2->pair->two->path;\n \t\t\tenum rename_type rename_type;\n \t\t\tif (strcmp(ren1_src, ren2_src) != 0)\n-\t\t\t\tdie(\"ren1_src != ren2_src\");\n+\t\t\t\tdie(\"BUG: ren1_src != ren2_src\");\n \t\t\tren2->dst_entry->processed = 1;\n \t\t\tren2->processed = 1;\n \t\t\tif (strcmp(ren1_dst, ren2_dst) != 0) {\n@@ -1388,7 +1387,7 @@ static int process_renames(struct merge_options *o,\n \t\t\tren2 = lookup->util;\n \t\t\tren2_dst = ren2->pair->two->path;\n \t\t\tif (strcmp(ren1_dst, ren2_dst) != 0)\n-\t\t\t\tdie(\"ren1_dst != ren2_dst\");\n+\t\t\t\tdie(\"BUG: ren1_dst != ren2_dst\");\n \n \t\t\tclean_merge = 0;\n \t\t\tren2->processed = 1;\n@@ -1812,7 +1811,7 @@ static int process_entry(struct merge_options *o,\n \t\t */\n \t\tremove_file(o, 1, path, !a_mode);\n \t} else\n-\t\tdie(_(\"Fatal merge failure, shouldn't happen.\"));\n+\t\tdie(_(\"BUG: fatal merge failure, shouldn't happen.\"));\n \n \treturn clean_merge;\n }\n@@ -1870,7 +1869,7 @@ int merge_trees(struct merge_options *o,\n \t\tfor (i = 0; i < entries->nr; i++) {\n \t\t\tstruct stage_data *e = entries->items[i].util;\n \t\t\tif (!e->processed)\n-\t\t\t\tdie(_(\"Unprocessed path??? %s\"),\n+\t\t\t\tdie(_(\"BUG: unprocessed path??? %s\"),\n \t\t\t\t    entries->items[i].string);\n \t\t}\n \ndiff --git a/sha1_file.c b/sha1_file.c\nindex d5e1121..5085fe0 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -795,7 +795,7 @@ void close_all_packs(void)\n \n \tfor (p = packed_git; p; p = p->next)\n \t\tif (p->do_not_close)\n-\t\t\tdie(\"BUG! Want to close pack marked 'do-not-close'\");\n+\t\t\tdie(\"BUG: want to close pack marked 'do-not-close'\");\n \t\telse\n \t\t\tclose_pack(p);\n }\n@@ -2330,7 +2330,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,\n \tcase OBJ_OFS_DELTA:\n \tcase OBJ_REF_DELTA:\n \t\tif (data)\n-\t\t\tdie(\"BUG in unpack_entry: left loop at a valid delta\");\n+\t\t\tdie(\"BUG: unpack_entry: left loop at a valid delta\");\n \t\tbreak;\n \tcase OBJ_COMMIT:\n \tcase OBJ_TREE:\ndiff --git a/trailer.c b/trailer.c\nindex 8e48a5c..c6ea9ac 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -562,7 +562,7 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)\n \t\t\twarning(_(\"unknown value '%s' for key '%s'\"), value, conf_key);\n \t\tbreak;\n \tdefault:\n-\t\tdie(\"internal bug in trailer.c\");\n+\t\tdie(\"BUG: trailer.c: unhandled type %d\", type);\n \t}\n \treturn 0;\n }\ndiff --git a/transport.c b/transport.c\nindex b233e3e..04d9454 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -566,7 +566,7 @@ void transport_take_over(struct transport *transport,\n \tstruct git_transport_data *data;\n \n \tif (!transport->smart_options)\n-\t\tdie(\"Bug detected: Taking over transport requires non-NULL \"\n+\t\tdie(\"BUG: taking over transport requires non-NULL \"\n \t\t    \"smart_options field.\");\n \n \tdata = xcalloc(1, sizeof(*data));\ndiff --git a/wt-status.c b/wt-status.c\nindex 19cbc39..f8ae0c2 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -263,7 +263,7 @@ static const char *wt_status_unmerged_status_string(int stagemask)\n \tcase 7:\n \t\treturn _(\"both modified:\");\n \tdefault:\n-\t\tdie(\"bug: unhandled unmerged status %x\", stagemask);\n+\t\tdie(\"BUG: unhandled unmerged status %x\", stagemask);\n \t}\n }\n \n@@ -388,7 +388,7 @@ static void wt_status_print_change_data(struct wt_status *s,\n \tstatus_printf(s, color(WT_STATUS_HEADER, s), \"\\t\");\n \twhat = wt_status_diff_status_string(status);\n \tif (!what)\n-\t\tdie(\"bug: unhandled diff status %c\", status);\n+\t\tdie(\"BUG: unhandled diff status %c\", status);\n \tlen = label_width - utf8_strwidth(what);\n \tassert(len >= 0);\n \tif (status == DIFF_STATUS_COPIED || status == DIFF_STATUS_RENAMED)\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292219","messageId":"765d70cf1697a76f4da0f22f3b243cfde1dae7aa.1469547160.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469547160.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 03/16] Avoid translating bug messages","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-26T16:05:53Z","receivedAt":"2016-07-26T16:06:29Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"While working on the patch series that avoids die()ing in recursive\nmerges, the issue came up that bug reports (i.e. die(\"BUG: ...\")\nconstructs) should never be translated, as the target audience is the\nGit developer community, not necessarily the current user, and hence\na translated message would make it *harder* to address the problem.\n\nSo let's stop translating the obvious ones. As it is really, really\noutside the purview of this patch series to see whether there are more\ndie() statements that report bugs and are currently translated, that\ntask is left for another day and patch.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 4338b73..1b6db87 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -967,7 +967,7 @@ static struct merge_file_info merge_file_1(struct merge_options *o,\n \t\t\tif (!oid_eq(&a->oid, &b->oid))\n \t\t\t\tresult.clean = 0;\n \t\t} else\n-\t\t\tdie(_(\"BUG: unsupported object type in the tree\"));\n+\t\t\tdie(\"BUG: unsupported object type in the tree\");\n \t}\n \n \treturn result;\n@@ -1811,7 +1811,7 @@ static int process_entry(struct merge_options *o,\n \t\t */\n \t\tremove_file(o, 1, path, !a_mode);\n \t} else\n-\t\tdie(_(\"BUG: fatal merge failure, shouldn't happen.\"));\n+\t\tdie(\"BUG: fatal merge failure, shouldn't happen.\");\n \n \treturn clean_merge;\n }\n@@ -1869,7 +1869,7 @@ int merge_trees(struct merge_options *o,\n \t\tfor (i = 0; i < entries->nr; i++) {\n \t\t\tstruct stage_data *e = entries->items[i].util;\n \t\t\tif (!e->processed)\n-\t\t\t\tdie(_(\"BUG: unprocessed path??? %s\"),\n+\t\t\t\tdie(\"BUG: unprocessed path??? %s\",\n \t\t\t\t    entries->items[i].string);\n \t\t}\n \n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292220","messageId":"b8e122f0c27453171334a59fa6f0c8675350e9f1.1469547160.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469547160.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 04/16] merge-recursive: clarify code in was_tracked()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-26T16:05:57Z","receivedAt":"2016-07-26T16:06:32Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It can be puzzling to see that was_tracked() asks to get an index entry\nby name, but does not take a negative return value for an answer.\n\nThe reason we have to do this is that cache_name_pos() only looks for\nentries in stage 0, even if nobody asked for any stage in particular.\n\nLet's rewrite the logic a little bit, to handle the easy case early: if\ncache_name_pos() returned a non-negative position, we know it is a match,\nand we do not even have to compare the name again (cache_name_pos() did\nthat for us already). We can say right away: yes, this file was tracked.\n\nOnly if there was no exact match do we need to look harder for any\nmatching entry in stage 2.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 30 ++++++++++++++----------------\n 1 file changed, 14 insertions(+), 16 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 1b6db87..3a652b7 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -667,23 +667,21 @@ static int was_tracked(const char *path)\n {\n \tint pos = cache_name_pos(path, strlen(path));\n \n-\tif (pos < 0)\n-\t\tpos = -1 - pos;\n-\twhile (pos < active_nr &&\n-\t       !strcmp(path, active_cache[pos]->name)) {\n-\t\t/*\n-\t\t * If stage #0, it is definitely tracked.\n-\t\t * If it has stage #2 then it was tracked\n-\t\t * before this merge started.  All other\n-\t\t * cases the path was not tracked.\n-\t\t */\n-\t\tswitch (ce_stage(active_cache[pos])) {\n-\t\tcase 0:\n-\t\tcase 2:\n+\tif (0 <= pos)\n+\t\t/* we have been tracking this path */\n+\t\treturn 1;\n+\n+\t/*\n+\t * Look for an unmerged entry for the path,\n+\t * specifically stage #2, which would indicate\n+\t * that \"our\" side before the merge started\n+\t * had the path tracked (and resulted in a conflict).\n+\t */\n+\tfor (pos = -1 - pos;\n+\t     pos < active_nr && !strcmp(path, active_cache[pos]->name);\n+\t     pos++)\n+\t\tif (ce_stage(active_cache[pos]) == 2)\n \t\t\treturn 1;\n-\t\t}\n-\t\tpos++;\n-\t}\n \treturn 0;\n }\n \n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292221","messageId":"136725a05911bad0f8aefd2803f8d49771d0bbb7.1469547160.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469547160.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 05/16] Prepare the builtins for a libified merge_recursive()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-26T16:06:02Z","receivedAt":"2016-07-26T16:06:35Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Previously, callers of merge_trees() or merge_recursive() expected that\ncode to die() with an error message. This used to be okay because we\ncalled those commands from scripts, and had a chance to print out a\nmessage in case the command failed fatally (read: with exit code 128).\n\nAs scripting incurs its own set of problems (portability, speed,\nidiosynchracies of different shells, limited data structures leading to\ninefficient code), we are converting more and more of these scripts into\nbuiltins, using library functions directly.\n\nWe already tried to use merge_recursive() directly in the builtin\ngit-am, for example. Unfortunately, we had to roll it back temporarily\nbecause some of the code in merge-recursive.c still deemed it okay to\ncall die(), when the builtin am code really wanted to print out a useful\nadvice after the merge failed fatally. In the next commits, we want to\nfix that.\n\nThe code touched by this commit expected merge_trees() to die() with\nsome useful message when there is an error condition, but merge_trees()\nis going to be improved by converting all die() calls to return error()\ninstead (i.e. return value -1 after printing out the message as before),\nso that the caller can react more flexibly.\n\nThis is a step to prepare for the version of merge_trees() that no\nlonger dies,  even if we just imitate the previous behavior by calling\nexit(128): this is what callers of e.g. `git merge` have come to expect.\n\nNote that the callers of the sequencer (revert and cherry-pick) already\nfail fast even for the return value -1; The only difference is that they\nnow get a chance to say \"<command> failed\".\n\nA caller of merge_trees() might want handle error messages themselves\n(or even suppress them). As this patch is already complex enough, we\nleave that change for a later patch.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/checkout.c | 4 +++-\n builtin/merge.c    | 2 ++\n sequencer.c        | 4 ++++\n 3 files changed, 9 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 27c1a05..07dea3b 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -567,8 +567,10 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\to.ancestor = old->name;\n \t\t\to.branch1 = new->name;\n \t\t\to.branch2 = \"local\";\n-\t\t\tmerge_trees(&o, new->commit->tree, work,\n+\t\t\tret = merge_trees(&o, new->commit->tree, work,\n \t\t\t\told->commit->tree, &result);\n+\t\t\tif (ret < 0)\n+\t\t\t\texit(128);\n \t\t\tret = reset_tree(new->commit->tree, opts, 0,\n \t\t\t\t\t writeout_error);\n \t\t\tif (ret)\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 19b3bc2..148a9a5 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -673,6 +673,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,\n \t\thold_locked_index(&lock, 1);\n \t\tclean = merge_recursive(&o, head,\n \t\t\t\tremoteheads->item, reversed, &result);\n+\t\tif (clean < 0)\n+\t\t\texit(128);\n \t\tif (active_cache_changed &&\n \t\t    write_locked_index(&the_index, &lock, COMMIT_LOCK))\n \t\t\tdie (_(\"unable to write %s\"), get_index_file());\ndiff --git a/sequencer.c b/sequencer.c\nindex cdfac82..286a435 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -293,6 +293,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \tclean = merge_trees(&o,\n \t\t\t    head_tree,\n \t\t\t    next_tree, base_tree, &result);\n+\tif (clean < 0)\n+\t\treturn clean;\n \n \tif (active_cache_changed &&\n \t    write_locked_index(&the_index, &index_lock, COMMIT_LOCK))\n@@ -559,6 +561,8 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || opts->action == REPLAY_REVERT) {\n \t\tres = do_recursive_merge(base, next, base_label, next_label,\n \t\t\t\t\t head, &msgbuf, opts);\n+\t\tif (res < 0)\n+\t\t\treturn res;\n \t\twrite_message(&msgbuf, git_path_merge_msg());\n \t} else {\n \t\tstruct commit_list *common = NULL;\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292222","messageId":"fb1738d311fc1b6bf0a903acb14155f1ae7b575b.1469547160.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469547160.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 06/16] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-26T16:06:07Z","receivedAt":"2016-07-26T16:06:40Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"There are a couple of places where return values never indicated errors\nbefore, as wie simply died instead of returning.\n\nBut now negative return values mean that there was an error and we have to\nabort the operation. Let's do exactly that.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 17 ++++++++++++-----\n 1 file changed, 12 insertions(+), 5 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 3a652b7..58ced25 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1949,17 +1949,19 @@ int merge_recursive(struct merge_options *o,\n \t\t/*\n \t\t * When the merge fails, the result contains files\n \t\t * with conflict markers. The cleanness flag is\n-\t\t * ignored, it was never actually used, as result of\n-\t\t * merge_trees has always overwritten it: the committed\n-\t\t * \"conflicts\" were already resolved.\n+\t\t * ignored (unless indicating an error), it was never\n+\t\t * actually used, as result of merge_trees has always\n+\t\t * overwritten it: the committed \"conflicts\" were\n+\t\t * already resolved.\n \t\t */\n \t\tdiscard_cache();\n \t\tsaved_b1 = o->branch1;\n \t\tsaved_b2 = o->branch2;\n \t\to->branch1 = \"Temporary merge branch 1\";\n \t\to->branch2 = \"Temporary merge branch 2\";\n-\t\tmerge_recursive(o, merged_common_ancestors, iter->item,\n-\t\t\t\tNULL, &merged_common_ancestors);\n+\t\tif (merge_recursive(o, merged_common_ancestors, iter->item,\n+\t\t\t\t    NULL, &merged_common_ancestors) < 0)\n+\t\t\treturn -1;\n \t\to->branch1 = saved_b1;\n \t\to->branch2 = saved_b2;\n \t\to->call_depth--;\n@@ -1975,6 +1977,8 @@ int merge_recursive(struct merge_options *o,\n \to->ancestor = \"merged common ancestors\";\n \tclean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree,\n \t\t\t    &mrtree);\n+\tif (clean < 0)\n+\t\treturn clean;\n \n \tif (o->call_depth) {\n \t\t*result = make_virtual_commit(mrtree, \"merged tree\");\n@@ -2031,6 +2035,9 @@ int merge_recursive_generic(struct merge_options *o,\n \thold_locked_index(lock, 1);\n \tclean = merge_recursive(o, head_commit, next_commit, ca,\n \t\t\tresult);\n+\tif (clean < 0)\n+\t\treturn clean;\n+\n \tif (active_cache_changed &&\n \t    write_locked_index(&the_index, lock, COMMIT_LOCK))\n \t\treturn error(_(\"Unable to write index.\"));\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292223","messageId":"cover.1469547160.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469187652.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 00/16] Use merge_recursive() directly in the builtin am","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-26T16:05:37Z","receivedAt":"2016-07-26T16:06:43Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This is the fifth iteration of the long-awaited re-roll of the attempt to\navoid spawning merge-recursive from the builtin am and use merge_recursive()\ndirectly instead.\n\nThe *real* reason for the reroll is that I need a libified recursive\nmerge to accelerate the interactive rebase by teaching the sequencer to\ndo rebase -i's grunt work. Coming with a very nice 3x-5x speedup of\n`rebase -i`.\n\nIn this endeavor, we need to be extra careful to retain backwards\ncompatibility. The test script t6022-merge-rename.sh, for example, verifies\nthat `git pull` exits with status 128 in case of a fatal error. To that end,\nwe need to make sure that fatal errors are handled by existing (builtin)\nusers via exit(128) (or die(), which calls exit(128) at the end).  New users\n(such as a builtin helper doing rebase -i's grunt work) may want to print\nsome helpful advice what happened and how to get out of this mess before\nerroring out.\n\nThe changes relative to the fourth iteration of this patch series:\n\n- the first patch which introduces a tests case to t5520 was prettified:\n\n  - it uses test_seq now,\n  - it avoids `printf` when `echo` does the job, too,\n  - it adds a missing empty line between test cases, and\n  - it clarifies why we need to check out with force.\n\n- the change that would have made the bug report about a too-small\n  buffer in imap-send was reverted, because it did more than was claimed\n  by the commit message.\n\n- the \"Let's teach them manners\" part of one commit message was replaced\n  with a less flippant description.\n\n- the comment before merge_recursive() that claims that the return value\n  is ignored now clarifies that it is ignored unless it indicates an\n  error.\n\n- during the rebase to `master`, a trivial merge conflict with the\n  `jc/renormalize-merge-kill-safer-crlf` branch was resolved.\n\nThis patch series touches rather important code. Now that I addressed\nconcerns such as prettifying some test code, I would appreciate thorough\nreviews with a focus on the critical parts of the code, those that could\nresult in regressions.\n\n\nJohannes Schindelin (16):\n  t5520: verify that `pull --rebase` shows the helpful advice when\n    failing\n  Report bugs consistently\n  Avoid translating bug messages\n  merge-recursive: clarify code in was_tracked()\n  Prepare the builtins for a libified merge_recursive()\n  merge_recursive: abort properly upon errors\n  merge-recursive: avoid returning a wholesale struct\n  merge-recursive: allow write_tree_from_memory() to error out\n  merge-recursive: handle return values indicating errors\n  merge-recursive: switch to returning errors instead of dying\n  am -3: use merge_recursive() directly again\n  merge-recursive: flush output buffer before printing error messages\n  merge-recursive: write the commit title in one go\n  merge-recursive: offer an option to retain the output in 'obuf'\n  Ensure that the output buffer is released after calling merge_trees()\n  merge-recursive: flush output buffer even when erroring out\n\n builtin/am.c           |  62 ++----\n builtin/checkout.c     |   5 +-\n builtin/ls-files.c     |   3 +-\n builtin/merge.c        |   2 +\n builtin/update-index.c |   2 +-\n grep.c                 |   8 +-\n imap-send.c            |   2 +-\n merge-recursive.c      | 578 +++++++++++++++++++++++++++++--------------------\n merge-recursive.h      |   2 +-\n sequencer.c            |   5 +\n sha1_file.c            |   4 +-\n t/t5520-pull.sh        |  32 +++\n trailer.c              |   2 +-\n transport.c            |   2 +-\n wt-status.c            |   4 +-\n 15 files changed, 418 insertions(+), 295 deletions(-)\n\nPublished-As: https://github.com/dscho/git/releases/tag/am-3-merge-recursive-direct-v5\nInterdiff vs v4:\n\n diff --git a/imap-send.c b/imap-send.c\n index 67d67f8..0f5f476 100644\n --- a/imap-send.c\n +++ b/imap-send.c\n @@ -506,12 +506,12 @@ static char *next_arg(char **s)\n  \n  static int nfsnprintf(char *buf, int blen, const char *fmt, ...)\n  {\n -\tint ret = -1;\n +\tint ret;\n  \tva_list va;\n  \n  \tva_start(va, fmt);\n  \tif (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)\n -\t\tdie(\"BUG: buffer too small (%d < %d)\", ret, blen);\n +\t\tdie(\"BUG: buffer too small. Please report a bug.\");\n  \tva_end(va);\n  \treturn ret;\n  }\n diff --git a/merge-recursive.c b/merge-recursive.c\n index a3d12e6..66e93e0 100644\n --- a/merge-recursive.c\n +++ b/merge-recursive.c\n @@ -2041,9 +2041,10 @@ int merge_recursive(struct merge_options *o,\n  \t\t/*\n  \t\t * When the merge fails, the result contains files\n  \t\t * with conflict markers. The cleanness flag is\n -\t\t * ignored, it was never actually used, as result of\n -\t\t * merge_trees has always overwritten it: the committed\n -\t\t * \"conflicts\" were already resolved.\n +\t\t * ignored (unless indicating an error), it was never\n +\t\t * actually used, as result of merge_trees has always\n +\t\t * overwritten it: the committed \"conflicts\" were\n +\t\t * already resolved.\n  \t\t */\n  \t\tdiscard_cache();\n  \t\tsaved_b1 = o->branch1;\n diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\n index d289056..6ad37b5 100755\n --- a/t/t5520-pull.sh\n +++ b/t/t5520-pull.sh\n @@ -258,20 +258,21 @@ test_expect_success '--rebase' '\n  test_expect_success '--rebase with conflicts shows advice' '\n  \ttest_when_finished \"git rebase --abort; git checkout -f to-rebase\" &&\n  \tgit checkout -b seq &&\n -\tprintf \"1\\\\n2\\\\n3\\\\n4\\\\n5\\\\n\" >seq.txt &&\n +\ttest_seq 5 >seq.txt &&\n  \tgit add seq.txt &&\n  \ttest_tick &&\n  \tgit commit -m \"Add seq.txt\" &&\n -\tprintf \"6\\\\n\" >>seq.txt &&\n +\techo 6 >>seq.txt &&\n  \ttest_tick &&\n  \tgit commit -m \"Append to seq.txt\" seq.txt &&\n  \tgit checkout -b with-conflicts HEAD^ &&\n -\tprintf \"conflicting\\\\n\" >>seq.txt &&\n +\techo conflicting >>seq.txt &&\n  \ttest_tick &&\n  \tgit commit -m \"Create conflict\" seq.txt &&\n  \ttest_must_fail git pull --rebase . seq 2>err >out &&\n  \tgrep \"When you have resolved this problem\" out\n  '\n +\n  test_expect_success 'failed --rebase shows advice' '\n  \ttest_when_finished \"git rebase --abort; git checkout -f to-rebase\" &&\n  \tgit checkout -b diverging &&\n @@ -279,6 +280,7 @@ test_expect_success 'failed --rebase shows advice' '\n  \tsha1=\"$(printf \"1\\\\r\\\\n\" | git hash-object -w --stdin)\" &&\n  \tgit update-index --cacheinfo 0644 $sha1 file &&\n  \tgit commit -m v1-with-cr &&\n +\t# force checkout because `git reset --hard` will not leave clean `file`\n  \tgit checkout -f -b fails-to-rebase HEAD^ &&\n  \ttest_commit v2-without-cr file \"2\" file2-lf &&\n  \ttest_must_fail git pull --rebase . diverging 2>err >out &&\n\n-- \n2.9.0.281.g286a8d9\n\nbase-commit: 8c6d1f9807c67532e7fb545a944b064faff0f70b\n"},{"id":"292224","messageId":"fd7d4935058478491b5c4fa060b3b2795e276e06.1469547160.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469547160.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 07/16] merge-recursive: avoid returning a wholesale struct","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-26T16:06:10Z","receivedAt":"2016-07-26T16:06:45Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It is technically allowed, as per C89, for functions' return type to\nbe complete structs (i.e. *not* just pointers to structs).\n\nHowever, it was just an oversight of this developer when converting\nPython code to C code in 6d297f8 (Status update on merge-recursive in\nC, 2006-07-08) which introduced such a return type.\n\nBesides, by converting this construct to pass in the struct, we can now\nstart returning a value that can indicate errors in future patches. This\nwill help the current effort to libify merge-recursive.c.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 106 ++++++++++++++++++++++++++++--------------------------\n 1 file changed, 56 insertions(+), 50 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 58ced25..2be1e17 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -894,47 +894,47 @@ static int merge_3way(struct merge_options *o,\n \treturn merge_status;\n }\n \n-static struct merge_file_info merge_file_1(struct merge_options *o,\n+static int merge_file_1(struct merge_options *o,\n \t\t\t\t\t   const struct diff_filespec *one,\n \t\t\t\t\t   const struct diff_filespec *a,\n \t\t\t\t\t   const struct diff_filespec *b,\n \t\t\t\t\t   const char *branch1,\n-\t\t\t\t\t   const char *branch2)\n+\t\t\t\t\t   const char *branch2,\n+\t\t\t\t\t   struct merge_file_info *result)\n {\n-\tstruct merge_file_info result;\n-\tresult.merge = 0;\n-\tresult.clean = 1;\n+\tresult->merge = 0;\n+\tresult->clean = 1;\n \n \tif ((S_IFMT & a->mode) != (S_IFMT & b->mode)) {\n-\t\tresult.clean = 0;\n+\t\tresult->clean = 0;\n \t\tif (S_ISREG(a->mode)) {\n-\t\t\tresult.mode = a->mode;\n-\t\t\toidcpy(&result.oid, &a->oid);\n+\t\t\tresult->mode = a->mode;\n+\t\t\toidcpy(&result->oid, &a->oid);\n \t\t} else {\n-\t\t\tresult.mode = b->mode;\n-\t\t\toidcpy(&result.oid, &b->oid);\n+\t\t\tresult->mode = b->mode;\n+\t\t\toidcpy(&result->oid, &b->oid);\n \t\t}\n \t} else {\n \t\tif (!oid_eq(&a->oid, &one->oid) && !oid_eq(&b->oid, &one->oid))\n-\t\t\tresult.merge = 1;\n+\t\t\tresult->merge = 1;\n \n \t\t/*\n \t\t * Merge modes\n \t\t */\n \t\tif (a->mode == b->mode || a->mode == one->mode)\n-\t\t\tresult.mode = b->mode;\n+\t\t\tresult->mode = b->mode;\n \t\telse {\n-\t\t\tresult.mode = a->mode;\n+\t\t\tresult->mode = a->mode;\n \t\t\tif (b->mode != one->mode) {\n-\t\t\t\tresult.clean = 0;\n-\t\t\t\tresult.merge = 1;\n+\t\t\t\tresult->clean = 0;\n+\t\t\t\tresult->merge = 1;\n \t\t\t}\n \t\t}\n \n \t\tif (oid_eq(&a->oid, &b->oid) || oid_eq(&a->oid, &one->oid))\n-\t\t\toidcpy(&result.oid, &b->oid);\n+\t\t\toidcpy(&result->oid, &b->oid);\n \t\telse if (oid_eq(&b->oid, &one->oid))\n-\t\t\toidcpy(&result.oid, &a->oid);\n+\t\t\toidcpy(&result->oid, &a->oid);\n \t\telse if (S_ISREG(a->mode)) {\n \t\t\tmmbuffer_t result_buf;\n \t\t\tint merge_status;\n@@ -946,64 +946,66 @@ static struct merge_file_info merge_file_1(struct merge_options *o,\n \t\t\t\tdie(_(\"Failed to execute internal merge\"));\n \n \t\t\tif (write_sha1_file(result_buf.ptr, result_buf.size,\n-\t\t\t\t\t    blob_type, result.oid.hash))\n+\t\t\t\t\t    blob_type, result->oid.hash))\n \t\t\t\tdie(_(\"Unable to add %s to database\"),\n \t\t\t\t    a->path);\n \n \t\t\tfree(result_buf.ptr);\n-\t\t\tresult.clean = (merge_status == 0);\n+\t\t\tresult->clean = (merge_status == 0);\n \t\t} else if (S_ISGITLINK(a->mode)) {\n-\t\t\tresult.clean = merge_submodule(result.oid.hash,\n+\t\t\tresult->clean = merge_submodule(result->oid.hash,\n \t\t\t\t\t\t       one->path,\n \t\t\t\t\t\t       one->oid.hash,\n \t\t\t\t\t\t       a->oid.hash,\n \t\t\t\t\t\t       b->oid.hash,\n \t\t\t\t\t\t       !o->call_depth);\n \t\t} else if (S_ISLNK(a->mode)) {\n-\t\t\toidcpy(&result.oid, &a->oid);\n+\t\t\toidcpy(&result->oid, &a->oid);\n \n \t\t\tif (!oid_eq(&a->oid, &b->oid))\n-\t\t\t\tresult.clean = 0;\n+\t\t\t\tresult->clean = 0;\n \t\t} else\n \t\t\tdie(\"BUG: unsupported object type in the tree\");\n \t}\n \n-\treturn result;\n+\treturn 0;\n }\n \n-static struct merge_file_info\n-merge_file_special_markers(struct merge_options *o,\n+static int merge_file_special_markers(struct merge_options *o,\n \t\t\t   const struct diff_filespec *one,\n \t\t\t   const struct diff_filespec *a,\n \t\t\t   const struct diff_filespec *b,\n \t\t\t   const char *branch1,\n \t\t\t   const char *filename1,\n \t\t\t   const char *branch2,\n-\t\t\t   const char *filename2)\n+\t\t\t   const char *filename2,\n+\t\t\t   struct merge_file_info *mfi)\n {\n \tchar *side1 = NULL;\n \tchar *side2 = NULL;\n-\tstruct merge_file_info mfi;\n+\tint ret;\n \n \tif (filename1)\n \t\tside1 = xstrfmt(\"%s:%s\", branch1, filename1);\n \tif (filename2)\n \t\tside2 = xstrfmt(\"%s:%s\", branch2, filename2);\n \n-\tmfi = merge_file_1(o, one, a, b,\n-\t\t\t   side1 ? side1 : branch1, side2 ? side2 : branch2);\n+\tret = merge_file_1(o, one, a, b,\n+\t\t\t   side1 ? side1 : branch1,\n+\t\t\t   side2 ? side2 : branch2, mfi);\n \tfree(side1);\n \tfree(side2);\n-\treturn mfi;\n+\treturn ret;\n }\n \n-static struct merge_file_info merge_file_one(struct merge_options *o,\n+static int merge_file_one(struct merge_options *o,\n \t\t\t\t\t const char *path,\n \t\t\t\t\t const struct object_id *o_oid, int o_mode,\n \t\t\t\t\t const struct object_id *a_oid, int a_mode,\n \t\t\t\t\t const struct object_id *b_oid, int b_mode,\n \t\t\t\t\t const char *branch1,\n-\t\t\t\t\t const char *branch2)\n+\t\t\t\t\t const char *branch2,\n+\t\t\t\t\t struct merge_file_info *mfi)\n {\n \tstruct diff_filespec one, a, b;\n \n@@ -1014,7 +1016,7 @@ static struct merge_file_info merge_file_one(struct merge_options *o,\n \ta.mode = a_mode;\n \toidcpy(&b.oid, b_oid);\n \tb.mode = b_mode;\n-\treturn merge_file_1(o, &one, &a, &b, branch1, branch2);\n+\treturn merge_file_1(o, &one, &a, &b, branch1, branch2, mfi);\n }\n \n static void handle_change_delete(struct merge_options *o,\n@@ -1187,11 +1189,12 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n \t\tstruct merge_file_info mfi;\n \t\tstruct diff_filespec other;\n \t\tstruct diff_filespec *add;\n-\t\tmfi = merge_file_one(o, one->path,\n+\t\tif (merge_file_one(o, one->path,\n \t\t\t\t &one->oid, one->mode,\n \t\t\t\t &a->oid, a->mode,\n \t\t\t\t &b->oid, b->mode,\n-\t\t\t\t ci->branch1, ci->branch2);\n+\t\t\t\t ci->branch1, ci->branch2, &mfi))\n+\t\t\treturn;\n \t\t/*\n \t\t * FIXME: For rename/add-source conflicts (if we could detect\n \t\t * such), this is wrong.  We should instead find a unique\n@@ -1245,12 +1248,13 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \tremove_file(o, 1, a->path, o->call_depth || would_lose_untracked(a->path));\n \tremove_file(o, 1, b->path, o->call_depth || would_lose_untracked(b->path));\n \n-\tmfi_c1 = merge_file_special_markers(o, a, c1, &ci->ren1_other,\n-\t\t\t\t\t    o->branch1, c1->path,\n-\t\t\t\t\t    o->branch2, ci->ren1_other.path);\n-\tmfi_c2 = merge_file_special_markers(o, b, &ci->ren2_other, c2,\n-\t\t\t\t\t    o->branch1, ci->ren2_other.path,\n-\t\t\t\t\t    o->branch2, c2->path);\n+\tif (merge_file_special_markers(o, a, c1, &ci->ren1_other,\n+\t\t\t\t       o->branch1, c1->path,\n+\t\t\t\t       o->branch2, ci->ren1_other.path, &mfi_c1) ||\n+\t    merge_file_special_markers(o, b, &ci->ren2_other, c2,\n+\t\t\t\t       o->branch1, ci->ren2_other.path,\n+\t\t\t\t       o->branch2, c2->path, &mfi_c2))\n+\t\treturn;\n \n \tif (o->call_depth) {\n \t\t/*\n@@ -1473,12 +1477,13 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t       ren1_dst, branch2);\n \t\t\t\tif (o->call_depth) {\n \t\t\t\t\tstruct merge_file_info mfi;\n-\t\t\t\t\tmfi = merge_file_one(o, ren1_dst, &null_oid, 0,\n-\t\t\t\t\t\t\t &ren1->pair->two->oid,\n-\t\t\t\t\t\t\t ren1->pair->two->mode,\n-\t\t\t\t\t\t\t &dst_other.oid,\n-\t\t\t\t\t\t\t dst_other.mode,\n-\t\t\t\t\t\t\t branch1, branch2);\n+\t\t\t\t\tif (merge_file_one(o, ren1_dst, &null_oid, 0,\n+\t\t\t\t\t\t\t   &ren1->pair->two->oid,\n+\t\t\t\t\t\t\t   ren1->pair->two->mode,\n+\t\t\t\t\t\t\t   &dst_other.oid,\n+\t\t\t\t\t\t\t   dst_other.mode,\n+\t\t\t\t\t\t\t   branch1, branch2, &mfi))\n+\t\t\t\t\t\treturn -1;\n \t\t\t\t\toutput(o, 1, _(\"Adding merged %s\"), ren1_dst);\n \t\t\t\t\tupdate_file(o, 0, &mfi.oid,\n \t\t\t\t\t\t    mfi.mode, ren1_dst);\n@@ -1636,9 +1641,10 @@ static int merge_content(struct merge_options *o,\n \t\tif (dir_in_way(path, !o->call_depth))\n \t\t\tdf_conflict_remains = 1;\n \t}\n-\tmfi = merge_file_special_markers(o, &one, &a, &b,\n-\t\t\t\t\t o->branch1, path1,\n-\t\t\t\t\t o->branch2, path2);\n+\tif (merge_file_special_markers(o, &one, &a, &b,\n+\t\t\t\t       o->branch1, path1,\n+\t\t\t\t       o->branch2, path2, &mfi))\n+\t\treturn -1;\n \n \tif (mfi.clean && !df_conflict_remains &&\n \t    oid_eq(&mfi.oid, a_oid) && mfi.mode == a_mode) {\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292225","messageId":"743ecd0b59dff75017dd56013f5fdf6f14766d5c.1469547160.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469547160.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 08/16] merge-recursive: allow write_tree_from_memory() to error out","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-26T16:06:17Z","receivedAt":"2016-07-26T16:06:48Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It is possible that a tree cannot be written (think: disk full). We\nwill want to give the caller a chance to clean up instead of letting\nthe program die() in such a case.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 2be1e17..1f86338 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1888,8 +1888,8 @@ int merge_trees(struct merge_options *o,\n \telse\n \t\tclean = 1;\n \n-\tif (o->call_depth)\n-\t\t*result = write_tree_from_memory(o);\n+\tif (o->call_depth && !(*result = write_tree_from_memory(o)))\n+\t\treturn -1;\n \n \treturn clean;\n }\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292226","messageId":"c1b13b1581863ef94f557ab142fbd96c3e2d68c7.1469547160.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469547160.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 09/16] merge-recursive: handle return values indicating errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-26T16:06:21Z","receivedAt":"2016-07-26T16:06:51Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We are about to libify the recursive merge machinery, where we only\ndie() in case of a bug or memory contention. To that end, we must heed\nnegative return values as indicating errors.\n\nThis requires our functions to be careful to pass through error\nconditions in call chains, and for quite a few functions this means\nthat they have to return values to begin with.\n\nThe next step will be to convert the places where we currently die() to\nreturn negative values (read: -1) instead.\n\nNote that we ignore errors reported by make_room_for_path(), consistent\nwith the previous behavior (update_file_flags() used the return value of\nmake_room_for_path() only to indicate an early return, but not a fatal\nerror): if the error is really a fatal error, we will notice later; If\nnot, it was not that serious a problem to begin with. (Witnesses in\nfavor of this reasoning are t4151-am-abort and t7610-mergetool, which\nwould start failing if we stopped on errors reported by\nmake_room_for_path()).\n\nAlso note: while this patch makes the code slightly less readable in\nupdate_file_flags() (we introduce a new \"goto free_buf;\" instead of\nan explicit \"free(buf); return;\"), it is a preparatory change for\nthe next patch where we will convert all of the die() calls in the same\nfunction to go through the free_buf return path instead.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 252 ++++++++++++++++++++++++++++++++----------------------\n 1 file changed, 150 insertions(+), 102 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 1f86338..6beb1e4 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -742,12 +742,12 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n }\n \n-static void update_file_flags(struct merge_options *o,\n-\t\t\t      const struct object_id *oid,\n-\t\t\t      unsigned mode,\n-\t\t\t      const char *path,\n-\t\t\t      int update_cache,\n-\t\t\t      int update_wd)\n+static int update_file_flags(struct merge_options *o,\n+\t\t\t     const struct object_id *oid,\n+\t\t\t     unsigned mode,\n+\t\t\t     const char *path,\n+\t\t\t     int update_cache,\n+\t\t\t     int update_wd)\n {\n \tif (o->call_depth)\n \t\tupdate_wd = 0;\n@@ -783,8 +783,7 @@ static void update_file_flags(struct merge_options *o,\n \n \t\tif (make_room_for_path(o, path) < 0) {\n \t\t\tupdate_wd = 0;\n-\t\t\tfree(buf);\n-\t\t\tgoto update_index;\n+\t\t\tgoto free_buf;\n \t\t}\n \t\tif (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) {\n \t\t\tint fd;\n@@ -807,20 +806,22 @@ static void update_file_flags(struct merge_options *o,\n \t\t} else\n \t\t\tdie(_(\"do not know what to do with %06o %s '%s'\"),\n \t\t\t    mode, oid_to_hex(oid), path);\n+ free_buf:\n \t\tfree(buf);\n \t}\n  update_index:\n \tif (update_cache)\n \t\tadd_cacheinfo(mode, oid, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n+\treturn 0;\n }\n \n-static void update_file(struct merge_options *o,\n-\t\t\tint clean,\n-\t\t\tconst struct object_id *oid,\n-\t\t\tunsigned mode,\n-\t\t\tconst char *path)\n+static int update_file(struct merge_options *o,\n+\t\t       int clean,\n+\t\t       const struct object_id *oid,\n+\t\t       unsigned mode,\n+\t\t       const char *path)\n {\n-\tupdate_file_flags(o, oid, mode, path, o->call_depth || clean, !o->call_depth);\n+\treturn update_file_flags(o, oid, mode, path, o->call_depth || clean, !o->call_depth);\n }\n \n /* Low level file merging, update and removal */\n@@ -1019,7 +1020,7 @@ static int merge_file_one(struct merge_options *o,\n \treturn merge_file_1(o, &one, &a, &b, branch1, branch2, mfi);\n }\n \n-static void handle_change_delete(struct merge_options *o,\n+static int handle_change_delete(struct merge_options *o,\n \t\t\t\t const char *path,\n \t\t\t\t const struct object_id *o_oid, int o_mode,\n \t\t\t\t const struct object_id *a_oid, int a_mode,\n@@ -1027,6 +1028,7 @@ static void handle_change_delete(struct merge_options *o,\n \t\t\t\t const char *change, const char *change_past)\n {\n \tchar *renamed = NULL;\n+\tint ret = 0;\n \tif (dir_in_way(path, !o->call_depth)) {\n \t\trenamed = unique_path(o, path, a_oid ? o->branch1 : o->branch2);\n \t}\n@@ -1037,21 +1039,23 @@ static void handle_change_delete(struct merge_options *o,\n \t\t * correct; since there is no true \"middle point\" between\n \t\t * them, simply reuse the base version for virtual merge base.\n \t\t */\n-\t\tremove_file_from_cache(path);\n-\t\tupdate_file(o, 0, o_oid, o_mode, renamed ? renamed : path);\n+\t\tret = remove_file_from_cache(path);\n+\t\tif (!ret)\n+\t\t\tret = update_file(o, 0, o_oid, o_mode,\n+\t\t\t\t\t  renamed ? renamed : path);\n \t} else if (!a_oid) {\n \t\tif (!renamed) {\n \t\t\toutput(o, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n \t\t\t       \"and %s in %s. Version %s of %s left in tree.\"),\n \t\t\t       change, path, o->branch1, change_past,\n \t\t\t       o->branch2, o->branch2, path);\n-\t\t\tupdate_file(o, 0, b_oid, b_mode, path);\n+\t\t\tret = update_file(o, 0, b_oid, b_mode, path);\n \t\t} else {\n \t\t\toutput(o, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n \t\t\t       \"and %s in %s. Version %s of %s left in tree at %s.\"),\n \t\t\t       change, path, o->branch1, change_past,\n \t\t\t       o->branch2, o->branch2, path, renamed);\n-\t\t\tupdate_file(o, 0, b_oid, b_mode, renamed);\n+\t\t\tret = update_file(o, 0, b_oid, b_mode, renamed);\n \t\t}\n \t} else {\n \t\tif (!renamed) {\n@@ -1064,7 +1068,7 @@ static void handle_change_delete(struct merge_options *o,\n \t\t\t       \"and %s in %s. Version %s of %s left in tree at %s.\"),\n \t\t\t       change, path, o->branch2, change_past,\n \t\t\t       o->branch1, o->branch1, path, renamed);\n-\t\t\tupdate_file(o, 0, a_oid, a_mode, renamed);\n+\t\t\tret = update_file(o, 0, a_oid, a_mode, renamed);\n \t\t}\n \t\t/*\n \t\t * No need to call update_file() on path when !renamed, since\n@@ -1074,9 +1078,11 @@ static void handle_change_delete(struct merge_options *o,\n \t\t */\n \t}\n \tfree(renamed);\n+\n+\treturn ret;\n }\n \n-static void conflict_rename_delete(struct merge_options *o,\n+static int conflict_rename_delete(struct merge_options *o,\n \t\t\t\t   struct diff_filepair *pair,\n \t\t\t\t   const char *rename_branch,\n \t\t\t\t   const char *other_branch)\n@@ -1096,21 +1102,20 @@ static void conflict_rename_delete(struct merge_options *o,\n \t\tb_mode = dest->mode;\n \t}\n \n-\thandle_change_delete(o,\n-\t\t\t     o->call_depth ? orig->path : dest->path,\n-\t\t\t     &orig->oid, orig->mode,\n-\t\t\t     a_oid, a_mode,\n-\t\t\t     b_oid, b_mode,\n-\t\t\t     _(\"rename\"), _(\"renamed\"));\n-\n-\tif (o->call_depth) {\n-\t\tremove_file_from_cache(dest->path);\n-\t} else {\n-\t\tupdate_stages(dest->path, NULL,\n-\t\t\t      rename_branch == o->branch1 ? dest : NULL,\n-\t\t\t      rename_branch == o->branch1 ? NULL : dest);\n-\t}\n+\tif (handle_change_delete(o,\n+\t\t\t\t o->call_depth ? orig->path : dest->path,\n+\t\t\t\t &orig->oid, orig->mode,\n+\t\t\t\t a_oid, a_mode,\n+\t\t\t\t b_oid, b_mode,\n+\t\t\t\t _(\"rename\"), _(\"renamed\")))\n+\t\treturn -1;\n \n+\tif (o->call_depth)\n+\t\treturn remove_file_from_cache(dest->path);\n+\telse\n+\t\treturn update_stages(dest->path, NULL,\n+\t\t\t\t     rename_branch == o->branch1 ? dest : NULL,\n+\t\t\t\t     rename_branch == o->branch1 ? NULL : dest);\n }\n \n static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,\n@@ -1126,7 +1131,7 @@ static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,\n \treturn target;\n }\n \n-static void handle_file(struct merge_options *o,\n+static int handle_file(struct merge_options *o,\n \t\t\tstruct diff_filespec *rename,\n \t\t\tint stage,\n \t\t\tstruct rename_conflict_info *ci)\n@@ -1136,6 +1141,7 @@ static void handle_file(struct merge_options *o,\n \tconst char *cur_branch, *other_branch;\n \tstruct diff_filespec other;\n \tstruct diff_filespec *add;\n+\tint ret;\n \n \tif (stage == 2) {\n \t\tdst_entry = ci->dst_entry1;\n@@ -1150,7 +1156,8 @@ static void handle_file(struct merge_options *o,\n \tadd = filespec_from_entry(&other, dst_entry, stage ^ 1);\n \tif (add) {\n \t\tchar *add_name = unique_path(o, rename->path, other_branch);\n-\t\tupdate_file(o, 0, &add->oid, add->mode, add_name);\n+\t\tif (update_file(o, 0, &add->oid, add->mode, add_name))\n+\t\t\treturn -1;\n \n \t\tremove_file(o, 0, rename->path, 0);\n \t\tdst_name = unique_path(o, rename->path, cur_branch);\n@@ -1161,17 +1168,20 @@ static void handle_file(struct merge_options *o,\n \t\t\t       rename->path, other_branch, dst_name);\n \t\t}\n \t}\n-\tupdate_file(o, 0, &rename->oid, rename->mode, dst_name);\n-\tif (stage == 2)\n-\t\tupdate_stages(rename->path, NULL, rename, add);\n+\tif ((ret = update_file(o, 0, &rename->oid, rename->mode, dst_name)))\n+\t\t; /* fall through, do allow dst_name to be released */\n+\telse if (stage == 2)\n+\t\tret = update_stages(rename->path, NULL, rename, add);\n \telse\n-\t\tupdate_stages(rename->path, NULL, add, rename);\n+\t\tret = update_stages(rename->path, NULL, add, rename);\n \n \tif (dst_name != rename->path)\n \t\tfree(dst_name);\n+\n+\treturn ret;\n }\n \n-static void conflict_rename_rename_1to2(struct merge_options *o,\n+static int conflict_rename_rename_1to2(struct merge_options *o,\n \t\t\t\t\tstruct rename_conflict_info *ci)\n {\n \t/* One file was renamed in both branches, but to different names. */\n@@ -1194,14 +1204,16 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n \t\t\t\t &a->oid, a->mode,\n \t\t\t\t &b->oid, b->mode,\n \t\t\t\t ci->branch1, ci->branch2, &mfi))\n-\t\t\treturn;\n+\t\t\treturn -1;\n+\n \t\t/*\n \t\t * FIXME: For rename/add-source conflicts (if we could detect\n \t\t * such), this is wrong.  We should instead find a unique\n \t\t * pathname and then either rename the add-source file to that\n \t\t * unique path, or use that unique path instead of src here.\n \t\t */\n-\t\tupdate_file(o, 0, &mfi.oid, mfi.mode, one->path);\n+\t\tif (update_file(o, 0, &mfi.oid, mfi.mode, one->path))\n+\t\t\treturn -1;\n \n \t\t/*\n \t\t * Above, we put the merged content at the merge-base's\n@@ -1212,22 +1224,26 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n \t\t * resolving the conflict at that path in its favor.\n \t\t */\n \t\tadd = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);\n-\t\tif (add)\n-\t\t\tupdate_file(o, 0, &add->oid, add->mode, a->path);\n+\t\tif (add) {\n+\t\t\tif (update_file(o, 0, &add->oid, add->mode, a->path))\n+\t\t\t\treturn -1;\n+\t\t}\n \t\telse\n \t\t\tremove_file_from_cache(a->path);\n \t\tadd = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);\n-\t\tif (add)\n-\t\t\tupdate_file(o, 0, &add->oid, add->mode, b->path);\n+\t\tif (add) {\n+\t\t\tif (update_file(o, 0, &add->oid, add->mode, b->path))\n+\t\t\t\treturn -1;\n+\t\t}\n \t\telse\n \t\t\tremove_file_from_cache(b->path);\n-\t} else {\n-\t\thandle_file(o, a, 2, ci);\n-\t\thandle_file(o, b, 3, ci);\n-\t}\n+\t} else if (handle_file(o, a, 2, ci) || handle_file(o, b, 3, ci))\n+\t\treturn -1;\n+\n+\treturn 0;\n }\n \n-static void conflict_rename_rename_2to1(struct merge_options *o,\n+static int conflict_rename_rename_2to1(struct merge_options *o,\n \t\t\t\t\tstruct rename_conflict_info *ci)\n {\n \t/* Two files, a & b, were renamed to the same thing, c. */\n@@ -1238,6 +1254,7 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \tchar *path = c1->path; /* == c2->path */\n \tstruct merge_file_info mfi_c1;\n \tstruct merge_file_info mfi_c2;\n+\tint ret;\n \n \toutput(o, 1, _(\"CONFLICT (rename/rename): \"\n \t       \"Rename %s->%s in %s. \"\n@@ -1254,7 +1271,7 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \t    merge_file_special_markers(o, b, &ci->ren2_other, c2,\n \t\t\t\t       o->branch1, ci->ren2_other.path,\n \t\t\t\t       o->branch2, c2->path, &mfi_c2))\n-\t\treturn;\n+\t\treturn -1;\n \n \tif (o->call_depth) {\n \t\t/*\n@@ -1265,19 +1282,25 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \t\t * again later for the non-recursive merge.\n \t\t */\n \t\tremove_file(o, 0, path, 0);\n-\t\tupdate_file(o, 0, &mfi_c1.oid, mfi_c1.mode, a->path);\n-\t\tupdate_file(o, 0, &mfi_c2.oid, mfi_c2.mode, b->path);\n+\t\tret = update_file(o, 0, &mfi_c1.oid, mfi_c1.mode, a->path);\n+\t\tif (!ret)\n+\t\t\tret = update_file(o, 0, &mfi_c2.oid, mfi_c2.mode,\n+\t\t\t\t\t  b->path);\n \t} else {\n \t\tchar *new_path1 = unique_path(o, path, ci->branch1);\n \t\tchar *new_path2 = unique_path(o, path, ci->branch2);\n \t\toutput(o, 1, _(\"Renaming %s to %s and %s to %s instead\"),\n \t\t       a->path, new_path1, b->path, new_path2);\n \t\tremove_file(o, 0, path, 0);\n-\t\tupdate_file(o, 0, &mfi_c1.oid, mfi_c1.mode, new_path1);\n-\t\tupdate_file(o, 0, &mfi_c2.oid, mfi_c2.mode, new_path2);\n+\t\tret = update_file(o, 0, &mfi_c1.oid, mfi_c1.mode, new_path1);\n+\t\tif (!ret)\n+\t\t\tret = update_file(o, 0, &mfi_c2.oid, mfi_c2.mode,\n+\t\t\t\t\t  new_path2);\n \t\tfree(new_path2);\n \t\tfree(new_path1);\n \t}\n+\n+\treturn ret;\n }\n \n static int process_renames(struct merge_options *o,\n@@ -1462,12 +1485,13 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t * update_file_flags() instead of\n \t\t\t\t * update_file().\n \t\t\t\t */\n-\t\t\t\tupdate_file_flags(o,\n-\t\t\t\t\t\t  &ren1->pair->two->oid,\n-\t\t\t\t\t\t  ren1->pair->two->mode,\n-\t\t\t\t\t\t  ren1_dst,\n-\t\t\t\t\t\t  1, /* update_cache */\n-\t\t\t\t\t\t  0  /* update_wd    */);\n+\t\t\t\tif (update_file_flags(o,\n+\t\t\t\t\t\t      &ren1->pair->two->oid,\n+\t\t\t\t\t\t      ren1->pair->two->mode,\n+\t\t\t\t\t\t      ren1_dst,\n+\t\t\t\t\t\t      1, /* update_cache */\n+\t\t\t\t\t\t      0  /* update_wd    */))\n+\t\t\t\t\tclean_merge = -1;\n \t\t\t} else if (!oid_eq(&dst_other.oid, &null_oid)) {\n \t\t\t\tclean_merge = 0;\n \t\t\t\ttry_merge = 1;\n@@ -1482,22 +1506,28 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t\t\t\t   ren1->pair->two->mode,\n \t\t\t\t\t\t\t   &dst_other.oid,\n \t\t\t\t\t\t\t   dst_other.mode,\n-\t\t\t\t\t\t\t   branch1, branch2, &mfi))\n-\t\t\t\t\t\treturn -1;\n+\t\t\t\t\t\t\t   branch1, branch2, &mfi)) {\n+\t\t\t\t\t\tclean_merge = -1;\n+\t\t\t\t\t\tgoto cleanup_and_return;\n+\t\t\t\t\t}\n \t\t\t\t\toutput(o, 1, _(\"Adding merged %s\"), ren1_dst);\n-\t\t\t\t\tupdate_file(o, 0, &mfi.oid,\n-\t\t\t\t\t\t    mfi.mode, ren1_dst);\n+\t\t\t\t\tif (update_file(o, 0, &mfi.oid,\n+\t\t\t\t\t\t\tmfi.mode, ren1_dst))\n+\t\t\t\t\t\tclean_merge = -1;\n \t\t\t\t\ttry_merge = 0;\n \t\t\t\t} else {\n \t\t\t\t\tchar *new_path = unique_path(o, ren1_dst, branch2);\n \t\t\t\t\toutput(o, 1, _(\"Adding as %s instead\"), new_path);\n-\t\t\t\t\tupdate_file(o, 0, &dst_other.oid,\n-\t\t\t\t\t\t    dst_other.mode, new_path);\n+\t\t\t\t\tif (update_file(o, 0, &dst_other.oid,\n+\t\t\t\t\t\t\tdst_other.mode, new_path))\n+\t\t\t\t\t\tclean_merge = -1;\n \t\t\t\t\tfree(new_path);\n \t\t\t\t}\n \t\t\t} else\n \t\t\t\ttry_merge = 1;\n \n+\t\t\tif (clean_merge < 0)\n+\t\t\t\tgoto cleanup_and_return;\n \t\t\tif (try_merge) {\n \t\t\t\tstruct diff_filespec *one, *a, *b;\n \t\t\t\tsrc_other.path = (char *)ren1_src;\n@@ -1524,6 +1554,7 @@ static int process_renames(struct merge_options *o,\n \t\t\t}\n \t\t}\n \t}\n+cleanup_and_return:\n \tstring_list_clear(&a_by_dst, 0);\n \tstring_list_clear(&b_by_dst, 0);\n \n@@ -1586,18 +1617,18 @@ static int blob_unchanged(const struct object_id *o_oid,\n \treturn ret;\n }\n \n-static void handle_modify_delete(struct merge_options *o,\n+static int handle_modify_delete(struct merge_options *o,\n \t\t\t\t const char *path,\n \t\t\t\t struct object_id *o_oid, int o_mode,\n \t\t\t\t struct object_id *a_oid, int a_mode,\n \t\t\t\t struct object_id *b_oid, int b_mode)\n {\n-\thandle_change_delete(o,\n-\t\t\t     path,\n-\t\t\t     o_oid, o_mode,\n-\t\t\t     a_oid, a_mode,\n-\t\t\t     b_oid, b_mode,\n-\t\t\t     _(\"modify\"), _(\"modified\"));\n+\treturn handle_change_delete(o,\n+\t\t\t\t    path,\n+\t\t\t\t    o_oid, o_mode,\n+\t\t\t\t    a_oid, a_mode,\n+\t\t\t\t    b_oid, b_mode,\n+\t\t\t\t    _(\"modify\"), _(\"modified\"));\n }\n \n static int merge_content(struct merge_options *o,\n@@ -1671,7 +1702,8 @@ static int merge_content(struct merge_options *o,\n \t\toutput(o, 1, _(\"CONFLICT (%s): Merge conflict in %s\"),\n \t\t\t\treason, path);\n \t\tif (rename_conflict_info && !df_conflict_remains)\n-\t\t\tupdate_stages(path, &one, &a, &b);\n+\t\t\tif (update_stages(path, &one, &a, &b))\n+\t\t\t\treturn -1;\n \t}\n \n \tif (df_conflict_remains) {\n@@ -1679,30 +1711,33 @@ static int merge_content(struct merge_options *o,\n \t\tif (o->call_depth) {\n \t\t\tremove_file_from_cache(path);\n \t\t} else {\n-\t\t\tif (!mfi.clean)\n-\t\t\t\tupdate_stages(path, &one, &a, &b);\n-\t\t\telse {\n+\t\t\tif (!mfi.clean) {\n+\t\t\t\tif (update_stages(path, &one, &a, &b))\n+\t\t\t\t\treturn -1;\n+\t\t\t} else {\n \t\t\t\tint file_from_stage2 = was_tracked(path);\n \t\t\t\tstruct diff_filespec merged;\n \t\t\t\toidcpy(&merged.oid, &mfi.oid);\n \t\t\t\tmerged.mode = mfi.mode;\n \n-\t\t\t\tupdate_stages(path, NULL,\n-\t\t\t\t\t      file_from_stage2 ? &merged : NULL,\n-\t\t\t\t\t      file_from_stage2 ? NULL : &merged);\n+\t\t\t\tif (update_stages(path, NULL,\n+\t\t\t\t\t\t  file_from_stage2 ? &merged : NULL,\n+\t\t\t\t\t\t  file_from_stage2 ? NULL : &merged))\n+\t\t\t\t\treturn -1;\n \t\t\t}\n \n \t\t}\n \t\tnew_path = unique_path(o, path, rename_conflict_info->branch1);\n \t\toutput(o, 1, _(\"Adding as %s instead\"), new_path);\n-\t\tupdate_file(o, 0, &mfi.oid, mfi.mode, new_path);\n+\t\tif (update_file(o, 0, &mfi.oid, mfi.mode, new_path)) {\n+\t\t\tfree(new_path);\n+\t\t\treturn -1;\n+\t\t}\n \t\tfree(new_path);\n \t\tmfi.clean = 0;\n-\t} else {\n-\t\tupdate_file(o, mfi.clean, &mfi.oid, mfi.mode, path);\n-\t}\n+\t} else if (update_file(o, mfi.clean, &mfi.oid, mfi.mode, path))\n+\t\treturn -1;\n \treturn mfi.clean;\n-\n }\n \n /* Per entry merge function */\n@@ -1730,17 +1765,21 @@ static int process_entry(struct merge_options *o,\n \t\t\tbreak;\n \t\tcase RENAME_DELETE:\n \t\t\tclean_merge = 0;\n-\t\t\tconflict_rename_delete(o, conflict_info->pair1,\n-\t\t\t\t\t       conflict_info->branch1,\n-\t\t\t\t\t       conflict_info->branch2);\n+\t\t\tif (conflict_rename_delete(o,\n+\t\t\t\t\t\t   conflict_info->pair1,\n+\t\t\t\t\t\t   conflict_info->branch1,\n+\t\t\t\t\t\t   conflict_info->branch2))\n+\t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_ONE_FILE_TO_TWO:\n \t\t\tclean_merge = 0;\n-\t\t\tconflict_rename_rename_1to2(o, conflict_info);\n+\t\t\tif (conflict_rename_rename_1to2(o, conflict_info))\n+\t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_TWO_FILES_TO_ONE:\n \t\t\tclean_merge = 0;\n-\t\t\tconflict_rename_rename_2to1(o, conflict_info);\n+\t\t\tif (conflict_rename_rename_2to1(o, conflict_info))\n+\t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tdefault:\n \t\t\tentry->processed = 0;\n@@ -1760,8 +1799,9 @@ static int process_entry(struct merge_options *o,\n \t\t} else {\n \t\t\t/* Modify/delete; deleted side may have put a directory in the way */\n \t\t\tclean_merge = 0;\n-\t\t\thandle_modify_delete(o, path, o_oid, o_mode,\n-\t\t\t\t\t     a_oid, a_mode, b_oid, b_mode);\n+\t\t\tif (handle_modify_delete(o, path, o_oid, o_mode,\n+\t\t\t\t\t\t a_oid, a_mode, b_oid, b_mode))\n+\t\t\t\tclean_merge = -1;\n \t\t}\n \t} else if ((!o_oid && a_oid && !b_oid) ||\n \t\t   (!o_oid && !a_oid && b_oid)) {\n@@ -1793,14 +1833,16 @@ static int process_entry(struct merge_options *o,\n \t\t\toutput(o, 1, _(\"CONFLICT (%s): There is a directory with name %s in %s. \"\n \t\t\t       \"Adding %s as %s\"),\n \t\t\t       conf, path, other_branch, path, new_path);\n-\t\t\tupdate_file(o, 0, oid, mode, new_path);\n-\t\t\tif (o->call_depth)\n+\t\t\tif (update_file(o, 0, oid, mode, new_path))\n+\t\t\t\tclean_merge = -1;\n+\t\t\telse if (o->call_depth)\n \t\t\t\tremove_file_from_cache(path);\n \t\t\tfree(new_path);\n \t\t} else {\n \t\t\toutput(o, 2, _(\"Adding %s\"), path);\n \t\t\t/* do not overwrite file if already present */\n-\t\t\tupdate_file_flags(o, oid, mode, path, 1, !a_oid);\n+\t\t\tif (update_file_flags(o, oid, mode, path, 1, !a_oid))\n+\t\t\t\tclean_merge = -1;\n \t\t}\n \t} else if (a_oid && b_oid) {\n \t\t/* Case C: Added in both (check for same permissions) and */\n@@ -1863,12 +1905,18 @@ int merge_trees(struct merge_options *o,\n \t\tre_head  = get_renames(o, head, common, head, merge, entries);\n \t\tre_merge = get_renames(o, merge, common, head, merge, entries);\n \t\tclean = process_renames(o, re_head, re_merge);\n+\t\tif (clean < 0)\n+\t\t\treturn clean;\n \t\tfor (i = entries->nr-1; 0 <= i; i--) {\n \t\t\tconst char *path = entries->items[i].string;\n \t\t\tstruct stage_data *e = entries->items[i].util;\n-\t\t\tif (!e->processed\n-\t\t\t\t&& !process_entry(o, path, e))\n-\t\t\t\tclean = 0;\n+\t\t\tif (!e->processed) {\n+\t\t\t\tint ret = process_entry(o, path, e);\n+\t\t\t\tif (!ret)\n+\t\t\t\t\tclean = 0;\n+\t\t\t\telse if (ret < 0)\n+\t\t\t\t\treturn ret;\n+\t\t\t}\n \t\t}\n \t\tfor (i = 0; i < entries->nr; i++) {\n \t\t\tstruct stage_data *e = entries->items[i].util;\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292227","messageId":"2729997e6e07e3548ab6aae34ffca5f6828ff6cf.1469547160.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469547160.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 10/16] merge-recursive: switch to returning errors instead of dying","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-26T16:06:26Z","receivedAt":"2016-07-26T16:06:55Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The recursive merge machinery is supposed to be a library function, i.e.\nit should return an error when it fails. Originally the functions were\npart of the builtin \"merge-recursive\", though, where it was simpler to\ncall die() and be done with error handling.\n\nThe existing callers were already prepared to detect negative return\nvalues to indicate errors and to behave as previously: exit with code 128\n(which is the same thing that die() does, after printing the message).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 62 +++++++++++++++++++++++++++++++------------------------\n 1 file changed, 35 insertions(+), 27 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 6beb1e4..bc59815 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -275,8 +275,10 @@ struct tree *write_tree_from_memory(struct merge_options *o)\n \t\tactive_cache_tree = cache_tree();\n \n \tif (!cache_tree_fully_valid(active_cache_tree) &&\n-\t    cache_tree_update(&the_index, 0) < 0)\n-\t\tdie(_(\"error building trees\"));\n+\t    cache_tree_update(&the_index, 0) < 0) {\n+\t\terror(_(\"error building trees\"));\n+\t\treturn NULL;\n+\t}\n \n \tresult = lookup_tree(active_cache_tree->sha1);\n \n@@ -716,12 +718,10 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \t/* Make sure leading directories are created */\n \tstatus = safe_create_leading_directories_const(path);\n \tif (status) {\n-\t\tif (status == SCLD_EXISTS) {\n+\t\tif (status == SCLD_EXISTS)\n \t\t\t/* something else exists */\n-\t\t\terror(msg, path, _(\": perhaps a D/F conflict?\"));\n-\t\t\treturn -1;\n-\t\t}\n-\t\tdie(msg, path, \"\");\n+\t\t\treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n+\t\treturn error(msg, path, \"\");\n \t}\n \n \t/*\n@@ -749,6 +749,8 @@ static int update_file_flags(struct merge_options *o,\n \t\t\t     int update_cache,\n \t\t\t     int update_wd)\n {\n+\tint ret = 0;\n+\n \tif (o->call_depth)\n \t\tupdate_wd = 0;\n \n@@ -769,9 +771,11 @@ static int update_file_flags(struct merge_options *o,\n \n \t\tbuf = read_sha1_file(oid->hash, &type, &size);\n \t\tif (!buf)\n-\t\t\tdie(_(\"cannot read object %s '%s'\"), oid_to_hex(oid), path);\n-\t\tif (type != OBJ_BLOB)\n-\t\t\tdie(_(\"blob expected for %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\treturn error(_(\"cannot read object %s '%s'\"), oid_to_hex(oid), path);\n+\t\tif (type != OBJ_BLOB) {\n+\t\t\tret = error(_(\"blob expected for %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\tgoto free_buf;\n+\t\t}\n \t\tif (S_ISREG(mode)) {\n \t\t\tstruct strbuf strbuf = STRBUF_INIT;\n \t\t\tif (convert_to_working_tree(path, buf, size, &strbuf)) {\n@@ -792,8 +796,11 @@ static int update_file_flags(struct merge_options *o,\n \t\t\telse\n \t\t\t\tmode = 0666;\n \t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n-\t\t\tif (fd < 0)\n-\t\t\t\tdie_errno(_(\"failed to open '%s'\"), path);\n+\t\t\tif (fd < 0) {\n+\t\t\t\tret = error_errno(_(\"failed to open '%s'\"),\n+\t\t\t\t\t\t  path);\n+\t\t\t\tgoto free_buf;\n+\t\t\t}\n \t\t\twrite_in_full(fd, buf, size);\n \t\t\tclose(fd);\n \t\t} else if (S_ISLNK(mode)) {\n@@ -801,18 +808,18 @@ static int update_file_flags(struct merge_options *o,\n \t\t\tsafe_create_leading_directories_const(path);\n \t\t\tunlink(path);\n \t\t\tif (symlink(lnk, path))\n-\t\t\t\tdie_errno(_(\"failed to symlink '%s'\"), path);\n+\t\t\t\tret = error_errno(_(\"failed to symlink '%s'\"), path);\n \t\t\tfree(lnk);\n \t\t} else\n-\t\t\tdie(_(\"do not know what to do with %06o %s '%s'\"),\n-\t\t\t    mode, oid_to_hex(oid), path);\n+\t\t\tret = error(_(\"do not know what to do with %06o %s '%s'\"),\n+\t\t\t\t    mode, oid_to_hex(oid), path);\n  free_buf:\n \t\tfree(buf);\n \t}\n  update_index:\n-\tif (update_cache)\n+\tif (!ret && update_cache)\n \t\tadd_cacheinfo(mode, oid, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n-\treturn 0;\n+\treturn ret;\n }\n \n static int update_file(struct merge_options *o,\n@@ -938,20 +945,22 @@ static int merge_file_1(struct merge_options *o,\n \t\t\toidcpy(&result->oid, &a->oid);\n \t\telse if (S_ISREG(a->mode)) {\n \t\t\tmmbuffer_t result_buf;\n-\t\t\tint merge_status;\n+\t\t\tint ret = 0, merge_status;\n \n \t\t\tmerge_status = merge_3way(o, &result_buf, one, a, b,\n \t\t\t\t\t\t  branch1, branch2);\n \n \t\t\tif ((merge_status < 0) || !result_buf.ptr)\n-\t\t\t\tdie(_(\"Failed to execute internal merge\"));\n+\t\t\t\tret = error(_(\"Failed to execute internal merge\"));\n \n-\t\t\tif (write_sha1_file(result_buf.ptr, result_buf.size,\n-\t\t\t\t\t    blob_type, result->oid.hash))\n-\t\t\t\tdie(_(\"Unable to add %s to database\"),\n-\t\t\t\t    a->path);\n+\t\t\tif (!ret && write_sha1_file(result_buf.ptr, result_buf.size,\n+\t\t\t\t\t\t    blob_type, result->oid.hash))\n+\t\t\t\tret = error(_(\"Unable to add %s to database\"),\n+\t\t\t\t\t    a->path);\n \n \t\t\tfree(result_buf.ptr);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n \t\t\tresult->clean = (merge_status == 0);\n \t\t} else if (S_ISGITLINK(a->mode)) {\n \t\t\tresult->clean = merge_submodule(result->oid.hash,\n@@ -1885,11 +1894,10 @@ int merge_trees(struct merge_options *o,\n \n \tif (code != 0) {\n \t\tif (show(o, 4) || o->call_depth)\n-\t\t\tdie(_(\"merging of trees %s and %s failed\"),\n+\t\t\terror(_(\"merging of trees %s and %s failed\"),\n \t\t\t    oid_to_hex(&head->object.oid),\n \t\t\t    oid_to_hex(&merge->object.oid));\n-\t\telse\n-\t\t\texit(128);\n+\t\treturn -1;\n \t}\n \n \tif (unmerged_cache()) {\n@@ -2021,7 +2029,7 @@ int merge_recursive(struct merge_options *o,\n \t\to->call_depth--;\n \n \t\tif (!merged_common_ancestors)\n-\t\t\tdie(_(\"merge returned no commit\"));\n+\t\t\treturn error(_(\"merge returned no commit\"));\n \t}\n \n \tdiscard_cache();\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292228","messageId":"882273dd0067de30fe4b672050457708d56f317e.1469547160.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469547160.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 13/16] merge-recursive: write the commit title in one go","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-26T16:06:38Z","receivedAt":"2016-07-26T16:07:04Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"In 66a155b (Enable output buffering in merge-recursive., 2007-01-14), we\nchanged the code such that it prints the output in one go, to avoid\ninterfering with the progress output.\n\nLet's make sure that the same holds true when outputting the commit\ntitle: previously, we used several printf() statements to stdout and\nspeculated that stdout's buffer is large enough to hold the entire\ncommit title.\n\nApart from making that speculation unnecessary, we change the code to\nadd the message to the output buffer before flushing for another reason:\nthe next commit will introduce a new level of output buffering, where\nthe caller can request the output not to be flushed, but to be retained\nfor further processing.\n\nThis latter feature will be needed when teaching the sequencer to do\nrebase -i's brunt work: it wants to control the output of the\ncherry-picks (i.e. recursive merges).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 17 +++++++++--------\n 1 file changed, 9 insertions(+), 8 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 71a0aa0..723b8d0 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -191,25 +191,26 @@ static void output(struct merge_options *o, int v, const char *fmt, ...)\n \n static void output_commit_title(struct merge_options *o, struct commit *commit)\n {\n-\tint i;\n-\tflush_output(o);\n-\tfor (i = o->call_depth; i--;)\n-\t\tfputs(\"  \", stdout);\n+\tstrbuf_addchars(&o->obuf, ' ', o->call_depth * 2);\n \tif (commit->util)\n-\t\tprintf(\"virtual %s\\n\", merge_remote_util(commit)->name);\n+\t\tstrbuf_addf(&o->obuf, \"virtual %s\\n\",\n+\t\t\tmerge_remote_util(commit)->name);\n \telse {\n-\t\tprintf(\"%s \", find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV));\n+\t\tstrbuf_addf(&o->obuf, \"%s \",\n+\t\t\tfind_unique_abbrev(commit->object.oid.hash,\n+\t\t\t\tDEFAULT_ABBREV));\n \t\tif (parse_commit(commit) != 0)\n-\t\t\tprintf(_(\"(bad commit)\\n\"));\n+\t\t\tstrbuf_addf(&o->obuf, _(\"(bad commit)\\n\"));\n \t\telse {\n \t\t\tconst char *title;\n \t\t\tconst char *msg = get_commit_buffer(commit, NULL);\n \t\t\tint len = find_commit_subject(msg, &title);\n \t\t\tif (len)\n-\t\t\t\tprintf(\"%.*s\\n\", len, title);\n+\t\t\t\tstrbuf_addf(&o->obuf, \"%.*s\\n\", len, title);\n \t\t\tunuse_commit_buffer(commit, msg);\n \t\t}\n \t}\n+\tflush_output(o);\n }\n \n static int add_cacheinfo(struct merge_options *o,\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292229","messageId":"9068aaf45f3956da3f02f265b0a77c03742cd6cd.1469547160.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469547160.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 11/16] am -3: use merge_recursive() directly again","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-26T16:06:30Z","receivedAt":"2016-07-26T16:07:07Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Last October, we had to change this code to run `git merge-recursive`\nin a child process: git-am wants to print some helpful advice when the\nmerge failed, but the code in question was not prepared to return, it\ndie()d instead.\n\nWe are finally at a point when the code *is* prepared to return errors,\nand can avoid the child process again.\n\nThis reverts commit c63d4b2 (am -3: do not let failed merge from\ncompleting the error codepath, 2015-10-09), with the necessary changes\nto adjust for the fact that Git's source code changed in the meantime\n(such as: using OIDs instead of hashes in the recursive merge, and a\nremoved gender bias).\n\nNote: the code now calls merge_recursive_generic() again. Unlike\nmerge_trees() and merge_recursive(), this function returns 0 upon success,\nas most of Git's functions. Therefore, the error value -1 naturally is\nhandled correctly, and we do not have to take care of it specifically.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/am.c | 62 +++++++++++++++++++++---------------------------------------\n 1 file changed, 22 insertions(+), 40 deletions(-)\n\ndiff --git a/builtin/am.c b/builtin/am.c\nindex b77bf11..cfb79ea 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -1579,47 +1579,18 @@ static int build_fake_ancestor(const struct am_state *state, const char *index_f\n }\n \n /**\n- * Do the three-way merge using fake ancestor, their tree constructed\n- * from the fake ancestor and the postimage of the patch, and our\n- * state.\n- */\n-static int run_fallback_merge_recursive(const struct am_state *state,\n-\t\t\t\t\tunsigned char *orig_tree,\n-\t\t\t\t\tunsigned char *our_tree,\n-\t\t\t\t\tunsigned char *their_tree)\n-{\n-\tstruct child_process cp = CHILD_PROCESS_INIT;\n-\tint status;\n-\n-\tcp.git_cmd = 1;\n-\n-\targv_array_pushf(&cp.env_array, \"GITHEAD_%s=%.*s\",\n-\t\t\t sha1_to_hex(their_tree), linelen(state->msg), state->msg);\n-\tif (state->quiet)\n-\t\targv_array_push(&cp.env_array, \"GIT_MERGE_VERBOSITY=0\");\n-\n-\targv_array_push(&cp.args, \"merge-recursive\");\n-\targv_array_push(&cp.args, sha1_to_hex(orig_tree));\n-\targv_array_push(&cp.args, \"--\");\n-\targv_array_push(&cp.args, sha1_to_hex(our_tree));\n-\targv_array_push(&cp.args, sha1_to_hex(their_tree));\n-\n-\tstatus = run_command(&cp) ? (-1) : 0;\n-\tdiscard_cache();\n-\tread_cache();\n-\treturn status;\n-}\n-\n-/**\n  * Attempt a threeway merge, using index_path as the temporary index.\n  */\n static int fall_back_threeway(const struct am_state *state, const char *index_path)\n {\n-\tunsigned char orig_tree[GIT_SHA1_RAWSZ], their_tree[GIT_SHA1_RAWSZ],\n-\t\t      our_tree[GIT_SHA1_RAWSZ];\n+\tstruct object_id orig_tree, their_tree, our_tree;\n+\tconst struct object_id *bases[1] = { &orig_tree };\n+\tstruct merge_options o;\n+\tstruct commit *result;\n+\tchar *their_tree_name;\n \n-\tif (get_sha1(\"HEAD\", our_tree) < 0)\n-\t\thashcpy(our_tree, EMPTY_TREE_SHA1_BIN);\n+\tif (get_oid(\"HEAD\", &our_tree) < 0)\n+\t\thashcpy(our_tree.hash, EMPTY_TREE_SHA1_BIN);\n \n \tif (build_fake_ancestor(state, index_path))\n \t\treturn error(\"could not build fake ancestor\");\n@@ -1627,7 +1598,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \tdiscard_cache();\n \tread_cache_from(index_path);\n \n-\tif (write_index_as_tree(orig_tree, &the_index, index_path, 0, NULL))\n+\tif (write_index_as_tree(orig_tree.hash, &the_index, index_path, 0, NULL))\n \t\treturn error(_(\"Repository lacks necessary blobs to fall back on 3-way merge.\"));\n \n \tsay(state, stdout, _(\"Using index info to reconstruct a base tree...\"));\n@@ -1643,7 +1614,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \t\tinit_revisions(&rev_info, NULL);\n \t\trev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;\n \t\tdiff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix);\n-\t\tadd_pending_sha1(&rev_info, \"HEAD\", our_tree, 0);\n+\t\tadd_pending_sha1(&rev_info, \"HEAD\", our_tree.hash, 0);\n \t\tdiff_setup_done(&rev_info.diffopt);\n \t\trun_diff_index(&rev_info, 1);\n \t}\n@@ -1652,7 +1623,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \t\treturn error(_(\"Did you hand edit your patch?\\n\"\n \t\t\t\t\"It does not apply to blobs recorded in its index.\"));\n \n-\tif (write_index_as_tree(their_tree, &the_index, index_path, 0, NULL))\n+\tif (write_index_as_tree(their_tree.hash, &the_index, index_path, 0, NULL))\n \t\treturn error(\"could not write tree\");\n \n \tsay(state, stdout, _(\"Falling back to patching base and 3-way merge...\"));\n@@ -1668,11 +1639,22 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \t * changes.\n \t */\n \n-\tif (run_fallback_merge_recursive(state, orig_tree, our_tree, their_tree)) {\n+\tinit_merge_options(&o);\n+\n+\to.branch1 = \"HEAD\";\n+\ttheir_tree_name = xstrfmt(\"%.*s\", linelen(state->msg), state->msg);\n+\to.branch2 = their_tree_name;\n+\n+\tif (state->quiet)\n+\t\to.verbosity = 0;\n+\n+\tif (merge_recursive_generic(&o, &our_tree, &their_tree, 1, bases, &result)) {\n \t\trerere(state->allow_rerere_autoupdate);\n+\t\tfree(their_tree_name);\n \t\treturn error(_(\"Failed to merge in the changes.\"));\n \t}\n \n+\tfree(their_tree_name);\n \treturn 0;\n }\n \n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292230","messageId":"349bdfacfffa11d06241655c9e0b62506e58758b.1469547160.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469547160.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 12/16] merge-recursive: flush output buffer before printing error messages","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-26T16:06:34Z","receivedAt":"2016-07-26T16:07:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The data structure passed to the recursive merge machinery has a feature\nwhere the caller can ask for the output to be buffered into a strbuf, by\nsetting the field 'buffer_output'.\n\nPreviously, we simply swallowed the buffered output when showing error\nmessages. With this patch, we show the output first, and only then print\nthe error message.\n\nCurrently, the only user of that buffering is merge_recursive() itself,\nto avoid the progress output to interfere.\n\nIn the next patches, we will introduce a new buffer_output mode that\nforces merge_recursive() to retain the output buffer for further\nprocessing by the caller. If the caller asked for that, we will then\nalso write the error messages into the output buffer. This is necessary\nto give the caller more control not only how to react in case of errors\nbut also control how/if to display the error messages.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 116 ++++++++++++++++++++++++++++++++----------------------\n 1 file changed, 68 insertions(+), 48 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex bc59815..71a0aa0 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -23,6 +23,28 @@\n #include \"dir.h\"\n #include \"submodule.h\"\n \n+static void flush_output(struct merge_options *o)\n+{\n+\tif (o->obuf.len) {\n+\t\tfputs(o->obuf.buf, stdout);\n+\t\tstrbuf_reset(&o->obuf);\n+\t}\n+}\n+\n+static int err(struct merge_options *o, const char *err, ...)\n+{\n+\tva_list params;\n+\n+\tva_start(params, err);\n+\tflush_output(o);\n+\tstrbuf_vaddf(&o->obuf, err, params);\n+\terror(\"%s\", o->obuf.buf);\n+\tstrbuf_reset(&o->obuf);\n+\tva_end(params);\n+\n+\treturn -1;\n+}\n+\n static struct tree *shift_tree_object(struct tree *one, struct tree *two,\n \t\t\t\t      const char *subtree_shift)\n {\n@@ -148,14 +170,6 @@ static int show(struct merge_options *o, int v)\n \treturn (!o->call_depth && o->verbosity >= v) || o->verbosity >= 5;\n }\n \n-static void flush_output(struct merge_options *o)\n-{\n-\tif (o->obuf.len) {\n-\t\tfputs(o->obuf.buf, stdout);\n-\t\tstrbuf_reset(&o->obuf);\n-\t}\n-}\n-\n __attribute__((format (printf, 3, 4)))\n static void output(struct merge_options *o, int v, const char *fmt, ...)\n {\n@@ -198,7 +212,8 @@ static void output_commit_title(struct merge_options *o, struct commit *commit)\n \t}\n }\n \n-static int add_cacheinfo(unsigned int mode, const struct object_id *oid,\n+static int add_cacheinfo(struct merge_options *o,\n+\t\tunsigned int mode, const struct object_id *oid,\n \t\tconst char *path, int stage, int refresh, int options)\n {\n \tstruct cache_entry *ce;\n@@ -206,7 +221,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid,\n \n \tce = make_cache_entry(mode, oid ? oid->hash : null_sha1, path, stage, 0);\n \tif (!ce)\n-\t\treturn error(_(\"addinfo_cache failed for path '%s'\"), path);\n+\t\treturn err(o, _(\"addinfo_cache failed for path '%s'\"), path);\n \n \tret = add_cache_entry(ce, options);\n \tif (refresh) {\n@@ -276,7 +291,7 @@ struct tree *write_tree_from_memory(struct merge_options *o)\n \n \tif (!cache_tree_fully_valid(active_cache_tree) &&\n \t    cache_tree_update(&the_index, 0) < 0) {\n-\t\terror(_(\"error building trees\"));\n+\t\terr(o, _(\"error building trees\"));\n \t\treturn NULL;\n \t}\n \n@@ -544,7 +559,8 @@ static struct string_list *get_renames(struct merge_options *o,\n \treturn renames;\n }\n \n-static int update_stages(const char *path, const struct diff_filespec *o,\n+static int update_stages(struct merge_options *opt, const char *path,\n+\t\t\t const struct diff_filespec *o,\n \t\t\t const struct diff_filespec *a,\n \t\t\t const struct diff_filespec *b)\n {\n@@ -563,13 +579,13 @@ static int update_stages(const char *path, const struct diff_filespec *o,\n \t\tif (remove_file_from_cache(path))\n \t\t\treturn -1;\n \tif (o)\n-\t\tif (add_cacheinfo(o->mode, &o->oid, path, 1, 0, options))\n+\t\tif (add_cacheinfo(opt, o->mode, &o->oid, path, 1, 0, options))\n \t\t\treturn -1;\n \tif (a)\n-\t\tif (add_cacheinfo(a->mode, &a->oid, path, 2, 0, options))\n+\t\tif (add_cacheinfo(opt, a->mode, &a->oid, path, 2, 0, options))\n \t\t\treturn -1;\n \tif (b)\n-\t\tif (add_cacheinfo(b->mode, &b->oid, path, 3, 0, options))\n+\t\tif (add_cacheinfo(opt, b->mode, &b->oid, path, 3, 0, options))\n \t\t\treturn -1;\n \treturn 0;\n }\n@@ -720,8 +736,8 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \tif (status) {\n \t\tif (status == SCLD_EXISTS)\n \t\t\t/* something else exists */\n-\t\t\treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n-\t\treturn error(msg, path, \"\");\n+\t\t\treturn err(o, msg, path, _(\": perhaps a D/F conflict?\"));\n+\t\treturn err(o, msg, path, \"\");\n \t}\n \n \t/*\n@@ -729,7 +745,7 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \t * tracking it.\n \t */\n \tif (would_lose_untracked(path))\n-\t\treturn error(_(\"refusing to lose untracked file at '%s'\"),\n+\t\treturn err(o, _(\"refusing to lose untracked file at '%s'\"),\n \t\t\t     path);\n \n \t/* Successful unlink is good.. */\n@@ -739,7 +755,7 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \tif (errno == ENOENT)\n \t\treturn 0;\n \t/* .. but not some other error (who really cares what?) */\n-\treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n+\treturn err(o, msg, path, _(\": perhaps a D/F conflict?\"));\n }\n \n static int update_file_flags(struct merge_options *o,\n@@ -771,9 +787,9 @@ static int update_file_flags(struct merge_options *o,\n \n \t\tbuf = read_sha1_file(oid->hash, &type, &size);\n \t\tif (!buf)\n-\t\t\treturn error(_(\"cannot read object %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\treturn err(o, _(\"cannot read object %s '%s'\"), oid_to_hex(oid), path);\n \t\tif (type != OBJ_BLOB) {\n-\t\t\tret = error(_(\"blob expected for %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\tret = err(o, _(\"blob expected for %s '%s'\"), oid_to_hex(oid), path);\n \t\t\tgoto free_buf;\n \t\t}\n \t\tif (S_ISREG(mode)) {\n@@ -797,8 +813,8 @@ static int update_file_flags(struct merge_options *o,\n \t\t\t\tmode = 0666;\n \t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n \t\t\tif (fd < 0) {\n-\t\t\t\tret = error_errno(_(\"failed to open '%s'\"),\n-\t\t\t\t\t\t  path);\n+\t\t\t\tret = err(o, _(\"failed to open '%s': %s\"),\n+\t\t\t\t\t  path, strerror(errno));\n \t\t\t\tgoto free_buf;\n \t\t\t}\n \t\t\twrite_in_full(fd, buf, size);\n@@ -808,17 +824,19 @@ static int update_file_flags(struct merge_options *o,\n \t\t\tsafe_create_leading_directories_const(path);\n \t\t\tunlink(path);\n \t\t\tif (symlink(lnk, path))\n-\t\t\t\tret = error_errno(_(\"failed to symlink '%s'\"), path);\n+\t\t\t\tret = err(o, _(\"failed to symlink '%s': %s\"),\n+\t\t\t\t\tpath, strerror(errno));\n \t\t\tfree(lnk);\n \t\t} else\n-\t\t\tret = error(_(\"do not know what to do with %06o %s '%s'\"),\n-\t\t\t\t    mode, oid_to_hex(oid), path);\n+\t\t\tret = err(o,\n+\t\t\t\t  _(\"do not know what to do with %06o %s '%s'\"),\n+\t\t\t\t  mode, oid_to_hex(oid), path);\n  free_buf:\n \t\tfree(buf);\n \t}\n  update_index:\n \tif (!ret && update_cache)\n-\t\tadd_cacheinfo(mode, oid, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n+\t\tadd_cacheinfo(o, mode, oid, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n \treturn ret;\n }\n \n@@ -951,12 +969,12 @@ static int merge_file_1(struct merge_options *o,\n \t\t\t\t\t\t  branch1, branch2);\n \n \t\t\tif ((merge_status < 0) || !result_buf.ptr)\n-\t\t\t\tret = error(_(\"Failed to execute internal merge\"));\n+\t\t\t\tret = err(o, _(\"Failed to execute internal merge\"));\n \n \t\t\tif (!ret && write_sha1_file(result_buf.ptr, result_buf.size,\n \t\t\t\t\t\t    blob_type, result->oid.hash))\n-\t\t\t\tret = error(_(\"Unable to add %s to database\"),\n-\t\t\t\t\t    a->path);\n+\t\t\t\tret = err(o, _(\"Unable to add %s to database\"),\n+\t\t\t\t\t  a->path);\n \n \t\t\tfree(result_buf.ptr);\n \t\t\tif (ret)\n@@ -1122,7 +1140,7 @@ static int conflict_rename_delete(struct merge_options *o,\n \tif (o->call_depth)\n \t\treturn remove_file_from_cache(dest->path);\n \telse\n-\t\treturn update_stages(dest->path, NULL,\n+\t\treturn update_stages(o, dest->path, NULL,\n \t\t\t\t     rename_branch == o->branch1 ? dest : NULL,\n \t\t\t\t     rename_branch == o->branch1 ? NULL : dest);\n }\n@@ -1180,9 +1198,9 @@ static int handle_file(struct merge_options *o,\n \tif ((ret = update_file(o, 0, &rename->oid, rename->mode, dst_name)))\n \t\t; /* fall through, do allow dst_name to be released */\n \telse if (stage == 2)\n-\t\tret = update_stages(rename->path, NULL, rename, add);\n+\t\tret = update_stages(o, rename->path, NULL, rename, add);\n \telse\n-\t\tret = update_stages(rename->path, NULL, add, rename);\n+\t\tret = update_stages(o, rename->path, NULL, add, rename);\n \n \tif (dst_name != rename->path)\n \t\tfree(dst_name);\n@@ -1575,23 +1593,25 @@ static struct object_id *stage_oid(const struct object_id *oid, unsigned mode)\n \treturn (is_null_oid(oid) || mode == 0) ? NULL: (struct object_id *)oid;\n }\n \n-static int read_oid_strbuf(const struct object_id *oid, struct strbuf *dst)\n+static int read_oid_strbuf(struct merge_options *o,\n+\tconst struct object_id *oid, struct strbuf *dst)\n {\n \tvoid *buf;\n \tenum object_type type;\n \tunsigned long size;\n \tbuf = read_sha1_file(oid->hash, &type, &size);\n \tif (!buf)\n-\t\treturn error(_(\"cannot read object %s\"), oid_to_hex(oid));\n+\t\treturn err(o, _(\"cannot read object %s\"), oid_to_hex(oid));\n \tif (type != OBJ_BLOB) {\n \t\tfree(buf);\n-\t\treturn error(_(\"object %s is not a blob\"), oid_to_hex(oid));\n+\t\treturn err(o, _(\"object %s is not a blob\"), oid_to_hex(oid));\n \t}\n \tstrbuf_attach(dst, buf, size, size + 1);\n \treturn 0;\n }\n \n-static int blob_unchanged(const struct object_id *o_oid,\n+static int blob_unchanged(struct merge_options *opt,\n+\t\t\t  const struct object_id *o_oid,\n \t\t\t  unsigned o_mode,\n \t\t\t  const struct object_id *a_oid,\n \t\t\t  unsigned a_mode,\n@@ -1609,7 +1629,7 @@ static int blob_unchanged(const struct object_id *o_oid,\n \t\treturn 0;\n \n \tassert(o_oid && a_oid);\n-\tif (read_oid_strbuf(o_oid, &o) || read_oid_strbuf(a_oid, &a))\n+\tif (read_oid_strbuf(opt, o_oid, &o) || read_oid_strbuf(opt, a_oid, &a))\n \t\tgoto error_return;\n \t/*\n \t * Note: binary | is used so that both renormalizations are\n@@ -1698,7 +1718,7 @@ static int merge_content(struct merge_options *o,\n \t\t */\n \t\tpath_renamed_outside_HEAD = !path2 || !strcmp(path, path2);\n \t\tif (!path_renamed_outside_HEAD) {\n-\t\t\tadd_cacheinfo(mfi.mode, &mfi.oid, path,\n+\t\t\tadd_cacheinfo(o, mfi.mode, &mfi.oid, path,\n \t\t\t\t      0, (!o->call_depth), 0);\n \t\t\treturn mfi.clean;\n \t\t}\n@@ -1711,7 +1731,7 @@ static int merge_content(struct merge_options *o,\n \t\toutput(o, 1, _(\"CONFLICT (%s): Merge conflict in %s\"),\n \t\t\t\treason, path);\n \t\tif (rename_conflict_info && !df_conflict_remains)\n-\t\t\tif (update_stages(path, &one, &a, &b))\n+\t\t\tif (update_stages(o, path, &one, &a, &b))\n \t\t\t\treturn -1;\n \t}\n \n@@ -1721,7 +1741,7 @@ static int merge_content(struct merge_options *o,\n \t\t\tremove_file_from_cache(path);\n \t\t} else {\n \t\t\tif (!mfi.clean) {\n-\t\t\t\tif (update_stages(path, &one, &a, &b))\n+\t\t\t\tif (update_stages(o, path, &one, &a, &b))\n \t\t\t\t\treturn -1;\n \t\t\t} else {\n \t\t\t\tint file_from_stage2 = was_tracked(path);\n@@ -1729,7 +1749,7 @@ static int merge_content(struct merge_options *o,\n \t\t\t\toidcpy(&merged.oid, &mfi.oid);\n \t\t\t\tmerged.mode = mfi.mode;\n \n-\t\t\t\tif (update_stages(path, NULL,\n+\t\t\t\tif (update_stages(o, path, NULL,\n \t\t\t\t\t\t  file_from_stage2 ? &merged : NULL,\n \t\t\t\t\t\t  file_from_stage2 ? NULL : &merged))\n \t\t\t\t\treturn -1;\n@@ -1797,8 +1817,8 @@ static int process_entry(struct merge_options *o,\n \t} else if (o_oid && (!a_oid || !b_oid)) {\n \t\t/* Case A: Deleted in one */\n \t\tif ((!a_oid && !b_oid) ||\n-\t\t    (!b_oid && blob_unchanged(o_oid, o_mode, a_oid, a_mode, normalize, path)) ||\n-\t\t    (!a_oid && blob_unchanged(o_oid, o_mode, b_oid, b_mode, normalize, path))) {\n+\t\t    (!b_oid && blob_unchanged(o, o_oid, o_mode, a_oid, a_mode, normalize, path)) ||\n+\t\t    (!a_oid && blob_unchanged(o, o_oid, o_mode, b_oid, b_mode, normalize, path))) {\n \t\t\t/* Deleted in both or deleted in one and\n \t\t\t * unchanged in the other */\n \t\t\tif (a_oid)\n@@ -1894,7 +1914,7 @@ int merge_trees(struct merge_options *o,\n \n \tif (code != 0) {\n \t\tif (show(o, 4) || o->call_depth)\n-\t\t\terror(_(\"merging of trees %s and %s failed\"),\n+\t\t\terr(o, _(\"merging of trees %s and %s failed\"),\n \t\t\t    oid_to_hex(&head->object.oid),\n \t\t\t    oid_to_hex(&merge->object.oid));\n \t\treturn -1;\n@@ -2029,7 +2049,7 @@ int merge_recursive(struct merge_options *o,\n \t\to->call_depth--;\n \n \t\tif (!merged_common_ancestors)\n-\t\t\treturn error(_(\"merge returned no commit\"));\n+\t\t\treturn err(o, _(\"merge returned no commit\"));\n \t}\n \n \tdiscard_cache();\n@@ -2088,7 +2108,7 @@ int merge_recursive_generic(struct merge_options *o,\n \t\tfor (i = 0; i < num_base_list; ++i) {\n \t\t\tstruct commit *base;\n \t\t\tif (!(base = get_ref(base_list[i], oid_to_hex(base_list[i]))))\n-\t\t\t\treturn error(_(\"Could not parse object '%s'\"),\n+\t\t\t\treturn err(o, _(\"Could not parse object '%s'\"),\n \t\t\t\t\toid_to_hex(base_list[i]));\n \t\t\tcommit_list_insert(base, &ca);\n \t\t}\n@@ -2102,7 +2122,7 @@ int merge_recursive_generic(struct merge_options *o,\n \n \tif (active_cache_changed &&\n \t    write_locked_index(&the_index, lock, COMMIT_LOCK))\n-\t\treturn error(_(\"Unable to write index.\"));\n+\t\treturn err(o, _(\"Unable to write index.\"));\n \n \treturn clean ? 0 : 1;\n }\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292231","messageId":"0ba371955b9a4aeb752ce08fc22bbd8171f413c4.1469547160.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469547160.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 14/16] merge-recursive: offer an option to retain the output in 'obuf'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-26T16:06:42Z","receivedAt":"2016-07-26T16:07:16Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Since 66a155b (Enable output buffering in merge-recursive., 2007-01-14),\nwe already accumulate the output in a buffer. The idea was to avoid\ninterfering with the progress output that goes to stderr, which is\nunbuffered, when we write to stdout, which is buffered.\n\nWe extend that buffering to allow the caller to handle the output\n(possibly suppressing it). This will help us when extending the\nsequencer to do rebase -i's brunt work: it does not want the picks to\nprint anything by default but instead determine itself whether to print\nthe output or not.\n\nNote that we also redirect the error messages into the output buffer\nwhen the caller asked not to flush the output buffer, for two reasons:\n1) to retain the correct output order, and 2) to allow the caller to\nsuppress *all* output.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 17 +++++++++++++----\n merge-recursive.h |  2 +-\n 2 files changed, 14 insertions(+), 5 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 723b8d0..311cfa4 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -25,7 +25,7 @@\n \n static void flush_output(struct merge_options *o)\n {\n-\tif (o->obuf.len) {\n+\tif (o->buffer_output < 2 && o->obuf.len) {\n \t\tfputs(o->obuf.buf, stdout);\n \t\tstrbuf_reset(&o->obuf);\n \t}\n@@ -36,10 +36,19 @@ static int err(struct merge_options *o, const char *err, ...)\n \tva_list params;\n \n \tva_start(params, err);\n-\tflush_output(o);\n+\tif (o->buffer_output < 2)\n+\t\tflush_output(o);\n+\telse {\n+\t\tstrbuf_complete(&o->obuf, '\\n');\n+\t\tstrbuf_addstr(&o->obuf, \"error: \");\n+\t}\n \tstrbuf_vaddf(&o->obuf, err, params);\n-\terror(\"%s\", o->obuf.buf);\n-\tstrbuf_reset(&o->obuf);\n+\tif (o->buffer_output > 1)\n+\t\tstrbuf_addch(&o->obuf, '\\n');\n+\telse {\n+\t\terror(\"%s\", o->obuf.buf);\n+\t\tstrbuf_reset(&o->obuf);\n+\t}\n \tva_end(params);\n \n \treturn -1;\ndiff --git a/merge-recursive.h b/merge-recursive.h\nindex d415724..340704c 100644\n--- a/merge-recursive.h\n+++ b/merge-recursive.h\n@@ -13,7 +13,7 @@ struct merge_options {\n \t\tMERGE_RECURSIVE_THEIRS\n \t} recursive_variant;\n \tconst char *subtree_shift;\n-\tunsigned buffer_output : 1;\n+\tunsigned buffer_output : 2; /* 1: output at end, 2: keep buffered */\n \tunsigned renormalize : 1;\n \tlong xdl_opts;\n \tint verbosity;\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292232","messageId":"a2af8827e4df9f1555821111aac92606496006be.1469547160.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469547160.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 15/16] Ensure that the output buffer is released after calling merge_trees()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-26T16:06:45Z","receivedAt":"2016-07-26T16:07:20Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The recursive merge machinery accumulates its output in an output\nbuffer, to be flushed at the end of merge_recursive(). At this point,\nwe forgot to release the output buffer.\n\nWhen calling merge_trees() (i.e. the non-recursive part of the recursive\nmerge) directly, the output buffer is never flushed because the caller\nmay be merge_recursive() which wants to flush the output itself.\n\nFor the same reason, merge_trees() cannot release the output buffer: it\nmay still be needed.\n\nForgetting to release the output buffer did not matter much when running\ngit-checkout, or git-merge-recursive, because we exited after the\noperation anyway. Ever since cherry-pick learned to pick a commit range,\nhowever, this memory leak had the potential of becoming a problem.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/checkout.c | 1 +\n merge-recursive.c  | 2 ++\n sequencer.c        | 1 +\n 3 files changed, 4 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 07dea3b..8d852d4 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -573,6 +573,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\t\texit(128);\n \t\t\tret = reset_tree(new->commit->tree, opts, 0,\n \t\t\t\t\t writeout_error);\n+\t\t\tstrbuf_release(&o.obuf);\n \t\t\tif (ret)\n \t\t\t\treturn ret;\n \t\t}\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 311cfa4..a16b150 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -2078,6 +2078,8 @@ int merge_recursive(struct merge_options *o,\n \t\tcommit_list_insert(h2, &(*result)->parents->next);\n \t}\n \tflush_output(o);\n+\tif (o->buffer_output < 2)\n+\t\tstrbuf_release(&o->obuf);\n \tif (show(o, 2))\n \t\tdiff_warn_rename_limit(\"merge.renamelimit\",\n \t\t\t\t       o->needed_rename_limit, 0);\ndiff --git a/sequencer.c b/sequencer.c\nindex 286a435..ec50519 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -293,6 +293,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \tclean = merge_trees(&o,\n \t\t\t    head_tree,\n \t\t\t    next_tree, base_tree, &result);\n+\tstrbuf_release(&o.obuf);\n \tif (clean < 0)\n \t\treturn clean;\n \n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292233","messageId":"af195979d2c0cf9958b7811b4d2294deeea30b75.1469547160.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469547160.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 16/16] merge-recursive: flush output buffer even when erroring out","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-26T16:06:49Z","receivedAt":"2016-07-26T16:07:21Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Ever since 66a155b (Enable output buffering in merge-recursive.,\n2007-01-14), we had a problem: When the merge failed in a fatal way, all\nregular output was swallowed because we called die() and did not get a\nchance to drain the output buffers.\n\nTo fix this, several modifications were necessary:\n\n- we needed to stop die()ing, to give callers a chance to do something\n  when an error occurred (in this case, flush the output buffers),\n\n- we needed to delay printing the error message so that the caller can\n  print the buffered output before that, and\n\n- we needed to make sure that the output buffers are flushed even when\n  the return value indicates an error.\n\nThe first two changes were introduced through earlier commits in this\npatch series, and this commit addresses the third one.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex a16b150..66e93e0 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -2069,6 +2069,7 @@ int merge_recursive(struct merge_options *o,\n \to->ancestor = \"merged common ancestors\";\n \tclean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree,\n \t\t\t    &mrtree);\n+\tflush_output(o);\n \tif (clean < 0)\n \t\treturn clean;\n \n@@ -2077,7 +2078,6 @@ int merge_recursive(struct merge_options *o,\n \t\tcommit_list_insert(h1, &(*result)->parents);\n \t\tcommit_list_insert(h2, &(*result)->parents->next);\n \t}\n-\tflush_output(o);\n \tif (o->buffer_output < 2)\n \t\tstrbuf_release(&o->obuf);\n \tif (show(o, 2))\n-- \n2.9.0.281.g286a8d9\n"},{"id":"292238","messageId":"xmqqr3ag1fyn.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1607261426340.14111@virtualbox","subject":"Re: [PATCH v4 11/16] am -3: use merge_recursive() directly again","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-26T17:12:16Z","receivedAt":"2016-07-26T17:14:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> If you want, I can break out the subsequent patches into a separate\n> series.\n\nI do not think that would help anybody, as we'll have to review them\nanyway.  If some of the the later ones were \"oops, this earlier step\ndid an incomplete job and here is a fix-up\", then squashing them\ninto the step where such a mistake happens may reduce the review\nload, but I suspect that is not the case (iow, they are enhancements\nand the earlier ones can stand on their own).\n\n> I may have missed something as stupid as an unclosed file handle, after\n> all.\n\nYes, reviewing the same change over and over, especially if the\nchange is your own, tends to have diminishing rate of bug yields,\nwhich is why they need to be reviewed by fresh set of eyes.\n\n\n\n"},{"id":"292374","messageId":"xmqqh9bavk2x.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"349bdfacfffa11d06241655c9e0b62506e58758b.1469547160.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v5 12/16] merge-recursive: flush output buffer before printing error messages","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-27T21:37:26Z","receivedAt":"2016-07-27T21:37:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> The data structure passed to the recursive merge machinery has a feature\n> where the caller can ask for the output to be buffered into a strbuf, by\n> setting the field 'buffer_output'.\n>\n> Previously, we simply swallowed the buffered output when showing error\n> messages. With this patch, we show the output first, and only then print\n> the error message.\n\nI didn't quite understand this paragraph until I realized that you\nmeant \"when showing die message\".  We died without flushing, losing\naccumulated output.\n\n> +static int err(struct merge_options *o, const char *err, ...)\n> +{\n> +\tva_list params;\n> +\n> +\tva_start(params, err);\n> +\tflush_output(o);\n\nI would have written the above two swapped; va_start() logically\nis about what happens in the next four lines.\n\n> +\tstrbuf_vaddf(&o->obuf, err, params);\n> +\terror(\"%s\", o->obuf.buf);\n> +\tstrbuf_reset(&o->obuf);\n\nSneaky ;-)\n\nThe remainder replaces error(...) with err(o, ...) and updates the\ncallchain to pass the merge_options around, which looked good.\n\nThanks.\n"},{"id":"292375","messageId":"CAPc5daUv_csGM963pE=PD+LSSnTFZq57_Shse-x+PsJBSh0dZg@mail.gmail.com","threadId":"42743","inReplyTo":"xmqqh9bavk2x.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v5 12/16] merge-recursive: flush output buffer before printing error messages","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-27T21:53:08Z","receivedAt":"2016-07-27T21:53:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"On Wed, Jul 27, 2016 at 2:37 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>\n>> +     strbuf_vaddf(&o->obuf, err, params);\n>> +     error(\"%s\", o->obuf.buf);\n>> +     strbuf_reset(&o->obuf);\n>\n> Sneaky ;-)\n\nJust to avoid confusion, I am _fine_ with this \"we happen to have\na strbuf that we know to be empty at this point, so let's reuse it\nand clean after ourselves before returning\".\n\nI just found it somewhere between clever and ugly, and \"sneaky\"\nwas the first word that came to my mind.\n"},{"id":"292377","messageId":"xmqqd1lyvim4.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"0ba371955b9a4aeb752ce08fc22bbd8171f413c4.1469547160.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v5 14/16] merge-recursive: offer an option to retain the output in 'obuf'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-27T22:09:07Z","receivedAt":"2016-07-27T22:09:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> Since 66a155b (Enable output buffering in merge-recursive., 2007-01-14),\n> we already accumulate the output in a buffer. The idea was to avoid\n> interfering with the progress output that goes to stderr, which is\n> unbuffered, when we write to stdout, which is buffered.\n>\n> We extend that buffering to allow the caller to handle the output\n> (possibly suppressing it). This will help us when extending the\n> sequencer to do rebase -i's brunt work: it does not want the picks to\n> print anything by default but instead determine itself whether to print\n> the output or not.\n>\n> Note that we also redirect the error messages into the output buffer\n> when the caller asked not to flush the output buffer, for two reasons:\n> 1) to retain the correct output order, and 2) to allow the caller to\n> suppress *all* output.\n\nI am not yet sure if it makes sense to mix both the regular output\nand an error into the same buffer for the callers to process (your\n\"reason 1)\" above), and this looks like a wrong way to allow a\ncaller that wants no output (your \"reason 2)\" above).  A caller that\nwants to massage the output would want to know which ones are errors\nand which ones are not, I would imagine, and setting a knob to make\nboth output() and err() a no-op would be a more suitable way to give\na caller a total silence.\n\nAt least I cannot yet judge if this is a good change, only from the\nmaterial presented here.  That does not mean I have a concrete\nreason to say this is a bad change, either.  Only from the material\npresented here, I cannot judge that, either.\n\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  merge-recursive.c | 17 +++++++++++++----\n>  merge-recursive.h |  2 +-\n>  2 files changed, 14 insertions(+), 5 deletions(-)\n>\n> diff --git a/merge-recursive.c b/merge-recursive.c\n> index 723b8d0..311cfa4 100644\n> --- a/merge-recursive.c\n> +++ b/merge-recursive.c\n> @@ -25,7 +25,7 @@\n>  \n>  static void flush_output(struct merge_options *o)\n>  {\n> -\tif (o->obuf.len) {\n> +\tif (o->buffer_output < 2 && o->obuf.len) {\n>  \t\tfputs(o->obuf.buf, stdout);\n>  \t\tstrbuf_reset(&o->obuf);\n>  \t}\n> @@ -36,10 +36,19 @@ static int err(struct merge_options *o, const char *err, ...)\n>  \tva_list params;\n>  \n>  \tva_start(params, err);\n> -\tflush_output(o);\n> +\tif (o->buffer_output < 2)\n> +\t\tflush_output(o);\n> +\telse {\n> +\t\tstrbuf_complete(&o->obuf, '\\n');\n> +\t\tstrbuf_addstr(&o->obuf, \"error: \");\n> +\t}\n>  \tstrbuf_vaddf(&o->obuf, err, params);\n> -\terror(\"%s\", o->obuf.buf);\n> -\tstrbuf_reset(&o->obuf);\n> +\tif (o->buffer_output > 1)\n> +\t\tstrbuf_addch(&o->obuf, '\\n');\n> +\telse {\n> +\t\terror(\"%s\", o->obuf.buf);\n> +\t\tstrbuf_reset(&o->obuf);\n> +\t}\n>  \tva_end(params);\n>  \n>  \treturn -1;\n> diff --git a/merge-recursive.h b/merge-recursive.h\n> index d415724..340704c 100644\n> --- a/merge-recursive.h\n> +++ b/merge-recursive.h\n> @@ -13,7 +13,7 @@ struct merge_options {\n>  \t\tMERGE_RECURSIVE_THEIRS\n>  \t} recursive_variant;\n>  \tconst char *subtree_shift;\n> -\tunsigned buffer_output : 1;\n> +\tunsigned buffer_output : 2; /* 1: output at end, 2: keep buffered */\n>  \tunsigned renormalize : 1;\n\nOnce a field ceases to be a boolean, it is OK not to squish it into\na bitfield like this for a struct that we will have only a very\nsmall number of instances of.  Treating it just like \"verbosity\",\nwhich occupies a whole int even though it can only get up to 5 or\nso, would be more appropriate.\n\n>  \tlong xdl_opts;\n>  \tint verbosity;\n"},{"id":"292378","messageId":"xmqq8twmvib5.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"a2af8827e4df9f1555821111aac92606496006be.1469547160.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v5 15/16] Ensure that the output buffer is released after calling merge_trees()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-27T22:15:42Z","receivedAt":"2016-07-27T22:16:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> diff --git a/merge-recursive.c b/merge-recursive.c\n> index 311cfa4..a16b150 100644\n> --- a/merge-recursive.c\n> +++ b/merge-recursive.c\n> @@ -2078,6 +2078,8 @@ int merge_recursive(struct merge_options *o,\n>  \t\tcommit_list_insert(h2, &(*result)->parents->next);\n>  \t}\n>  \tflush_output(o);\n> +\tif (o->buffer_output < 2)\n> +\t\tstrbuf_release(&o->obuf);\n>  \tif (show(o, 2))\n>  \t\tdiff_warn_rename_limit(\"merge.renamelimit\",\n>  \t\t\t\t       o->needed_rename_limit, 0);\n\nOther two hunks looked good, but this one I am not sure what is\ngoing on.  It this were \"if call-depth says we are called by another\nmerge_recursive, do not discard the buffer\", I would understand, but\nwhy does this have to be tied to o->buffer_output being \"we buffer\nthe output but not errors\"?\n"},{"id":"292379","messageId":"xmqq4m7avi32.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"af195979d2c0cf9958b7811b4d2294deeea30b75.1469547160.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v5 16/16] merge-recursive: flush output buffer even when erroring out","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-27T22:20:33Z","receivedAt":"2016-07-27T22:20:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> diff --git a/merge-recursive.c b/merge-recursive.c\n> index a16b150..66e93e0 100644\n> --- a/merge-recursive.c\n> +++ b/merge-recursive.c\n> @@ -2069,6 +2069,7 @@ int merge_recursive(struct merge_options *o,\n>  \to->ancestor = \"merged common ancestors\";\n>  \tclean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree,\n>  \t\t\t    &mrtree);\n> +\tflush_output(o);\n>  \tif (clean < 0)\n>  \t\treturn clean;\n\nThis is of course a good change, but we need to assume that no\nfurther output is made from the remainder of the function for the\nchange in the next hunk to remove the existing flush to be correct.\n\nAnd once we assume that, then the \"we no longer need this buffer, so\nrelease it\" added in 15/16 can also move here, right?\n\nI am wondering if there is a low-impact way to make sure that\nassumption will not be broken.\n\n> @@ -2077,7 +2078,6 @@ int merge_recursive(struct merge_options *o,\n>  \t\tcommit_list_insert(h1, &(*result)->parents);\n>  \t\tcommit_list_insert(h2, &(*result)->parents->next);\n>  \t}\n> -\tflush_output(o);\n>  \tif (o->buffer_output < 2)\n>  \t\tstrbuf_release(&o->obuf);\n>  \tif (show(o, 2))\n"},{"id":"292380","messageId":"xmqqzip2u2sb.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"882273dd0067de30fe4b672050457708d56f317e.1469547160.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v5 13/16] merge-recursive: write the commit title in one go","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-27T22:36:20Z","receivedAt":"2016-07-27T22:36:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> Let's make sure that the same holds true when outputting the commit\n> title: previously, we used several printf() statements to stdout and\n> speculated that stdout's buffer is large enough to hold the entire\n> commit title.\n\ns/speculate/assume/; other than that looks very sensible.\n"},{"id":"292386","messageId":"xmqqmvl2ty31.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"xmqqd1lyvim4.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v5 14/16] merge-recursive: offer an option to retain the output in 'obuf'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-28T00:17:54Z","receivedAt":"2016-07-28T00:18:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> I am not yet sure if it makes sense to mix both the regular output\n> and an error into the same buffer for the callers to process (your\n> \"reason 1)\" above), and this looks like a wrong way to allow a\n> caller that wants no output (your \"reason 2)\" above).  A caller that\n> wants to massage the output would want to know which ones are errors\n> and which ones are not, I would imagine, and setting a knob to make\n> both output() and err() a no-op would be a more suitable way to give\n> a caller a total silence.\n\nI actually now see how this would work well for \"reason 2)\".  If a\ncaller wants to run the function and wants to pretend as if it did\nnot run anything when it failed, for example, using this to spool\nall output and error to a strbuf and discard it when the function\nreturns an error, and emit the spooled output to standard output and\nstandard error in the order the lines were collected when the\nfunction returns a success, would be a good way to do so.\n\nThat however brings me back to the \"reason 1\" thing.  Shouldn't the\nspooling be done not just with a single strbuf, but with an array of\n<bool, const char *> tuples, where the bool says if it is for the\nstandard output, and the string holds the message?  output() would\nmark its element in the array with true, while err() would do so\nwith false.\n"},{"id":"292711","messageId":"alpine.DEB.2.20.1608011118480.149069@virtualbox","threadId":"42743","inReplyTo":"xmqqh9bavk2x.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v5 12/16] merge-recursive: flush output buffer before printing error messages","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-01T09:18:52Z","receivedAt":"2016-08-01T09:19:12Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 27 Jul 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > The data structure passed to the recursive merge machinery has a feature\n> > where the caller can ask for the output to be buffered into a strbuf, by\n> > setting the field 'buffer_output'.\n> >\n> > Previously, we simply swallowed the buffered output when showing error\n> > messages. With this patch, we show the output first, and only then print\n> > the error message.\n> \n> I didn't quite understand this paragraph until I realized that you\n> meant \"when showing die message\".  We died without flushing, losing\n> accumulated output.\n\nI rephrased it, using your explanation.\n\n> > +static int err(struct merge_options *o, const char *err, ...)\n> > +{\n> > +\tva_list params;\n> > +\n> > +\tva_start(params, err);\n> > +\tflush_output(o);\n> \n> I would have written the above two swapped; va_start() logically\n> is about what happens in the next four lines.\n\nFor some reason, I thought that `va_start()` must be the first statement\nof the function. Fixed.\n\n> > +\tstrbuf_vaddf(&o->obuf, err, params);\n> > +\terror(\"%s\", o->obuf.buf);\n> > +\tstrbuf_reset(&o->obuf);\n> \n> Sneaky ;-)\n\nThanks ;-)\nDscho\n"},{"id":"292712","messageId":"alpine.DEB.2.20.1608011124440.149069@virtualbox","threadId":"42743","inReplyTo":"xmqqmvl2ty31.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v5 14/16] merge-recursive: offer an option to retain the output in 'obuf'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-01T09:34:49Z","receivedAt":"2016-08-01T09:35:39Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 27 Jul 2016, Junio C Hamano wrote:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n> \n> > I am not yet sure if it makes sense to mix both the regular output\n> > and an error into the same buffer for the callers to process (your\n> > \"reason 1)\" above), and this looks like a wrong way to allow a\n> > caller that wants no output (your \"reason 2)\" above).  A caller that\n> > wants to massage the output would want to know which ones are errors\n> > and which ones are not, I would imagine, and setting a knob to make\n> > both output() and err() a no-op would be a more suitable way to give\n> > a caller a total silence.\n> \n> I actually now see how this would work well for \"reason 2)\".  If a\n> caller wants to run the function and wants to pretend as if it did\n> not run anything when it failed, for example, using this to spool\n> all output and error to a strbuf and discard it when the function\n> returns an error, and emit the spooled output to standard output and\n> standard error in the order the lines were collected when the\n> function returns a success, would be a good way to do so.\n\nThat is actually the exact opposite of the intended usage: when any `pick`\nin an interactive rebase succeeds, its output is discarded so as not to\nbother the user. We show the complete output only when it fails.\n\n> That however brings me back to the \"reason 1\" thing.  Shouldn't the\n> spooling be done not just with a single strbuf, but with an array of\n> <bool, const char *> tuples, where the bool says if it is for the\n> standard output, and the string holds the message?  output() would\n> mark its element in the array with true, while err() would do so\n> with false.\n\nI would like to *not* deviate further from my original focus. My\nwillingness to address comments that have little to nothing to do with\naccelerating the interactive rebase has cost me already over a month, with\nthis patch series alone.\n\nDo not get me wrong: I think you are right in your assessment that a\nfuture caller of the recursive merge might need to be able to tell apart\nwhich lines of the output are error messages from the rest, and still\nretain the order.\n\nBut then, maybe no future caller will require this.\n\nWhat is certain: right now, no caller requires it, and neither do my\nqueued-up patches to speed up the interactive rebase.\n\nAnd another thing is also certain: *iff* a future caller really will\nrequire that fine-grained information of the output, then it will be\ndramatically easier to implement this on top of the patches we are\ndiscussing right now.\n\nIn short: I hope you agree with me that it is safe to defer the <bool,\nconst char*> tuple implementation to the time when we will *actually* need\nit.\n\nCiao,\nDscho\n"},{"id":"292713","messageId":"alpine.DEB.2.20.1608011135010.149069@virtualbox","threadId":"42743","inReplyTo":"xmqqd1lyvim4.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v5 14/16] merge-recursive: offer an option to retain the output in 'obuf'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-01T09:35:55Z","receivedAt":"2016-08-01T09:36:25Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 27 Jul 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > diff --git a/merge-recursive.h b/merge-recursive.h\n> > index d415724..340704c 100644\n> > --- a/merge-recursive.h\n> > +++ b/merge-recursive.h\n> > @@ -13,7 +13,7 @@ struct merge_options {\n> >  \t\tMERGE_RECURSIVE_THEIRS\n> >  \t} recursive_variant;\n> >  \tconst char *subtree_shift;\n> > -\tunsigned buffer_output : 1;\n> > +\tunsigned buffer_output : 2; /* 1: output at end, 2: keep buffered */\n> >  \tunsigned renormalize : 1;\n> \n> Once a field ceases to be a boolean, it is OK not to squish it into\n> a bitfield like this for a struct that we will have only a very\n> small number of instances of.  Treating it just like \"verbosity\",\n> which occupies a whole int even though it can only get up to 5 or\n> so, would be more appropriate.\n\nI changed it to an int.\n\nThanks,\nDscho\n"},{"id":"292714","messageId":"alpine.DEB.2.20.1608011138110.149069@virtualbox","threadId":"42743","inReplyTo":"xmqq8twmvib5.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v5 15/16] Ensure that the output buffer is released after calling merge_trees()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-01T09:40:49Z","receivedAt":"2016-08-01T09:42:29Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 27 Jul 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > diff --git a/merge-recursive.c b/merge-recursive.c\n> > index 311cfa4..a16b150 100644\n> > --- a/merge-recursive.c\n> > +++ b/merge-recursive.c\n> > @@ -2078,6 +2078,8 @@ int merge_recursive(struct merge_options *o,\n> >  \t\tcommit_list_insert(h2, &(*result)->parents->next);\n> >  \t}\n> >  \tflush_output(o);\n> > +\tif (o->buffer_output < 2)\n> > +\t\tstrbuf_release(&o->obuf);\n> >  \tif (show(o, 2))\n> >  \t\tdiff_warn_rename_limit(\"merge.renamelimit\",\n> >  \t\t\t\t       o->needed_rename_limit, 0);\n> \n> Other two hunks looked good, but this one I am not sure what is\n> going on.  It this were \"if call-depth says we are called by another\n> merge_recursive, do not discard the buffer\", I would understand, but\n> why does this have to be tied to o->buffer_output being \"we buffer\n> the output but not errors\"?\n\nGood point. I changed it to test for !o->call_depth in addition.\n\nWe must not release the output buffer here if buffer_output >= 2: the\nlevel \"2\" of the buffer_output field says that the caller of the recursive\nmerge expects the output to remain in the buffer until after\n`merge_recursive()` returns.\n\nCiao,\nDscho\n"},{"id":"292715","messageId":"alpine.DEB.2.20.1608011141380.149069@virtualbox","threadId":"42743","inReplyTo":"xmqq4m7avi32.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v5 16/16] merge-recursive: flush output buffer even when erroring out","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-01T09:49:37Z","receivedAt":"2016-08-01T09:51:16Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 27 Jul 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > diff --git a/merge-recursive.c b/merge-recursive.c\n> > index a16b150..66e93e0 100644\n> > --- a/merge-recursive.c\n> > +++ b/merge-recursive.c\n> > @@ -2069,6 +2069,7 @@ int merge_recursive(struct merge_options *o,\n> >  \to->ancestor = \"merged common ancestors\";\n> >  \tclean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree,\n> >  \t\t\t    &mrtree);\n> > +\tflush_output(o);\n> >  \tif (clean < 0)\n> >  \t\treturn clean;\n> \n> This is of course a good change, but we need to assume that no\n> further output is made from the remainder of the function for the\n> change in the next hunk to remove the existing flush to be correct.\n\nPlease note that nothing prevents the code further down from adding more\noutput. All we do here is flushing the output *so far*, in case we return\nan error. And of course nothing gets flushed if buffer_output == 2,\nbecause that value states that the caller wants to take care of displaying\nthe output herself.\n\nBut you made me realize that I cannot simply *move* the flush_output()\ncall here, in case that code in between will eventually add output.\n\nThanks,\nDscho\n"},{"id":"292716","messageId":"alpine.DEB.2.20.1608011153310.149069@virtualbox","threadId":"42743","inReplyTo":"xmqqzip2u2sb.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v5 13/16] merge-recursive: write the commit title in one go","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-01T09:53:58Z","receivedAt":"2016-08-01T09:54:40Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 27 Jul 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > Let's make sure that the same holds true when outputting the commit\n> > title: previously, we used several printf() statements to stdout and\n> > speculated that stdout's buffer is large enough to hold the entire\n> > commit title.\n> \n> s/speculate/assume/; other than that looks very sensible.\n\nFixed.\n\nThanks,\nDscho\n"},{"id":"292721","messageId":"aa807cf08ddfd45d5a1339825ddec24071cabc01.1470051326.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1470051326.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 01/16] t5520: verify that `pull --rebase` shows the helpful advice when failing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-01T11:36:29Z","receivedAt":"2016-08-01T11:38:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It was noticed by Brendan Forster last October that the builtin `git am`\nregressed on that. Our hot fix reverted to spawning the recursive merge\ninstead of using it as a library function.\n\nAs we are about to revert that hot fix, after making the recursive merge a\ntrue library function (i.e. a function that does not die() in case of\n\"normal\" errors), let's add a test that verifies that we do not regress on\nthe same problem which made the hot fix necessary in the first place.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n t/t5520-pull.sh | 32 ++++++++++++++++++++++++++++++++\n 1 file changed, 32 insertions(+)\n\ndiff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\nindex 37ebbcf..6ad37b5 100755\n--- a/t/t5520-pull.sh\n+++ b/t/t5520-pull.sh\n@@ -255,6 +255,38 @@ test_expect_success '--rebase' '\n \ttest new = \"$(git show HEAD:file2)\"\n '\n \n+test_expect_success '--rebase with conflicts shows advice' '\n+\ttest_when_finished \"git rebase --abort; git checkout -f to-rebase\" &&\n+\tgit checkout -b seq &&\n+\ttest_seq 5 >seq.txt &&\n+\tgit add seq.txt &&\n+\ttest_tick &&\n+\tgit commit -m \"Add seq.txt\" &&\n+\techo 6 >>seq.txt &&\n+\ttest_tick &&\n+\tgit commit -m \"Append to seq.txt\" seq.txt &&\n+\tgit checkout -b with-conflicts HEAD^ &&\n+\techo conflicting >>seq.txt &&\n+\ttest_tick &&\n+\tgit commit -m \"Create conflict\" seq.txt &&\n+\ttest_must_fail git pull --rebase . seq 2>err >out &&\n+\tgrep \"When you have resolved this problem\" out\n+'\n+\n+test_expect_success 'failed --rebase shows advice' '\n+\ttest_when_finished \"git rebase --abort; git checkout -f to-rebase\" &&\n+\tgit checkout -b diverging &&\n+\ttest_commit attributes .gitattributes \"* text=auto\" attrs &&\n+\tsha1=\"$(printf \"1\\\\r\\\\n\" | git hash-object -w --stdin)\" &&\n+\tgit update-index --cacheinfo 0644 $sha1 file &&\n+\tgit commit -m v1-with-cr &&\n+\t# force checkout because `git reset --hard` will not leave clean `file`\n+\tgit checkout -f -b fails-to-rebase HEAD^ &&\n+\ttest_commit v2-without-cr file \"2\" file2-lf &&\n+\ttest_must_fail git pull --rebase . diverging 2>err >out &&\n+\tgrep \"When you have resolved this problem\" out\n+'\n+\n test_expect_success '--rebase fails with multiple branches' '\n \tgit reset --hard before-rebase &&\n \ttest_must_fail git pull --rebase . copy master 2>err &&\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292722","messageId":"652c5d3fc999a19e163e7a3591391d2866c70ea1.1470051326.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1470051326.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 02/16] Report bugs consistently","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-01T11:36:46Z","receivedAt":"2016-08-01T11:38:51Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The vast majority of error messages in Git's source code which report a\nbug use the convention to prefix the message with \"BUG:\".\n\nAs part of cleaning up merge-recursive to stop die()ing except in case of\ndetected bugs, let's just make the remainder of the bug reports consistent\nwith the de facto rule.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/ls-files.c     |  3 ++-\n builtin/update-index.c |  2 +-\n grep.c                 |  8 ++++----\n imap-send.c            |  2 +-\n merge-recursive.c      | 15 +++++++--------\n sha1_file.c            |  4 ++--\n trailer.c              |  2 +-\n transport.c            |  2 +-\n wt-status.c            |  4 ++--\n 9 files changed, 21 insertions(+), 21 deletions(-)\n\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex f02e3d2..00ea91a 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -118,7 +118,8 @@ static void show_killed_files(struct dir_struct *dir)\n \t\t\t\t */\n \t\t\t\tpos = cache_name_pos(ent->name, ent->len);\n \t\t\t\tif (0 <= pos)\n-\t\t\t\t\tdie(\"bug in show-killed-files\");\n+\t\t\t\t\tdie(\"BUG: killed-file %.*s not found\",\n+\t\t\t\t\t\tent->len, ent->name);\n \t\t\t\tpos = -pos - 1;\n \t\t\t\twhile (pos < active_nr &&\n \t\t\t\t       ce_stage(active_cache[pos]))\ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex 6cdfd5f..ba04b19 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -1146,7 +1146,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)\n \t\treport(_(\"Untracked cache enabled for '%s'\"), get_git_work_tree());\n \t\tbreak;\n \tdefault:\n-\t\tdie(\"Bug: bad untracked_cache value: %d\", untracked_cache);\n+\t\tdie(\"BUG: bad untracked_cache value: %d\", untracked_cache);\n \t}\n \n \tif (active_cache_changed) {\ndiff --git a/grep.c b/grep.c\nindex 394c856..22cbb73 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -693,10 +693,10 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)\n \n \tfor (p = opt->header_list; p; p = p->next) {\n \t\tif (p->token != GREP_PATTERN_HEAD)\n-\t\t\tdie(\"bug: a non-header pattern in grep header list.\");\n+\t\t\tdie(\"BUG: a non-header pattern in grep header list.\");\n \t\tif (p->field < GREP_HEADER_FIELD_MIN ||\n \t\t    GREP_HEADER_FIELD_MAX <= p->field)\n-\t\t\tdie(\"bug: unknown header field %d\", p->field);\n+\t\t\tdie(\"BUG: unknown header field %d\", p->field);\n \t\tcompile_regexp(p, opt);\n \t}\n \n@@ -709,7 +709,7 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)\n \n \t\th = compile_pattern_atom(&pp);\n \t\tif (!h || pp != p->next)\n-\t\t\tdie(\"bug: malformed header expr\");\n+\t\t\tdie(\"BUG: malformed header expr\");\n \t\tif (!header_group[p->field]) {\n \t\t\theader_group[p->field] = h;\n \t\t\tcontinue;\n@@ -1514,7 +1514,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle\n \t\tcase GREP_BINARY_TEXT:\n \t\t\tbreak;\n \t\tdefault:\n-\t\t\tdie(\"bug: unknown binary handling mode\");\n+\t\t\tdie(\"BUG: unknown binary handling mode\");\n \t\t}\n \t}\n \ndiff --git a/imap-send.c b/imap-send.c\nindex db0fafe..0f5f476 100644\n--- a/imap-send.c\n+++ b/imap-send.c\n@@ -511,7 +511,7 @@ static int nfsnprintf(char *buf, int blen, const char *fmt, ...)\n \n \tva_start(va, fmt);\n \tif (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)\n-\t\tdie(\"Fatal: buffer too small. Please report a bug.\");\n+\t\tdie(\"BUG: buffer too small. Please report a bug.\");\n \tva_end(va);\n \treturn ret;\n }\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex a4a1195..4338b73 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -268,7 +268,7 @@ struct tree *write_tree_from_memory(struct merge_options *o)\n \t\t\t\tfprintf(stderr, \"BUG: %d %.*s\\n\", ce_stage(ce),\n \t\t\t\t\t(int)ce_namelen(ce), ce->name);\n \t\t}\n-\t\tdie(\"Bug in merge-recursive.c\");\n+\t\tdie(\"BUG: unmerged index entries in merge-recursive.c\");\n \t}\n \n \tif (!active_cache_tree)\n@@ -966,9 +966,8 @@ static struct merge_file_info merge_file_1(struct merge_options *o,\n \n \t\t\tif (!oid_eq(&a->oid, &b->oid))\n \t\t\t\tresult.clean = 0;\n-\t\t} else {\n-\t\t\tdie(_(\"unsupported object type in the tree\"));\n-\t\t}\n+\t\t} else\n+\t\t\tdie(_(\"BUG: unsupported object type in the tree\"));\n \t}\n \n \treturn result;\n@@ -1354,7 +1353,7 @@ static int process_renames(struct merge_options *o,\n \t\t\tconst char *ren2_dst = ren2->pair->two->path;\n \t\t\tenum rename_type rename_type;\n \t\t\tif (strcmp(ren1_src, ren2_src) != 0)\n-\t\t\t\tdie(\"ren1_src != ren2_src\");\n+\t\t\t\tdie(\"BUG: ren1_src != ren2_src\");\n \t\t\tren2->dst_entry->processed = 1;\n \t\t\tren2->processed = 1;\n \t\t\tif (strcmp(ren1_dst, ren2_dst) != 0) {\n@@ -1388,7 +1387,7 @@ static int process_renames(struct merge_options *o,\n \t\t\tren2 = lookup->util;\n \t\t\tren2_dst = ren2->pair->two->path;\n \t\t\tif (strcmp(ren1_dst, ren2_dst) != 0)\n-\t\t\t\tdie(\"ren1_dst != ren2_dst\");\n+\t\t\t\tdie(\"BUG: ren1_dst != ren2_dst\");\n \n \t\t\tclean_merge = 0;\n \t\t\tren2->processed = 1;\n@@ -1812,7 +1811,7 @@ static int process_entry(struct merge_options *o,\n \t\t */\n \t\tremove_file(o, 1, path, !a_mode);\n \t} else\n-\t\tdie(_(\"Fatal merge failure, shouldn't happen.\"));\n+\t\tdie(_(\"BUG: fatal merge failure, shouldn't happen.\"));\n \n \treturn clean_merge;\n }\n@@ -1870,7 +1869,7 @@ int merge_trees(struct merge_options *o,\n \t\tfor (i = 0; i < entries->nr; i++) {\n \t\t\tstruct stage_data *e = entries->items[i].util;\n \t\t\tif (!e->processed)\n-\t\t\t\tdie(_(\"Unprocessed path??? %s\"),\n+\t\t\t\tdie(_(\"BUG: unprocessed path??? %s\"),\n \t\t\t\t    entries->items[i].string);\n \t\t}\n \ndiff --git a/sha1_file.c b/sha1_file.c\nindex cb571ac..ebc640e 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -795,7 +795,7 @@ void close_all_packs(void)\n \n \tfor (p = packed_git; p; p = p->next)\n \t\tif (p->do_not_close)\n-\t\t\tdie(\"BUG! Want to close pack marked 'do-not-close'\");\n+\t\t\tdie(\"BUG: want to close pack marked 'do-not-close'\");\n \t\telse\n \t\t\tclose_pack(p);\n }\n@@ -2330,7 +2330,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,\n \tcase OBJ_OFS_DELTA:\n \tcase OBJ_REF_DELTA:\n \t\tif (data)\n-\t\t\tdie(\"BUG in unpack_entry: left loop at a valid delta\");\n+\t\t\tdie(\"BUG: unpack_entry: left loop at a valid delta\");\n \t\tbreak;\n \tcase OBJ_COMMIT:\n \tcase OBJ_TREE:\ndiff --git a/trailer.c b/trailer.c\nindex 8e48a5c..c6ea9ac 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -562,7 +562,7 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)\n \t\t\twarning(_(\"unknown value '%s' for key '%s'\"), value, conf_key);\n \t\tbreak;\n \tdefault:\n-\t\tdie(\"internal bug in trailer.c\");\n+\t\tdie(\"BUG: trailer.c: unhandled type %d\", type);\n \t}\n \treturn 0;\n }\ndiff --git a/transport.c b/transport.c\nindex b233e3e..04d9454 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -566,7 +566,7 @@ void transport_take_over(struct transport *transport,\n \tstruct git_transport_data *data;\n \n \tif (!transport->smart_options)\n-\t\tdie(\"Bug detected: Taking over transport requires non-NULL \"\n+\t\tdie(\"BUG: taking over transport requires non-NULL \"\n \t\t    \"smart_options field.\");\n \n \tdata = xcalloc(1, sizeof(*data));\ndiff --git a/wt-status.c b/wt-status.c\nindex 19cbc39..f8ae0c2 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -263,7 +263,7 @@ static const char *wt_status_unmerged_status_string(int stagemask)\n \tcase 7:\n \t\treturn _(\"both modified:\");\n \tdefault:\n-\t\tdie(\"bug: unhandled unmerged status %x\", stagemask);\n+\t\tdie(\"BUG: unhandled unmerged status %x\", stagemask);\n \t}\n }\n \n@@ -388,7 +388,7 @@ static void wt_status_print_change_data(struct wt_status *s,\n \tstatus_printf(s, color(WT_STATUS_HEADER, s), \"\\t\");\n \twhat = wt_status_diff_status_string(status);\n \tif (!what)\n-\t\tdie(\"bug: unhandled diff status %c\", status);\n+\t\tdie(\"BUG: unhandled diff status %c\", status);\n \tlen = label_width - utf8_strwidth(what);\n \tassert(len >= 0);\n \tif (status == DIFF_STATUS_COPIED || status == DIFF_STATUS_RENAMED)\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292723","messageId":"cover.1470051326.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1469547160.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 00/16] Use merge_recursive() directly in the builtin am","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-01T11:36:08Z","receivedAt":"2016-08-01T11:43:19Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This is the sixth iteration of the long-awaited re-roll of the attempt to\navoid spawning merge-recursive from the builtin am and use merge_recursive()\ndirectly instead.\n\nThe *real* reason for the reroll is that I need a libified recursive\nmerge to accelerate the interactive rebase by teaching the sequencer to\ndo rebase -i's grunt work. Coming with a very nice 3x-5x speedup of\n`rebase -i`.\n\nIn this endeavor, we need to be extra careful to retain backwards\ncompatibility. The test script t6022-merge-rename.sh, for example, verifies\nthat `git pull` exits with status 128 in case of a fatal error. To that end,\nwe need to make sure that fatal errors are handled by existing (builtin)\nusers via exit(128) (or die(), which calls exit(128) at the end).  New users\n(such as a builtin helper doing rebase -i's grunt work) may want to print\nsome helpful advice what happened and how to get out of this mess before\nerroring out.\n\nThe changes relative to the fifth iteration of this patch series:\n\n- the commit message that talked about swallowing messages was improved\n\n- the va_start()/va_end() statements were moved closer to the vararg usage\n\n- the `buffer_output` field is no longer a bit field because it is now\n  logically more like the verbosity level, which we traditionally keep\n  as a full `int`\n\n- the flush_output() call is no longer moved, but instead added to the\n  code path when we return early\n\n- the commit message saying that we speculated about stdout's buffer\n  size now instead states that we made an assumption\n\nThis patch series touches rather important code. I appreciate thorough\nreviews with a focus on the critical parts of the code, those that could\nresult in regressions.\n\n\nJohannes Schindelin (16):\n  t5520: verify that `pull --rebase` shows the helpful advice when\n    failing\n  Report bugs consistently\n  Avoid translating bug messages\n  merge-recursive: clarify code in was_tracked()\n  Prepare the builtins for a libified merge_recursive()\n  merge_recursive: abort properly upon errors\n  merge-recursive: avoid returning a wholesale struct\n  merge-recursive: allow write_tree_from_memory() to error out\n  merge-recursive: handle return values indicating errors\n  merge-recursive: switch to returning errors instead of dying\n  am -3: use merge_recursive() directly again\n  merge-recursive: flush output buffer before printing error messages\n  merge-recursive: write the commit title in one go\n  merge-recursive: offer an option to retain the output in 'obuf'\n  Ensure that the output buffer is released after calling merge_trees()\n  merge-recursive: flush output buffer even when erroring out\n\n builtin/am.c           |  62 ++----\n builtin/checkout.c     |   5 +-\n builtin/ls-files.c     |   3 +-\n builtin/merge.c        |   2 +\n builtin/update-index.c |   2 +-\n grep.c                 |   8 +-\n imap-send.c            |   2 +-\n merge-recursive.c      | 578 +++++++++++++++++++++++++++++--------------------\n merge-recursive.h      |   2 +-\n sequencer.c            |   5 +\n sha1_file.c            |   4 +-\n t/t5520-pull.sh        |  32 +++\n trailer.c              |   2 +-\n transport.c            |   2 +-\n wt-status.c            |   4 +-\n 15 files changed, 419 insertions(+), 294 deletions(-)\n\nPublished-As: https://github.com/dscho/git/releases/tag/am-3-merge-recursive-direct-v6\nInterdiff vs v5:\n\n diff --git a/merge-recursive.c b/merge-recursive.c\n index 66e93e0..c9e4dbc 100644\n --- a/merge-recursive.c\n +++ b/merge-recursive.c\n @@ -35,21 +35,21 @@ static int err(struct merge_options *o, const char *err, ...)\n  {\n  \tva_list params;\n  \n -\tva_start(params, err);\n  \tif (o->buffer_output < 2)\n  \t\tflush_output(o);\n  \telse {\n  \t\tstrbuf_complete(&o->obuf, '\\n');\n  \t\tstrbuf_addstr(&o->obuf, \"error: \");\n  \t}\n +\tva_start(params, err);\n  \tstrbuf_vaddf(&o->obuf, err, params);\n +\tva_end(params);\n  \tif (o->buffer_output > 1)\n  \t\tstrbuf_addch(&o->obuf, '\\n');\n  \telse {\n  \t\terror(\"%s\", o->obuf.buf);\n  \t\tstrbuf_reset(&o->obuf);\n  \t}\n -\tva_end(params);\n  \n  \treturn -1;\n  }\n @@ -2069,16 +2069,18 @@ int merge_recursive(struct merge_options *o,\n  \to->ancestor = \"merged common ancestors\";\n  \tclean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree,\n  \t\t\t    &mrtree);\n -\tflush_output(o);\n -\tif (clean < 0)\n +\tif (clean < 0) {\n +\t\tflush_output(o);\n  \t\treturn clean;\n +\t}\n  \n  \tif (o->call_depth) {\n  \t\t*result = make_virtual_commit(mrtree, \"merged tree\");\n  \t\tcommit_list_insert(h1, &(*result)->parents);\n  \t\tcommit_list_insert(h2, &(*result)->parents->next);\n  \t}\n -\tif (o->buffer_output < 2)\n +\tflush_output(o);\n +\tif (!o->call_depth && o->buffer_output < 2)\n  \t\tstrbuf_release(&o->obuf);\n  \tif (show(o, 2))\n  \t\tdiff_warn_rename_limit(\"merge.renamelimit\",\n diff --git a/merge-recursive.h b/merge-recursive.h\n index 340704c..735343b 100644\n --- a/merge-recursive.h\n +++ b/merge-recursive.h\n @@ -13,7 +13,7 @@ struct merge_options {\n  \t\tMERGE_RECURSIVE_THEIRS\n  \t} recursive_variant;\n  \tconst char *subtree_shift;\n -\tunsigned buffer_output : 2; /* 1: output at end, 2: keep buffered */\n +\tunsigned buffer_output; /* 1: output at end, 2: keep buffered */\n  \tunsigned renormalize : 1;\n  \tlong xdl_opts;\n  \tint verbosity;\n\n-- \n2.9.0.281.g286a8d9\n\nbase-commit: f8f7adce9fc50a11a764d57815602dcb818d1816\n"},{"id":"292724","messageId":"d3c5678faf46391ce684aa79927a54cf15beea3f.1470051326.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1470051326.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 05/16] Prepare the builtins for a libified merge_recursive()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-01T11:44:08Z","receivedAt":"2016-08-01T11:45:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Previously, callers of merge_trees() or merge_recursive() expected that\ncode to die() with an error message. This used to be okay because we\ncalled those commands from scripts, and had a chance to print out a\nmessage in case the command failed fatally (read: with exit code 128).\n\nAs scripting incurs its own set of problems (portability, speed,\nidiosynchracies of different shells, limited data structures leading to\ninefficient code), we are converting more and more of these scripts into\nbuiltins, using library functions directly.\n\nWe already tried to use merge_recursive() directly in the builtin\ngit-am, for example. Unfortunately, we had to roll it back temporarily\nbecause some of the code in merge-recursive.c still deemed it okay to\ncall die(), when the builtin am code really wanted to print out a useful\nadvice after the merge failed fatally. In the next commits, we want to\nfix that.\n\nThe code touched by this commit expected merge_trees() to die() with\nsome useful message when there is an error condition, but merge_trees()\nis going to be improved by converting all die() calls to return error()\ninstead (i.e. return value -1 after printing out the message as before),\nso that the caller can react more flexibly.\n\nThis is a step to prepare for the version of merge_trees() that no\nlonger dies,  even if we just imitate the previous behavior by calling\nexit(128): this is what callers of e.g. `git merge` have come to expect.\n\nNote that the callers of the sequencer (revert and cherry-pick) already\nfail fast even for the return value -1; The only difference is that they\nnow get a chance to say \"<command> failed\".\n\nA caller of merge_trees() might want handle error messages themselves\n(or even suppress them). As this patch is already complex enough, we\nleave that change for a later patch.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/checkout.c | 4 +++-\n builtin/merge.c    | 2 ++\n sequencer.c        | 4 ++++\n 3 files changed, 9 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 27c1a05..07dea3b 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -567,8 +567,10 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\to.ancestor = old->name;\n \t\t\to.branch1 = new->name;\n \t\t\to.branch2 = \"local\";\n-\t\t\tmerge_trees(&o, new->commit->tree, work,\n+\t\t\tret = merge_trees(&o, new->commit->tree, work,\n \t\t\t\told->commit->tree, &result);\n+\t\t\tif (ret < 0)\n+\t\t\t\texit(128);\n \t\t\tret = reset_tree(new->commit->tree, opts, 0,\n \t\t\t\t\t writeout_error);\n \t\t\tif (ret)\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 19b3bc2..148a9a5 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -673,6 +673,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,\n \t\thold_locked_index(&lock, 1);\n \t\tclean = merge_recursive(&o, head,\n \t\t\t\tremoteheads->item, reversed, &result);\n+\t\tif (clean < 0)\n+\t\t\texit(128);\n \t\tif (active_cache_changed &&\n \t\t    write_locked_index(&the_index, &lock, COMMIT_LOCK))\n \t\t\tdie (_(\"unable to write %s\"), get_index_file());\ndiff --git a/sequencer.c b/sequencer.c\nindex cdfac82..286a435 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -293,6 +293,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \tclean = merge_trees(&o,\n \t\t\t    head_tree,\n \t\t\t    next_tree, base_tree, &result);\n+\tif (clean < 0)\n+\t\treturn clean;\n \n \tif (active_cache_changed &&\n \t    write_locked_index(&the_index, &index_lock, COMMIT_LOCK))\n@@ -559,6 +561,8 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || opts->action == REPLAY_REVERT) {\n \t\tres = do_recursive_merge(base, next, base_label, next_label,\n \t\t\t\t\t head, &msgbuf, opts);\n+\t\tif (res < 0)\n+\t\t\treturn res;\n \t\twrite_message(&msgbuf, git_path_merge_msg());\n \t} else {\n \t\tstruct commit_list *common = NULL;\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292725","messageId":"5b581313f33ed2b924ad996c285509f9014bb863.1470051326.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1470051326.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 04/16] merge-recursive: clarify code in was_tracked()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-01T11:44:04Z","receivedAt":"2016-08-01T11:45:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It can be puzzling to see that was_tracked() asks to get an index entry\nby name, but does not take a negative return value for an answer.\n\nThe reason we have to do this is that cache_name_pos() only looks for\nentries in stage 0, even if nobody asked for any stage in particular.\n\nLet's rewrite the logic a little bit, to handle the easy case early: if\ncache_name_pos() returned a non-negative position, we know it is a match,\nand we do not even have to compare the name again (cache_name_pos() did\nthat for us already). We can say right away: yes, this file was tracked.\n\nOnly if there was no exact match do we need to look harder for any\nmatching entry in stage 2.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 30 ++++++++++++++----------------\n 1 file changed, 14 insertions(+), 16 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 1b6db87..3a652b7 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -667,23 +667,21 @@ static int was_tracked(const char *path)\n {\n \tint pos = cache_name_pos(path, strlen(path));\n \n-\tif (pos < 0)\n-\t\tpos = -1 - pos;\n-\twhile (pos < active_nr &&\n-\t       !strcmp(path, active_cache[pos]->name)) {\n-\t\t/*\n-\t\t * If stage #0, it is definitely tracked.\n-\t\t * If it has stage #2 then it was tracked\n-\t\t * before this merge started.  All other\n-\t\t * cases the path was not tracked.\n-\t\t */\n-\t\tswitch (ce_stage(active_cache[pos])) {\n-\t\tcase 0:\n-\t\tcase 2:\n+\tif (0 <= pos)\n+\t\t/* we have been tracking this path */\n+\t\treturn 1;\n+\n+\t/*\n+\t * Look for an unmerged entry for the path,\n+\t * specifically stage #2, which would indicate\n+\t * that \"our\" side before the merge started\n+\t * had the path tracked (and resulted in a conflict).\n+\t */\n+\tfor (pos = -1 - pos;\n+\t     pos < active_nr && !strcmp(path, active_cache[pos]->name);\n+\t     pos++)\n+\t\tif (ce_stage(active_cache[pos]) == 2)\n \t\t\treturn 1;\n-\t\t}\n-\t\tpos++;\n-\t}\n \treturn 0;\n }\n \n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292726","messageId":"d739dc9aa781a551d8654f5cba17f1a32c8d90d4.1470051326.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1470051326.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 03/16] Avoid translating bug messages","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-01T11:43:53Z","receivedAt":"2016-08-01T11:45:26Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"While working on the patch series that avoids die()ing in recursive\nmerges, the issue came up that bug reports (i.e. die(\"BUG: ...\")\nconstructs) should never be translated, as the target audience is the\nGit developer community, not necessarily the current user, and hence\na translated message would make it *harder* to address the problem.\n\nSo let's stop translating the obvious ones. As it is really, really\noutside the purview of this patch series to see whether there are more\ndie() statements that report bugs and are currently translated, that\ntask is left for another day and patch.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 4338b73..1b6db87 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -967,7 +967,7 @@ static struct merge_file_info merge_file_1(struct merge_options *o,\n \t\t\tif (!oid_eq(&a->oid, &b->oid))\n \t\t\t\tresult.clean = 0;\n \t\t} else\n-\t\t\tdie(_(\"BUG: unsupported object type in the tree\"));\n+\t\t\tdie(\"BUG: unsupported object type in the tree\");\n \t}\n \n \treturn result;\n@@ -1811,7 +1811,7 @@ static int process_entry(struct merge_options *o,\n \t\t */\n \t\tremove_file(o, 1, path, !a_mode);\n \t} else\n-\t\tdie(_(\"BUG: fatal merge failure, shouldn't happen.\"));\n+\t\tdie(\"BUG: fatal merge failure, shouldn't happen.\");\n \n \treturn clean_merge;\n }\n@@ -1869,7 +1869,7 @@ int merge_trees(struct merge_options *o,\n \t\tfor (i = 0; i < entries->nr; i++) {\n \t\t\tstruct stage_data *e = entries->items[i].util;\n \t\t\tif (!e->processed)\n-\t\t\t\tdie(_(\"BUG: unprocessed path??? %s\"),\n+\t\t\t\tdie(\"BUG: unprocessed path??? %s\",\n \t\t\t\t    entries->items[i].string);\n \t\t}\n \n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292727","messageId":"aa986f6cd9f099dad9874e86b0d54641c9029c5e.1470051326.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1470051326.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 11/16] am -3: use merge_recursive() directly again","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-01T11:44:33Z","receivedAt":"2016-08-01T11:45:29Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Last October, we had to change this code to run `git merge-recursive`\nin a child process: git-am wants to print some helpful advice when the\nmerge failed, but the code in question was not prepared to return, it\ndie()d instead.\n\nWe are finally at a point when the code *is* prepared to return errors,\nand can avoid the child process again.\n\nThis reverts commit c63d4b2 (am -3: do not let failed merge from\ncompleting the error codepath, 2015-10-09), with the necessary changes\nto adjust for the fact that Git's source code changed in the meantime\n(such as: using OIDs instead of hashes in the recursive merge, and a\nremoved gender bias).\n\nNote: the code now calls merge_recursive_generic() again. Unlike\nmerge_trees() and merge_recursive(), this function returns 0 upon success,\nas most of Git's functions. Therefore, the error value -1 naturally is\nhandled correctly, and we do not have to take care of it specifically.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/am.c | 62 +++++++++++++++++++++---------------------------------------\n 1 file changed, 22 insertions(+), 40 deletions(-)\n\ndiff --git a/builtin/am.c b/builtin/am.c\nindex b77bf11..cfb79ea 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -1579,47 +1579,18 @@ static int build_fake_ancestor(const struct am_state *state, const char *index_f\n }\n \n /**\n- * Do the three-way merge using fake ancestor, their tree constructed\n- * from the fake ancestor and the postimage of the patch, and our\n- * state.\n- */\n-static int run_fallback_merge_recursive(const struct am_state *state,\n-\t\t\t\t\tunsigned char *orig_tree,\n-\t\t\t\t\tunsigned char *our_tree,\n-\t\t\t\t\tunsigned char *their_tree)\n-{\n-\tstruct child_process cp = CHILD_PROCESS_INIT;\n-\tint status;\n-\n-\tcp.git_cmd = 1;\n-\n-\targv_array_pushf(&cp.env_array, \"GITHEAD_%s=%.*s\",\n-\t\t\t sha1_to_hex(their_tree), linelen(state->msg), state->msg);\n-\tif (state->quiet)\n-\t\targv_array_push(&cp.env_array, \"GIT_MERGE_VERBOSITY=0\");\n-\n-\targv_array_push(&cp.args, \"merge-recursive\");\n-\targv_array_push(&cp.args, sha1_to_hex(orig_tree));\n-\targv_array_push(&cp.args, \"--\");\n-\targv_array_push(&cp.args, sha1_to_hex(our_tree));\n-\targv_array_push(&cp.args, sha1_to_hex(their_tree));\n-\n-\tstatus = run_command(&cp) ? (-1) : 0;\n-\tdiscard_cache();\n-\tread_cache();\n-\treturn status;\n-}\n-\n-/**\n  * Attempt a threeway merge, using index_path as the temporary index.\n  */\n static int fall_back_threeway(const struct am_state *state, const char *index_path)\n {\n-\tunsigned char orig_tree[GIT_SHA1_RAWSZ], their_tree[GIT_SHA1_RAWSZ],\n-\t\t      our_tree[GIT_SHA1_RAWSZ];\n+\tstruct object_id orig_tree, their_tree, our_tree;\n+\tconst struct object_id *bases[1] = { &orig_tree };\n+\tstruct merge_options o;\n+\tstruct commit *result;\n+\tchar *their_tree_name;\n \n-\tif (get_sha1(\"HEAD\", our_tree) < 0)\n-\t\thashcpy(our_tree, EMPTY_TREE_SHA1_BIN);\n+\tif (get_oid(\"HEAD\", &our_tree) < 0)\n+\t\thashcpy(our_tree.hash, EMPTY_TREE_SHA1_BIN);\n \n \tif (build_fake_ancestor(state, index_path))\n \t\treturn error(\"could not build fake ancestor\");\n@@ -1627,7 +1598,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \tdiscard_cache();\n \tread_cache_from(index_path);\n \n-\tif (write_index_as_tree(orig_tree, &the_index, index_path, 0, NULL))\n+\tif (write_index_as_tree(orig_tree.hash, &the_index, index_path, 0, NULL))\n \t\treturn error(_(\"Repository lacks necessary blobs to fall back on 3-way merge.\"));\n \n \tsay(state, stdout, _(\"Using index info to reconstruct a base tree...\"));\n@@ -1643,7 +1614,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \t\tinit_revisions(&rev_info, NULL);\n \t\trev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;\n \t\tdiff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix);\n-\t\tadd_pending_sha1(&rev_info, \"HEAD\", our_tree, 0);\n+\t\tadd_pending_sha1(&rev_info, \"HEAD\", our_tree.hash, 0);\n \t\tdiff_setup_done(&rev_info.diffopt);\n \t\trun_diff_index(&rev_info, 1);\n \t}\n@@ -1652,7 +1623,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \t\treturn error(_(\"Did you hand edit your patch?\\n\"\n \t\t\t\t\"It does not apply to blobs recorded in its index.\"));\n \n-\tif (write_index_as_tree(their_tree, &the_index, index_path, 0, NULL))\n+\tif (write_index_as_tree(their_tree.hash, &the_index, index_path, 0, NULL))\n \t\treturn error(\"could not write tree\");\n \n \tsay(state, stdout, _(\"Falling back to patching base and 3-way merge...\"));\n@@ -1668,11 +1639,22 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \t * changes.\n \t */\n \n-\tif (run_fallback_merge_recursive(state, orig_tree, our_tree, their_tree)) {\n+\tinit_merge_options(&o);\n+\n+\to.branch1 = \"HEAD\";\n+\ttheir_tree_name = xstrfmt(\"%.*s\", linelen(state->msg), state->msg);\n+\to.branch2 = their_tree_name;\n+\n+\tif (state->quiet)\n+\t\to.verbosity = 0;\n+\n+\tif (merge_recursive_generic(&o, &our_tree, &their_tree, 1, bases, &result)) {\n \t\trerere(state->allow_rerere_autoupdate);\n+\t\tfree(their_tree_name);\n \t\treturn error(_(\"Failed to merge in the changes.\"));\n \t}\n \n+\tfree(their_tree_name);\n \treturn 0;\n }\n \n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292728","messageId":"f585a96373145c7c0e4f053557e80e37afb7ca1a.1470051326.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1470051326.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 09/16] merge-recursive: handle return values indicating errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-01T11:44:26Z","receivedAt":"2016-08-01T11:45:31Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We are about to libify the recursive merge machinery, where we only\ndie() in case of a bug or memory contention. To that end, we must heed\nnegative return values as indicating errors.\n\nThis requires our functions to be careful to pass through error\nconditions in call chains, and for quite a few functions this means\nthat they have to return values to begin with.\n\nThe next step will be to convert the places where we currently die() to\nreturn negative values (read: -1) instead.\n\nNote that we ignore errors reported by make_room_for_path(), consistent\nwith the previous behavior (update_file_flags() used the return value of\nmake_room_for_path() only to indicate an early return, but not a fatal\nerror): if the error is really a fatal error, we will notice later; If\nnot, it was not that serious a problem to begin with. (Witnesses in\nfavor of this reasoning are t4151-am-abort and t7610-mergetool, which\nwould start failing if we stopped on errors reported by\nmake_room_for_path()).\n\nAlso note: while this patch makes the code slightly less readable in\nupdate_file_flags() (we introduce a new \"goto free_buf;\" instead of\nan explicit \"free(buf); return;\"), it is a preparatory change for\nthe next patch where we will convert all of the die() calls in the same\nfunction to go through the free_buf return path instead.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 252 ++++++++++++++++++++++++++++++++----------------------\n 1 file changed, 150 insertions(+), 102 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 1f86338..6beb1e4 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -742,12 +742,12 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n }\n \n-static void update_file_flags(struct merge_options *o,\n-\t\t\t      const struct object_id *oid,\n-\t\t\t      unsigned mode,\n-\t\t\t      const char *path,\n-\t\t\t      int update_cache,\n-\t\t\t      int update_wd)\n+static int update_file_flags(struct merge_options *o,\n+\t\t\t     const struct object_id *oid,\n+\t\t\t     unsigned mode,\n+\t\t\t     const char *path,\n+\t\t\t     int update_cache,\n+\t\t\t     int update_wd)\n {\n \tif (o->call_depth)\n \t\tupdate_wd = 0;\n@@ -783,8 +783,7 @@ static void update_file_flags(struct merge_options *o,\n \n \t\tif (make_room_for_path(o, path) < 0) {\n \t\t\tupdate_wd = 0;\n-\t\t\tfree(buf);\n-\t\t\tgoto update_index;\n+\t\t\tgoto free_buf;\n \t\t}\n \t\tif (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) {\n \t\t\tint fd;\n@@ -807,20 +806,22 @@ static void update_file_flags(struct merge_options *o,\n \t\t} else\n \t\t\tdie(_(\"do not know what to do with %06o %s '%s'\"),\n \t\t\t    mode, oid_to_hex(oid), path);\n+ free_buf:\n \t\tfree(buf);\n \t}\n  update_index:\n \tif (update_cache)\n \t\tadd_cacheinfo(mode, oid, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n+\treturn 0;\n }\n \n-static void update_file(struct merge_options *o,\n-\t\t\tint clean,\n-\t\t\tconst struct object_id *oid,\n-\t\t\tunsigned mode,\n-\t\t\tconst char *path)\n+static int update_file(struct merge_options *o,\n+\t\t       int clean,\n+\t\t       const struct object_id *oid,\n+\t\t       unsigned mode,\n+\t\t       const char *path)\n {\n-\tupdate_file_flags(o, oid, mode, path, o->call_depth || clean, !o->call_depth);\n+\treturn update_file_flags(o, oid, mode, path, o->call_depth || clean, !o->call_depth);\n }\n \n /* Low level file merging, update and removal */\n@@ -1019,7 +1020,7 @@ static int merge_file_one(struct merge_options *o,\n \treturn merge_file_1(o, &one, &a, &b, branch1, branch2, mfi);\n }\n \n-static void handle_change_delete(struct merge_options *o,\n+static int handle_change_delete(struct merge_options *o,\n \t\t\t\t const char *path,\n \t\t\t\t const struct object_id *o_oid, int o_mode,\n \t\t\t\t const struct object_id *a_oid, int a_mode,\n@@ -1027,6 +1028,7 @@ static void handle_change_delete(struct merge_options *o,\n \t\t\t\t const char *change, const char *change_past)\n {\n \tchar *renamed = NULL;\n+\tint ret = 0;\n \tif (dir_in_way(path, !o->call_depth)) {\n \t\trenamed = unique_path(o, path, a_oid ? o->branch1 : o->branch2);\n \t}\n@@ -1037,21 +1039,23 @@ static void handle_change_delete(struct merge_options *o,\n \t\t * correct; since there is no true \"middle point\" between\n \t\t * them, simply reuse the base version for virtual merge base.\n \t\t */\n-\t\tremove_file_from_cache(path);\n-\t\tupdate_file(o, 0, o_oid, o_mode, renamed ? renamed : path);\n+\t\tret = remove_file_from_cache(path);\n+\t\tif (!ret)\n+\t\t\tret = update_file(o, 0, o_oid, o_mode,\n+\t\t\t\t\t  renamed ? renamed : path);\n \t} else if (!a_oid) {\n \t\tif (!renamed) {\n \t\t\toutput(o, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n \t\t\t       \"and %s in %s. Version %s of %s left in tree.\"),\n \t\t\t       change, path, o->branch1, change_past,\n \t\t\t       o->branch2, o->branch2, path);\n-\t\t\tupdate_file(o, 0, b_oid, b_mode, path);\n+\t\t\tret = update_file(o, 0, b_oid, b_mode, path);\n \t\t} else {\n \t\t\toutput(o, 1, _(\"CONFLICT (%s/delete): %s deleted in %s \"\n \t\t\t       \"and %s in %s. Version %s of %s left in tree at %s.\"),\n \t\t\t       change, path, o->branch1, change_past,\n \t\t\t       o->branch2, o->branch2, path, renamed);\n-\t\t\tupdate_file(o, 0, b_oid, b_mode, renamed);\n+\t\t\tret = update_file(o, 0, b_oid, b_mode, renamed);\n \t\t}\n \t} else {\n \t\tif (!renamed) {\n@@ -1064,7 +1068,7 @@ static void handle_change_delete(struct merge_options *o,\n \t\t\t       \"and %s in %s. Version %s of %s left in tree at %s.\"),\n \t\t\t       change, path, o->branch2, change_past,\n \t\t\t       o->branch1, o->branch1, path, renamed);\n-\t\t\tupdate_file(o, 0, a_oid, a_mode, renamed);\n+\t\t\tret = update_file(o, 0, a_oid, a_mode, renamed);\n \t\t}\n \t\t/*\n \t\t * No need to call update_file() on path when !renamed, since\n@@ -1074,9 +1078,11 @@ static void handle_change_delete(struct merge_options *o,\n \t\t */\n \t}\n \tfree(renamed);\n+\n+\treturn ret;\n }\n \n-static void conflict_rename_delete(struct merge_options *o,\n+static int conflict_rename_delete(struct merge_options *o,\n \t\t\t\t   struct diff_filepair *pair,\n \t\t\t\t   const char *rename_branch,\n \t\t\t\t   const char *other_branch)\n@@ -1096,21 +1102,20 @@ static void conflict_rename_delete(struct merge_options *o,\n \t\tb_mode = dest->mode;\n \t}\n \n-\thandle_change_delete(o,\n-\t\t\t     o->call_depth ? orig->path : dest->path,\n-\t\t\t     &orig->oid, orig->mode,\n-\t\t\t     a_oid, a_mode,\n-\t\t\t     b_oid, b_mode,\n-\t\t\t     _(\"rename\"), _(\"renamed\"));\n-\n-\tif (o->call_depth) {\n-\t\tremove_file_from_cache(dest->path);\n-\t} else {\n-\t\tupdate_stages(dest->path, NULL,\n-\t\t\t      rename_branch == o->branch1 ? dest : NULL,\n-\t\t\t      rename_branch == o->branch1 ? NULL : dest);\n-\t}\n+\tif (handle_change_delete(o,\n+\t\t\t\t o->call_depth ? orig->path : dest->path,\n+\t\t\t\t &orig->oid, orig->mode,\n+\t\t\t\t a_oid, a_mode,\n+\t\t\t\t b_oid, b_mode,\n+\t\t\t\t _(\"rename\"), _(\"renamed\")))\n+\t\treturn -1;\n \n+\tif (o->call_depth)\n+\t\treturn remove_file_from_cache(dest->path);\n+\telse\n+\t\treturn update_stages(dest->path, NULL,\n+\t\t\t\t     rename_branch == o->branch1 ? dest : NULL,\n+\t\t\t\t     rename_branch == o->branch1 ? NULL : dest);\n }\n \n static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,\n@@ -1126,7 +1131,7 @@ static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,\n \treturn target;\n }\n \n-static void handle_file(struct merge_options *o,\n+static int handle_file(struct merge_options *o,\n \t\t\tstruct diff_filespec *rename,\n \t\t\tint stage,\n \t\t\tstruct rename_conflict_info *ci)\n@@ -1136,6 +1141,7 @@ static void handle_file(struct merge_options *o,\n \tconst char *cur_branch, *other_branch;\n \tstruct diff_filespec other;\n \tstruct diff_filespec *add;\n+\tint ret;\n \n \tif (stage == 2) {\n \t\tdst_entry = ci->dst_entry1;\n@@ -1150,7 +1156,8 @@ static void handle_file(struct merge_options *o,\n \tadd = filespec_from_entry(&other, dst_entry, stage ^ 1);\n \tif (add) {\n \t\tchar *add_name = unique_path(o, rename->path, other_branch);\n-\t\tupdate_file(o, 0, &add->oid, add->mode, add_name);\n+\t\tif (update_file(o, 0, &add->oid, add->mode, add_name))\n+\t\t\treturn -1;\n \n \t\tremove_file(o, 0, rename->path, 0);\n \t\tdst_name = unique_path(o, rename->path, cur_branch);\n@@ -1161,17 +1168,20 @@ static void handle_file(struct merge_options *o,\n \t\t\t       rename->path, other_branch, dst_name);\n \t\t}\n \t}\n-\tupdate_file(o, 0, &rename->oid, rename->mode, dst_name);\n-\tif (stage == 2)\n-\t\tupdate_stages(rename->path, NULL, rename, add);\n+\tif ((ret = update_file(o, 0, &rename->oid, rename->mode, dst_name)))\n+\t\t; /* fall through, do allow dst_name to be released */\n+\telse if (stage == 2)\n+\t\tret = update_stages(rename->path, NULL, rename, add);\n \telse\n-\t\tupdate_stages(rename->path, NULL, add, rename);\n+\t\tret = update_stages(rename->path, NULL, add, rename);\n \n \tif (dst_name != rename->path)\n \t\tfree(dst_name);\n+\n+\treturn ret;\n }\n \n-static void conflict_rename_rename_1to2(struct merge_options *o,\n+static int conflict_rename_rename_1to2(struct merge_options *o,\n \t\t\t\t\tstruct rename_conflict_info *ci)\n {\n \t/* One file was renamed in both branches, but to different names. */\n@@ -1194,14 +1204,16 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n \t\t\t\t &a->oid, a->mode,\n \t\t\t\t &b->oid, b->mode,\n \t\t\t\t ci->branch1, ci->branch2, &mfi))\n-\t\t\treturn;\n+\t\t\treturn -1;\n+\n \t\t/*\n \t\t * FIXME: For rename/add-source conflicts (if we could detect\n \t\t * such), this is wrong.  We should instead find a unique\n \t\t * pathname and then either rename the add-source file to that\n \t\t * unique path, or use that unique path instead of src here.\n \t\t */\n-\t\tupdate_file(o, 0, &mfi.oid, mfi.mode, one->path);\n+\t\tif (update_file(o, 0, &mfi.oid, mfi.mode, one->path))\n+\t\t\treturn -1;\n \n \t\t/*\n \t\t * Above, we put the merged content at the merge-base's\n@@ -1212,22 +1224,26 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n \t\t * resolving the conflict at that path in its favor.\n \t\t */\n \t\tadd = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);\n-\t\tif (add)\n-\t\t\tupdate_file(o, 0, &add->oid, add->mode, a->path);\n+\t\tif (add) {\n+\t\t\tif (update_file(o, 0, &add->oid, add->mode, a->path))\n+\t\t\t\treturn -1;\n+\t\t}\n \t\telse\n \t\t\tremove_file_from_cache(a->path);\n \t\tadd = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);\n-\t\tif (add)\n-\t\t\tupdate_file(o, 0, &add->oid, add->mode, b->path);\n+\t\tif (add) {\n+\t\t\tif (update_file(o, 0, &add->oid, add->mode, b->path))\n+\t\t\t\treturn -1;\n+\t\t}\n \t\telse\n \t\t\tremove_file_from_cache(b->path);\n-\t} else {\n-\t\thandle_file(o, a, 2, ci);\n-\t\thandle_file(o, b, 3, ci);\n-\t}\n+\t} else if (handle_file(o, a, 2, ci) || handle_file(o, b, 3, ci))\n+\t\treturn -1;\n+\n+\treturn 0;\n }\n \n-static void conflict_rename_rename_2to1(struct merge_options *o,\n+static int conflict_rename_rename_2to1(struct merge_options *o,\n \t\t\t\t\tstruct rename_conflict_info *ci)\n {\n \t/* Two files, a & b, were renamed to the same thing, c. */\n@@ -1238,6 +1254,7 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \tchar *path = c1->path; /* == c2->path */\n \tstruct merge_file_info mfi_c1;\n \tstruct merge_file_info mfi_c2;\n+\tint ret;\n \n \toutput(o, 1, _(\"CONFLICT (rename/rename): \"\n \t       \"Rename %s->%s in %s. \"\n@@ -1254,7 +1271,7 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \t    merge_file_special_markers(o, b, &ci->ren2_other, c2,\n \t\t\t\t       o->branch1, ci->ren2_other.path,\n \t\t\t\t       o->branch2, c2->path, &mfi_c2))\n-\t\treturn;\n+\t\treturn -1;\n \n \tif (o->call_depth) {\n \t\t/*\n@@ -1265,19 +1282,25 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \t\t * again later for the non-recursive merge.\n \t\t */\n \t\tremove_file(o, 0, path, 0);\n-\t\tupdate_file(o, 0, &mfi_c1.oid, mfi_c1.mode, a->path);\n-\t\tupdate_file(o, 0, &mfi_c2.oid, mfi_c2.mode, b->path);\n+\t\tret = update_file(o, 0, &mfi_c1.oid, mfi_c1.mode, a->path);\n+\t\tif (!ret)\n+\t\t\tret = update_file(o, 0, &mfi_c2.oid, mfi_c2.mode,\n+\t\t\t\t\t  b->path);\n \t} else {\n \t\tchar *new_path1 = unique_path(o, path, ci->branch1);\n \t\tchar *new_path2 = unique_path(o, path, ci->branch2);\n \t\toutput(o, 1, _(\"Renaming %s to %s and %s to %s instead\"),\n \t\t       a->path, new_path1, b->path, new_path2);\n \t\tremove_file(o, 0, path, 0);\n-\t\tupdate_file(o, 0, &mfi_c1.oid, mfi_c1.mode, new_path1);\n-\t\tupdate_file(o, 0, &mfi_c2.oid, mfi_c2.mode, new_path2);\n+\t\tret = update_file(o, 0, &mfi_c1.oid, mfi_c1.mode, new_path1);\n+\t\tif (!ret)\n+\t\t\tret = update_file(o, 0, &mfi_c2.oid, mfi_c2.mode,\n+\t\t\t\t\t  new_path2);\n \t\tfree(new_path2);\n \t\tfree(new_path1);\n \t}\n+\n+\treturn ret;\n }\n \n static int process_renames(struct merge_options *o,\n@@ -1462,12 +1485,13 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t * update_file_flags() instead of\n \t\t\t\t * update_file().\n \t\t\t\t */\n-\t\t\t\tupdate_file_flags(o,\n-\t\t\t\t\t\t  &ren1->pair->two->oid,\n-\t\t\t\t\t\t  ren1->pair->two->mode,\n-\t\t\t\t\t\t  ren1_dst,\n-\t\t\t\t\t\t  1, /* update_cache */\n-\t\t\t\t\t\t  0  /* update_wd    */);\n+\t\t\t\tif (update_file_flags(o,\n+\t\t\t\t\t\t      &ren1->pair->two->oid,\n+\t\t\t\t\t\t      ren1->pair->two->mode,\n+\t\t\t\t\t\t      ren1_dst,\n+\t\t\t\t\t\t      1, /* update_cache */\n+\t\t\t\t\t\t      0  /* update_wd    */))\n+\t\t\t\t\tclean_merge = -1;\n \t\t\t} else if (!oid_eq(&dst_other.oid, &null_oid)) {\n \t\t\t\tclean_merge = 0;\n \t\t\t\ttry_merge = 1;\n@@ -1482,22 +1506,28 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t\t\t\t   ren1->pair->two->mode,\n \t\t\t\t\t\t\t   &dst_other.oid,\n \t\t\t\t\t\t\t   dst_other.mode,\n-\t\t\t\t\t\t\t   branch1, branch2, &mfi))\n-\t\t\t\t\t\treturn -1;\n+\t\t\t\t\t\t\t   branch1, branch2, &mfi)) {\n+\t\t\t\t\t\tclean_merge = -1;\n+\t\t\t\t\t\tgoto cleanup_and_return;\n+\t\t\t\t\t}\n \t\t\t\t\toutput(o, 1, _(\"Adding merged %s\"), ren1_dst);\n-\t\t\t\t\tupdate_file(o, 0, &mfi.oid,\n-\t\t\t\t\t\t    mfi.mode, ren1_dst);\n+\t\t\t\t\tif (update_file(o, 0, &mfi.oid,\n+\t\t\t\t\t\t\tmfi.mode, ren1_dst))\n+\t\t\t\t\t\tclean_merge = -1;\n \t\t\t\t\ttry_merge = 0;\n \t\t\t\t} else {\n \t\t\t\t\tchar *new_path = unique_path(o, ren1_dst, branch2);\n \t\t\t\t\toutput(o, 1, _(\"Adding as %s instead\"), new_path);\n-\t\t\t\t\tupdate_file(o, 0, &dst_other.oid,\n-\t\t\t\t\t\t    dst_other.mode, new_path);\n+\t\t\t\t\tif (update_file(o, 0, &dst_other.oid,\n+\t\t\t\t\t\t\tdst_other.mode, new_path))\n+\t\t\t\t\t\tclean_merge = -1;\n \t\t\t\t\tfree(new_path);\n \t\t\t\t}\n \t\t\t} else\n \t\t\t\ttry_merge = 1;\n \n+\t\t\tif (clean_merge < 0)\n+\t\t\t\tgoto cleanup_and_return;\n \t\t\tif (try_merge) {\n \t\t\t\tstruct diff_filespec *one, *a, *b;\n \t\t\t\tsrc_other.path = (char *)ren1_src;\n@@ -1524,6 +1554,7 @@ static int process_renames(struct merge_options *o,\n \t\t\t}\n \t\t}\n \t}\n+cleanup_and_return:\n \tstring_list_clear(&a_by_dst, 0);\n \tstring_list_clear(&b_by_dst, 0);\n \n@@ -1586,18 +1617,18 @@ static int blob_unchanged(const struct object_id *o_oid,\n \treturn ret;\n }\n \n-static void handle_modify_delete(struct merge_options *o,\n+static int handle_modify_delete(struct merge_options *o,\n \t\t\t\t const char *path,\n \t\t\t\t struct object_id *o_oid, int o_mode,\n \t\t\t\t struct object_id *a_oid, int a_mode,\n \t\t\t\t struct object_id *b_oid, int b_mode)\n {\n-\thandle_change_delete(o,\n-\t\t\t     path,\n-\t\t\t     o_oid, o_mode,\n-\t\t\t     a_oid, a_mode,\n-\t\t\t     b_oid, b_mode,\n-\t\t\t     _(\"modify\"), _(\"modified\"));\n+\treturn handle_change_delete(o,\n+\t\t\t\t    path,\n+\t\t\t\t    o_oid, o_mode,\n+\t\t\t\t    a_oid, a_mode,\n+\t\t\t\t    b_oid, b_mode,\n+\t\t\t\t    _(\"modify\"), _(\"modified\"));\n }\n \n static int merge_content(struct merge_options *o,\n@@ -1671,7 +1702,8 @@ static int merge_content(struct merge_options *o,\n \t\toutput(o, 1, _(\"CONFLICT (%s): Merge conflict in %s\"),\n \t\t\t\treason, path);\n \t\tif (rename_conflict_info && !df_conflict_remains)\n-\t\t\tupdate_stages(path, &one, &a, &b);\n+\t\t\tif (update_stages(path, &one, &a, &b))\n+\t\t\t\treturn -1;\n \t}\n \n \tif (df_conflict_remains) {\n@@ -1679,30 +1711,33 @@ static int merge_content(struct merge_options *o,\n \t\tif (o->call_depth) {\n \t\t\tremove_file_from_cache(path);\n \t\t} else {\n-\t\t\tif (!mfi.clean)\n-\t\t\t\tupdate_stages(path, &one, &a, &b);\n-\t\t\telse {\n+\t\t\tif (!mfi.clean) {\n+\t\t\t\tif (update_stages(path, &one, &a, &b))\n+\t\t\t\t\treturn -1;\n+\t\t\t} else {\n \t\t\t\tint file_from_stage2 = was_tracked(path);\n \t\t\t\tstruct diff_filespec merged;\n \t\t\t\toidcpy(&merged.oid, &mfi.oid);\n \t\t\t\tmerged.mode = mfi.mode;\n \n-\t\t\t\tupdate_stages(path, NULL,\n-\t\t\t\t\t      file_from_stage2 ? &merged : NULL,\n-\t\t\t\t\t      file_from_stage2 ? NULL : &merged);\n+\t\t\t\tif (update_stages(path, NULL,\n+\t\t\t\t\t\t  file_from_stage2 ? &merged : NULL,\n+\t\t\t\t\t\t  file_from_stage2 ? NULL : &merged))\n+\t\t\t\t\treturn -1;\n \t\t\t}\n \n \t\t}\n \t\tnew_path = unique_path(o, path, rename_conflict_info->branch1);\n \t\toutput(o, 1, _(\"Adding as %s instead\"), new_path);\n-\t\tupdate_file(o, 0, &mfi.oid, mfi.mode, new_path);\n+\t\tif (update_file(o, 0, &mfi.oid, mfi.mode, new_path)) {\n+\t\t\tfree(new_path);\n+\t\t\treturn -1;\n+\t\t}\n \t\tfree(new_path);\n \t\tmfi.clean = 0;\n-\t} else {\n-\t\tupdate_file(o, mfi.clean, &mfi.oid, mfi.mode, path);\n-\t}\n+\t} else if (update_file(o, mfi.clean, &mfi.oid, mfi.mode, path))\n+\t\treturn -1;\n \treturn mfi.clean;\n-\n }\n \n /* Per entry merge function */\n@@ -1730,17 +1765,21 @@ static int process_entry(struct merge_options *o,\n \t\t\tbreak;\n \t\tcase RENAME_DELETE:\n \t\t\tclean_merge = 0;\n-\t\t\tconflict_rename_delete(o, conflict_info->pair1,\n-\t\t\t\t\t       conflict_info->branch1,\n-\t\t\t\t\t       conflict_info->branch2);\n+\t\t\tif (conflict_rename_delete(o,\n+\t\t\t\t\t\t   conflict_info->pair1,\n+\t\t\t\t\t\t   conflict_info->branch1,\n+\t\t\t\t\t\t   conflict_info->branch2))\n+\t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_ONE_FILE_TO_TWO:\n \t\t\tclean_merge = 0;\n-\t\t\tconflict_rename_rename_1to2(o, conflict_info);\n+\t\t\tif (conflict_rename_rename_1to2(o, conflict_info))\n+\t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tcase RENAME_TWO_FILES_TO_ONE:\n \t\t\tclean_merge = 0;\n-\t\t\tconflict_rename_rename_2to1(o, conflict_info);\n+\t\t\tif (conflict_rename_rename_2to1(o, conflict_info))\n+\t\t\t\tclean_merge = -1;\n \t\t\tbreak;\n \t\tdefault:\n \t\t\tentry->processed = 0;\n@@ -1760,8 +1799,9 @@ static int process_entry(struct merge_options *o,\n \t\t} else {\n \t\t\t/* Modify/delete; deleted side may have put a directory in the way */\n \t\t\tclean_merge = 0;\n-\t\t\thandle_modify_delete(o, path, o_oid, o_mode,\n-\t\t\t\t\t     a_oid, a_mode, b_oid, b_mode);\n+\t\t\tif (handle_modify_delete(o, path, o_oid, o_mode,\n+\t\t\t\t\t\t a_oid, a_mode, b_oid, b_mode))\n+\t\t\t\tclean_merge = -1;\n \t\t}\n \t} else if ((!o_oid && a_oid && !b_oid) ||\n \t\t   (!o_oid && !a_oid && b_oid)) {\n@@ -1793,14 +1833,16 @@ static int process_entry(struct merge_options *o,\n \t\t\toutput(o, 1, _(\"CONFLICT (%s): There is a directory with name %s in %s. \"\n \t\t\t       \"Adding %s as %s\"),\n \t\t\t       conf, path, other_branch, path, new_path);\n-\t\t\tupdate_file(o, 0, oid, mode, new_path);\n-\t\t\tif (o->call_depth)\n+\t\t\tif (update_file(o, 0, oid, mode, new_path))\n+\t\t\t\tclean_merge = -1;\n+\t\t\telse if (o->call_depth)\n \t\t\t\tremove_file_from_cache(path);\n \t\t\tfree(new_path);\n \t\t} else {\n \t\t\toutput(o, 2, _(\"Adding %s\"), path);\n \t\t\t/* do not overwrite file if already present */\n-\t\t\tupdate_file_flags(o, oid, mode, path, 1, !a_oid);\n+\t\t\tif (update_file_flags(o, oid, mode, path, 1, !a_oid))\n+\t\t\t\tclean_merge = -1;\n \t\t}\n \t} else if (a_oid && b_oid) {\n \t\t/* Case C: Added in both (check for same permissions) and */\n@@ -1863,12 +1905,18 @@ int merge_trees(struct merge_options *o,\n \t\tre_head  = get_renames(o, head, common, head, merge, entries);\n \t\tre_merge = get_renames(o, merge, common, head, merge, entries);\n \t\tclean = process_renames(o, re_head, re_merge);\n+\t\tif (clean < 0)\n+\t\t\treturn clean;\n \t\tfor (i = entries->nr-1; 0 <= i; i--) {\n \t\t\tconst char *path = entries->items[i].string;\n \t\t\tstruct stage_data *e = entries->items[i].util;\n-\t\t\tif (!e->processed\n-\t\t\t\t&& !process_entry(o, path, e))\n-\t\t\t\tclean = 0;\n+\t\t\tif (!e->processed) {\n+\t\t\t\tint ret = process_entry(o, path, e);\n+\t\t\t\tif (!ret)\n+\t\t\t\t\tclean = 0;\n+\t\t\t\telse if (ret < 0)\n+\t\t\t\t\treturn ret;\n+\t\t\t}\n \t\t}\n \t\tfor (i = 0; i < entries->nr; i++) {\n \t\t\tstruct stage_data *e = entries->items[i].util;\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292729","messageId":"2983ef4f7596c110b771f1da82dc84e426de19fd.1470051326.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1470051326.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 14/16] merge-recursive: offer an option to retain the output in 'obuf'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-01T11:44:50Z","receivedAt":"2016-08-01T11:45:34Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Since 66a155b (Enable output buffering in merge-recursive., 2007-01-14),\nwe already accumulate the output in a buffer. The idea was to avoid\ninterfering with the progress output that goes to stderr, which is\nunbuffered, when we write to stdout, which is buffered.\n\nWe extend that buffering to allow the caller to handle the output\n(possibly suppressing it). This will help us when extending the\nsequencer to do rebase -i's brunt work: it does not want the picks to\nprint anything by default but instead determine itself whether to print\nthe output or not.\n\nNote that we also redirect the error messages into the output buffer\nwhen the caller asked not to flush the output buffer, for two reasons:\n1) to retain the correct output order, and 2) to allow the caller to\nsuppress *all* output.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 17 +++++++++++++----\n merge-recursive.h |  2 +-\n 2 files changed, 14 insertions(+), 5 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 99c9635..ec50932 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -25,7 +25,7 @@\n \n static void flush_output(struct merge_options *o)\n {\n-\tif (o->obuf.len) {\n+\tif (o->buffer_output < 2 && o->obuf.len) {\n \t\tfputs(o->obuf.buf, stdout);\n \t\tstrbuf_reset(&o->obuf);\n \t}\n@@ -35,12 +35,21 @@ static int err(struct merge_options *o, const char *err, ...)\n {\n \tva_list params;\n \n-\tflush_output(o);\n+\tif (o->buffer_output < 2)\n+\t\tflush_output(o);\n+\telse {\n+\t\tstrbuf_complete(&o->obuf, '\\n');\n+\t\tstrbuf_addstr(&o->obuf, \"error: \");\n+\t}\n \tva_start(params, err);\n \tstrbuf_vaddf(&o->obuf, err, params);\n \tva_end(params);\n-\terror(\"%s\", o->obuf.buf);\n-\tstrbuf_reset(&o->obuf);\n+\tif (o->buffer_output > 1)\n+\t\tstrbuf_addch(&o->obuf, '\\n');\n+\telse {\n+\t\terror(\"%s\", o->obuf.buf);\n+\t\tstrbuf_reset(&o->obuf);\n+\t}\n \n \treturn -1;\n }\ndiff --git a/merge-recursive.h b/merge-recursive.h\nindex d415724..735343b 100644\n--- a/merge-recursive.h\n+++ b/merge-recursive.h\n@@ -13,7 +13,7 @@ struct merge_options {\n \t\tMERGE_RECURSIVE_THEIRS\n \t} recursive_variant;\n \tconst char *subtree_shift;\n-\tunsigned buffer_output : 1;\n+\tunsigned buffer_output; /* 1: output at end, 2: keep buffered */\n \tunsigned renormalize : 1;\n \tlong xdl_opts;\n \tint verbosity;\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292730","messageId":"9bea9a08c378376490b2dd46e95f51b278dfbc1b.1470051326.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1470051326.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 10/16] merge-recursive: switch to returning errors instead of dying","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-01T11:44:29Z","receivedAt":"2016-08-01T11:45:35Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The recursive merge machinery is supposed to be a library function, i.e.\nit should return an error when it fails. Originally the functions were\npart of the builtin \"merge-recursive\", though, where it was simpler to\ncall die() and be done with error handling.\n\nThe existing callers were already prepared to detect negative return\nvalues to indicate errors and to behave as previously: exit with code 128\n(which is the same thing that die() does, after printing the message).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 62 +++++++++++++++++++++++++++++++------------------------\n 1 file changed, 35 insertions(+), 27 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 6beb1e4..bc59815 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -275,8 +275,10 @@ struct tree *write_tree_from_memory(struct merge_options *o)\n \t\tactive_cache_tree = cache_tree();\n \n \tif (!cache_tree_fully_valid(active_cache_tree) &&\n-\t    cache_tree_update(&the_index, 0) < 0)\n-\t\tdie(_(\"error building trees\"));\n+\t    cache_tree_update(&the_index, 0) < 0) {\n+\t\terror(_(\"error building trees\"));\n+\t\treturn NULL;\n+\t}\n \n \tresult = lookup_tree(active_cache_tree->sha1);\n \n@@ -716,12 +718,10 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \t/* Make sure leading directories are created */\n \tstatus = safe_create_leading_directories_const(path);\n \tif (status) {\n-\t\tif (status == SCLD_EXISTS) {\n+\t\tif (status == SCLD_EXISTS)\n \t\t\t/* something else exists */\n-\t\t\terror(msg, path, _(\": perhaps a D/F conflict?\"));\n-\t\t\treturn -1;\n-\t\t}\n-\t\tdie(msg, path, \"\");\n+\t\t\treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n+\t\treturn error(msg, path, \"\");\n \t}\n \n \t/*\n@@ -749,6 +749,8 @@ static int update_file_flags(struct merge_options *o,\n \t\t\t     int update_cache,\n \t\t\t     int update_wd)\n {\n+\tint ret = 0;\n+\n \tif (o->call_depth)\n \t\tupdate_wd = 0;\n \n@@ -769,9 +771,11 @@ static int update_file_flags(struct merge_options *o,\n \n \t\tbuf = read_sha1_file(oid->hash, &type, &size);\n \t\tif (!buf)\n-\t\t\tdie(_(\"cannot read object %s '%s'\"), oid_to_hex(oid), path);\n-\t\tif (type != OBJ_BLOB)\n-\t\t\tdie(_(\"blob expected for %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\treturn error(_(\"cannot read object %s '%s'\"), oid_to_hex(oid), path);\n+\t\tif (type != OBJ_BLOB) {\n+\t\t\tret = error(_(\"blob expected for %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\tgoto free_buf;\n+\t\t}\n \t\tif (S_ISREG(mode)) {\n \t\t\tstruct strbuf strbuf = STRBUF_INIT;\n \t\t\tif (convert_to_working_tree(path, buf, size, &strbuf)) {\n@@ -792,8 +796,11 @@ static int update_file_flags(struct merge_options *o,\n \t\t\telse\n \t\t\t\tmode = 0666;\n \t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n-\t\t\tif (fd < 0)\n-\t\t\t\tdie_errno(_(\"failed to open '%s'\"), path);\n+\t\t\tif (fd < 0) {\n+\t\t\t\tret = error_errno(_(\"failed to open '%s'\"),\n+\t\t\t\t\t\t  path);\n+\t\t\t\tgoto free_buf;\n+\t\t\t}\n \t\t\twrite_in_full(fd, buf, size);\n \t\t\tclose(fd);\n \t\t} else if (S_ISLNK(mode)) {\n@@ -801,18 +808,18 @@ static int update_file_flags(struct merge_options *o,\n \t\t\tsafe_create_leading_directories_const(path);\n \t\t\tunlink(path);\n \t\t\tif (symlink(lnk, path))\n-\t\t\t\tdie_errno(_(\"failed to symlink '%s'\"), path);\n+\t\t\t\tret = error_errno(_(\"failed to symlink '%s'\"), path);\n \t\t\tfree(lnk);\n \t\t} else\n-\t\t\tdie(_(\"do not know what to do with %06o %s '%s'\"),\n-\t\t\t    mode, oid_to_hex(oid), path);\n+\t\t\tret = error(_(\"do not know what to do with %06o %s '%s'\"),\n+\t\t\t\t    mode, oid_to_hex(oid), path);\n  free_buf:\n \t\tfree(buf);\n \t}\n  update_index:\n-\tif (update_cache)\n+\tif (!ret && update_cache)\n \t\tadd_cacheinfo(mode, oid, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n-\treturn 0;\n+\treturn ret;\n }\n \n static int update_file(struct merge_options *o,\n@@ -938,20 +945,22 @@ static int merge_file_1(struct merge_options *o,\n \t\t\toidcpy(&result->oid, &a->oid);\n \t\telse if (S_ISREG(a->mode)) {\n \t\t\tmmbuffer_t result_buf;\n-\t\t\tint merge_status;\n+\t\t\tint ret = 0, merge_status;\n \n \t\t\tmerge_status = merge_3way(o, &result_buf, one, a, b,\n \t\t\t\t\t\t  branch1, branch2);\n \n \t\t\tif ((merge_status < 0) || !result_buf.ptr)\n-\t\t\t\tdie(_(\"Failed to execute internal merge\"));\n+\t\t\t\tret = error(_(\"Failed to execute internal merge\"));\n \n-\t\t\tif (write_sha1_file(result_buf.ptr, result_buf.size,\n-\t\t\t\t\t    blob_type, result->oid.hash))\n-\t\t\t\tdie(_(\"Unable to add %s to database\"),\n-\t\t\t\t    a->path);\n+\t\t\tif (!ret && write_sha1_file(result_buf.ptr, result_buf.size,\n+\t\t\t\t\t\t    blob_type, result->oid.hash))\n+\t\t\t\tret = error(_(\"Unable to add %s to database\"),\n+\t\t\t\t\t    a->path);\n \n \t\t\tfree(result_buf.ptr);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n \t\t\tresult->clean = (merge_status == 0);\n \t\t} else if (S_ISGITLINK(a->mode)) {\n \t\t\tresult->clean = merge_submodule(result->oid.hash,\n@@ -1885,11 +1894,10 @@ int merge_trees(struct merge_options *o,\n \n \tif (code != 0) {\n \t\tif (show(o, 4) || o->call_depth)\n-\t\t\tdie(_(\"merging of trees %s and %s failed\"),\n+\t\t\terror(_(\"merging of trees %s and %s failed\"),\n \t\t\t    oid_to_hex(&head->object.oid),\n \t\t\t    oid_to_hex(&merge->object.oid));\n-\t\telse\n-\t\t\texit(128);\n+\t\treturn -1;\n \t}\n \n \tif (unmerged_cache()) {\n@@ -2021,7 +2029,7 @@ int merge_recursive(struct merge_options *o,\n \t\to->call_depth--;\n \n \t\tif (!merged_common_ancestors)\n-\t\t\tdie(_(\"merge returned no commit\"));\n+\t\t\treturn error(_(\"merge returned no commit\"));\n \t}\n \n \tdiscard_cache();\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292731","messageId":"3a2f8fb52988dd0cf974b4e9dd954609c352ab44.1470051326.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1470051326.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 12/16] merge-recursive: flush output buffer before printing error messages","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-01T11:44:37Z","receivedAt":"2016-08-01T11:45:39Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The data structure passed to the recursive merge machinery has a feature\nwhere the caller can ask for the output to be buffered into a strbuf, by\nsetting the field 'buffer_output'.\n\nPreviously, we died without flushing, losing accumulated output.  With\nthis patch, we show the output first, and only then print the error\nmessage.\n\nCurrently, the only user of that buffering is merge_recursive() itself,\nto avoid the progress output to interfere.\n\nIn the next patches, we will introduce a new buffer_output mode that\nforces merge_recursive() to retain the output buffer for further\nprocessing by the caller. If the caller asked for that, we will then\nalso write the error messages into the output buffer. This is necessary\nto give the caller more control not only how to react in case of errors\nbut also control how/if to display the error messages.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 116 ++++++++++++++++++++++++++++++++----------------------\n 1 file changed, 68 insertions(+), 48 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex bc59815..b972a83 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -23,6 +23,28 @@\n #include \"dir.h\"\n #include \"submodule.h\"\n \n+static void flush_output(struct merge_options *o)\n+{\n+\tif (o->obuf.len) {\n+\t\tfputs(o->obuf.buf, stdout);\n+\t\tstrbuf_reset(&o->obuf);\n+\t}\n+}\n+\n+static int err(struct merge_options *o, const char *err, ...)\n+{\n+\tva_list params;\n+\n+\tflush_output(o);\n+\tva_start(params, err);\n+\tstrbuf_vaddf(&o->obuf, err, params);\n+\tva_end(params);\n+\terror(\"%s\", o->obuf.buf);\n+\tstrbuf_reset(&o->obuf);\n+\n+\treturn -1;\n+}\n+\n static struct tree *shift_tree_object(struct tree *one, struct tree *two,\n \t\t\t\t      const char *subtree_shift)\n {\n@@ -148,14 +170,6 @@ static int show(struct merge_options *o, int v)\n \treturn (!o->call_depth && o->verbosity >= v) || o->verbosity >= 5;\n }\n \n-static void flush_output(struct merge_options *o)\n-{\n-\tif (o->obuf.len) {\n-\t\tfputs(o->obuf.buf, stdout);\n-\t\tstrbuf_reset(&o->obuf);\n-\t}\n-}\n-\n __attribute__((format (printf, 3, 4)))\n static void output(struct merge_options *o, int v, const char *fmt, ...)\n {\n@@ -198,7 +212,8 @@ static void output_commit_title(struct merge_options *o, struct commit *commit)\n \t}\n }\n \n-static int add_cacheinfo(unsigned int mode, const struct object_id *oid,\n+static int add_cacheinfo(struct merge_options *o,\n+\t\tunsigned int mode, const struct object_id *oid,\n \t\tconst char *path, int stage, int refresh, int options)\n {\n \tstruct cache_entry *ce;\n@@ -206,7 +221,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid,\n \n \tce = make_cache_entry(mode, oid ? oid->hash : null_sha1, path, stage, 0);\n \tif (!ce)\n-\t\treturn error(_(\"addinfo_cache failed for path '%s'\"), path);\n+\t\treturn err(o, _(\"addinfo_cache failed for path '%s'\"), path);\n \n \tret = add_cache_entry(ce, options);\n \tif (refresh) {\n@@ -276,7 +291,7 @@ struct tree *write_tree_from_memory(struct merge_options *o)\n \n \tif (!cache_tree_fully_valid(active_cache_tree) &&\n \t    cache_tree_update(&the_index, 0) < 0) {\n-\t\terror(_(\"error building trees\"));\n+\t\terr(o, _(\"error building trees\"));\n \t\treturn NULL;\n \t}\n \n@@ -544,7 +559,8 @@ static struct string_list *get_renames(struct merge_options *o,\n \treturn renames;\n }\n \n-static int update_stages(const char *path, const struct diff_filespec *o,\n+static int update_stages(struct merge_options *opt, const char *path,\n+\t\t\t const struct diff_filespec *o,\n \t\t\t const struct diff_filespec *a,\n \t\t\t const struct diff_filespec *b)\n {\n@@ -563,13 +579,13 @@ static int update_stages(const char *path, const struct diff_filespec *o,\n \t\tif (remove_file_from_cache(path))\n \t\t\treturn -1;\n \tif (o)\n-\t\tif (add_cacheinfo(o->mode, &o->oid, path, 1, 0, options))\n+\t\tif (add_cacheinfo(opt, o->mode, &o->oid, path, 1, 0, options))\n \t\t\treturn -1;\n \tif (a)\n-\t\tif (add_cacheinfo(a->mode, &a->oid, path, 2, 0, options))\n+\t\tif (add_cacheinfo(opt, a->mode, &a->oid, path, 2, 0, options))\n \t\t\treturn -1;\n \tif (b)\n-\t\tif (add_cacheinfo(b->mode, &b->oid, path, 3, 0, options))\n+\t\tif (add_cacheinfo(opt, b->mode, &b->oid, path, 3, 0, options))\n \t\t\treturn -1;\n \treturn 0;\n }\n@@ -720,8 +736,8 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \tif (status) {\n \t\tif (status == SCLD_EXISTS)\n \t\t\t/* something else exists */\n-\t\t\treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n-\t\treturn error(msg, path, \"\");\n+\t\t\treturn err(o, msg, path, _(\": perhaps a D/F conflict?\"));\n+\t\treturn err(o, msg, path, \"\");\n \t}\n \n \t/*\n@@ -729,7 +745,7 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \t * tracking it.\n \t */\n \tif (would_lose_untracked(path))\n-\t\treturn error(_(\"refusing to lose untracked file at '%s'\"),\n+\t\treturn err(o, _(\"refusing to lose untracked file at '%s'\"),\n \t\t\t     path);\n \n \t/* Successful unlink is good.. */\n@@ -739,7 +755,7 @@ static int make_room_for_path(struct merge_options *o, const char *path)\n \tif (errno == ENOENT)\n \t\treturn 0;\n \t/* .. but not some other error (who really cares what?) */\n-\treturn error(msg, path, _(\": perhaps a D/F conflict?\"));\n+\treturn err(o, msg, path, _(\": perhaps a D/F conflict?\"));\n }\n \n static int update_file_flags(struct merge_options *o,\n@@ -771,9 +787,9 @@ static int update_file_flags(struct merge_options *o,\n \n \t\tbuf = read_sha1_file(oid->hash, &type, &size);\n \t\tif (!buf)\n-\t\t\treturn error(_(\"cannot read object %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\treturn err(o, _(\"cannot read object %s '%s'\"), oid_to_hex(oid), path);\n \t\tif (type != OBJ_BLOB) {\n-\t\t\tret = error(_(\"blob expected for %s '%s'\"), oid_to_hex(oid), path);\n+\t\t\tret = err(o, _(\"blob expected for %s '%s'\"), oid_to_hex(oid), path);\n \t\t\tgoto free_buf;\n \t\t}\n \t\tif (S_ISREG(mode)) {\n@@ -797,8 +813,8 @@ static int update_file_flags(struct merge_options *o,\n \t\t\t\tmode = 0666;\n \t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);\n \t\t\tif (fd < 0) {\n-\t\t\t\tret = error_errno(_(\"failed to open '%s'\"),\n-\t\t\t\t\t\t  path);\n+\t\t\t\tret = err(o, _(\"failed to open '%s': %s\"),\n+\t\t\t\t\t  path, strerror(errno));\n \t\t\t\tgoto free_buf;\n \t\t\t}\n \t\t\twrite_in_full(fd, buf, size);\n@@ -808,17 +824,19 @@ static int update_file_flags(struct merge_options *o,\n \t\t\tsafe_create_leading_directories_const(path);\n \t\t\tunlink(path);\n \t\t\tif (symlink(lnk, path))\n-\t\t\t\tret = error_errno(_(\"failed to symlink '%s'\"), path);\n+\t\t\t\tret = err(o, _(\"failed to symlink '%s': %s\"),\n+\t\t\t\t\tpath, strerror(errno));\n \t\t\tfree(lnk);\n \t\t} else\n-\t\t\tret = error(_(\"do not know what to do with %06o %s '%s'\"),\n-\t\t\t\t    mode, oid_to_hex(oid), path);\n+\t\t\tret = err(o,\n+\t\t\t\t  _(\"do not know what to do with %06o %s '%s'\"),\n+\t\t\t\t  mode, oid_to_hex(oid), path);\n  free_buf:\n \t\tfree(buf);\n \t}\n  update_index:\n \tif (!ret && update_cache)\n-\t\tadd_cacheinfo(mode, oid, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n+\t\tadd_cacheinfo(o, mode, oid, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);\n \treturn ret;\n }\n \n@@ -951,12 +969,12 @@ static int merge_file_1(struct merge_options *o,\n \t\t\t\t\t\t  branch1, branch2);\n \n \t\t\tif ((merge_status < 0) || !result_buf.ptr)\n-\t\t\t\tret = error(_(\"Failed to execute internal merge\"));\n+\t\t\t\tret = err(o, _(\"Failed to execute internal merge\"));\n \n \t\t\tif (!ret && write_sha1_file(result_buf.ptr, result_buf.size,\n \t\t\t\t\t\t    blob_type, result->oid.hash))\n-\t\t\t\tret = error(_(\"Unable to add %s to database\"),\n-\t\t\t\t\t    a->path);\n+\t\t\t\tret = err(o, _(\"Unable to add %s to database\"),\n+\t\t\t\t\t  a->path);\n \n \t\t\tfree(result_buf.ptr);\n \t\t\tif (ret)\n@@ -1122,7 +1140,7 @@ static int conflict_rename_delete(struct merge_options *o,\n \tif (o->call_depth)\n \t\treturn remove_file_from_cache(dest->path);\n \telse\n-\t\treturn update_stages(dest->path, NULL,\n+\t\treturn update_stages(o, dest->path, NULL,\n \t\t\t\t     rename_branch == o->branch1 ? dest : NULL,\n \t\t\t\t     rename_branch == o->branch1 ? NULL : dest);\n }\n@@ -1180,9 +1198,9 @@ static int handle_file(struct merge_options *o,\n \tif ((ret = update_file(o, 0, &rename->oid, rename->mode, dst_name)))\n \t\t; /* fall through, do allow dst_name to be released */\n \telse if (stage == 2)\n-\t\tret = update_stages(rename->path, NULL, rename, add);\n+\t\tret = update_stages(o, rename->path, NULL, rename, add);\n \telse\n-\t\tret = update_stages(rename->path, NULL, add, rename);\n+\t\tret = update_stages(o, rename->path, NULL, add, rename);\n \n \tif (dst_name != rename->path)\n \t\tfree(dst_name);\n@@ -1575,23 +1593,25 @@ static struct object_id *stage_oid(const struct object_id *oid, unsigned mode)\n \treturn (is_null_oid(oid) || mode == 0) ? NULL: (struct object_id *)oid;\n }\n \n-static int read_oid_strbuf(const struct object_id *oid, struct strbuf *dst)\n+static int read_oid_strbuf(struct merge_options *o,\n+\tconst struct object_id *oid, struct strbuf *dst)\n {\n \tvoid *buf;\n \tenum object_type type;\n \tunsigned long size;\n \tbuf = read_sha1_file(oid->hash, &type, &size);\n \tif (!buf)\n-\t\treturn error(_(\"cannot read object %s\"), oid_to_hex(oid));\n+\t\treturn err(o, _(\"cannot read object %s\"), oid_to_hex(oid));\n \tif (type != OBJ_BLOB) {\n \t\tfree(buf);\n-\t\treturn error(_(\"object %s is not a blob\"), oid_to_hex(oid));\n+\t\treturn err(o, _(\"object %s is not a blob\"), oid_to_hex(oid));\n \t}\n \tstrbuf_attach(dst, buf, size, size + 1);\n \treturn 0;\n }\n \n-static int blob_unchanged(const struct object_id *o_oid,\n+static int blob_unchanged(struct merge_options *opt,\n+\t\t\t  const struct object_id *o_oid,\n \t\t\t  unsigned o_mode,\n \t\t\t  const struct object_id *a_oid,\n \t\t\t  unsigned a_mode,\n@@ -1609,7 +1629,7 @@ static int blob_unchanged(const struct object_id *o_oid,\n \t\treturn 0;\n \n \tassert(o_oid && a_oid);\n-\tif (read_oid_strbuf(o_oid, &o) || read_oid_strbuf(a_oid, &a))\n+\tif (read_oid_strbuf(opt, o_oid, &o) || read_oid_strbuf(opt, a_oid, &a))\n \t\tgoto error_return;\n \t/*\n \t * Note: binary | is used so that both renormalizations are\n@@ -1698,7 +1718,7 @@ static int merge_content(struct merge_options *o,\n \t\t */\n \t\tpath_renamed_outside_HEAD = !path2 || !strcmp(path, path2);\n \t\tif (!path_renamed_outside_HEAD) {\n-\t\t\tadd_cacheinfo(mfi.mode, &mfi.oid, path,\n+\t\t\tadd_cacheinfo(o, mfi.mode, &mfi.oid, path,\n \t\t\t\t      0, (!o->call_depth), 0);\n \t\t\treturn mfi.clean;\n \t\t}\n@@ -1711,7 +1731,7 @@ static int merge_content(struct merge_options *o,\n \t\toutput(o, 1, _(\"CONFLICT (%s): Merge conflict in %s\"),\n \t\t\t\treason, path);\n \t\tif (rename_conflict_info && !df_conflict_remains)\n-\t\t\tif (update_stages(path, &one, &a, &b))\n+\t\t\tif (update_stages(o, path, &one, &a, &b))\n \t\t\t\treturn -1;\n \t}\n \n@@ -1721,7 +1741,7 @@ static int merge_content(struct merge_options *o,\n \t\t\tremove_file_from_cache(path);\n \t\t} else {\n \t\t\tif (!mfi.clean) {\n-\t\t\t\tif (update_stages(path, &one, &a, &b))\n+\t\t\t\tif (update_stages(o, path, &one, &a, &b))\n \t\t\t\t\treturn -1;\n \t\t\t} else {\n \t\t\t\tint file_from_stage2 = was_tracked(path);\n@@ -1729,7 +1749,7 @@ static int merge_content(struct merge_options *o,\n \t\t\t\toidcpy(&merged.oid, &mfi.oid);\n \t\t\t\tmerged.mode = mfi.mode;\n \n-\t\t\t\tif (update_stages(path, NULL,\n+\t\t\t\tif (update_stages(o, path, NULL,\n \t\t\t\t\t\t  file_from_stage2 ? &merged : NULL,\n \t\t\t\t\t\t  file_from_stage2 ? NULL : &merged))\n \t\t\t\t\treturn -1;\n@@ -1797,8 +1817,8 @@ static int process_entry(struct merge_options *o,\n \t} else if (o_oid && (!a_oid || !b_oid)) {\n \t\t/* Case A: Deleted in one */\n \t\tif ((!a_oid && !b_oid) ||\n-\t\t    (!b_oid && blob_unchanged(o_oid, o_mode, a_oid, a_mode, normalize, path)) ||\n-\t\t    (!a_oid && blob_unchanged(o_oid, o_mode, b_oid, b_mode, normalize, path))) {\n+\t\t    (!b_oid && blob_unchanged(o, o_oid, o_mode, a_oid, a_mode, normalize, path)) ||\n+\t\t    (!a_oid && blob_unchanged(o, o_oid, o_mode, b_oid, b_mode, normalize, path))) {\n \t\t\t/* Deleted in both or deleted in one and\n \t\t\t * unchanged in the other */\n \t\t\tif (a_oid)\n@@ -1894,7 +1914,7 @@ int merge_trees(struct merge_options *o,\n \n \tif (code != 0) {\n \t\tif (show(o, 4) || o->call_depth)\n-\t\t\terror(_(\"merging of trees %s and %s failed\"),\n+\t\t\terr(o, _(\"merging of trees %s and %s failed\"),\n \t\t\t    oid_to_hex(&head->object.oid),\n \t\t\t    oid_to_hex(&merge->object.oid));\n \t\treturn -1;\n@@ -2029,7 +2049,7 @@ int merge_recursive(struct merge_options *o,\n \t\to->call_depth--;\n \n \t\tif (!merged_common_ancestors)\n-\t\t\treturn error(_(\"merge returned no commit\"));\n+\t\t\treturn err(o, _(\"merge returned no commit\"));\n \t}\n \n \tdiscard_cache();\n@@ -2088,7 +2108,7 @@ int merge_recursive_generic(struct merge_options *o,\n \t\tfor (i = 0; i < num_base_list; ++i) {\n \t\t\tstruct commit *base;\n \t\t\tif (!(base = get_ref(base_list[i], oid_to_hex(base_list[i]))))\n-\t\t\t\treturn error(_(\"Could not parse object '%s'\"),\n+\t\t\t\treturn err(o, _(\"Could not parse object '%s'\"),\n \t\t\t\t\toid_to_hex(base_list[i]));\n \t\t\tcommit_list_insert(base, &ca);\n \t\t}\n@@ -2102,7 +2122,7 @@ int merge_recursive_generic(struct merge_options *o,\n \n \tif (active_cache_changed &&\n \t    write_locked_index(&the_index, lock, COMMIT_LOCK))\n-\t\treturn error(_(\"Unable to write index.\"));\n+\t\treturn err(o, _(\"Unable to write index.\"));\n \n \treturn clean ? 0 : 1;\n }\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292732","messageId":"4cbe2757bd921a202c359382086fadeb2616434a.1470051326.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1470051326.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 07/16] merge-recursive: avoid returning a wholesale struct","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-01T11:44:17Z","receivedAt":"2016-08-01T11:45:41Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It is technically allowed, as per C89, for functions' return type to\nbe complete structs (i.e. *not* just pointers to structs).\n\nHowever, it was just an oversight of this developer when converting\nPython code to C code in 6d297f8 (Status update on merge-recursive in\nC, 2006-07-08) which introduced such a return type.\n\nBesides, by converting this construct to pass in the struct, we can now\nstart returning a value that can indicate errors in future patches. This\nwill help the current effort to libify merge-recursive.c.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 106 ++++++++++++++++++++++++++++--------------------------\n 1 file changed, 56 insertions(+), 50 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 58ced25..2be1e17 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -894,47 +894,47 @@ static int merge_3way(struct merge_options *o,\n \treturn merge_status;\n }\n \n-static struct merge_file_info merge_file_1(struct merge_options *o,\n+static int merge_file_1(struct merge_options *o,\n \t\t\t\t\t   const struct diff_filespec *one,\n \t\t\t\t\t   const struct diff_filespec *a,\n \t\t\t\t\t   const struct diff_filespec *b,\n \t\t\t\t\t   const char *branch1,\n-\t\t\t\t\t   const char *branch2)\n+\t\t\t\t\t   const char *branch2,\n+\t\t\t\t\t   struct merge_file_info *result)\n {\n-\tstruct merge_file_info result;\n-\tresult.merge = 0;\n-\tresult.clean = 1;\n+\tresult->merge = 0;\n+\tresult->clean = 1;\n \n \tif ((S_IFMT & a->mode) != (S_IFMT & b->mode)) {\n-\t\tresult.clean = 0;\n+\t\tresult->clean = 0;\n \t\tif (S_ISREG(a->mode)) {\n-\t\t\tresult.mode = a->mode;\n-\t\t\toidcpy(&result.oid, &a->oid);\n+\t\t\tresult->mode = a->mode;\n+\t\t\toidcpy(&result->oid, &a->oid);\n \t\t} else {\n-\t\t\tresult.mode = b->mode;\n-\t\t\toidcpy(&result.oid, &b->oid);\n+\t\t\tresult->mode = b->mode;\n+\t\t\toidcpy(&result->oid, &b->oid);\n \t\t}\n \t} else {\n \t\tif (!oid_eq(&a->oid, &one->oid) && !oid_eq(&b->oid, &one->oid))\n-\t\t\tresult.merge = 1;\n+\t\t\tresult->merge = 1;\n \n \t\t/*\n \t\t * Merge modes\n \t\t */\n \t\tif (a->mode == b->mode || a->mode == one->mode)\n-\t\t\tresult.mode = b->mode;\n+\t\t\tresult->mode = b->mode;\n \t\telse {\n-\t\t\tresult.mode = a->mode;\n+\t\t\tresult->mode = a->mode;\n \t\t\tif (b->mode != one->mode) {\n-\t\t\t\tresult.clean = 0;\n-\t\t\t\tresult.merge = 1;\n+\t\t\t\tresult->clean = 0;\n+\t\t\t\tresult->merge = 1;\n \t\t\t}\n \t\t}\n \n \t\tif (oid_eq(&a->oid, &b->oid) || oid_eq(&a->oid, &one->oid))\n-\t\t\toidcpy(&result.oid, &b->oid);\n+\t\t\toidcpy(&result->oid, &b->oid);\n \t\telse if (oid_eq(&b->oid, &one->oid))\n-\t\t\toidcpy(&result.oid, &a->oid);\n+\t\t\toidcpy(&result->oid, &a->oid);\n \t\telse if (S_ISREG(a->mode)) {\n \t\t\tmmbuffer_t result_buf;\n \t\t\tint merge_status;\n@@ -946,64 +946,66 @@ static struct merge_file_info merge_file_1(struct merge_options *o,\n \t\t\t\tdie(_(\"Failed to execute internal merge\"));\n \n \t\t\tif (write_sha1_file(result_buf.ptr, result_buf.size,\n-\t\t\t\t\t    blob_type, result.oid.hash))\n+\t\t\t\t\t    blob_type, result->oid.hash))\n \t\t\t\tdie(_(\"Unable to add %s to database\"),\n \t\t\t\t    a->path);\n \n \t\t\tfree(result_buf.ptr);\n-\t\t\tresult.clean = (merge_status == 0);\n+\t\t\tresult->clean = (merge_status == 0);\n \t\t} else if (S_ISGITLINK(a->mode)) {\n-\t\t\tresult.clean = merge_submodule(result.oid.hash,\n+\t\t\tresult->clean = merge_submodule(result->oid.hash,\n \t\t\t\t\t\t       one->path,\n \t\t\t\t\t\t       one->oid.hash,\n \t\t\t\t\t\t       a->oid.hash,\n \t\t\t\t\t\t       b->oid.hash,\n \t\t\t\t\t\t       !o->call_depth);\n \t\t} else if (S_ISLNK(a->mode)) {\n-\t\t\toidcpy(&result.oid, &a->oid);\n+\t\t\toidcpy(&result->oid, &a->oid);\n \n \t\t\tif (!oid_eq(&a->oid, &b->oid))\n-\t\t\t\tresult.clean = 0;\n+\t\t\t\tresult->clean = 0;\n \t\t} else\n \t\t\tdie(\"BUG: unsupported object type in the tree\");\n \t}\n \n-\treturn result;\n+\treturn 0;\n }\n \n-static struct merge_file_info\n-merge_file_special_markers(struct merge_options *o,\n+static int merge_file_special_markers(struct merge_options *o,\n \t\t\t   const struct diff_filespec *one,\n \t\t\t   const struct diff_filespec *a,\n \t\t\t   const struct diff_filespec *b,\n \t\t\t   const char *branch1,\n \t\t\t   const char *filename1,\n \t\t\t   const char *branch2,\n-\t\t\t   const char *filename2)\n+\t\t\t   const char *filename2,\n+\t\t\t   struct merge_file_info *mfi)\n {\n \tchar *side1 = NULL;\n \tchar *side2 = NULL;\n-\tstruct merge_file_info mfi;\n+\tint ret;\n \n \tif (filename1)\n \t\tside1 = xstrfmt(\"%s:%s\", branch1, filename1);\n \tif (filename2)\n \t\tside2 = xstrfmt(\"%s:%s\", branch2, filename2);\n \n-\tmfi = merge_file_1(o, one, a, b,\n-\t\t\t   side1 ? side1 : branch1, side2 ? side2 : branch2);\n+\tret = merge_file_1(o, one, a, b,\n+\t\t\t   side1 ? side1 : branch1,\n+\t\t\t   side2 ? side2 : branch2, mfi);\n \tfree(side1);\n \tfree(side2);\n-\treturn mfi;\n+\treturn ret;\n }\n \n-static struct merge_file_info merge_file_one(struct merge_options *o,\n+static int merge_file_one(struct merge_options *o,\n \t\t\t\t\t const char *path,\n \t\t\t\t\t const struct object_id *o_oid, int o_mode,\n \t\t\t\t\t const struct object_id *a_oid, int a_mode,\n \t\t\t\t\t const struct object_id *b_oid, int b_mode,\n \t\t\t\t\t const char *branch1,\n-\t\t\t\t\t const char *branch2)\n+\t\t\t\t\t const char *branch2,\n+\t\t\t\t\t struct merge_file_info *mfi)\n {\n \tstruct diff_filespec one, a, b;\n \n@@ -1014,7 +1016,7 @@ static struct merge_file_info merge_file_one(struct merge_options *o,\n \ta.mode = a_mode;\n \toidcpy(&b.oid, b_oid);\n \tb.mode = b_mode;\n-\treturn merge_file_1(o, &one, &a, &b, branch1, branch2);\n+\treturn merge_file_1(o, &one, &a, &b, branch1, branch2, mfi);\n }\n \n static void handle_change_delete(struct merge_options *o,\n@@ -1187,11 +1189,12 @@ static void conflict_rename_rename_1to2(struct merge_options *o,\n \t\tstruct merge_file_info mfi;\n \t\tstruct diff_filespec other;\n \t\tstruct diff_filespec *add;\n-\t\tmfi = merge_file_one(o, one->path,\n+\t\tif (merge_file_one(o, one->path,\n \t\t\t\t &one->oid, one->mode,\n \t\t\t\t &a->oid, a->mode,\n \t\t\t\t &b->oid, b->mode,\n-\t\t\t\t ci->branch1, ci->branch2);\n+\t\t\t\t ci->branch1, ci->branch2, &mfi))\n+\t\t\treturn;\n \t\t/*\n \t\t * FIXME: For rename/add-source conflicts (if we could detect\n \t\t * such), this is wrong.  We should instead find a unique\n@@ -1245,12 +1248,13 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \tremove_file(o, 1, a->path, o->call_depth || would_lose_untracked(a->path));\n \tremove_file(o, 1, b->path, o->call_depth || would_lose_untracked(b->path));\n \n-\tmfi_c1 = merge_file_special_markers(o, a, c1, &ci->ren1_other,\n-\t\t\t\t\t    o->branch1, c1->path,\n-\t\t\t\t\t    o->branch2, ci->ren1_other.path);\n-\tmfi_c2 = merge_file_special_markers(o, b, &ci->ren2_other, c2,\n-\t\t\t\t\t    o->branch1, ci->ren2_other.path,\n-\t\t\t\t\t    o->branch2, c2->path);\n+\tif (merge_file_special_markers(o, a, c1, &ci->ren1_other,\n+\t\t\t\t       o->branch1, c1->path,\n+\t\t\t\t       o->branch2, ci->ren1_other.path, &mfi_c1) ||\n+\t    merge_file_special_markers(o, b, &ci->ren2_other, c2,\n+\t\t\t\t       o->branch1, ci->ren2_other.path,\n+\t\t\t\t       o->branch2, c2->path, &mfi_c2))\n+\t\treturn;\n \n \tif (o->call_depth) {\n \t\t/*\n@@ -1473,12 +1477,13 @@ static int process_renames(struct merge_options *o,\n \t\t\t\t       ren1_dst, branch2);\n \t\t\t\tif (o->call_depth) {\n \t\t\t\t\tstruct merge_file_info mfi;\n-\t\t\t\t\tmfi = merge_file_one(o, ren1_dst, &null_oid, 0,\n-\t\t\t\t\t\t\t &ren1->pair->two->oid,\n-\t\t\t\t\t\t\t ren1->pair->two->mode,\n-\t\t\t\t\t\t\t &dst_other.oid,\n-\t\t\t\t\t\t\t dst_other.mode,\n-\t\t\t\t\t\t\t branch1, branch2);\n+\t\t\t\t\tif (merge_file_one(o, ren1_dst, &null_oid, 0,\n+\t\t\t\t\t\t\t   &ren1->pair->two->oid,\n+\t\t\t\t\t\t\t   ren1->pair->two->mode,\n+\t\t\t\t\t\t\t   &dst_other.oid,\n+\t\t\t\t\t\t\t   dst_other.mode,\n+\t\t\t\t\t\t\t   branch1, branch2, &mfi))\n+\t\t\t\t\t\treturn -1;\n \t\t\t\t\toutput(o, 1, _(\"Adding merged %s\"), ren1_dst);\n \t\t\t\t\tupdate_file(o, 0, &mfi.oid,\n \t\t\t\t\t\t    mfi.mode, ren1_dst);\n@@ -1636,9 +1641,10 @@ static int merge_content(struct merge_options *o,\n \t\tif (dir_in_way(path, !o->call_depth))\n \t\t\tdf_conflict_remains = 1;\n \t}\n-\tmfi = merge_file_special_markers(o, &one, &a, &b,\n-\t\t\t\t\t o->branch1, path1,\n-\t\t\t\t\t o->branch2, path2);\n+\tif (merge_file_special_markers(o, &one, &a, &b,\n+\t\t\t\t       o->branch1, path1,\n+\t\t\t\t       o->branch2, path2, &mfi))\n+\t\treturn -1;\n \n \tif (mfi.clean && !df_conflict_remains &&\n \t    oid_eq(&mfi.oid, a_oid) && mfi.mode == a_mode) {\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292733","messageId":"0fa117f8ae42e2f7c9b1cc803261b236d5cd6b17.1470051326.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1470051326.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 15/16] Ensure that the output buffer is released after calling merge_trees()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-01T11:44:53Z","receivedAt":"2016-08-01T11:45:44Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The recursive merge machinery accumulates its output in an output\nbuffer, to be flushed at the end of merge_recursive(). At this point,\nwe forgot to release the output buffer.\n\nWhen calling merge_trees() (i.e. the non-recursive part of the recursive\nmerge) directly, the output buffer is never flushed because the caller\nmay be merge_recursive() which wants to flush the output itself.\n\nFor the same reason, merge_trees() cannot release the output buffer: it\nmay still be needed.\n\nForgetting to release the output buffer did not matter much when running\ngit-checkout, or git-merge-recursive, because we exited after the\noperation anyway. Ever since cherry-pick learned to pick a commit range,\nhowever, this memory leak had the potential of becoming a problem.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/checkout.c | 1 +\n merge-recursive.c  | 2 ++\n sequencer.c        | 1 +\n 3 files changed, 4 insertions(+)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 07dea3b..8d852d4 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -573,6 +573,7 @@ static int merge_working_tree(const struct checkout_opts *opts,\n \t\t\t\texit(128);\n \t\t\tret = reset_tree(new->commit->tree, opts, 0,\n \t\t\t\t\t writeout_error);\n+\t\t\tstrbuf_release(&o.obuf);\n \t\t\tif (ret)\n \t\t\t\treturn ret;\n \t\t}\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex ec50932..9e527de 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -2078,6 +2078,8 @@ int merge_recursive(struct merge_options *o,\n \t\tcommit_list_insert(h2, &(*result)->parents->next);\n \t}\n \tflush_output(o);\n+\tif (!o->call_depth && o->buffer_output < 2)\n+\t\tstrbuf_release(&o->obuf);\n \tif (show(o, 2))\n \t\tdiff_warn_rename_limit(\"merge.renamelimit\",\n \t\t\t\t       o->needed_rename_limit, 0);\ndiff --git a/sequencer.c b/sequencer.c\nindex 286a435..ec50519 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -293,6 +293,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \tclean = merge_trees(&o,\n \t\t\t    head_tree,\n \t\t\t    next_tree, base_tree, &result);\n+\tstrbuf_release(&o.obuf);\n \tif (clean < 0)\n \t\treturn clean;\n \n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292734","messageId":"b20cdc35797d5ed97a738e85928819088e31a01a.1470051326.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1470051326.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 08/16] merge-recursive: allow write_tree_from_memory() to error out","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-01T11:44:23Z","receivedAt":"2016-08-01T11:45:46Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It is possible that a tree cannot be written (think: disk full). We\nwill want to give the caller a chance to clean up instead of letting\nthe program die() in such a case.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 2be1e17..1f86338 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1888,8 +1888,8 @@ int merge_trees(struct merge_options *o,\n \telse\n \t\tclean = 1;\n \n-\tif (o->call_depth)\n-\t\t*result = write_tree_from_memory(o);\n+\tif (o->call_depth && !(*result = write_tree_from_memory(o)))\n+\t\treturn -1;\n \n \treturn clean;\n }\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292735","messageId":"8ff71aba37be979f05abf88f467ec932aa522bdd.1470051326.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1470051326.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-01T11:44:13Z","receivedAt":"2016-08-01T11:45:49Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"There are a couple of places where return values never indicated errors\nbefore, as wie simply died instead of returning.\n\nBut now negative return values mean that there was an error and we have to\nabort the operation. Let's do exactly that.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 17 ++++++++++++-----\n 1 file changed, 12 insertions(+), 5 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 3a652b7..58ced25 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1949,17 +1949,19 @@ int merge_recursive(struct merge_options *o,\n \t\t/*\n \t\t * When the merge fails, the result contains files\n \t\t * with conflict markers. The cleanness flag is\n-\t\t * ignored, it was never actually used, as result of\n-\t\t * merge_trees has always overwritten it: the committed\n-\t\t * \"conflicts\" were already resolved.\n+\t\t * ignored (unless indicating an error), it was never\n+\t\t * actually used, as result of merge_trees has always\n+\t\t * overwritten it: the committed \"conflicts\" were\n+\t\t * already resolved.\n \t\t */\n \t\tdiscard_cache();\n \t\tsaved_b1 = o->branch1;\n \t\tsaved_b2 = o->branch2;\n \t\to->branch1 = \"Temporary merge branch 1\";\n \t\to->branch2 = \"Temporary merge branch 2\";\n-\t\tmerge_recursive(o, merged_common_ancestors, iter->item,\n-\t\t\t\tNULL, &merged_common_ancestors);\n+\t\tif (merge_recursive(o, merged_common_ancestors, iter->item,\n+\t\t\t\t    NULL, &merged_common_ancestors) < 0)\n+\t\t\treturn -1;\n \t\to->branch1 = saved_b1;\n \t\to->branch2 = saved_b2;\n \t\to->call_depth--;\n@@ -1975,6 +1977,8 @@ int merge_recursive(struct merge_options *o,\n \to->ancestor = \"merged common ancestors\";\n \tclean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree,\n \t\t\t    &mrtree);\n+\tif (clean < 0)\n+\t\treturn clean;\n \n \tif (o->call_depth) {\n \t\t*result = make_virtual_commit(mrtree, \"merged tree\");\n@@ -2031,6 +2035,9 @@ int merge_recursive_generic(struct merge_options *o,\n \thold_locked_index(lock, 1);\n \tclean = merge_recursive(o, head_commit, next_commit, ca,\n \t\t\tresult);\n+\tif (clean < 0)\n+\t\treturn clean;\n+\n \tif (active_cache_changed &&\n \t    write_locked_index(&the_index, lock, COMMIT_LOCK))\n \t\treturn error(_(\"Unable to write index.\"));\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292736","messageId":"2c46a4a536b4b70bb1ef26fe13faa00f3d287727.1470051326.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1470051326.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 13/16] merge-recursive: write the commit title in one go","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-01T11:44:45Z","receivedAt":"2016-08-01T11:45:52Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"In 66a155b (Enable output buffering in merge-recursive., 2007-01-14), we\nchanged the code such that it prints the output in one go, to avoid\ninterfering with the progress output.\n\nLet's make sure that the same holds true when outputting the commit\ntitle: previously, we used several printf() statements to stdout and\nassumed that stdout's buffer is large enough to hold the entire\ncommit title.\n\nApart from making that speculation unnecessary, we change the code to\nadd the message to the output buffer before flushing for another reason:\nthe next commit will introduce a new level of output buffering, where\nthe caller can request the output not to be flushed, but to be retained\nfor further processing.\n\nThis latter feature will be needed when teaching the sequencer to do\nrebase -i's brunt work: it wants to control the output of the\ncherry-picks (i.e. recursive merges).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 17 +++++++++--------\n 1 file changed, 9 insertions(+), 8 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex b972a83..99c9635 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -191,25 +191,26 @@ static void output(struct merge_options *o, int v, const char *fmt, ...)\n \n static void output_commit_title(struct merge_options *o, struct commit *commit)\n {\n-\tint i;\n-\tflush_output(o);\n-\tfor (i = o->call_depth; i--;)\n-\t\tfputs(\"  \", stdout);\n+\tstrbuf_addchars(&o->obuf, ' ', o->call_depth * 2);\n \tif (commit->util)\n-\t\tprintf(\"virtual %s\\n\", merge_remote_util(commit)->name);\n+\t\tstrbuf_addf(&o->obuf, \"virtual %s\\n\",\n+\t\t\tmerge_remote_util(commit)->name);\n \telse {\n-\t\tprintf(\"%s \", find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV));\n+\t\tstrbuf_addf(&o->obuf, \"%s \",\n+\t\t\tfind_unique_abbrev(commit->object.oid.hash,\n+\t\t\t\tDEFAULT_ABBREV));\n \t\tif (parse_commit(commit) != 0)\n-\t\t\tprintf(_(\"(bad commit)\\n\"));\n+\t\t\tstrbuf_addf(&o->obuf, _(\"(bad commit)\\n\"));\n \t\telse {\n \t\t\tconst char *title;\n \t\t\tconst char *msg = get_commit_buffer(commit, NULL);\n \t\t\tint len = find_commit_subject(msg, &title);\n \t\t\tif (len)\n-\t\t\t\tprintf(\"%.*s\\n\", len, title);\n+\t\t\t\tstrbuf_addf(&o->obuf, \"%.*s\\n\", len, title);\n \t\t\tunuse_commit_buffer(commit, msg);\n \t\t}\n \t}\n+\tflush_output(o);\n }\n \n static int add_cacheinfo(struct merge_options *o,\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"292737","messageId":"3b4494c586574b59d80d0f1b88bd4c3d56b678cf.1470051326.git.johannes.schindelin@gmx.de","threadId":"42743","inReplyTo":"cover.1470051326.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 16/16] merge-recursive: flush output buffer even when erroring out","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-01T11:44:57Z","receivedAt":"2016-08-01T11:46:08Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Ever since 66a155b (Enable output buffering in merge-recursive.,\n2007-01-14), we had a problem: When the merge failed in a fatal way, all\nregular output was swallowed because we called die() and did not get a\nchance to drain the output buffers.\n\nTo fix this, several modifications were necessary:\n\n- we needed to stop die()ing, to give callers a chance to do something\n  when an error occurred (in this case, flush the output buffers),\n\n- we needed to delay printing the error message so that the caller can\n  print the buffered output before that, and\n\n- we needed to make sure that the output buffers are flushed even when\n  the return value indicates an error.\n\nThe first two changes were introduced through earlier commits in this\npatch series, and this commit addresses the third one.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n merge-recursive.c | 4 +++-\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 9e527de..c9e4dbc 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -2069,8 +2069,10 @@ int merge_recursive(struct merge_options *o,\n \to->ancestor = \"merged common ancestors\";\n \tclean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree,\n \t\t\t    &mrtree);\n-\tif (clean < 0)\n+\tif (clean < 0) {\n+\t\tflush_output(o);\n \t\treturn clean;\n+\t}\n \n \tif (o->call_depth) {\n \t\t*result = make_virtual_commit(mrtree, \"merged tree\");\n-- \n2.9.0.281.g286a8d9\n"},{"id":"292773","messageId":"xmqqtwf4jq6j.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1608011141380.149069@virtualbox","subject":"Re: [PATCH v5 16/16] merge-recursive: flush output buffer even when erroring out","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-01T18:32:20Z","receivedAt":"2016-08-01T18:32:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> This is of course a good change, but we need to assume that no\n>> further output is made from the remainder of the function for the\n>> change in the next hunk to remove the existing flush to be correct.\n> ...\n> But you made me realize that I cannot simply *move* the flush_output()\n> call here, in case that code in between will eventually add output.\n\nYup, that removal of the original one was the only thing I was\npointing out.\n\n\n"},{"id":"292774","messageId":"xmqqlh0gjpr6.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"8ff71aba37be979f05abf88f467ec932aa522bdd.1470051326.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-01T18:41:33Z","receivedAt":"2016-08-01T18:42:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> Subject: Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors\n\ns/merge_/merge-/; for this one alone.\n\n> There are a couple of places where return values never indicated errors\n> before, as wie simply died instead of returning.\n\ns/wie/we/;\n"},{"id":"292778","messageId":"xmqqpopsjpso.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"d3c5678faf46391ce684aa79927a54cf15beea3f.1470051326.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v6 05/16] Prepare the builtins for a libified merge_recursive()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-01T18:40:39Z","receivedAt":"2016-08-01T19:14:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> Previously, callers of merge_trees() or merge_recursive() expected that\n> code to die() with an error message. This used to be okay because we\n> called those commands from scripts, and had a chance to print out a\n> message in case the command failed fatally (read: with exit code 128).\n>\n> As scripting incurs its own set of problems (portability, speed,\n> idiosynchracies of different shells, limited data structures leading to\n\nI think I typofixed this when I queued the previous one on 'pu'\nalready, but s/synch/sync/; \n\n"},{"id":"292779","messageId":"xmqqbn1cjogd.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1608011124440.149069@virtualbox","subject":"Re: [PATCH v5 14/16] merge-recursive: offer an option to retain the output in 'obuf'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-01T19:09:38Z","receivedAt":"2016-08-01T19:15:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> I actually now see how this would work well for \"reason 2)\".  If a\n>> caller wants to run the function and wants to pretend as if it did\n>> not run anything when it failed, for example, using this to spool\n>> all output and error to a strbuf and discard it when the function\n>> returns an error, and emit the spooled output to standard output and\n>> standard error in the order the lines were collected when the\n>> function returns a success, would be a good way to do so.\n>\n> That is actually the exact opposite of the intended usage: when any `pick`\n> in an interactive rebase succeeds, its output is discarded so as not to\n> bother the user. We show the complete output only when it fails.\n\nOh, it makes sense, too, to show the output only when there is an\nerror.\n\nBut in that case, there would be both messages meant for the\nstandard output and also meant for the standard error, and we need\nsome way to make sure they go to the right channel.\n\nI however do not think an array of <bool, const char *> is the only\nway to achieve that.  We can get away by a single strbuf that\naccumulates all output() that we have seen so far, i.e. \"we only\naccumulate output() and ignore flush() as long as what we see are\nonly from output()\" mode.\n\nThen the err() routine operating under this new mode can show what\nhas been accumulated to the standard output (because with this tweak\nI am outlining here, by definition, the strbuf will only keep the\noutput() material and not err() things), show the err() message, and\nswitch back to the normal \"we accumulate output() and honor flush()\"\nmode.  Of course, when we are doing multiple rounds, the mode must\nbe reset to \"accumulate output and ignore flush\" mode at the\nbeginning of each rouhd.\n\nThat would give us \"silence if there is no error, but if we are\nshowing error, show them to the standard error, while giving\nnon-error message to the standard output\".\n"},{"id":"292820","messageId":"alpine.DEB.2.20.1608020948220.79248@virtualbox","threadId":"42743","inReplyTo":"xmqqbn1cjogd.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v5 14/16] merge-recursive: offer an option to retain the output in 'obuf'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-02T08:01:42Z","receivedAt":"2016-08-02T08:09:30Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Mon, 1 Aug 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> >> I actually now see how this would work well for \"reason 2)\".  If a\n> >> caller wants to run the function and wants to pretend as if it did\n> >> not run anything when it failed, for example, using this to spool all\n> >> output and error to a strbuf and discard it when the function returns\n> >> an error, and emit the spooled output to standard output and standard\n> >> error in the order the lines were collected when the function returns\n> >> a success, would be a good way to do so.\n> >\n> > That is actually the exact opposite of the intended usage: when any\n> > `pick` in an interactive rebase succeeds, its output is discarded so\n> > as not to bother the user. We show the complete output only when it\n> > fails.\n> \n> Oh, it makes sense, too, to show the output only when there is an error.\n\nThanks ;-)\n\n> But in that case, there would be both messages meant for the\n> standard output and also meant for the standard error, and we need\n> some way to make sure they go to the right channel.\n\nNot necessarily. Let's have a look at our existing code in\ngit-rebase.sh:\n\n\toutput () {\n\t\tcase \"$verbose\" in\n\t\t'')\n\t\t\toutput=$(\"$@\" 2>&1 )\n\t\t\tstatus=$?\n\t\t\ttest $status != 0 && printf \"%s\\n\" \"$output\"\n\t\t\treturn $status\n\t\t\t;;\n\t\t*)\n\t\t\t\"$@\"\n\t\t\t;;\n\t\tesac\n\t}\n\nThis incredibly well-named function (</sarcasm>, my fault: dfa49f3 (Shut \"git\nrebase -i\" up when no --verbose was given, 2007-07-23)) accumulates all\noutput, both stdout and stderr, and shows it only in case of an error.\n\nCrucially, *all* output goes to stdout. No distinction is being made\nbetween stdout and stderr.\n\nThis is the existing behavior of rebase -i.\n\n> I however do not think an array of <bool, const char *> is the only\n> way to achieve that.  We can get away by a single strbuf that\n> accumulates all output() that we have seen so far, i.e. \"we only\n> accumulate output() and ignore flush() as long as what we see are\n> only from output()\" mode.\n> \n> Then the err() routine operating under this new mode can show what\n> has been accumulated to the standard output (because with this tweak\n> I am outlining here, by definition, the strbuf will only keep the\n> output() material and not err() things), show the err() message, and\n> switch back to the normal \"we accumulate output() and honor flush()\"\n> mode.  Of course, when we are doing multiple rounds, the mode must\n> be reset to \"accumulate output and ignore flush\" mode at the\n> beginning of each rouhd.\n> \n> That would give us \"silence if there is no error, but if we are\n> showing error, show them to the standard error, while giving\n> non-error message to the standard output\".\n\nIt all makes sense what you say. In case you want to preserve the channel\nin some future modification.\n\nHowever, I am right now most concerned about keeping existing behavior as\nfaithfully as possible (with the exception of execution speed, which I\nwant to improve dramatically).\n\nAs such, it would be a serious mistake to implement that mode and use it\nin the rebase--helper: it would very likely cause regressions in existing\nscripts, probably even my own.\n\nSo I do understand your concern, and I agree that it would make for a fine\ndesign, in a different context than this patch series.\n\nCiao,\nDscho\n"},{"id":"292821","messageId":"alpine.DEB.2.20.1608021001580.79248@virtualbox","threadId":"42743","inReplyTo":"xmqqpopsjpso.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v6 05/16] Prepare the builtins for a libified merge_recursive()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-02T08:02:26Z","receivedAt":"2016-08-02T08:10:32Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Mon, 1 Aug 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > Previously, callers of merge_trees() or merge_recursive() expected that\n> > code to die() with an error message. This used to be okay because we\n> > called those commands from scripts, and had a chance to print out a\n> > message in case the command failed fatally (read: with exit code 128).\n> >\n> > As scripting incurs its own set of problems (portability, speed,\n> > idiosynchracies of different shells, limited data structures leading to\n> \n> I think I typofixed this when I queued the previous one on 'pu'\n> already, but s/synch/sync/; \n\nWhoops. Fixed locally.\n\nCiao,\nDscho\n"},{"id":"292822","messageId":"alpine.DEB.2.20.1608021004080.79248@virtualbox","threadId":"42743","inReplyTo":"xmqqlh0gjpr6.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-02T08:12:29Z","receivedAt":"2016-08-02T08:32:07Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Mon, 1 Aug 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > Subject: Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors\n> \n> s/merge_/merge-/; for this one alone.\n\nI see that de8946de1694a8cf311daab7b2c416d76cb04d23 still shows it with an\nunderscore instead of a dash, while you fixed my other tyops in this patch\nseries.\n\nI tend to think that the underscore is correct: this change is not so much\nabout the builtin (which is written with a dash) but about the function\n(written with an underscore, used by more than just merge-recursive, e.g.\ncherry-pick).\n\n> > There are a couple of places where return values never indicated errors\n> > before, as wie simply died instead of returning.\n> \n> s/wie/we/;\n\nRight. What can I say, I am surrounded by too many Germans.\n\nI fixed this locally, in case another re-roll should be required. What you\nhave in `pu` looks correct to me, though. Let me know if you want me to\nre-submit nevertheless.\n\nBTW I should have said this earlier: I run all my rebases, all my merges,\nall my cherry-picks using a Git version with these patches for months (of\ncourse, the patches have changed, but not in the most critical parts I was\nconcerned about, the parts where die() calls were replaced). If I would\nhave found any regression, I would have notified you immediately, of\ncourse.\n\nCiao,\nDscho\n"},{"id":"292868","messageId":"xmqq60ridg26.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1608020948220.79248@virtualbox","subject":"Re: [PATCH v5 14/16] merge-recursive: offer an option to retain the output in 'obuf'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-02T21:19:45Z","receivedAt":"2016-08-02T21:20:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> But in that case, there would be both messages meant for the\n>> standard output and also meant for the standard error, and we need\n>> some way to make sure they go to the right channel.\n>\n> Not necessarily. Let's have a look at our existing code in\n> git-rebase.sh:\n>\n> \toutput () {\n> \t\tcase \"$verbose\" in\n> \t\t'')\n> \t\t\toutput=$(\"$@\" 2>&1 )\n> \t\t\tstatus=$?\n> \t\t\ttest $status != 0 && printf \"%s\\n\" \"$output\"\n> \t\t\treturn $status\n> \t\t\t;;\n> \t\t*)\n> \t\t\t\"$@\"\n> \t\t\t;;\n> \t\tesac\n> \t}\n>\n> This incredibly well-named function (</sarcasm>, my fault: dfa49f3 (Shut \"git\n> rebase -i\" up when no --verbose was given, 2007-07-23)) accumulates all\n> output, both stdout and stderr, and shows it only in case of an error.\n>\n> Crucially, *all* output goes to stdout. No distinction is being made\n> between stdout and stderr.\n\n> ...\n> This is the existing behavior of rebase -i.\n> ...\n> As such, it would be a serious mistake to implement that mode and use it\n> in the rebase--helper: it would very likely cause regressions in existing\n> scripts, probably even my own.\n\nSounds like we are desperately trying to find an excuse to do a\nwrong thing by finding an existing piece of code that did a wrong\nthing already.\n\nThat leaves a bad taste in my mouth, but as \"rebase -i\" is meant to\nbe an \"interactive\" command, I would imagine that nobody would have\nexpected to run it as \"git rebase -i >/dev/null\" in order to view\nonly the error messages (or vice versa with \"2>errs\").\n\nSo OK then, at least for now.\n\n"},{"id":"292869","messageId":"xmqqy44ec15p.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1608021004080.79248@virtualbox","subject":"Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-02T21:26:58Z","receivedAt":"2016-08-02T21:35:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> > There are a couple of places where return values never indicated errors\n>> > before, as wie simply died instead of returning.\n>> \n>> s/wie/we/;\n>\n> Right. What can I say, I am surrounded by too many Germans.\n>\n> I fixed this locally, in case another re-roll should be required. What you\n> have in `pu` looks correct to me, though. Let me know if you want me to\n> re-submit nevertheless.\n\nI usually do this kind of obvious typofix and consistency fix\nwithout even mentioning them in my review comments to reduce the\nnoise levels.  But that works better ONLY if the patch authors then\nfetch from 'pu' and replace their copies with what I fixed up\nalready and base their reroll on top by amending and/or building on\ntop (of course, that also requires my local fix must all be limited\nto uncontroversial ones).\n\nSo either I should change my workflow and mention any and all\ntypofixes in my review comments (which consumes the review\nbandwidth), or I should force patch authors to do the \"fetch from\n'pu' and replace\" somehow to avoid this kind of back-and-forth.\n\nI am not sure which should be the way to go.\n\n"},{"id":"292875","messageId":"xmqqd1lqbybd.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1608021004080.79248@virtualbox","subject":"Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-02T22:28:22Z","receivedAt":"2016-08-02T22:29:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> I tend to think that the underscore is correct: this change is not so much\n> about the builtin (which is written with a dash) but about the function\n> (written with an underscore, used by more than just merge-recursive, e.g.\n> cherry-pick).\n\nYes, I agree.  \"merge-recursive:\" prefix is about either the\nbuilt-in command, or the machinery as a whole to support that\nbuilt-in command.  It is preferrable to use \"merge_recursive():\"\nif we are talking about a single function.\n\nThanks.\n"},{"id":"292892","messageId":"alpine.DEB.2.20.1608031021050.79248@virtualbox","threadId":"42743","inReplyTo":"xmqqy44ec15p.fsf@gitster.mtv.corp.google.com","subject":"patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-03T11:59:44Z","receivedAt":"2016-08-03T12:01:31Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 2 Aug 2016, Junio C Hamano wrote:\n\n> So either I should change my workflow and mention any and all\n> typofixes in my review comments (which consumes the review\n> bandwidth), or I should force patch authors to do the \"fetch from\n> 'pu' and replace\" somehow to avoid this kind of back-and-forth.\n\nIt never occurred to me that I should replace any of my local branches\nwith versions you have in `pu`. For several reasons:\n\n- just as you do not pull from me, lest patches enter your repository that\n  you have not reviewed, I do not simply pull from you,\n\n- I thought the point of avoiding GitHub in the review process at all\n  costs and require everybody to go via the mailing list instead was to\n  keep the review process open? These silent changes fly in the face of\n  that,\n\n- I agree that the mail-based process is cumbersome and error-prone, but\n  we won't fix it by asking contributors to dig through the `pu` of the\n  day, somehow stay up-to-date with possibly more silent typofixes the\n  next day, somehow manage to figure out what changes exactly were\n  introduced to their patches, and replace their local work.\n\n- even if we asked for all that trouble, the commits in `pu` are all\n  signed off by you. These sign-offs would have to be stripped out\n  tediously when making local changes.\n\nIn short, I agree that our patch submission process is a saber tooth tiger\nthat still reflects pre-Git times. While we use Git's tools, the workflow\nreally tries to cut out Git as much as possible, in favor of pure mails\nwith non-corrupted, non-HTML patches in them, a charmingly anachronistic\nrequirement until you try to use state-of-the-art mail clients to send\nthem.\n\nI disagree, however, with the suggestion to sift through your `pu` branch\nand to somehow replace local branches with the commits found there.\n\nI guess it is time to revisit our patch submission process if it does not\neven work between the two of us.\n\nIdeally, we would come up with a process that\n\n- makes everything easier for maintainers and contributors alike,\n\n- tracks the history of the patch iterations (answering the question \"what\n  changed between iterations?\"),\n\n- *actually* integrates with Git (to see what I mean, try to find the\n  commit corresponding to a given mail containing a patch, and then try to\n  find the previous iteration's version of the same commit, and weep),\n\n- provides machine-readable metadata about the context, e.g. to jump back\n  and forth between the full file contents and the patch, or to indicate\n  the dependency on another branch,\n\n- facilitates \"back contributions\", i.e. letting contributors accept\n  changes suggested by reviewers *with minimal effort*.\n\n- uses Git itself as much as possible, i.e. no additional tools written in\n  \"you must learn this new language, it's awesome, believe me, it's huge\"\n\nThe biggest obstacles I see are 1) the integration with the mailing list\n(which is ironic because contributing via the list used to be a boon, not\na burden) and 2) maintaining the integrity between what has been reviewed\nand what is actually in the branch.\n\nThis is nothing we will solve overnight, of course. But I think we will\nhave to fix this.\n\nFood for thought.\nDscho\n"},{"id":"292912","messageId":"20160803163449.iwjv4youmsf6okme@sigill.intra.peff.net","threadId":"42743","inReplyTo":"CAPc5daXJzMsJf5K84XBFuQ5=q_OwtYUW2FikZ2QsZWk8fa9jgg@mail.gmail.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-08-03T16:34:49Z","receivedAt":"2016-08-03T17:04:57Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Aug 03, 2016 at 08:33:12AM -0700, Junio C Hamano wrote:\n\n> On Wed, Aug 3, 2016 at 4:59 AM, Johannes Schindelin\n> <Johannes.Schindelin@gmx.de> wrote:\n> >\n> > I disagree, however, with the suggestion to sift through your `pu` branch\n> > and to somehow replace local branches with the commits found there.\n> \n> To be more in line with the \"e-mailed patch\" workflow, I think what I should\n> do is to send the version I queued with fixups back to the list as follow-up.\n> Just like reviewers review, the maintainer reviews and queues, the original\n> author should be able to work in the same workflow, i.e. reading and applying\n> an improved version of the patch from her mailbox.\n\nLeaving aside Dscho's questions of whether pulling patches from email is\nconvenient for most submitters (it certainly is for me, but I recognize\nthat it is not for many), I would much rather see incremental fixup\npatches from you than whole \"here's what I queued\" responses.\n\nThe reason is that your fixups may not be the only ones needed. There\nmay be others on the list that come before or after, and I may even have\nalready made fixes locally for \"v2\" that haven't been on the list. If I\nhaven't made any changes yet, I can throw out my topic, start with what\nyou queued, and then apply other changes incrementally. But if I have,\nthen I need to convert yours to a diff, which requires checking out the\nsame base, applying yours, and running diff. Much easier to get the diff\nin the first place. :)\n\nThat only covers changes to the code, though. It does not help with\nfixups to commit messages. It would be neat to have a microformat for\nspecifying and applying patches to commit messages.\n\n-Peff\n"},{"id":"292919","messageId":"alpine.DEB.2.20.1608031753431.107993@virtualbox","threadId":"42743","inReplyTo":"CAPc5daXJzMsJf5K84XBFuQ5=q_OwtYUW2FikZ2QsZWk8fa9jgg@mail.gmail.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-03T16:07:45Z","receivedAt":"2016-08-03T17:05:08Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 3 Aug 2016, Junio C Hamano wrote:\n\n> On Wed, Aug 3, 2016 at 4:59 AM, Johannes Schindelin\n> <Johannes.Schindelin@gmx.de> wrote:\n> >\n> > I disagree, however, with the suggestion to sift through your `pu` branch\n> > and to somehow replace local branches with the commits found there.\n> \n> To be more in line with the \"e-mailed patch\" workflow, I think what I should\n> do is to send the version I queued with fixups back to the list as follow-up.\n> Just like reviewers review, the maintainer reviews and queues, the original\n> author should be able to work in the same workflow, i.e. reading and applying\n> an improved version of the patch from her mailbox.\n\nYou seem to assume that it isn't cumbersome for people like me to extract\npatches out of mails and to replace existing commits using those patches.\n\nSo it probably comes as a huge surprise to you to learn that this *is*\ncumbersome for me.\n\nI got too used to the ease of git push, git pull with or without --rebase,\nand many other Git commands. Having to transmogrify code changes from\ncommits in Git into a completely different universe: plain text patches in\nmy mailbox, and back, losing all kinds of data in the process, is just\nnot at all that easy. And it costs a lot of time.\n\nIn short: if you start \"submitting patches\" back to me via mail, it does\nnot help me. It makes things harder for me. In particular when you add\nyour sign-off to every patch and I have to strip it.\n\nIf you change your workflow, I would humbly request that you do it in a\nway that makes things easier on both sides, not harder.\n\nIt would be a totally different matter, of course, if you used the\nbranches I publish via my GitHub repository, added fixup! and squash!\ncommits, published the result to a public repository and then told me to\npull from there, that would make things easier. We could even introduce a\nreword! construct, to make the review of the suggested edits of the commit\nmessage easier. I could easily verify that my branch head agrees with the\nbase commit of your branch, I could build proper tooling around this\nworkflow, and it would lighten my load.\n\nI guess what I am saying is that we might just as well start using this\nawesome tool to work with code, that tool named \"Git\".\n\nCiao,\nDscho\n"},{"id":"292921","messageId":"20160803165652.zek5df7tv5reg6w4@sigill.intra.peff.net","threadId":"42743","inReplyTo":"xmqqbn19aj5t.fsf@gitster.mtv.corp.google.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-08-03T16:56:52Z","receivedAt":"2016-08-03T17:05:10Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Aug 03, 2016 at 09:53:18AM -0700, Junio C Hamano wrote:\n\n> > Leaving aside Dscho's questions of whether pulling patches from email is\n> > convenient for most submitters (it certainly is for me, but I recognize\n> > that it is not for many), I would much rather see incremental fixup\n> > patches from you than whole \"here's what I queued\" responses.\n> \n> Ah, yes, I misspoke.  It should be either an incremental diff or\n> in-line comment to spell out what got changed as a response to the\n> patch.\n> \n> I find myself fixing the title the most often, which is part of the\n> \"log message\" you pointed out that would not convey well with the\n> \"incremental diff\" approach.\n\nI mentioned a micro-format elsewhere in my message. And it certainly is\nnice to have something that can be applied in an automatic way. But in\npractice, most review comments, for the commit message _or_ the text,\nare given in human-readable terms. And as a human, I read and apply them\nin sequence.\n\nThat pushes work onto the submitter, but saves work from the reviewers,\nwho can quickly say \"something like this...\" without having to worry\nabout making a full change, formatting it as a diff, etc.\n\nI do think that's the right time-tradeoff to be making, as we have more\nsubmitters than reviewers.\n\n-Peff\n"},{"id":"292930","messageId":"xmqqbn19aj5t.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"20160803163449.iwjv4youmsf6okme@sigill.intra.peff.net","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-03T16:53:18Z","receivedAt":"2016-08-03T17:05:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Wed, Aug 03, 2016 at 08:33:12AM -0700, Junio C Hamano wrote:\n>\n>> On Wed, Aug 3, 2016 at 4:59 AM, Johannes Schindelin\n>> <Johannes.Schindelin@gmx.de> wrote:\n>> >\n>> > I disagree, however, with the suggestion to sift through your `pu` branch\n>> > and to somehow replace local branches with the commits found there.\n>> \n>> To be more in line with the \"e-mailed patch\" workflow, I think what I should\n>> do is to send the version I queued with fixups back to the list as follow-up.\n>> Just like reviewers review, the maintainer reviews and queues, the original\n>> author should be able to work in the same workflow, i.e. reading and applying\n>> an improved version of the patch from her mailbox.\n>\n> Leaving aside Dscho's questions of whether pulling patches from email is\n> convenient for most submitters (it certainly is for me, but I recognize\n> that it is not for many), I would much rather see incremental fixup\n> patches from you than whole \"here's what I queued\" responses.\n\nAh, yes, I misspoke.  It should be either an incremental diff or\nin-line comment to spell out what got changed as a response to the\npatch.\n\nI find myself fixing the title the most often, which is part of the\n\"log message\" you pointed out that would not convey well with the\n\"incremental diff\" approach.\n"},{"id":"292940","messageId":"CAPc5daXJzMsJf5K84XBFuQ5=q_OwtYUW2FikZ2QsZWk8fa9jgg@mail.gmail.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1608031021050.79248@virtualbox","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-03T15:33:12Z","receivedAt":"2016-08-03T17:05:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"On Wed, Aug 3, 2016 at 4:59 AM, Johannes Schindelin\n<Johannes.Schindelin@gmx.de> wrote:\n>\n> I disagree, however, with the suggestion to sift through your `pu` branch\n> and to somehow replace local branches with the commits found there.\n\nTo be more in line with the \"e-mailed patch\" workflow, I think what I should\ndo is to send the version I queued with fixups back to the list as follow-up.\nJust like reviewers review, the maintainer reviews and queues, the original\nauthor should be able to work in the same workflow, i.e. reading and applying\nan improved version of the patch from her mailbox.\n"},{"id":"292951","messageId":"CAGZ79kYWdZCNW_eBi5aLAacyBZJXQ9xyOWMBmjNsYT5NWjr-Og@mail.gmail.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1608031753431.107993@virtualbox","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-08-03T17:47:59Z","receivedAt":"2016-08-03T17:48:30Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Wed, Aug 3, 2016 at 9:07 AM, Johannes Schindelin\n<Johannes.Schindelin@gmx.de> wrote:\n> Hi Junio,\n>\n> On Wed, 3 Aug 2016, Junio C Hamano wrote:\n>\n>> On Wed, Aug 3, 2016 at 4:59 AM, Johannes Schindelin\n>> <Johannes.Schindelin@gmx.de> wrote:\n>> >\n>> > I disagree, however, with the suggestion to sift through your `pu` branch\n>> > and to somehow replace local branches with the commits found there.\n>>\n>> To be more in line with the \"e-mailed patch\" workflow, I think what I should\n>> do is to send the version I queued with fixups back to the list as follow-up.\n>> Just like reviewers review, the maintainer reviews and queues, the original\n>> author should be able to work in the same workflow, i.e. reading and applying\n>> an improved version of the patch from her mailbox.\n>\n> You seem to assume that it isn't cumbersome for people like me to extract\n> patches out of mails and to replace existing commits using those patches.\n>\n> So it probably comes as a huge surprise to you to learn that this *is*\n> cumbersome for me.\n\nIt is also cumbersome for me, because I never had the need to setup a proper\nmail client that has the strength to apply patches. The need was not there as\nI tend to apply only rarely patches by email, so I can go the painful\nway each time.\n\nBut if we as a community decide that we bounce emails back and forth,\nI (and you)\nmay have to find a proper email client that is easy to work with, e.g.\none key shortcut\nto apply a patch series to the HEAD of your local repository.\n\n>\n> I got too used to the ease of git push, git pull with or without --rebase,\n> and many other Git commands. Having to transmogrify code changes from\n> commits in Git into a completely different universe: plain text patches in\n> my mailbox, and back, losing all kinds of data in the process, is just\n> not at all that easy. And it costs a lot of time.\n>\n> In short: if you start \"submitting patches\" back to me via mail, it does\n> not help me. It makes things harder for me. In particular when you add\n> your sign-off to every patch and I have to strip it.\n\nYou don't have to strip the sign off, as it shows the flow of the patch,\ne.g.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Stefan Beller <sbeller@google.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n\nmay indicate you proposed a patch, Junio picked it up (and fixed a\ntypo optionally),\nI obtained the patch (via mail, via Git?) improved it, you improved it further\nand then Junio took it and merged it upstream.\n\n>\n> If you change your workflow, I would humbly request that you do it in a\n> way that makes things easier on both sides, not harder.\n\nWhen attending the Git Merge conference in May, gregkh said roughtly:\n\"We deliberately waste developers time, because it is not scarce.\nMaintainers time is scarce however \" and it stuck with me. (and I\nam a developer, not a maintainer ;( so at least the kernel community\ndeems it ok to waste my time).\n\nWhile that is true for the kernel community, I guess it is also true for the\nGit community, unless Junio (and the community) want to appoint a\nbunch of maintainer lieutenants, such that they outnumber the number\nof developers, e.g. divided by areas of the code:\na refs backend maintainer, a submodule maintainer, ...\nor rather by area of usage: a porcelain UI maintainer, a git-on-server\nmaintainer.\n\nThough Git is not as diverse and large as the kernel, so the horde of\nmaintainers would step onto each feet quite frequently IMHO.\n\n>\n> It would be a totally different matter, of course, if you used the\n> branches I publish via my GitHub repository, added fixup! and squash!\n> commits, published the result to a public repository and then told me to\n> pull from there, that would make things easier. We could even introduce a\n> reword! construct, to make the review of the suggested edits of the commit\n> message easier. I could easily verify that my branch head agrees with the\n> base commit of your branch, I could build proper tooling around this\n> workflow, and it would lighten my load.\n>\n> I guess what I am saying is that we might just as well start using this\n> awesome tool to work with code, that tool named \"Git\".\n\nI think Git itself is for the tracking the code and managing it, e.g. merging,\nmoving, keeping it. That doesn't quite include modifying and creating code\n(e.g. there is no \"git edit\" command)\n\nIf we were to change our workflows drastically, I'd propose to\ngo a way[1] similar to notedb in Gerrit, or git-series, which defines\na common review format, such that we have a \"protocol\" how to store\nthe review data and how to store the progress of potential collaboration\nand then we can develop tools against that protocol. Some people\nwant to have a web UI, whereas others want to have a text only thing\nas they are faster keyboard only.\n\n[1] http://git.661346.n2.nabble.com/Working-towards-a-common-review-format-for-git-td7645242.html\n\nThanks for starting this discussion,\nStefan\n"},{"id":"293087","messageId":"alpine.DEB.2.20.1608041706040.5786@virtualbox","threadId":"42743","inReplyTo":"20160803165652.zek5df7tv5reg6w4@sigill.intra.peff.net","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-04T15:29:52Z","receivedAt":"2016-08-04T15:30:37Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Peff,\n\nOn Wed, 3 Aug 2016, Jeff King wrote:\n\n> On Wed, Aug 03, 2016 at 09:53:18AM -0700, Junio C Hamano wrote:\n> \n> > > Leaving aside Dscho's questions of whether pulling patches from email is\n> > > convenient for most submitters (it certainly is for me, but I recognize\n> > > that it is not for many), I would much rather see incremental fixup\n> > > patches from you than whole \"here's what I queued\" responses.\n> > \n> > Ah, yes, I misspoke.  It should be either an incremental diff or\n> > in-line comment to spell out what got changed as a response to the\n> > patch.\n> > \n> > I find myself fixing the title the most often, which is part of the\n> > \"log message\" you pointed out that would not convey well with the\n> > \"incremental diff\" approach.\n> \n> I mentioned a micro-format elsewhere in my message. And it certainly is\n> nice to have something that can be applied in an automatic way.\n\nIndeed. This is what I meant by my (succinct to the point of being\nintelligible, admittedly) reword! suggestion.\n\nLet's clarify this idea.\n\nI find myself using fixup! and squash! commits a lot. Actually, let me\npull out the Linux key for that. I use those commits A LOT.\n\nI know, I opposed the introduction of this feature initially (and I think\nthat my concerns were nicely addressed by Junio's suggestion to guard this\nfeature behind the --autosquash option). Guess what: I was wrong.\n\nAnd I am really missing the same functionality for the commit message\nmunging. These days, I find myself using `git commit --allow-empty\n--squash=$COMMIT -c $COMMIT` very often, duplicating the first line,\nadding an empty line between them, deleting the \"squash! \" prefix from the\nnow-third line, and then editing the commit message as I want to. When it\ncomes to cleaning up the branch via rebase -ki, I simply jump to the empty\nline after the squash! line and delete everything before it.\n\nThis is as repetitive, tedious and un-fun to me as having to transmogrify\npatches from the nice and cozy Git universe into the not-at-all compatible\nuniverse of mails (I congratulate you personally, Peff, for finding a mail\nclient that works for you. I am still looking for one that does not suck,\nAlpine being the least sucky I settled for).\n\nSo my idea was to introduce a new --reword=<commit> option to `git commit`\nthat would commit an empty patch and let the user edit the commit message,\nlater replacing the original one with the new one. This is not *quite* as\nnice as I want it, because it makes the changes unobvious. On the other\nhand, I would not find a series of sed commands more obvious, in\nparticular because that limits you in the ways of sed. And, you know,\nregexp. I like them, but I know many people cannot really work with them.\n\n> But in practice, most review comments, for the commit message _or_ the\n> text, are given in human-readable terms. And as a human, I read and\n> apply them in sequence.\n\nSo true. I do the very same.\n\n> That pushes work onto the submitter, but saves work from the reviewers,\n> who can quickly say \"something like this...\" without having to worry\n> about making a full change, formatting it as a diff, etc.\n> \n> I do think that's the right time-tradeoff to be making, as we have more\n> submitters than reviewers.\n\nI agree that it is the right trade-off. TBH I was shocked when I learned\nhow much effort Junio puts into applying my patches. I do not want that. I\nwant my branch to reflect pretty precisely (modulo sign-off, of course)\nwhat is going to be integrated into Git's source code.\n\nI'd much prefer to resubmit a cleaned-up version, even if it was just the\ncommit subjects, and be certain that `pu` and my branch are on the same\npage.\n\nInstead, Junio puts in a tremendous amount of work, and it does not help\nanybody, because the local branches *still* do not have his fixups, and as\na consequence subsequent iterations of the patch series will have to be\nfixed up *again*.\n\nJust compare https://github.com/git/git/compare/1fd7e78...6999bc7 to\nhttps://github.com/dscho/git/compare/f8f7adc...3b4494c (the onelines are\nenough to show you just how different things are).\n\nI'd much prefer the contributor (me, in this case) to put in a little more\nwork, and have things consistent. And avoid unnecessary work on both\nsides.\n\nCiao,\nDscho\n"},{"id":"293092","messageId":"alpine.DEB.2.20.1608041730130.5786@virtualbox","threadId":"42743","inReplyTo":"CAGZ79kYWdZCNW_eBi5aLAacyBZJXQ9xyOWMBmjNsYT5NWjr-Og@mail.gmail.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-04T15:58:07Z","receivedAt":"2016-08-04T16:05:14Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Stefan,\n\nOn Wed, 3 Aug 2016, Stefan Beller wrote:\n\n> On Wed, Aug 3, 2016 at 9:07 AM, Johannes Schindelin\n> <Johannes.Schindelin@gmx.de> wrote:\n> >\n> > On Wed, 3 Aug 2016, Junio C Hamano wrote:\n> >\n> >> On Wed, Aug 3, 2016 at 4:59 AM, Johannes Schindelin\n> >> <Johannes.Schindelin@gmx.de> wrote:\n> >> >\n> >> > I disagree, however, with the suggestion to sift through your `pu`\n> >> > branch and to somehow replace local branches with the commits found\n> >> > there.\n> >>\n> >> To be more in line with the \"e-mailed patch\" workflow, I think what I\n> >> should do is to send the version I queued with fixups back to the\n> >> list as follow-up.  Just like reviewers review, the maintainer\n> >> reviews and queues, the original author should be able to work in the\n> >> same workflow, i.e. reading and applying an improved version of the\n> >> patch from her mailbox.\n> >\n> > You seem to assume that it isn't cumbersome for people like me to\n> > extract patches out of mails and to replace existing commits using\n> > those patches.\n> >\n> > So it probably comes as a huge surprise to you to learn that this *is*\n> > cumbersome for me.\n> \n> It is also cumbersome for me, because I never had the need to setup a\n> proper mail client that has the strength to apply patches. The need was\n> not there as I tend to apply only rarely patches by email, so I can go\n> the painful way each time.\n\nThe reason is clear, too. Mail clients serve humans. That is their\npurpose. Humans do not care all that much whether the text was preserved\nexactly as the sender wrote it, except rich text (read: HTML), of course.\n\n> > I got too used to the ease of git push, git pull with or without\n> > --rebase, and many other Git commands. Having to transmogrify code\n> > changes from commits in Git into a completely different universe:\n> > plain text patches in my mailbox, and back, losing all kinds of data\n> > in the process, is just not at all that easy. And it costs a lot of\n> > time.\n> >\n> > In short: if you start \"submitting patches\" back to me via mail, it\n> > does not help me. It makes things harder for me. In particular when\n> > you add your sign-off to every patch and I have to strip it.\n> \n> You don't have to strip the sign off, as it shows the flow of the patch,\n> e.g.\n> \n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> Signed-off-by: Stefan Beller <sbeller@google.com>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> \n> may indicate you proposed a patch, Junio picked it up (and fixed a typo\n> optionally), I obtained the patch (via mail, via Git?) improved it, you\n> improved it further and then Junio took it and merged it upstream.\n\nRecently, I got yelled at because I took one of Junio's patches, made a\ncouple of changes, and *added* my sign-off.\n\nBefore that incident, I agreed with you that it may make for a nice record\nof the back-and-forth that eventually resulted in the patch in question.\nNow, I am not so sure anymore.\n\n> > If you change your workflow, I would humbly request that you do it in\n> > a way that makes things easier on both sides, not harder.\n> \n> When attending the Git Merge conference in May, gregkh said roughtly:\n> \"We deliberately waste developers time, because it is not scarce.\n> Maintainers time is scarce however \" and it stuck with me. (and I am a\n> developer, not a maintainer ;( so at least the kernel community deems it\n> ok to waste my time).\n\nYeah. It was not the only thing I disagreed with in his talk. To be a\nlittle bit blunt (by my standards, not by LKML's standards, that is): the\nLinux kernel mailing list is not necessarily anything I would want to use\nas a role model.\n\nI agree that maintainers' time is scarce.\n\nI am one.\n\nSo of course I agree with that statement. What I disagree with is that it\nis okay to *waste* contributors' time. That's just inconsiderate. And I\nsay that also because I am a contributor *in addition* to being a\nmaintainer.\n\nAs a consequence, I commend Greg for recognizing that the patch submission\nprocess must be light on the maintainer. And I would have commended him\neven further if he had realized that proper tooling should waste nothing,\nand no one's time.\n\n> While that is true for the kernel community, I guess it is also true for\n> the Git community, unless Junio (and the community) want to appoint a\n> bunch of maintainer lieutenants, such that they outnumber the number of\n> developers, e.g. divided by areas of the code: a refs backend\n> maintainer, a submodule maintainer, ...  or rather by area of usage: a\n> porcelain UI maintainer, a git-on-server maintainer.\n\nAs I mentioned earlier, I do not care much about following LKML's example.\n\nWhat I see on this here list is that many a potential contributor is\nscared away, that we waste precious time (also the maintainer's) pointing\nout in what way certain contributions do not follow the guide lines, and\nthat even old-timers sometimes submit patches that are white-space\ncorrupted.\n\nThat is a *huge* waste of time. In my opinion, the culprit is that we do\nnot use appropriate tools. To a mail client, everything looks like a nail.\nWrong metaphor, but you get the point.\n\n> > It would be a totally different matter, of course, if you used the\n> > branches I publish via my GitHub repository, added fixup! and squash!\n> > commits, published the result to a public repository and then told me\n> > to pull from there, that would make things easier. We could even\n> > introduce a reword! construct, to make the review of the suggested\n> > edits of the commit message easier. I could easily verify that my\n> > branch head agrees with the base commit of your branch, I could build\n> > proper tooling around this workflow, and it would lighten my load.\n> >\n> > I guess what I am saying is that we might just as well start using this\n> > awesome tool to work with code, that tool named \"Git\".\n> \n> I think Git itself is for the tracking the code and managing it, e.g.\n> merging, moving, keeping it. That doesn't quite include modifying and\n> creating code (e.g. there is no \"git edit\" command)\n\nGit is not only for tracking the code. It knows about editors\n(core.editor), it can export .zip files (git-archive), it can show\nhuman-readable (not machine-readable) word diffs, etc.\n\nWhenever we needed a certain functionality, we added it.\n\n> If we were to change our workflows drastically, I'd propose to\n> go a way[1] similar to notedb in Gerrit, or git-series,\n\nGerrit is a huge, non-distributed system. Complex, too. If we change the\npatch submission process, we should make things *easier*, not *harder*. So\nI think Gerrit is pretty much out of the question.\n\nEven requiring every contributor to register with GitHub would be too much\nof a limitation, I would wager.\n\nAnd when I said I have zero interest in tools that use the \"latest and\ngreatest language\", I was hinting at git-series. Rust may be a fine and\nwonderful language. Implementing git-series in Rust, however, immediately\nlimited the potential engagement with developers dramatically.\n\nAdditionally, I would like to point out that defining a way to store\nreviews in Git is not necessarily improving the way our code contribution\nprocess works. If you want to record the discussions revolving around the\ncode, I think public-inbox already does a pretty good job at that.\n\nI guess I have no really good idea yet, either, how to retain the ease of\naccess of sending mails to the list, yet somehow keep a strong tie with\nthe original data stored in Git.\n\nCiao,\nDscho\n"},{"id":"293101","messageId":"CAGZ79kaTT3NgKj8akB8t9b1BF3i6sXe7Un9oq5KP8077Wz-E+g@mail.gmail.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1608041730130.5786@virtualbox","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-08-04T16:42:18Z","receivedAt":"2016-08-04T16:48:35Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Thu, Aug 4, 2016 at 8:58 AM, Johannes Schindelin\n<Johannes.Schindelin@gmx.de> wrote:\n>\n>> If we were to change our workflows drastically, I'd propose to\n>> go a way[1] similar to notedb in Gerrit, or git-series,\n>\n> Gerrit is a huge, non-distributed system. Complex, too. If we change the\n> patch submission process, we should make things *easier*, not *harder*. So\n> I think Gerrit is pretty much out of the question.\n\nI did not propose to use Gerrit or git-series or git appraise.\n\nHowever whenever I see these projects talking to each other, the talk focuses on\na \"unified review storage format\" pretty often, which would make them compatible\nwith each other. So then I could choose git-series, while you could go with\ngit appraise (although that is written in go, so maybe too fancy already ;)\nOr there could be another new program written in C that follows the \"review\nformat\".\n\n\n>\n> Even requiring every contributor to register with GitHub would be too much\n> of a limitation, I would wager.\n>\n> And when I said I have zero interest in tools that use the \"latest and\n> greatest language\", I was hinting at git-series. Rust may be a fine and\n> wonderful language. Implementing git-series in Rust, however, immediately\n> limited the potential engagement with developers dramatically.\n>\n> Additionally, I would like to point out that defining a way to store\n> reviews in Git is not necessarily improving the way our code contribution\n> process works. If you want to record the discussions revolving around the\n> code, I think public-inbox already does a pretty good job at that.\n\nYeah recording is great, but we want to talk about replying and modifying\na series? So if I see a patch flying by on the mailing list, ideally I could\nattach a \"!fixup, signed off by Stefan\" thing to that patch. (I said \"thing\"\nas I do not necessarily mean email here.\n\n>\n> I guess I have no really good idea yet, either, how to retain the ease of\n> access of sending mails to the list, yet somehow keep a strong tie with\n> the original data stored in Git.\n\nDoes it have to be email? Transmitting text could be solved differently as well.\n\nWith git push/fetch we can interact with a git remote and pertain the state\n(commits, ancestor graph) at a full level even including notes that comment\non commits.\n\ngit send-email/format-patch recently learned to include a base commit\n(xy/format-patch-base), maybe we need a counter part to git send-email\nthat downloads a series from your mailbox, such that a local branch\ncan be transmitted to via\n\n    \"git send-email --base=origin/master --include-notes --name=sb/new-series\"\n\nand completely reconstructed (i.e. the commit sha1s even match) including\nnotes via:\n\n    git fetch-email --name=sb/new-series\n\nThat way would ensure we have a \"simple\" way to transmit patches back and forth\nand adding potential fixups.\n\n\nYou wrote:\n> In short, I agree that our patch submission process is a saber tooth tiger\n> that still reflects pre-Git times. While we use Git's tools, the workflow\n> really tries to cut out Git as much as possible, in favor of pure mails\n> with non-corrupted, non-HTML patches in them, a charmingly anachronistic\n> requirement until you try to use state-of-the-art mail clients to send\n> them.\n\nAnd there are two ways out:\n* either we teach git how to deal with emails (completely, i.e.\nsending+receiving)\n* or we change the development model (e.g. no emails any more)\n\nThere is no golden third way IMHO.\n\nThanks,\nStefan\n"},{"id":"293110","messageId":"20160804180749.foowbsmce72s46ww@sigill.intra.peff.net","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1608041706040.5786@virtualbox","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-08-04T18:07:49Z","receivedAt":"2016-08-04T18:08:02Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Aug 04, 2016 at 05:29:52PM +0200, Johannes Schindelin wrote:\n\n> So my idea was to introduce a new --reword=<commit> option to `git commit`\n> that would commit an empty patch and let the user edit the commit message,\n> later replacing the original one with the new one. This is not *quite* as\n> nice as I want it, because it makes the changes unobvious. On the other\n> hand, I would not find a series of sed commands more obvious, in\n> particular because that limits you in the ways of sed. And, you know,\n> regexp. I like them, but I know many people cannot really work with them.\n\nI don't have a real opinion on this. I probably wouldn't use it, but I\nhave no problem with it existing. I think it's somewhat orthogonal to\nthe idea of _transmitting_ those reword operations to somebody else.\n\n> > That pushes work onto the submitter, but saves work from the reviewers,\n> > who can quickly say \"something like this...\" without having to worry\n> > about making a full change, formatting it as a diff, etc.\n> > \n> > I do think that's the right time-tradeoff to be making, as we have more\n> > submitters than reviewers.\n> \n> I agree that it is the right trade-off. TBH I was shocked when I learned\n> how much effort Junio puts into applying my patches. I do not want that. I\n> want my branch to reflect pretty precisely (modulo sign-off, of course)\n> what is going to be integrated into Git's source code.\n\nLike you, I have occasionally been bitten by Junio doing a fixup, and\nthen I end up re-rolling, and lose that fixup (or have to deal with\nporting it forward with awkward tools).\n\nBut I think such fixups are a calculated risk. Sometimes they save a lot\nof time, both for the maintainer and the contributor, when they manage\nto prevent another round-trip of the patch series to the list.\n\nIOW, if the flow is something like:\n\n  1. Contributor sends patches. People review.\n\n  2. Minor fixups noticed by maintainer, fixed while applying.\n\n  3. Only one small fixup needed from review. Contributor sends\n     squashable patch. Maintainer squashes.\n\nthen I think that is a net win over sending the whole series again, for\nthe contributor (who does not bother sending), reviewers (who really\nonly need to look at the interdiff, which is what that squash is in the\nfirst place), and the maintainer (who can squash just as easily as\nre-applying the whole series).\n\nIt does mean the \"final\" version of the series is never on the list. It\nhas to be pieced together from the squash (and sometimes step 2 is not\neven mentioned on-list).\n\nSo I think it is really a judgement call for step (3) on what is a\n\"small\" fixup, and whether it is easier for everybody to look at the\nsquash interdiff and say \"yep, that's right\", versus re-reviewing the\nwhole series.\n\n> I'd much prefer to resubmit a cleaned-up version, even if it was just the\n> commit subjects, and be certain that `pu` and my branch are on the same\n> page.\n> \n> Instead, Junio puts in a tremendous amount of work, and it does not help\n> anybody, because the local branches *still* do not have his fixups, and as\n> a consequence subsequent iterations of the patch series will have to be\n> fixed up *again*.\n\nAnd that is the flip side. If the flow above does not happen, then step\n2 just becomes a pain.\n\nI don't have a silver bullet or anything. I'm mostly just musing.\n\n-Peff\n"},{"id":"293111","messageId":"xmqqk2fw4d8z.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"4cbe2757bd921a202c359382086fadeb2616434a.1470051326.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v6 07/16] merge-recursive: avoid returning a wholesale struct","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-04T18:09:48Z","receivedAt":"2016-08-04T18:09:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> It is technically allowed, as per C89, for functions' return type to\n> be complete structs (i.e. *not* just pointers to structs).\n>\n> However, it was just an oversight of this developer when converting\n> Python code to C code in 6d297f8 (Status update on merge-recursive in\n> C, 2006-07-08) which introduced such a return type.\n>\n> Besides, by converting this construct to pass in the struct, we can now\n> start returning a value that can indicate errors in future patches. This\n> will help the current effort to libify merge-recursive.c.\n\nI do not think returning a small struct by value is unconditionally\na bad thing, but I do agree with you that this change makes the \nresulting code much easier to read, especially once this starts\nreturning errors.\n\nGood.\n\n"},{"id":"293112","messageId":"xmqqfuqk4d1y.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"b20cdc35797d5ed97a738e85928819088e31a01a.1470051326.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v6 08/16] merge-recursive: allow write_tree_from_memory() to error out","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-04T18:14:01Z","receivedAt":"2016-08-04T18:14:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> It is possible that a tree cannot be written (think: disk full). We\n> will want to give the caller a chance to clean up instead of letting\n> the program die() in such a case.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  merge-recursive.c | 4 ++--\n>  1 file changed, 2 insertions(+), 2 deletions(-)\n>\n> diff --git a/merge-recursive.c b/merge-recursive.c\n> index 2be1e17..1f86338 100644\n> --- a/merge-recursive.c\n> +++ b/merge-recursive.c\n> @@ -1888,8 +1888,8 @@ int merge_trees(struct merge_options *o,\n>  \telse\n>  \t\tclean = 1;\n>  \n> -\tif (o->call_depth)\n> -\t\t*result = write_tree_from_memory(o);\n> +\tif (o->call_depth && !(*result = write_tree_from_memory(o)))\n> +\t\treturn -1;\n\nI'll let it pass, but we avoid assignment in a conditional part for\na good reason: it can become unreadable pretty quickly.  Writing it\nin a long-hand, e.g.\n\n\tif (o->call_depth) {\n        \t*result = write_tree_from_memory(o);\n                if (!*result)\n                \treturn -1;\n\t}\n\nfuture-proofs against the \"o->call_depth\" condition part and\n\"write_tree_from_memory(o)\" operation part becoming longer, possibly\nneeding multiple statements.\n\nThe change itself is correct.\n"},{"id":"293113","messageId":"xmqqbn184cuo.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"0fa117f8ae42e2f7c9b1cc803261b236d5cd6b17.1470051326.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v6 15/16] Ensure that the output buffer is released after calling merge_trees()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-04T18:18:23Z","receivedAt":"2016-08-04T18:18:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> The recursive merge machinery accumulates its output in an output\n> buffer, to be flushed at the end of merge_recursive(). At this point,\n> we forgot to release the output buffer.\n> ...\n> diff --git a/merge-recursive.c b/merge-recursive.c\n> index ec50932..9e527de 100644\n> --- a/merge-recursive.c\n> +++ b/merge-recursive.c\n> @@ -2078,6 +2078,8 @@ int merge_recursive(struct merge_options *o,\n>  \t\tcommit_list_insert(h2, &(*result)->parents->next);\n>  \t}\n>  \tflush_output(o);\n> +\tif (!o->call_depth && o->buffer_output < 2)\n> +\t\tstrbuf_release(&o->obuf);\n\nOK, with !o->call_depth the change makes sense to me.\n"},{"id":"293114","messageId":"xmqq7fbw4cqo.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"3b4494c586574b59d80d0f1b88bd4c3d56b678cf.1470051326.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v6 16/16] merge-recursive: flush output buffer even when erroring out","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-04T18:20:47Z","receivedAt":"2016-08-04T18:20:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> Ever since 66a155b (Enable output buffering in merge-recursive.,\n> 2007-01-14), we had a problem: When the merge failed in a fatal way, all\n> regular output was swallowed because we called die() and did not get a\n> chance to drain the output buffers.\n\nOK.  Even though I really wanted to see somebody else review this\nseries as well, I finished reading it through one more time before\nthat happened, which is unfortunate because I think this is ready to\nstart cooking in 'next' even though I no longer have much faith in\nmy eyes alone after staring at this series so many times---you start\nmissing details.\n\nThanks.\n"},{"id":"293135","messageId":"20160804201751.GA9592@starla","threadId":"42743","inReplyTo":"CAGZ79kaTT3NgKj8akB8t9b1BF3i6sXe7Un9oq5KP8077Wz-E+g@mail.gmail.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2016-08-04T20:17:51Z","receivedAt":"2016-08-04T20:18:14Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Stefan Beller <sbeller@google.com> wrote:\n> On Thu, Aug 4, 2016 at 8:58 AM, Johannes Schindelin\n> <Johannes.Schindelin@gmx.de> wrote:\n> > I guess I have no really good idea yet, either, how to retain the ease of\n> > access of sending mails to the list, yet somehow keep a strong tie with\n> > the original data stored in Git.\n> \n> Does it have to be email? Transmitting text could be solved\n> differently as well.\n\nI've thought a lot about this over the years still think email\nis the least bad.\n\nAnti-spam tools for other messaging systems are far behind,\nproprietary, or non-existent.  bugzilla.kernel.org has been hit\nhard lately and I see plenty of bug-tracker-to-mail spam as a\nresult from projects that use web-based bug trackers.\n\nAnd email spam filtering isn't even that great\n(and I think it needs to be better for IPv6 and .onion adoption\n since much of it is still IPv4-oriented blacklisting).\n\nI guess a blockchain (*coin) implementation might work (like\nhashcash is used for email anti-spam), but the ones I've glanced\nat all look like a bigger waste of electricity than email\nfilters.\n\n\nOf course, centralized systems are unacceptable to me;\nand with that I'll never claim any network service I run\nwill be reliable :)\n"},{"id":"293140","messageId":"xmqq37mk1bnt.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"20160804180749.foowbsmce72s46ww@sigill.intra.peff.net","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-04T21:12:22Z","receivedAt":"2016-08-04T21:12:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> Like you, I have occasionally been bitten by Junio doing a fixup, and\n> then I end up re-rolling, and lose that fixup.\n\n... which usually is caught when I receive the reroll, as I try to\napply to the same base and compare the result with the previous\nround.\n\n> But I think such fixups are a calculated risk. Sometimes they save a lot\n> of time, both for the maintainer and the contributor, when they manage\n> to prevent another round-trip of the patch series to the list.\n\nYes.\n\n> IOW, if the flow is something like:\n>\n>   1. Contributor sends patches. People review.\n>\n>   2. Minor fixups noticed by maintainer, fixed while applying.\n\nThis includes different kinds of things:\n\n    a) Trivially correct fixes given in other people's review.\n\n    b) Minor fixups by the maintainer, to code.\n\n    c) Minor fixups by the maintainer, to proposed log message.\n\n    d) \"apply --whitespace=fix\" whose result I do not even actively\n       keep track of.\n\n>   3. Only one small fixup needed from review. Contributor sends\n>      squashable patch. Maintainer squashes.\n>\n> then I think that is a net win over sending the whole series again, for\n> the contributor (who does not bother sending), reviewers (who really\n> only need to look at the interdiff, which is what that squash is in the\n> first place), and the maintainer (who can squash just as easily as\n> re-applying the whole series).\n\n> And that is the flip side. If the flow above does not happen, then step\n> 2 just becomes a pain.\n\nI think I can\n\n * stop taking 2-a).  This is less work for me, but some\n   contributors are leaky and can lose obviously good suggestions,\n   so I am not sure if that is an overall win for the quality of the\n   end product;\n\n * do a separate \"SQUASH???\" commit and send them out for 2-b).\n   This is a lot more work for a large series, but about the same\n   amount of work (except for \"remembering to send them out\" part)\n   for a small-ish topic.  A contributor needs to realize that I\n   deal with _all_ the incoming topics, not just from topics from\n   one contributor, and small additional work tends to add up.\n\nto reduce #2.  Essentially, doing these two are about moving more\nresponsibility of keeping track of good suggestions in the review\ndiscussion to the contributor, but a bad thing is that it does not\nmean that the maintainer can stop keeping track of them.  We need a\nway to make sure leaky contributors do not repeat the same issue in\ntheir reroll that has already been pointed out and whose solutions\npresented in the previous review.  Fetching from 'pu' and compare\nhas been one way to do so (that is why I publish 'pu' in the first\nplace, not to \"build on\", but to \"view\"), but apparently not many\ncontributors are comfortable with that idea.\n\nThere is no good way to reduce 2-c) and 2-d), but on the other hand,\nthere is no strong need for a special channel to propagate these\nback.  2-c) can be a regular review comment (but again that risks\n\"the leaky contributor\" problem) and 2-d) will happen automatically\nwhen applying the rerolled version.\n"},{"id":"293171","messageId":"20160805081754.5tjn7xtly5igdi63@sigill.intra.peff.net","threadId":"42743","inReplyTo":"xmqq37mk1bnt.fsf@gitster.mtv.corp.google.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-08-05T08:17:55Z","receivedAt":"2016-08-05T08:18:11Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Aug 04, 2016 at 02:12:22PM -0700, Junio C Hamano wrote:\n\n> >   2. Minor fixups noticed by maintainer, fixed while applying.\n> \n> This includes different kinds of things:\n> \n>     a) Trivially correct fixes given in other people's review.\n> \n>     b) Minor fixups by the maintainer, to code.\n> \n>     c) Minor fixups by the maintainer, to proposed log message.\n> \n>     d) \"apply --whitespace=fix\" whose result I do not even actively\n>        keep track of.\n>\n> [...]\n>\n> I think I can\n> \n>  * stop taking 2-a).  This is less work for me, but some\n>    contributors are leaky and can lose obviously good suggestions,\n>    so I am not sure if that is an overall win for the quality of the\n>    end product;\n\nActually, I think the 2-a class is what often saves a re-roll. Somebody\npoints out a typo in a commit message or a comment, and it quite often\ngets picked up by you without having another round-trip to the list.\n\nIf you want to save work by not doing so, that's fine with me. But this\nis the gamble I was talking about. I think it's actually often less work\nto do the fixup than to look at another re-roll (especially with the\n\"leaky contributor\" thing where you have to make sure all fixes were\napplied). So it's a win if it saves the re-roll, but sometimes you end\nup having to look at the re-roll anyway.\n\n>  * do a separate \"SQUASH???\" commit and send them out for 2-b).\n>    This is a lot more work for a large series, but about the same\n>    amount of work (except for \"remembering to send them out\" part)\n>    for a small-ish topic.  A contributor needs to realize that I\n>    deal with _all_ the incoming topics, not just from topics from\n>    one contributor, and small additional work tends to add up.\n\nI think these are largely the same as 2-a. You are just wearing two\nhats, reviewer and maintainer. Which I guess lets you take a shortcut\nsometimes (and just fix without mentioning it), but fundamentally the\n\"gamble\" aspect is the same, I think.\n\n-Peff\n"},{"id":"293172","messageId":"alpine.DEB.2.20.1608050925240.5786@virtualbox","threadId":"42743","inReplyTo":"CAGZ79kaTT3NgKj8akB8t9b1BF3i6sXe7Un9oq5KP8077Wz-E+g@mail.gmail.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-05T08:20:47Z","receivedAt":"2016-08-05T08:21:29Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Stefan,\n\nOn Thu, 4 Aug 2016, Stefan Beller wrote:\n\n> On Thu, Aug 4, 2016 at 8:58 AM, Johannes Schindelin\n> <Johannes.Schindelin@gmx.de> wrote:\n> >\n> >> If we were to change our workflows drastically, I'd propose to go a\n> >> way[1] similar to notedb in Gerrit, or git-series,\n> >\n> > Gerrit is a huge, non-distributed system. Complex, too. If we change\n> > the patch submission process, we should make things *easier*, not\n> > *harder*. So I think Gerrit is pretty much out of the question.\n> \n> I did not propose to use Gerrit or git-series or git appraise.\n> \n> However whenever I see these projects talking to each other, the talk\n> focuses on a \"unified review storage format\" pretty often, which would\n> make them compatible with each other.\n\nUnless you have a splendid idea how to integrate that unified format with\nour review process on the mailing list, I think this makes for a fine\ndiscussion elsewhere. I'd really like to focus on the Git project and its\npatch contribution process in this here thread.\n\n> So then I could choose git-series, while you could go with git appraise\n> (although that is written in go, so maybe too fancy already ;)\n\nI think you misunderstood me in a big way.\n\nNew languages are awesome. I play with new toys whenever I find the time\n(and streamlining the contribution process would give me more time for\nthat). You are talking to a person who implemented the Householder\ntransformation in Postscript, a 6502 assembler in Forth, and a music\ncomposing system in Emacs Lisp. No language is to fancy for me. \"me\", as\nin \"me, personally\".\n\nNow let's think about Git for a moment, and its language choices, and the\nrationale behind them. The majority of the critically important code is\nwritten in C. Is C a good language? Decent, yes, but of course also\nlimiting, Resource leaks are very easy to overlook, and we have a share of\nthem. No object orientation, so when we need to \"subclass\", we have no\ncompile time safety. The pre-processor constructs make static analysis\nnigh impossible. Plenty of downsides. So why was it chosen? Developers are\n*familiar* with it, that's why. Similar considerations apply to the use of\nshell scripts, and to Perl, to a certain extent.\n\nI am not talking about contrib/ of course. That's fair game, it contains\nonly non-critical/fringe stuff.\n\nNote that the same rationale goes for choosing to accept patch submissions\nvia mail to a list that is not subscribers-only.\n\nWhen it comes to inviting developers to contribute to your project,\npersonal preferences become irrelevant, the deciding factor becomes how\neasy it is to join. Is the language popular, many developers already\nfamiliar with it? Is the build system readily available? Are the\nmaintainers responsive?\n\nI vividly remember my reaction to Darcs, for example. It's written in\nHaskell. I am a mathematician originally, so Haskell appeals to me. Did\nthe choice of language appear to be designed to keep contributors out? To\nme, it looked that way.\n\nOther example: submitGit. I really like what its intention. For a while, I\neven hoped to move my *own* patch submissions to submitGit. I planned to\nhelp get the kinks out of the code by contributing to it. It is written in\nScala, using a web application and testing framework I have not\nencountered elsewhere. I struggled with installing it locally and wrapping\nmy head around the coding paradigms, for 1.5 days (which was all I could\nafford at that time). Then I had to give up. Which made me very sad. I\nwould not have written my mail-path-series.sh tool if submitGit had been\nwritten in node.js, for example, with which I am familiar enough to jump\nright in.\n\nSo I hope you understand better now why I find Rust a poor choice for\nsomething like git-series, because it should not waste contributors' time\nby insisting that their price of entry is learning a new language they are\nunfamiliar with, using a new packaging system, installing a new build\nsetup. I would find Clojure, Crystal or Swift just as poor a choice. Even\nnode.js. It is just too much of a \"Keep Out\" sign for busy developers. And\nall the developers worth their salt are busy.\n\n> > Additionally, I would like to point out that defining a way to store\n> > reviews in Git is not necessarily improving the way our code\n> > contribution process works. If you want to record the discussions\n> > revolving around the code, I think public-inbox already does a pretty\n> > good job at that.\n> \n> Yeah recording is great, but we want to talk about replying and\n> modifying a series? So if I see a patch flying by on the mailing list,\n> ideally I could attach a \"!fixup, signed off by Stefan\" thing to that\n> patch. (I said \"thing\" as I do not necessarily mean email here.\n\nRight. I briefly considered suggesting a new tool that would operate on\nattachments, integrating tightly with the local git.git checkout. Briefly.\nI had to reject this idea because I do not think that requiring new tools\njust to contribute to Git would fly well.\n\n> > I guess I have no really good idea yet, either, how to retain the ease\n> > of access of sending mails to the list, yet somehow keep a strong tie\n> > with the original data stored in Git.\n> \n> Does it have to be email? Transmitting text could be solved differently\n> as well.\n\nWell, you can only convince old-timers like Junio and Peff incrementally,\nby showing them something that makes their life easier, and that they do\nnot *have* to use.\n\nAdditionally, keep in mind that the single thing *all* potential\ncontributors have in common is access to email.\n\nSo yes, I think that any improvement would have to happen incrementally,\nopt-in. Meaning: on top of the current process.\n\n> With git push/fetch we can interact with a git remote and pertain the\n> state (commits, ancestor graph) at a full level even including notes\n> that comment on commits.\n\nIncluding much more, in fact: *any* kind of data.\n\nBut how to build on top of the current process, where some reviewers jump\nin via NNTP, for crying out loud? How to ensure the integrity between what\nis flying around as mails and what is present in the Git repository?\n\n> git send-email/format-patch recently learned to include a base commit\n\nYou may have noticed that my mail-patch-series.sh-generated code\nsubmissions contain that base commit. But they still do not contain the\nSHA-1s of my local commits corresponding to the patches, and even if they\ndid, the replies with suggested edits would most likely have lost said\ninformation.\n\nI also hate to break it to you that git-send-email is not going to be part\nof any solution.\n\n> You wrote:\n> > In short, I agree that our patch submission process is a saber tooth\n> > tiger that still reflects pre-Git times. While we use Git's tools, the\n> > workflow really tries to cut out Git as much as possible, in favor of\n> > pure mails with non-corrupted, non-HTML patches in them, a charmingly\n> > anachronistic requirement until you try to use state-of-the-art mail\n> > clients to send them.\n> \n> And there are two ways out:\n> * either we teach git how to deal with emails (completely, i.e.\n> sending+receiving)\n> * or we change the development model (e.g. no emails any more)\n> \n> There is no golden third way IMHO.\n\nThere are plenty more options.\n\nIn Git for Windows, I would accept patches via mail (curiously, nobody\ntried that in the past 12 months, not that I recall). I accept Pull\nRequests. I try to use patches mentioned in issue comments (!) and apply\nthem.\n\nThe point is: you do not *have* to limit yourself to accepting patches\n*only* in one way.\n\nAnother option would be to come up with a non-opinionated tool that helps\nwith submitting *and accepting* patches via mail. Non-opinionated, as in:\nit does not expect to write an entire raw mail and have that entire raw\nmail transmitted intact. It could, for example, generate human-consumable\nplain text and *also* an attachment that the tool understands. This tool\ncould then even show a GUI to help with inspecting the relevant\ncode/patches.\n\nYet another option would be to have a tool that integrates with the Git\nrepository of the Git mailing list represented by public-inbox.\n\nPlenty more options.\n\nCiao,\nDscho\n"},{"id":"293173","messageId":"alpine.DEB.2.20.1608051023560.5786@virtualbox","threadId":"42743","inReplyTo":"20160804201751.GA9592@starla","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-05T08:24:00Z","receivedAt":"2016-08-05T08:25:45Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Eric,\n\nOn Thu, 4 Aug 2016, Eric Wong wrote:\n\n> Stefan Beller <sbeller@google.com> wrote:\n> > On Thu, Aug 4, 2016 at 8:58 AM, Johannes Schindelin\n> > <Johannes.Schindelin@gmx.de> wrote:\n> > > I guess I have no really good idea yet, either, how to retain the ease of\n> > > access of sending mails to the list, yet somehow keep a strong tie with\n> > > the original data stored in Git.\n> > \n> > Does it have to be email? Transmitting text could be solved\n> > differently as well.\n> \n> I've thought a lot about this over the years still think email\n> is the least bad.\n\nNot only that: people are *familiar* with it. And they have *access* to\nit.\n\n> Anti-spam tools for other messaging systems are far behind,\n> proprietary, or non-existent.  bugzilla.kernel.org has been hit\n> hard lately and I see plenty of bug-tracker-to-mail spam as a\n> result from projects that use web-based bug trackers.\n\nPlus, they are all centralized. Do you want to *require* contibutors to\nregister with a new service?\n\n> I guess a blockchain (*coin) implementation might work (like\n> hashcash is used for email anti-spam), but the ones I've glanced\n> at all look like a bigger waste of electricity than email\n> filters.\n\nI am not even so much concerned with ecological considerations here. Just\nthe price of entry would be prohibitive.\n\n> Of course, centralized systems are unacceptable to me;\n> and with that I'll never claim any network service I run\n> will be reliable :)\n\nHehehe. I guess that's why the public-inbox is backed by a Git\nrepository... BTW is it auto-mirrored anywhere?\n\nCiao,\nDscho\n"},{"id":"293176","messageId":"20160805085009.GA30906@starla","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1608051023560.5786@virtualbox","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2016-08-05T08:50:09Z","receivedAt":"2016-08-05T09:00:57Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n\nAgreed on all above points :>\n\n> On Thu, 4 Aug 2016, Eric Wong wrote:\n> > Of course, centralized systems are unacceptable to me;\n> > and with that I'll never claim any network service I run\n> > will be reliable :)\n> \n> Hehehe. I guess that's why the public-inbox is backed by a Git\n> repository... BTW is it auto-mirrored anywhere?\n\nYep, and the code is AGPL so others can always replicate it.\n\nI just have the onions mentioned at the bottom of the HTML pages\nrunning something like:\n\n\ttorsocks git fetch && public-inbox-index && sleep 30\n\nin a loop[1].\n\n\thttp://hjrcffqmbrq6wope.onion/git\n\thttp://czquwvybam4bgbro.onion/git\n\nI do encourage anybody who is able to, to run their own mirrors\n(off their existing email subscription) so my MX doesn't become\nan SPOF, either.  I sorta documented it in my original announcement:\n\nhttps://public-inbox.org/git/20160710004813.GA20210@dcvr.yhbt.net/\nhttps://public-inbox.org/INSTALL\n\nFeel free to ask me (+ meta@public-inbox.org) for\ninstall/running questions related to public-inbox\n(haven't gotten to making it work outside of Debian stable, though).\n\n[1] one of my far-off goals for git be: \"git fetch --wait-for-update\"\n    to avoid needless wakeups\n"},{"id":"293184","messageId":"20160805115911.GA4787@salo","threadId":"42743","inReplyTo":"CAGZ79kaTT3NgKj8akB8t9b1BF3i6sXe7Un9oq5KP8077Wz-E+g@mail.gmail.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Richard Ipsum","fromEmail":"richard.ipsum@codethink.co.uk","sentAt":"2016-08-05T11:59:11Z","receivedAt":"2016-08-05T11:59:43Z","isPatch":true,"sender":{"key":"richard.ipsum@codethink.co.uk","avatar":null},"body":"On Thu, Aug 04, 2016 at 09:42:18AM -0700, Stefan Beller wrote:\n> On Thu, Aug 4, 2016 at 8:58 AM, Johannes Schindelin\n> <Johannes.Schindelin@gmx.de> wrote:\n> >\n> >> If we were to change our workflows drastically, I'd propose to\n> >> go a way[1] similar to notedb in Gerrit, or git-series,\n> >\n> > Gerrit is a huge, non-distributed system. Complex, too. If we change the\n> > patch submission process, we should make things *easier*, not *harder*. So\n> > I think Gerrit is pretty much out of the question.\n> \n> I did not propose to use Gerrit or git-series or git appraise.\n> \n> However whenever I see these projects talking to each other, the talk focuses on\n> a \"unified review storage format\" pretty often, which would make them compatible\n> with each other. So then I could choose git-series, while you could go with\n> git appraise (although that is written in go, so maybe too fancy already ;)\n> Or there could be another new program written in C that follows the \"review\n> format\".\n\nThis \"unified review storage format\" really does seem to be the missing\npiece. The tool I've been working on for the past year (git-candidate)\nwas initially aimed at contrib[1], and was written in perl solely\nto satisfy contrib rules. It would have been python otherwise.\n\nThe feedback from that thread[1], was that while git-candidate itself\nseemed interesting it would be unreasonable to bless a particular\ntool's format. So it seems to me that even if git-series had been\nwritten in perl rather than rust it could have expected a similar\nresponse to that of git-candidate, possibly.\n\nAs Stefan says, if we're able to establish a standard for storing\nreview data in git then it doesn't really matter what the tools are written in.\n\nFor what it's worth my possibly quite shoddy attempt at a library\nimplementing a possible review format for git[2] is written in perl,\nmostly to satisfy contrib requirements.\n\n> >\n> > Even requiring every contributor to register with GitHub would be too much\n> > of a limitation, I would wager.\n> >\n> > And when I said I have zero interest in tools that use the \"latest and\n> > greatest language\", I was hinting at git-series. Rust may be a fine and\n> > wonderful language. Implementing git-series in Rust, however, immediately\n> > limited the potential engagement with developers dramatically.\n\nIronically contrib's language requirements actually raised the bar for me\nbecause it meant that I had to learn perl.\n\n> >\n> > Additionally, I would like to point out that defining a way to store\n> > reviews in Git is not necessarily improving the way our code contribution\n> > process works. If you want to record the discussions revolving around the\n> > code, I think public-inbox already does a pretty good job at that.\n\nI agree, and must apologise if this response has been too off topic,\nin any case I hope at least some of it was useful to someone.\n\nHope this helps,\nRichard Ipsum\n\n[1]: http://www.mail-archive.com/git%40vger.kernel.org/msg80972.html\n[2]: https://bitbucket.org/richardipsum/perl-notedb\n"},{"id":"293193","messageId":"CACsJy8Bzcwfvhc9dQ2EehmAJ+kGwC5VHL4d+4Z-GfmM6e2+3wg@mail.gmail.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1608031753431.107993@virtualbox","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-08-05T14:55:04Z","receivedAt":"2016-08-05T14:55:40Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Aug 3, 2016 at 6:07 PM, Johannes Schindelin\n<Johannes.Schindelin@gmx.de> wrote:\n> It would be a totally different matter, of course, if you used the\n> branches I publish via my GitHub repository, added fixup! and squash!\n> commits, published the result to a public repository and then told me to\n> pull from there, that would make things easier. We could even introduce a\n> reword! construct, to make the review of the suggested edits of the commit\n> message easier.\n\nOn the topic of fixup and squash and everything. Is anyone else\nannoyed that the commit title is taken for fixup!, squash!\ninstructions? After you have added a few of them, \"git log --oneline\"\nbecomes useless. All you see is \"fixup! A\", \"fixup! A\", \"fixup! B\",\n\"fixup! A\".\n\nWould it be better to let the user control the title? We still need\nthe cue \"fixup!\", \"squash!\"... at the beginning of the title, but the\noriginal commit reference is appended at the end, like s-o-b lines.\n-- \nDuy\n"},{"id":"293195","messageId":"alpine.DEB.2.20.1608051713090.5786@virtualbox","threadId":"42743","inReplyTo":"CACsJy8Bzcwfvhc9dQ2EehmAJ+kGwC5VHL4d+4Z-GfmM6e2+3wg@mail.gmail.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-05T15:13:50Z","receivedAt":"2016-08-05T15:14:52Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Duy,\n\nOn Fri, 5 Aug 2016, Duy Nguyen wrote:\n\n> On the topic of fixup and squash and everything. Is anyone else\n> annoyed that the commit title is taken for fixup!, squash!\n> instructions?\n\nI do not know about others, but I am not annoyed by those commit titles. I\nthink they make tons of sense.\n\nCiao,\nDscho\n"},{"id":"293196","messageId":"alpine.DEB.2.20.1608051714110.5786@virtualbox","threadId":"42743","inReplyTo":"20160805115911.GA4787@salo","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-05T15:24:14Z","receivedAt":"2016-08-05T15:24:47Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Richard,\n\nOn Fri, 5 Aug 2016, Richard Ipsum wrote:\n\n> On Thu, Aug 04, 2016 at 09:42:18AM -0700, Stefan Beller wrote:\n> > On Thu, Aug 4, 2016 at 8:58 AM, Johannes Schindelin\n> > <Johannes.Schindelin@gmx.de> wrote:\n> > >\n> > >> If we were to change our workflows drastically, I'd propose to go a\n> > >> way[1] similar to notedb in Gerrit, or git-series,\n> > >\n> > > Gerrit is a huge, non-distributed system. Complex, too. If we change\n> > > the patch submission process, we should make things *easier*, not\n> > > *harder*. So I think Gerrit is pretty much out of the question.\n> > \n> > I did not propose to use Gerrit or git-series or git appraise.\n> > \n> > However whenever I see these projects talking to each other, the talk\n> > focuses on a \"unified review storage format\" pretty often, which would\n> > make them compatible with each other. So then I could choose\n> > git-series, while you could go with git appraise (although that is\n> > written in go, so maybe too fancy already ;) Or there could be another\n> > new program written in C that follows the \"review format\".\n> \n> This \"unified review storage format\" really does seem to be the missing\n> piece.\n\nFWIW I do not think so. The real trick will be to come up with an\nimprovement to the process that lets Junio and Peff continue to work as\nbefore, because It Works For Them, while at the same time letting other\npeople (such as myself) use easy-to-configure tools that add substantial\nconvenience.\n\nWhich, to me, means that the missing piece is a clever idea how to\nintegrate with the mail-based process, without requiring everybody and her\ndog to switch to a specific mail client.\n\n> The tool I've been working on for the past year (git-candidate) was\n> initially aimed at contrib[1], and was written in perl solely to satisfy\n> contrib rules. It would have been python otherwise.\n\nOh...?\n\n$ git ls-files contrib/\\*.py | wc -l\n4\n\nAnd for that matter:\n\n$ git ls-files contrib/\\*.go | wc -l\n4\n\nIn fact, there are even PHP scripts:\n\n$ git ls-files contrib | sed -n 's/.*\\.//p' | sort | grep -v '.....' |\n\tuniq | tr '\\n' ' '\nbash c el Git go perl php pl pm py rst sh tcsh txt zsh\n\nBut again, I do not think that it makes sense to focus too much on a\nlanguage, or on a file format, before we came up with a strategy how to\n*not* require everybody to change their current ways.\n\nCiao,\nDscho\n"},{"id":"293200","messageId":"alpine.DEB.2.20.1608051739170.5786@virtualbox","threadId":"42743","inReplyTo":"xmqq7fbw4cqo.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v6 16/16] merge-recursive: flush output buffer even when erroring out","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-05T15:41:44Z","receivedAt":"2016-08-05T15:42:20Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Thu, 4 Aug 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > Ever since 66a155b (Enable output buffering in merge-recursive.,\n> > 2007-01-14), we had a problem: When the merge failed in a fatal way, all\n> > regular output was swallowed because we called die() and did not get a\n> > chance to drain the output buffers.\n> \n> OK.  Even though I really wanted to see somebody else review this\n> series as well, I finished reading it through one more time before\n> that happened, which is unfortunate because I think this is ready to\n> start cooking in 'next' even though I no longer have much faith in\n> my eyes alone after staring at this series so many times---you start\n> missing details.\n\nYeah, well, it is a rather crucial piece of the code.\n\nBut then, I really tried my best to re-review the series a couple of times\n(with my primary focus on robustness, not elegance) after working on\ndifferent tasks for a couple of days.\n\nCombined with my long-term dogfooding and my readiness to jump on any\nbreakage I may have introduced, I am relatively confident nevertheless.\n\nThank you so much for your patient reviews!\n\nCiao,\nDscho\n"},{"id":"293201","messageId":"alpine.DEB.2.20.1608051743410.5786@virtualbox","threadId":"42743","inReplyTo":"xmqq37mk1bnt.fsf@gitster.mtv.corp.google.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-05T15:51:43Z","receivedAt":"2016-08-05T15:52:08Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Thu, 4 Aug 2016, Junio C Hamano wrote:\n\n> Jeff King <peff@peff.net> writes:\n> \n> > Like you, I have occasionally been bitten by Junio doing a fixup, and\n> > then I end up re-rolling, and lose that fixup.\n> \n> ... which usually is caught when I receive the reroll, as I try to apply\n> to the same base and compare the result with the previous round.\n\nI find it incredibly nice of you to do those fix-ups, sorry to state that\nas clearly only now. It's just that I hate to see your time wasted, in\nparticular when *I* waste it because I missed the fix-ups in `pu`.\n\n> > But I think such fixups are a calculated risk. Sometimes they save a\n> > lot of time, both for the maintainer and the contributor, when they\n> > manage to prevent another round-trip of the patch series to the list.\n> \n> Yes.\n\nFWIW I am more than just willing to spend a little more time on applying\nfix-ups and re-rolling patch series (and dual-publishing them via my\npublic repository), if it helps lighten your burden.\n\n> > IOW, if the flow is something like:\n> >\n> >   1. Contributor sends patches. People review.\n> >\n> >   2. Minor fixups noticed by maintainer, fixed while applying.\n> \n> This includes different kinds of things:\n> \n>     a) Trivially correct fixes given in other people's review.\n> \n>     b) Minor fixups by the maintainer, to code.\n> \n>     c) Minor fixups by the maintainer, to proposed log message.\n> \n>     d) \"apply --whitespace=fix\" whose result I do not even actively\n>        keep track of.\n> \n> >   3. Only one small fixup needed from review. Contributor sends\n> >      squashable patch. Maintainer squashes.\n> >\n> > then I think that is a net win over sending the whole series again, for\n> > the contributor (who does not bother sending), reviewers (who really\n> > only need to look at the interdiff, which is what that squash is in the\n> > first place), and the maintainer (who can squash just as easily as\n> > re-applying the whole series).\n> \n> > And that is the flip side. If the flow above does not happen, then step\n> > 2 just becomes a pain.\n> \n> I think I can\n> \n>  * stop taking 2-a).  This is less work for me, but some\n>    contributors are leaky and can lose obviously good suggestions,\n>    so I am not sure if that is an overall win for the quality of the\n>    end product;\n\nIf you had a `git commit --reword` command to touch up commit messages,\nwould that help you, together with the `git commit --fixup` command for\ncode changes? The branches in `pu` would have your fix-ups as strictly\nseparate commits on top of the contributed patches, and the branches would\nneed to be sent through rebase -i before merging to `next`, of course.\n\nThe idea would be to not forget your fixups in subsequent iterations, but\nsimply rebase them on top of the new iteration.\n\nIt would still not solve my problem that there is no convenient way to\njump from your commits in `pu` to the corresponding ones in my local\nbranch. But that is my problem, not yours.\n\nCiao,\nDscho\n"},{"id":"293212","messageId":"CAGZ79ka5OknKYo_CgBpB16EQjwU5B35yNFWx569K-LPmHuSqWA@mail.gmail.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1608050925240.5786@virtualbox","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-08-05T17:59:37Z","receivedAt":"2016-08-05T18:00:08Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"Hi Johannes,\n\nOn Fri, Aug 5, 2016 at 1:20 AM, Johannes Schindelin\n<Johannes.Schindelin@gmx.de> wrote:\n> Hi Stefan,\n>\n> On Thu, 4 Aug 2016, Stefan Beller wrote:\n>\n>> On Thu, Aug 4, 2016 at 8:58 AM, Johannes Schindelin\n>> <Johannes.Schindelin@gmx.de> wrote:\n>> >\n>> >> If we were to change our workflows drastically, I'd propose to go a\n>> >> way[1] similar to notedb in Gerrit, or git-series,\n>> >\n>> > Gerrit is a huge, non-distributed system. Complex, too. If we change\n>> > the patch submission process, we should make things *easier*, not\n>> > *harder*. So I think Gerrit is pretty much out of the question.\n>>\n>> I did not propose to use Gerrit or git-series or git appraise.\n>>\n>> However whenever I see these projects talking to each other, the talk\n>> focuses on a \"unified review storage format\" pretty often, which would\n>> make them compatible with each other.\n>\n> Unless you have a splendid idea how to integrate that unified format with\n> our review process on the mailing list, I think this makes for a fine\n> discussion elsewhere. I'd really like to focus on the Git project and its\n> patch contribution process in this here thread.\n>\n>> So then I could choose git-series, while you could go with git appraise\n>> (although that is written in go, so maybe too fancy already ;)\n>\n> I think you misunderstood me in a big way.\n>\n> New languages are awesome. I play with new toys whenever I find the time\n> (and streamlining the contribution process would give me more time for\n> that). You are talking to a person who implemented the Householder\n> transformation in Postscript, a 6502 assembler in Forth, and a music\n> composing system in Emacs Lisp. No language is to fancy for me. \"me\", as\n> in \"me, personally\".\n>\n> Now let's think about Git for a moment, and its language choices, and the\n> rationale behind them. The majority of the critically important code is\n> written in C. Is C a good language? Decent, yes, but of course also\n> limiting, Resource leaks are very easy to overlook, and we have a share of\n> them. No object orientation, so when we need to \"subclass\", we have no\n> compile time safety. The pre-processor constructs make static analysis\n> nigh impossible. Plenty of downsides. So why was it chosen? Developers are\n> *familiar* with it, that's why. Similar considerations apply to the use of\n> shell scripts, and to Perl, to a certain extent.\n\nWhich is changing slowly over time IMHO. The \"new generation\" of\ndevelopers may not have in-depth knowledge of shelll or perl any more,\nbut rather python or java maybe.\n\n>\n> I am not talking about contrib/ of course. That's fair game, it contains\n> only non-critical/fringe stuff.\n>\n> Note that the same rationale goes for choosing to accept patch submissions\n> via mail to a list that is not subscribers-only.\n>\n> When it comes to inviting developers to contribute to your project,\n> personal preferences become irrelevant, the deciding factor becomes how\n> easy it is to join. Is the language popular, many developers already\n> familiar with it? Is the build system readily available? Are the\n> maintainers responsive?\n\nI agree. I really do.\n\nI had some discussion at lunch yesterday about different attitudes of open\nsource projects towards new contributions. An example was the Eclipse\nprojects that scare off potential new contributors (specially these\nfly by single patch submissions) as you need to interact through their\ninstance of Gerrit after signing a CLA.\n\n\nGit on the other hand doesn't do\na bad job, e.g. I started with a patch that ought to be a single shot drive\nby patch. About 780 others wrote one patch and were never seen again:\n\n    $ git shortlog -sne |grep 1 |wc -l\n    788\n\nSo our process is not too bad for the time. However email is as a\ncommunication tool is dying [1] eventually?\n\nLet's examine if we get less one time contributions over time:\n\n    for i in $(seq 11 -1 1) ; do\n        x_1=$(git shortlog -sne --since $i.years --until\n$(($i-1)).years |grep 1 |wc -l)\n        printf \"$i\\t$x_1\\n\"\n    done\n\n11       81\n10       94\n9        175\n8        148\n7        139\n6        118\n5        108\n4        149\n3        109\n2        106\n1        99\n\nLooking at these numbers the number of one time patch contributions\nspiked 9 years ago and declined since then (4 years ago seems to be\nan outlier)\n\nAnd I do not think the decline of one off contributions is because Git as\na project is in decline, but has other reasons. Maybe the project is in better\nshape now that there are less one-off worthy contributions? Or the\ncontribution process is not appealing to a lot of new comers?\n\n[1] I could not find a scientific paper that evaluates communcation\nhabits of people, but these may give a feel for it:\nhttps://www.quora.com/Why-are-young-people-abandoning-email\nhttps://news.ycombinator.com/item?id=3554466\nhttp://www.twistimage.com/blog/archives/nobody-uses-email-anymore/\nhttp://www.emailisnotdead.com/\n\n>\n> I vividly remember my reaction to Darcs, for example. It's written in\n> Haskell. I am a mathematician originally, so Haskell appeals to me. Did\n> the choice of language appear to be designed to keep contributors out? To\n> me, it looked that way.\n>\n> Other example: submitGit. I really like what its intention. For a while, I\n> even hoped to move my *own* patch submissions to submitGit. I planned to\n> help get the kinks out of the code by contributing to it. It is written in\n> Scala, using a web application and testing framework I have not\n> encountered elsewhere. I struggled with installing it locally and wrapping\n> my head around the coding paradigms, for 1.5 days (which was all I could\n> afford at that time). Then I had to give up. Which made me very sad. I\n> would not have written my mail-path-series.sh tool if submitGit had been\n> written in node.js, for example, with which I am familiar enough to jump\n> right in.\n\nWhy did you desire to set it up yourself and not just use it?\n(I'd guess one of the underlying reasons could be sticking to FOSS principles,\ni.e. you want to be in control of what you use; But there I would argue to\njust approach it pragmatically. When it ceases to exist, there is time to\nlook for an alternative.)\n\n>\n> So I hope you understand better now why I find Rust a poor choice for\n> something like git-series, because it should not waste contributors' time\n> by insisting that their price of entry is learning a new language they are\n> unfamiliar with, using a new packaging system, installing a new build\n> setup. I would find Clojure, Crystal or Swift just as poor a choice. Even\n> node.js. It is just too much of a \"Keep Out\" sign for busy developers. And\n> all the developers worth their salt are busy.\n\nIdeally you would not need to touch the internals of said tool, hence no\nneed to learn Rust? Of course no tool is perfect, so eventually you'd want\nto look at the internals.\n\nYou can take the same argument for our current contribution process.\nIs it easier to learn a new language or setup a proper mail client that\ndoesn't screw up your emails?\nDepending on the language, it is really easy to get started\nand have some results out in a day or two.\n\n>\n>> > Additionally, I would like to point out that defining a way to store\n>> > reviews in Git is not necessarily improving the way our code\n>> > contribution process works. If you want to record the discussions\n>> > revolving around the code, I think public-inbox already does a pretty\n>> > good job at that.\n>>\n>> Yeah recording is great, but we want to talk about replying and\n>> modifying a series? So if I see a patch flying by on the mailing list,\n>> ideally I could attach a \"!fixup, signed off by Stefan\" thing to that\n>> patch. (I said \"thing\" as I do not necessarily mean email here.\n>\n> Right. I briefly considered suggesting a new tool that would operate on\n> attachments, integrating tightly with the local git.git checkout. Briefly.\n> I had to reject this idea because I do not think that requiring new tools\n> just to contribute to Git would fly well.\n\nWell it would be part of Git?\nI consider Git a toolbox that has all kinds of useful tools to deal with\ncode, i.e. transporting, diffing, storing. And transporting is a broad\ncategory: we have push/pull with various protocols,\nbundles for sneaker net and send-email for \"pushing over email\".\n\nSo we can have another transport tool that helps our own workflow?\n\n>\n>> > I guess I have no really good idea yet, either, how to retain the ease\n>> > of access of sending mails to the list, yet somehow keep a strong tie\n>> > with the original data stored in Git.\n>>\n>> Does it have to be email? Transmitting text could be solved differently\n>> as well.\n>\n> Well, you can only convince old-timers like Junio and Peff incrementally,\n> by showing them something that makes their life easier, and that they do\n> not *have* to use.\n>\n> Additionally, keep in mind that the single thing *all* potential\n> contributors have in common is access to email.\n\nAs I said above, this may not be true any more in the far future.\nHowever I guess email is still a good base layer.\n\n\n>\n> So yes, I think that any improvement would have to happen incrementally,\n> opt-in. Meaning: on top of the current process.\n\nThe current process is free text on a mailing list for replies, so developing\na tool on top of this \"data format\" is really hard.\n\n>\n>> With git push/fetch we can interact with a git remote and pertain the\n>> state (commits, ancestor graph) at a full level even including notes\n>> that comment on commits.\n>\n> Including much more, in fact: *any* kind of data.\n>\n> But how to build on top of the current process, where some reviewers jump\n> in via NNTP, for crying out loud? How to ensure the integrity between what\n> is flying around as mails and what is present in the Git repository?\n\nCurrently we don't. It breaks all the time. (C.f. Junio: \"This is whitespace\nbroken,; no need to resend I'll fix it up locally\")\n\n>\n>> git send-email/format-patch recently learned to include a base commit\n>\n> You may have noticed that my mail-patch-series.sh-generated code\n> submissions contain that base commit. But they still do not contain the\n> SHA-1s of my local commits corresponding to the patches, and even if they\n> did, the replies with suggested edits would most likely have lost said\n> information.\n>\n> I also hate to break it to you that git-send-email is not going to be part\n> of any solution.\n\nIt's written in perl, so it's not one of the core parts of Git that you\nmentioned above. I do use it though for my submission process.\n\nBut both send-email as well as mail-patch-series as well as git-series\nare all about the *sending* part. Not about the back and forth part, i.e.\nthese don't deal with: \"here is a fixup on top\". And by that I mean\nreceiving mails and applying them. git-am is there as a front-end\nonce you obtained the mail, but from what I get, your original problem\nis to get up to date with the latest state, that is either in pu or a proposed\nfixup mail on top of your series?\n\n>\n>> You wrote:\n>> > In short, I agree that our patch submission process is a saber tooth\n>> > tiger that still reflects pre-Git times. While we use Git's tools, the\n>> > workflow really tries to cut out Git as much as possible, in favor of\n>> > pure mails with non-corrupted, non-HTML patches in them, a charmingly\n>> > anachronistic requirement until you try to use state-of-the-art mail\n>> > clients to send them.\n>>\n>> And there are two ways out:\n>> * either we teach git how to deal with emails (completely, i.e.\n>> sending+receiving)\n>> * or we change the development model (e.g. no emails any more)\n>>\n>> There is no golden third way IMHO.\n>\n> There are plenty more options.\n>\n> In Git for Windows, I would accept patches via mail (curiously, nobody\n> tried that in the past 12 months, not that I recall). I accept Pull\n> Requests. I try to use patches mentioned in issue comments (!) and apply\n> them.\n\nThat is great!\n\n>\n> The point is: you do not *have* to limit yourself to accepting patches\n> *only* in one way.\n\nI am not sure who the \"you\" is here.\n\nYou said:\n\n> Well, you can only convince old-timers like Junio and Peff incrementally,\n> by showing them something that makes their life easier, and that they do\n> not *have* to use.\n\nThey like email and it works for them. Are you suggesting Junio and Jeff\nshould accept pull request like you do?\n\nFor me as a contributor, I'll just use email for now for exactly this reason.\nI have it setup and know the quirks (which I occasionally forget, so it breaks\nagain), but it is working \"good enough\".\n\n>\n> Another option would be to come up with a non-opinionated tool that helps\n> with submitting *and accepting* patches via mail. Non-opinionated, as in:\n> it does not expect to write an entire raw mail and have that entire raw\n> mail transmitted intact. It could, for example, generate human-consumable\n> plain text and *also* an attachment that the tool understands. This tool\n> could then even show a GUI to help with inspecting the relevant\n> code/patches.\n\nSo a new email client that is specially adapted to the needs of the\nGit workflow?\n\n>\n> Yet another option would be to have a tool that integrates with the Git\n> repository of the Git mailing list represented by public-inbox.\n\nSo my first reaction to that would be: you could push you patches to\nthat public inbox and it is translated to emails sent on your behalf.\n\nWhich is reinventing submitGit just in a more accessible language?\n\n>\n> Plenty more options.\n>\n> Ciao,\n> Dscho\n\nThanks for writing such a long email, I missed some of the\nmore subtle points before indeed.\n\nThanks,\nStefan\n"},{"id":"293219","messageId":"FAE9116880074D6FA421942CCAEC368F@PhilipOakley","threadId":"42743","inReplyTo":"CACsJy8Bzcwfvhc9dQ2EehmAJ+kGwC5VHL4d+4Z-GfmM6e2+3wg@mail.gmail.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Philip Oakley","fromEmail":"philipoakley@iee.org","sentAt":"2016-08-05T18:42:10Z","receivedAt":"2016-08-05T18:42:19Z","isPatch":true,"sender":{"key":"philipoakley@iee.email","avatar":"https://avatars.githubusercontent.com/u/914343?v=4"},"body":"From: \"Duy Nguyen\" <pclouds@gmail.com>\n> On Wed, Aug 3, 2016 at 6:07 PM, Johannes Schindelin\n> <Johannes.Schindelin@gmx.de> wrote:\n>> It would be a totally different matter, of course, if you used the\n>> branches I publish via my GitHub repository, added fixup! and squash!\n>> commits, published the result to a public repository and then told me to\n>> pull from there, that would make things easier. We could even introduce a\n>> reword! construct, to make the review of the suggested edits of the \n>> commit\n>> message easier.\n>\n> On the topic of fixup and squash and everything. Is anyone else\n> annoyed that the commit title is taken for fixup!, squash!\n> instructions? After you have added a few of them, \"git log --oneline\"\n> becomes useless. All you see is \"fixup! A\", \"fixup! A\", \"fixup! B\",\n> \"fixup! A\".\n>\n> Would it be better to let the user control the title? We still need\n> the cue \"fixup!\", \"squash!\"... at the beginning of the title, but the\n> original commit reference is appended at the end, like s-o-b lines.\n\nIn the same vein, I always want,with my workflow, to use \"fixup! \n<short_sha1>\".\n\nThis would be to save trying to retype the title correctly, and simply use \nthe abbreviated sha1, which would nicely allow an extra short summaty of \nwhat the fixup is about.\n\nPhilip \n\n"},{"id":"293220","messageId":"20160805184656.GA463@starla","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1608050925240.5786@virtualbox","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2016-08-05T18:46:56Z","receivedAt":"2016-08-05T18:47:02Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> On Thu, 4 Aug 2016, Stefan Beller wrote:\n> > git send-email/format-patch recently learned to include a base commit\n> \n> You may have noticed that my mail-patch-series.sh-generated code\n> submissions contain that base commit. But they still do not contain the\n> SHA-1s of my local commits corresponding to the patches, and even if they\n> did, the replies with suggested edits would most likely have lost said\n> information.\n> \n> I also hate to break it to you that git-send-email is not going to be part\n> of any solution.\n\nI think it ought to be.  Some reasons I like emailing patches are:\n\n* there's no taking it back once it's sent\n\n* it's backed up within seconds by thousands of subscribers :)\n\n* doesn't require the reader to have an active connection\n  to fetch out-of-band\n\n* doesn't require the reader to be on the same machine capable\n  of cloning/building the project\n\nThere are times when I've been on a slow machine, or offline\nwhen I wanted to read some patches.\n\nHowever, I do like including a pull request in cover letters\nof a patch series (not necessary for one-offs).\n\n\n\nBut on a side note, I also find it depressing that SMTP is\nuncompressed and TLS compression is (still?) unsafe.  At least I\nuse ssh tunnels w/ compression for IMAP/SMTP to my own server.\n"},{"id":"293224","messageId":"20160805192108.oe3yd5eyu4jopvwn@x","threadId":"42743","inReplyTo":"CAGZ79ka5OknKYo_CgBpB16EQjwU5B35yNFWx569K-LPmHuSqWA@mail.gmail.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Josh Triplett","fromEmail":"josh@joshtriplett.org","sentAt":"2016-08-05T19:21:10Z","receivedAt":"2016-08-05T19:21:23Z","isPatch":true,"sender":{"key":"josh@joshtriplett.org","avatar":"https://avatars.githubusercontent.com/u/162737?v=4"},"body":"On Mon, Sep 17, 2001 at 10:00:00AM +0000, Stefan Beller wrote:\n> But both send-email as well as mail-patch-series as well as git-series\n> are all about the *sending* part. Not about the back and forth part, i.e.\n> these don't deal with: \"here is a fixup on top\". And by that I mean\n> receiving mails and applying them. git-am is there as a front-end\n> once you obtained the mail, but from what I get, your original problem\n> is to get up to date with the latest state, that is either in pu or a proposed\n> fixup mail on top of your series?\n\ngit-series, at least, is intended to handle the back-and-forth: if you\nactually publish the series and not just the final result, someone could\npull the series, make a (non-fast-forwarding) change to that, make a new\nseries commit, and publish their modified version of your series.  You\ncould then incorporate that change.  One of the use cases I developed it\nfor was collaborative development of a patch series.\n\n(That workflow still needs a lot more tool assistance to become fully\nusable, not least of which to assist with the process of merging changes\nto the series.  Working on that.)\n"},{"id":"293237","messageId":"20160805210129.GA11991@starla","threadId":"42743","inReplyTo":"CAGZ79ka5OknKYo_CgBpB16EQjwU5B35yNFWx569K-LPmHuSqWA@mail.gmail.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2016-08-05T21:01:29Z","receivedAt":"2016-08-05T21:02:17Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Stefan Beller <sbeller@google.com> wrote:\n> On Fri, Aug 5, 2016 at 1:20 AM, Johannes Schindelin\n> <Johannes.Schindelin@gmx.de> wrote:\n> > Yet another option would be to have a tool that integrates with the Git\n> > repository of the Git mailing list represented by public-inbox.\n> \n> So my first reaction to that would be: you could push you patches to\n> that public inbox and it is translated to emails sent on your behalf.\n> \n> Which is reinventing submitGit just in a more accessible language?\n\nMaybe vger.kernel.org could open its submission (587) port like\nDebian does.   Unfortunately, this hurts the ability to Cc:\nfolks directly and makes vger even more of an SPOF\n(I guess submitGit also has this problem)\n\nFwiw, I have a public-inbox for my miscellaneous patch barf at\nspew@80x24.org - https://80x24.org/spew/\nhttp://ou63pmih66umazou.onion/spew/\n\nI will throw up my untested/half-tested patches for various\nprojects so I can still access it across various NATs and\nfirewalls.\n\nUsing Tor for send-email often works (depending on which\nblacklists the exit node is on), but is totally optional\n(you'd expose your IP).\n\n==> ~/bin/spew <==\n#!/bin/sh\nexec torsocks git send-email \\\n\t--smtp-domain=80x24.org \\\n\t--smtp-debug=1 \\\n\t--smtp-server-port=submission \\\n\t--smtp-server=80x24.org \\\n\t--to ${DEST-spew@80x24.org} \\\n\t--suppress-cc=all \\\n\t\"$@\"\n\n\nAll: Feel free to use it for any Free Software projects, too;\nor better yet, host your own :)\n"},{"id":"293278","messageId":"xmqq8tw9iw7i.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1608061045240.5786@virtualbox","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-06T18:33:21Z","receivedAt":"2016-08-06T20:09:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> The problem is not Perl, but how fiddly it is to set up. And that you lose\n> all the niceties of an email client (e.g. when you want to add a comment\n> before the diff stat that is not intended to become a part of the commit\n> message).\n\nJust this part.  I do not think that is fair to send-email.  You are\nblaming its \"feature\" that allows it to drive format-patch, which I\ndo not consider is the proper part of the command and to which I\nkept saying from the early days of its introduction that I'd never\nuse it and I think we should discourage its use exactly because it\nencourages a bad workflow (i.e. you skip the final proof-reading\nbefore sending out, and you cannot add footnote comments).\n\nTreat it like an MSA just like Thunderbird, just designed to be more\nsuited to send out patches without corruption, and you will be OK.\nYou work, commit and write your message with your favourite editor,\ndo format-patch, reword or add footnote with your favourite editor,\nand then send it out.  You can avoid letting other MSAs that may\ncorrupt whitespaces touch what you will send out if you used\nsend-email, but that is not mandatory.  As long as your favourite\nMSA does not corrupt your message, you can use it.\n\nSomebody mentioned \"configuring it is hard--why does the user have\nto know SMTP subtleties\", and that may be a valid complaint against\nthe primary part of send-email.  The solution for that is not to\ndiscard it with bathwater, but make it just as easy as other MSAs,\nsay, Thunderbird, to configure for an average user who can configure\nthese other MUAs.\n"},{"id":"293284","messageId":"xmqq4m6xkg4n.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1608051739170.5786@virtualbox","subject":"Re: [PATCH v6 16/16] merge-recursive: flush output buffer even when erroring out","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-06T16:37:44Z","receivedAt":"2016-08-06T20:14:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> On Thu, 4 Aug 2016, Junio C Hamano wrote:\n>\n>> OK.  Even though I really wanted to see somebody else review this\n>> series as well, I finished reading it through one more time before\n>> that happened, which is unfortunate because I think this is ready to\n>> start cooking in 'next' even though I no longer have much faith in\n>> my eyes alone after staring at this series so many times---you start\n>> missing details.\n>\n> Yeah, well, it is a rather crucial piece of the code.\n> ...\n> Thank you so much for your patient reviews!\n\nThanks for working on this to you, too ;-)\n"},{"id":"293290","messageId":"20160806164554.GA24211@salo","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1608051714110.5786@virtualbox","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Richard Ipsum","fromEmail":"richard.ipsum@codethink.co.uk","sentAt":"2016-08-06T16:45:54Z","receivedAt":"2016-08-06T20:22:43Z","isPatch":true,"sender":{"key":"richard.ipsum@codethink.co.uk","avatar":null},"body":"On Fri, Aug 05, 2016 at 05:24:14PM +0200, Johannes Schindelin wrote:\n[snip]\n> > \n> > This \"unified review storage format\" really does seem to be the missing\n> > piece.\n> \n> FWIW I do not think so. The real trick will be to come up with an\n> improvement to the process that lets Junio and Peff continue to work as\n> before, because It Works For Them, while at the same time letting other\n> people (such as myself) use easy-to-configure tools that add substantial\n> convenience.\n> \n> Which, to me, means that the missing piece is a clever idea how to\n> integrate with the mail-based process, without requiring everybody and her\n> dog to switch to a specific mail client.\n\nFair enough, yes it seems to me that git's own review process\nis probably a separate discussion.\n\nAs far as review tools such as git-appraise, git-series and git-candidate\nare concerned, the review storage format really is the missing piece though,\nin my opinion,\nat least if we want to live in a world with compatible review tooling.\n\n> \n> > The tool I've been working on for the past year (git-candidate) was\n> > initially aimed at contrib[1], and was written in perl solely to satisfy\n> > contrib rules. It would have been python otherwise.\n> \n> Oh...?\n> \n> $ git ls-files contrib/\\*.py | wc -l\n> 4\n> \n> And for that matter:\n> \n> $ git ls-files contrib/\\*.go | wc -l\n> 4\n\nI read this guide[1] before I started, and wanted to be on the safe side.\nMaybe that was a mistake... :/\n\n> \n> In fact, there are even PHP scripts:\n> \n> $ git ls-files contrib | sed -n 's/.*\\.//p' | sort | grep -v '.....' |\n> \tuniq | tr '\\n' ' '\n> bash c el Git go perl php pl pm py rst sh tcsh txt zsh\n> \n> But again, I do not think that it makes sense to focus too much on a\n> language, or on a file format, before we came up with a strategy how to\n> *not* require everybody to change their current ways.\n\nFair enough. :)\n\nThanks,\nRichard Ipsum\n\n[1]: https://www.kernel.org/pub/software/scm/git/docs/howto/new-command.html\n"},{"id":"293294","messageId":"996096FBF9464263A35A5AEDED7F34EF@PhilipOakley","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1608061036270.5786@virtualbox","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Philip Oakley","fromEmail":"philipoakley@iee.org","sentAt":"2016-08-06T17:45:32Z","receivedAt":"2016-08-06T20:36:49Z","isPatch":true,"sender":{"key":"philipoakley@iee.email","avatar":"https://avatars.githubusercontent.com/u/914343?v=4"},"body":"From: \"Johannes Schindelin\" <Johannes.Schindelin@gmx.de>\n> Hi Philip,\n>\n> On Fri, 5 Aug 2016, Philip Oakley wrote:\n>\n>> In the same vein, I always want,with my workflow, to use \"fixup!\n>> <short_sha1>\".\n>\n> Good news for you: this is already supported. See here:\n>\n> https://github.com/git/git/blob/v2.9.2/git-rebase--interactive.sh#L786-L796\n>\n\nThat's odd <knowing look>, I never saw any of that in the documentation...\n\nBlame says it was 68d5d03 (rebase: teach --autosquash to match on sha1 in \naddition to message, 2010-11-04) which was before I discovered Git. Maybe \nanother documentation fixup needed ;-)\n\nMind you I'm not sure about 22c5b13 (rebase -i: handle fixup! fixup! \nin --autosquash, 2013-06-27) which looks to only allow one fixup, but maybe \nI'm misreading. [e.g. recieve multiple fixups from the list, or need extra \nfixups as code of documentation is tested]\n\nThe capability is still good to know.\n\nPhilip \n\n"},{"id":"293302","messageId":"20160806214325.GA9484@starla","threadId":"42743","inReplyTo":"xmqq8tw9iw7i.fsf@gitster.mtv.corp.google.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2016-08-06T21:43:25Z","receivedAt":"2016-08-06T21:43:30Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Junio C Hamano <gitster@pobox.com> wrote:\n> Somebody mentioned \"configuring it is hard--why does the user have\n> to know SMTP subtleties\", and that may be a valid complaint against\n> the primary part of send-email.  The solution for that is not to\n> discard it with bathwater, but make it just as easy as other MSAs,\n> say, Thunderbird, to configure for an average user who can configure\n> these other MUAs.\n\nSadly, the average user does not use an MUA, SMTP or IMAP, anymore.\nIt's all webmail or apps using proprietary protocols.\nEmbrace, extend, extinguish :<\n"},{"id":"293306","messageId":"alpine.DEB.2.20.1608061038460.5786@virtualbox","threadId":"42743","inReplyTo":"20160805184656.GA463@starla","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-06T08:44:56Z","receivedAt":"2016-08-06T22:31:51Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Eric,\n\nOn Fri, 5 Aug 2016, Eric Wong wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> > On Thu, 4 Aug 2016, Stefan Beller wrote:\n> > > git send-email/format-patch recently learned to include a base commit\n> > \n> > You may have noticed that my mail-patch-series.sh-generated code\n> > submissions contain that base commit. But they still do not contain the\n> > SHA-1s of my local commits corresponding to the patches, and even if they\n> > did, the replies with suggested edits would most likely have lost said\n> > information.\n> > \n> > I also hate to break it to you that git-send-email is not going to be part\n> > of any solution.\n> \n> I think it ought to be.  Some reasons I like emailing patches are:\n> \n> [...]\n\nWhat I said is that *git-send-email* is not going to be part of any\nsolution.\n\nNote that I said *git-send-email*, not \"emailing patches\".\n\nWhat many people on this list forget is that few email users *ever* touch\ntheir email configuration. Asking them to figure out their SMTP settings\nand then to make git-send-email work is, uhm, quite a bit unrealistic.\n\nCiao,\nDscho\n"},{"id":"293308","messageId":"alpine.DEB.2.20.1608061036270.5786@virtualbox","threadId":"42743","inReplyTo":"FAE9116880074D6FA421942CCAEC368F@PhilipOakley","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-06T08:38:16Z","receivedAt":"2016-08-06T22:37:23Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Philip,\n\nOn Fri, 5 Aug 2016, Philip Oakley wrote:\n\n> In the same vein, I always want,with my workflow, to use \"fixup!\n> <short_sha1>\".\n\nGood news for you: this is already supported. See here:\n\nhttps://github.com/git/git/blob/v2.9.2/git-rebase--interactive.sh#L786-L796\n\nCiao,\nDscho\n"},{"id":"293309","messageId":"alpine.DEB.2.20.1608061045240.5786@virtualbox","threadId":"42743","inReplyTo":"CAGZ79ka5OknKYo_CgBpB16EQjwU5B35yNFWx569K-LPmHuSqWA@mail.gmail.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-06T08:58:52Z","receivedAt":"2016-08-06T23:07:52Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Stefan,\n\njust quickly (i.e. addressing only one point, will try to address more at\na later date) because I want to be mostly offline this weekend:\n\nOn Fri, 5 Aug 2016, Stefan Beller wrote:\n\n> On Fri, Aug 5, 2016 at 1:20 AM, Johannes Schindelin\n> <Johannes.Schindelin@gmx.de> wrote:\n> >\n> > I also hate to break it to you that git-send-email is not going to be\n> > part of any solution.\n> \n> It's written in perl, so it's not one of the core parts of Git that you\n> mentioned above. I do use it though for my submission process.\n\nThe problem is not Perl, but how fiddly it is to set up. And that you lose\nall the niceties of an email client (e.g. when you want to add a comment\nbefore the diff stat that is not intended to become a part of the commit\nmessage).\n\nBut I had an apostrophe last night. I might have been a bit overzealous to\nclaim that git-send-email won't be a part of the solution. It cannot be\na *user-visible* part of any solution, that still holds true.\n\nSo here is the apostrophe: why not implement a bot that listens to the PRs\non GitHub, and accepts commands such as \"@<whatever>bot please send this\nupstream\" via comments on the PR. It would then send the patches to the\nlist, from its own email address, on behalf of the contributor.\n\nLots of things to kink out, such as: does it need to be moderated? Record\nwhat was submitted in its own git.git fork? Accept replies and attach them\nto the correct PR? Maybe annotate those replies with the commits whose\npatches were discussed? Maybe send out replies on the PR as emails? Maybe\ntry to auto-apply suggested patches? Cc: people who commented on earlier\niterations of the patch series? Maybe make interaction smarter using an AI\nbot framework?\n\nIf only I had lots of time on my hand, I'd start by prototyping a node.js\nserver and hooking it up via webhooks, then show it off so others can\ntinker with it.\n\nThis is not completely unlike submitGit, which was a good first attempt,\nbut I never used it because I needed it to do so much more than it already\ndid, *and* it complicated everything by requiring users to register with\nan extra step to allow submitGit to send email on their behalf. It also\nmade contributing to it harder by choosing some non-standard web app\nframework. Also, I really do not like having to go to a different website\njust to send a GitHub PR to the list.\n\nAnyway, that was my brain fart for the day.\n\nCiao,\nDscho\n"},{"id":"293315","messageId":"alpine.DEB.2.20.1608071039410.5786@virtualbox","threadId":"42743","inReplyTo":"20160806214325.GA9484@starla","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-07T08:46:35Z","receivedAt":"2016-08-07T08:47:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 6 Aug 2016, Eric Wong wrote:\n\n> Junio C Hamano <gitster@pobox.com> wrote:\n> > Somebody mentioned \"configuring it is hard--why does the user have\n> > to know SMTP subtleties\", and that may be a valid complaint against\n> > the primary part of send-email.  The solution for that is not to\n> > discard it with bathwater, but make it just as easy as other MSAs,\n> > say, Thunderbird, to configure for an average user who can configure\n> > these other MUAs.\n> \n> Sadly, the average user does not use an MUA, SMTP or IMAP, anymore.\n> It's all webmail or apps using proprietary protocols.\n> Embrace, extend, extinguish :<\n\nI think you both got it wrong. The original citizens were the mail clients\nthat allowed you to communicate with other human beings. Webmail is just a\nnew generation of the same commodity. It is our usage to transport\nmachine-readable content (and not as an attachment!) that is the intruder.\n\nIt's not making things better if we require users to use a second mail\nclient for sending out patches, and, oh, it does nothing to help with\nreintegrating patches back into Git, were they had been before taking that\nperilous and lossy journey through that medium called email.\n\nCiao,\nDscho\n"},{"id":"293323","messageId":"CFDF5DDE-C5D9-489E-B099-6D0D2479B331@gmail.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1608061045240.5786@virtualbox","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Lars Schneider","fromEmail":"larsxschneider@gmail.com","sentAt":"2016-08-07T11:09:28Z","receivedAt":"2016-08-07T11:09:37Z","isPatch":true,"sender":{"key":"larsxschneider@gmail.com","avatar":"https://avatars.githubusercontent.com/u/477434?v=4"},"body":"\n> On 06 Aug 2016, at 10:58, Johannes Schindelin <johannes.schindelin@gmx.de> wrote:\n> \n> Hi Stefan,\n> \n> just quickly (i.e. addressing only one point, will try to address more at\n> a later date) because I want to be mostly offline this weekend:\n> \n> On Fri, 5 Aug 2016, Stefan Beller wrote:\n> \n>> On Fri, Aug 5, 2016 at 1:20 AM, Johannes Schindelin\n>> <Johannes.Schindelin@gmx.de> wrote:\n>>> \n>>> I also hate to break it to you that git-send-email is not going to be\n>>> part of any solution.\n>> \n>> It's written in perl, so it's not one of the core parts of Git that you\n>> mentioned above. I do use it though for my submission process.\n> \n> The problem is not Perl, but how fiddly it is to set up. And that you lose\n> all the niceties of an email client (e.g. when you want to add a comment\n> before the diff stat that is not intended to become a part of the commit\n> message).\n> \n> But I had an apostrophe last night. I might have been a bit overzealous to\n> claim that git-send-email won't be a part of the solution. It cannot be\n> a *user-visible* part of any solution, that still holds true.\n> \n> So here is the apostrophe: why not implement a bot that listens to the PRs\n> on GitHub, and accepts commands such as \"@<whatever>bot please send this\n> upstream\" via comments on the PR. It would then send the patches to the\n> list, from its own email address, on behalf of the contributor.\n> \n> Lots of things to kink out, such as: does it need to be moderated? Record\n> what was submitted in its own git.git fork? Accept replies and attach them\n> to the correct PR? Maybe annotate those replies with the commits whose\n> patches were discussed? Maybe send out replies on the PR as emails? Maybe\n> try to auto-apply suggested patches? Cc: people who commented on earlier\n> iterations of the patch series? Maybe make interaction smarter using an AI\n> bot framework?\n> \n> If only I had lots of time on my hand, I'd start by prototyping a node.js\n> server and hooking it up via webhooks, then show it off so others can\n> tinker with it.\n> \n> This is not completely unlike submitGit, which was a good first attempt,\n> but I never used it because I needed it to do so much more than it already\n> did, *and* it complicated everything by requiring users to register with\n> an extra step to allow submitGit to send email on their behalf. It also\n> made contributing to it harder by choosing some non-standard web app\n> framework. Also, I really do not like having to go to a different website\n> just to send a GitHub PR to the list.\n> \n> Anyway, that was my brain fart for the day.\n\nGreat discussion! I would like to share my perspective a someone who is\na (relatively speaking) new Git contributor and who has never interacted\non mailing lists before Git:\n\n1.) \"git format-patch\" and \"git send-email\" work great for me. It took some\n    time to learn how they work but now I have my own \"submit.sh\" based\n    on those tools and posting a new series is a piece of cake.\n\n2.) Initially it was hard for me to ensure that my patches don't break build or \n    tests on Linux and OS X. Travis CI helps me a lot. I just wished we could\n    get Windows support, too.\n\n3.) I noticed that I get two types of reviews. The first kind points out things\n    in my patch that are obviously wrong. The second kind are things that require\n    a discussion. When I get feedback of the first kind, then I am always super\n    eager to send out a new roll just because I don't want any other reviewer\n    to waste time on obviously wrong patches. However, I have the impression\n    that frequent re-rolls are frowned upon. If we would use Git for the patches\n    instead of email, then I could add \"squash\" patches to indicate changes in\n    the current roll that will be squashed in the next roll (I know I could\n    send squash patches as email, too... but for me that gets confusing quickly).\n\n4.) Reviewing patches is super hard for me because my email client does not\n    support patch color highlighting and I can't easily expand context or look at\n    the history of code touched by the patch (e.g via git blame). I tried to setup\n    Alpine but I wasn't happy with the interface either. I like patches with a GitHub\n    URL for review but then I need to find the right line in the original email to\n    write a comment.\n\nAgain, this is just my point of view as a \"newbie\" and I definitively don't expect\nthe Git community to change their established workflows.\n\nCheers,\nLars"},{"id":"293367","messageId":"xmqqvazbfa5v.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1608071039410.5786@virtualbox","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-08T17:22:20Z","receivedAt":"2016-08-08T17:22:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> I think you both got it wrong. The original citizens were the mail\n> clients that allowed you to communicate with other human beings.\n> ... It is our usage to transport machine-readable content (and not\n> as an attachment!) that is the intruder.\n\nIt is not \"its is our usage\".\n\nYou are too young to remember or too old to remember the history, or\nyou are knowingly distorting it.  The original users of \"patch\" and\n\"diff\" expected that e-mail to be a medium to safely exchange\nchanges to programs among themselves.\n"},{"id":"293369","messageId":"xmqqr39zf9tt.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"CFDF5DDE-C5D9-489E-B099-6D0D2479B331@gmail.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-08T17:29:34Z","receivedAt":"2016-08-08T17:29:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Lars Schneider <larsxschneider@gmail.com> writes:\n\n>     ... then I am always super\n>     eager to send out a new roll just because I don't want any other reviewer\n>     to waste time on obviously wrong patches. However, I have the impression\n>     that frequent re-rolls are frowned upon.\n\nCorrect.  A good middle-ground is to just reply with \"Yes, thanks\nfor your suggestion, will fix in the next round\", while receiving\nreview comments.  Good reviewers who value their time will not to\nwaste their time by responding on a point that has already been\npointed out and acknowledged.\n\n> 4.) Reviewing patches is super hard for me because my email client does not\n>     support patch color highlighting and I can't easily expand context or look at\n>     the history of code touched by the patch (e.g via git blame). I tried to setup\n>     Alpine but I wasn't happy with the interface either. I like patches with a GitHub\n>     URL for review but then I need to find the right line in the original email to\n>     write a comment.\n\nUnless a patch is about an area you are super familiar with so that\nyou know what is beyond the context of the patch to be able to judge\nif the change is good in the context of the file being touched, it\nis always hard to review from inside a mail reader.\n\nRunning \"git am\" is a good first step to review such a patch, as\nthat lets you view the resulting code with the full power of Git.\nAs you gain experience on the codebase, you'll be able to spot more\nproblems while in your mail reader.\n"},{"id":"293435","messageId":"6c937f79-2b82-619d-51fe-adccbe09bd66@alum.mit.edu","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1608041730130.5786@virtualbox","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2016-08-08T22:20:22Z","receivedAt":"2016-08-08T22:20:46Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 08/04/2016 05:58 PM, Johannes Schindelin wrote:\n> [...]\n> Even requiring every contributor to register with GitHub would be too much\n> of a limitation, I would wager.\n> [...]\n\nIs it *really* so insane to consider moving collaboration on the Git\nproject to GitHub or some other similar platform?\n\n* Many, MANY of the most prominent open-source projects are already\nusing GitHub. Many potential contributors already know how to use it and\nalready have accounts. Casual observers (e.g., people who only want to\nclone the repo and/or read issues and PRs) don't even need an account.\n\n* Even if you don't already have a GitHub account, it is vastly easier\nto create one than to set up our current contribution workflow: figure\nout the correct SMTP settings for your email provider, configure\ngit-send-email, test it and work out the kinks, figure out how to use\ngit-am (and even then, actually using git-am is a tedious chore for\npeople who don't use an email client that can run it automatically) [1].\nWe've seen how difficult our current workflow is by observing GSoC\ncandidates attempting to send their first patch. What we haven't seen is\nthe invisible GSoC candidates and other potential contributors who never\neven get as far as attempting to send a patch.\n\n* Interactions that involve code are done using Git commands directly,\nvia exchanging bona fide Git commits. Which means that...\n\n* Commits have unambiguous SHA-1s, which we can use when discussing\nthem, linking to them, merging them, etc. It will no longer be a matter\nof detective work to find out whether a discussion is about v1 or v3 of\na patch series, let alone v3 with some cleanups that were silently added\nby Junio.\n\n* Discussion of pull requests can be done either\n  * via the website (super easy for beginners but powerful for\nexperienced users),\n  * by setting up email notifications for your account and replying to\nthose emails, or\n  * via an API.\n  Such discussion is all in markdown, which supports light formatting,\nhyperlinks, and @-mentions.\n\n* GitHub creates permalink URLs for all of the important artifacts:\ncommits, pull requests, pull request comments, etc. These all can be\nreferenced easily from any discussion. This web of cross-links\naccumulates over time and adds a lot of context to discussions.\n\n* GitHub keeps spam under control.\n\nI admit that if we move to GitHub we would be vulnerable if the company\nturns evil or goes bankrupt. But is that really a bigger risk than we\naccepted by relying on Gmane (a one-person hobbyist operation) for many\nof our historical permalinks, which are now broken? In any case, each of\nus has a mirror of the code, and there are utilities for backing up\nother GitHub metadata. *If* GitHub becomes evil, there will be a lot of\nother open-source projects in the same boat, so I am confident that the\ntooling for salvaging such information will quickly become excellent.\n\nCurrently we force potential Git contributors to learn an email-based\nworkflow that is almost unique to this project, rather than steering\nthem towards a workflow (Git plus, potentially, GitHub) that they\nprobably already know, or if not is worth learning because the knowledge\nwill carry across to most other open-source projects, not to mention\ntheir professional careers.\n\nWe would want to set down guidelines for how we use GitHub. For example,\nwe might insist that each version of a patch series (v1, v2, etc.) be\nsubmitted as a fresh pull request with references to the previous\nversion(s) (which also automatically creates forwards links from the\nprevious versions to the new version). We might want to set up some\nrobots to help with repetitive activities, like style review, pinging\nthe right people, etc.\n\nJunio, I'm very sensitive to the need not to decrease your efficiency.\nBut isn't there a chance that this could *increase* your efficiency? Is\nit worth an experiment?\n\nIs the Git project really such a unique snowflake that we need to use a\nworkflow (and force it on our contributors) that is different than the\nworkflows used by most other open-source projects?\n\nDisclaimer: I work for GitHub, but in this email I'm speaking for myself.\n\nMichael\n\n[1] I concede that people who refuse on ideological grounds to use\nproprietary software will find this step insurmountable. Perhaps we\ncould come up with a workaround for such people.\n\n"},{"id":"293437","messageId":"xmqqshuedh1i.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"6c937f79-2b82-619d-51fe-adccbe09bd66@alum.mit.edu","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-08T22:36:41Z","receivedAt":"2016-08-08T22:36:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Michael Haggerty <mhagger@alum.mit.edu> writes:\n\n> Is it *really* so insane to consider moving collaboration on the Git\n> project to GitHub or some other similar platform?\n\nI only know how \"pull requests\" and \"issues\" discussion in GitHub\nWeb interface _currently_ looks like, so if you have even more\nwonderful thing in the works, I _might_ be swayed otherwise, but I\ndo not think it is sane to expect that the same quality and quantity\nof reviews as we do here can be maintained with that thing.\n"},{"id":"293439","messageId":"3055f063-c9c1-0bf5-99bd-08256c253d33@alum.mit.edu","threadId":"42743","inReplyTo":"xmqqshuedh1i.fsf@gitster.mtv.corp.google.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2016-08-08T23:20:05Z","receivedAt":"2016-08-08T23:20:28Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 08/09/2016 12:36 AM, Junio C Hamano wrote:\n> Michael Haggerty <mhagger@alum.mit.edu> writes:\n> \n>> Is it *really* so insane to consider moving collaboration on the Git\n>> project to GitHub or some other similar platform?\n> \n> I only know how \"pull requests\" and \"issues\" discussion in GitHub\n> Web interface _currently_ looks like, so if you have even more\n> wonderful thing in the works, I _might_ be swayed otherwise,\n\nIf I did I couldn't say anyway, so let's assume that current GitHub is\nwhat's on the table [1].\n\nThere are a couple of recent code-review improvements that you might\nhave missed:\n\n* You can now get email updates about your own activity [2]. (Previously\nyou would only get emails about the activity of other people, which\nwould leave holes in the email record of the conversation.)\n\n* There is also now better visibility of code review comments regarding\nlines that are no longer part of a PR [3].\n\n> but I\n> do not think it is sane to expect that the same quality and quantity\n> of reviews as we do here can be maintained with that thing.\n\nCould you elaborate why you would expect quality and/or quantity of\nreviews to suffer? I'm really curious, and I'd be happy to pass your\nfeedback along to my colleagues.\n\nHere are some factors that I think will *improve* reviews:\n\n* While you are reviewing patches, you can \"zoom out\" to see code beyond\nthe usual diff context. Currently a reviewer who wants more context has\nto transition from reading the diff in email to applying the patch and\nviewing it in another tool. Then the reviewer has to go back to email to\nleave the comment.\n\n* If you want to compile/run/edit/profile the code, you just need to\n\"git fetch\" rather than messing around with \"git am\". For more involved\nsuggestions, it is possible to propose a PR against the original PR.\n\n* It is easy to summon somebody else into the review conversation by\n@-mentioning them. That person immediately can see the whole history of\nthe PR. (This is an improvement on the status quo, where a new entrant\nto a conversation might have to dig through the email trash or an email\narchive to see emails that they overlooked before they were added to the\nCC list.)\n\n* It is easy to subscribe/unsubscribe from particular discussions [4].\nThis makes it easier to follow the discussions you are interested in\nwithout getting swamped with emails about other discussions. You can\nunsubscribe from a discussion permanently, or in such a way that a new\n@-mention brings you back in.\n\n* It is easy to mention other PRs/commits/issues in a discussion, and\nthose mentions become clickable links (no jumping back and forth between\nemail client and web browser). Of course you can also link to arbitrary\nURLs (e.g., mailing list archives).\n\n* It is possible to search old issues and PRs for additional context.\n(Granted, the history of the project from its ML era would have to be\nsearched separately.)\n\nGiven that I work for GitHub, I'm uncomfortable doing any more advocacy\nhere. If people have concrete questions, I'd be happy to answer them, on\nthe list or in private.\n\nMichael\n\n[1] In general, GitHub *does* get better over time, and we would benefit\nfrom any future improvements.\n[2] https://github.com/blog/2203-email-updates-about-your-own-activity\n[3] https://github.com/blog/2123-more-code-review-tools\n[4] https://github.com/blog/2183-improvements-to-notification-emails\n\n"},{"id":"293452","messageId":"CACsJy8DeSv_ALHR+HrViEptgYCYhXu2ZczMmhzZfHGAwZumnzg@mail.gmail.com","threadId":"42743","inReplyTo":"6c937f79-2b82-619d-51fe-adccbe09bd66@alum.mit.edu","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-08-09T04:22:21Z","receivedAt":"2016-08-09T04:22:56Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Aug 9, 2016 at 12:20 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n> On 08/04/2016 05:58 PM, Johannes Schindelin wrote:\n>> [...]\n>> Even requiring every contributor to register with GitHub would be too much\n>> of a limitation, I would wager.\n>> [...]\n>\n> Is it *really* so insane to consider moving collaboration on the Git\n> project to GitHub or some other similar platform?\n\nIn the very unlikely event that github is shut down, how do we get all\nreview comments out of it, assuming that we will use pull requests for\nreview?\n-- \nDuy\n"},{"id":"293461","messageId":"6adc480d-15e3-c0ac-0e05-eb10c767a8ab@drmicha.warpmail.net","threadId":"42743","inReplyTo":"3055f063-c9c1-0bf5-99bd-08256c253d33@alum.mit.edu","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Michael J Gruber","fromEmail":"git@drmicha.warpmail.net","sentAt":"2016-08-09T08:11:30Z","receivedAt":"2016-08-09T08:11:41Z","isPatch":true,"sender":{"key":"git@grubix.eu","avatar":"https://avatars.githubusercontent.com/u/233215?v=4"},"body":"Michael Haggerty venit, vidit, dixit 09.08.2016 01:20:\n> Given that I work for GitHub, I'm uncomfortable doing any more advocacy\n> here. If people have concrete questions, I'd be happy to answer them, on\n> the list or in private.\n\nYou're doing a great job differentiating between your roles as a member\nof the Git devel community and as a GitHub employee, so please keep the\ndiscussion here.\n\nMaybe two more points of input for the discussion:\n\noff-line capabilities\n=====================\n\nThe current workflow here seems to work best when you are subscribed to\nthe git-ml and have your own (local, maybe selective) copy of git-ml in\nyour (text-based) MUA (mutt, (al)pine, emacs, ...) that can jump right\ninto git-am and such directly. I'm not sure how important the \"off-line\"\naspect of that is for some of us, and how that could be replicated on\nGitHub - while PRs and such may be Git based behind the scenes there\nseems to be no way to clone that info and work from a local clone.\n(Don't know if GitLab is more open.)\n\nMy own setup\n============\n\nMy usual MUA is Thunderbird because of its integration with calendars\nand address books. I usually read and post to mailing lists via\nnntp/gmane, that works best for me for several reasons (e.g. archive\navailable when I need it).\n\nFor git-ml, I had to learn early on to answer by e-mail to git-ml rather\nthan by nntp-reply because proper nntp-replies somehow didn't meet the\nexpectations of the e-mail users (double copies because of the cc-policy\nor such, I don't remember).\n\nI use git sendemail even for small throw-in patches because the git-ml\ndoes not allow attachments but wants patches (files) as in-line text,\nand Thunderbird possibly reformats text (because it's text, you know).\n\nWhen I want to try out a proposed patch I have to \"save message\" and run\ngit-am because patches don't come as file attachments on the git-ml\n(can't use \"save/open attachment\"+ git-apply) nor a PR (can't use git\nfetch nor view in browser). If it's a series, I have to do that for each\ninvididual patch, which usually means I simply don't (or rely on Junio\ndoing it and fetch his xy/topic branch).\n\nAnd more often than not, patches from series do not appear in sequence,\nnot threaded on top of the cover letter (in the gmane nntp version of\ngit-ml), and it usually happens for the same people again and again,\nwhich tells me it's a git sendemail config issue and not gmane.\n\nSo really, everytime I interact with the git-ml I think about switching\nto mutt or such just for git-ml, even though over time I have gotten\nused to the number of hoops that I have to jump through if I want to\ninteract with git-ml.\n\nSuggestion\n==========\n\nMaybe the current gmane woes are a good reason to try out something\ndifferent for a month or two, with copies to the git-ml, and with the\ndefault being to revert back to git-ml after that and discuss what we've\nlearned. As a result we may improve our workflow here, get GitHub to\nimprove, and maybe switch or not. Either way we could profit from that.\n\nMichael\n"},{"id":"293464","messageId":"20160809091722.GA1983@salo","threadId":"42743","inReplyTo":"CACsJy8DeSv_ALHR+HrViEptgYCYhXu2ZczMmhzZfHGAwZumnzg@mail.gmail.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Richard Ipsum","fromEmail":"richard.ipsum@codethink.co.uk","sentAt":"2016-08-09T09:17:22Z","receivedAt":"2016-08-09T09:17:49Z","isPatch":true,"sender":{"key":"richard.ipsum@codethink.co.uk","avatar":null},"body":"On Tue, Aug 09, 2016 at 06:22:21AM +0200, Duy Nguyen wrote:\n> On Tue, Aug 9, 2016 at 12:20 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n> > On 08/04/2016 05:58 PM, Johannes Schindelin wrote:\n> >> [...]\n> >> Even requiring every contributor to register with GitHub would be too much\n> >> of a limitation, I would wager.\n> >> [...]\n> >\n> > Is it *really* so insane to consider moving collaboration on the Git\n> > project to GitHub or some other similar platform?\n> \n> In the very unlikely event that github is shut down, how do we get all\n> review comments out of it, assuming that we will use pull requests for\n> review?\n\nFor what it's worth this is exactly why I think it would be worthwhile for git\nto establish a common review format, services like Github/Gitlab could then\nstart storing reviews and comments in the git repo rather than in a separate\nsql database.\n\nGerrit is already doing this with notedb, which literally gives you a\ngit log of a review. Admittedly with Gerrit the change metadata\nsits in a separate git repo, still,\nthis is much better than the current situation with\nGithub and Gitlab in my opinion.\n\nI apologise once again if my comments here are somewhat unrelated,\nbut I feel there is at least some overlap, since the existence of a\ncommon review format for git could potentially make Github/Gitlab a more\nattractive option, if Github/Gitlab chose to adopt such a format.\n\nReally I think that reviews shouldn't be stored on mailing lists,\nand they shouldn't be stored in sql databases,\nthey should be stored in git.\n"},{"id":"293466","messageId":"111a9cbe-a174-b8c0-23e5-f7f08635aa6e@alum.mit.edu","threadId":"42743","inReplyTo":"CACsJy8DeSv_ALHR+HrViEptgYCYhXu2ZczMmhzZfHGAwZumnzg@mail.gmail.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2016-08-09T10:19:00Z","receivedAt":"2016-08-09T10:19:24Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 08/09/2016 06:22 AM, Duy Nguyen wrote:\n> On Tue, Aug 9, 2016 at 12:20 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n>> On 08/04/2016 05:58 PM, Johannes Schindelin wrote:\n>>> [...]\n>>> Even requiring every contributor to register with GitHub would be too much\n>>> of a limitation, I would wager.\n>>> [...]\n>>\n>> Is it *really* so insane to consider moving collaboration on the Git\n>> project to GitHub or some other similar platform?\n> \n> In the very unlikely event that github is shut down, how do we get all\n> review comments out of it, assuming that we will use pull requests for\n> review?\n\nI don't have any experience with these tools, but a quick search turns\nup the following possibilities (among others):\n\n* github-backup (by Joey Hess): https://github.com/joeyh/github-backup\n* python-github-backup: https://github.com/josegonzalez/python-github-backup\n* BackHub (commercial service): https://backhub.co/\n* Import GitHub project into GitLab:\nhttp://docs.gitlab.com/ce/workflow/importing/import_projects_from_github.html\n\nMichael\n\n"},{"id":"293467","messageId":"20160809103455.kf5qk7ignqbgbvbu@sigill.intra.peff.net","threadId":"42743","inReplyTo":"20160809091722.GA1983@salo","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-08-09T10:34:55Z","receivedAt":"2016-08-09T10:35:03Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Aug 09, 2016 at 10:17:22AM +0100, Richard Ipsum wrote:\n\n> > In the very unlikely event that github is shut down, how do we get all\n> > review comments out of it, assuming that we will use pull requests for\n> > review?\n> \n> For what it's worth this is exactly why I think it would be worthwhile for git\n> to establish a common review format, services like Github/Gitlab could then\n> start storing reviews and comments in the git repo rather than in a separate\n> sql database.\n\nI doubt that the \"rather than\" part will ever happen. Git does not make\na very good database, and certainly not when you want to do things that\ncut across repositories (like, say, efficiently get all review comments\nmade by one user).\n\nIt would be nice to have a common interchange format, though. In theory\nthat could feed into (and out of) a more efficient representation on the\nbackend of the site. It doesn't _have_ to be git-based, but it would be\nnice if it was.\n\nSomebody asked elsewhere \"what happens if GitHub goes away?\". And the\nanswer is that you can already get all of that data out in a\nprogrammatic way, via the API. But since there's no common interchange\nformat, you'd be stuck writing a conversion to whatever format your new\ndestination uses.\n\n-Peff\n"},{"id":"293468","messageId":"20160809105705.pyzr7be3gvzw2pid@sigill.intra.peff.net","threadId":"42743","inReplyTo":"6adc480d-15e3-c0ac-0e05-eb10c767a8ab@drmicha.warpmail.net","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-08-09T10:57:05Z","receivedAt":"2016-08-09T10:57:13Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Aug 09, 2016 at 10:11:30AM +0200, Michael J Gruber wrote:\n\n> Maybe two more points of input for the discussion:\n> \n> off-line capabilities\n> =====================\n> \n> The current workflow here seems to work best when you are subscribed to\n> the git-ml and have your own (local, maybe selective) copy of git-ml in\n> your (text-based) MUA (mutt, (al)pine, emacs, ...) that can jump right\n> into git-am and such directly. I'm not sure how important the \"off-line\"\n> aspect of that is for some of us, and how that could be replicated on\n> GitHub - while PRs and such may be Git based behind the scenes there\n> seems to be no way to clone that info and work from a local clone.\n> (Don't know if GitLab is more open.)\n\nYou can pull it all out via GitHub's HTTP API, but the question is what\nformat you would use to store it locally (and which tools you would then\nuse to play with it).\n\nI haven't really tried this lately, though, so I don't know if there is\ninformation that the API would be missing.\n\nI do have a dream of writing a tool that sucks in GitHub PRs to a fake\nemail thread, lets me make my responses inline in an editor, and then\npushes it back up as PR comments (finding the right positions based on\nthe quoted context).\n\n> For git-ml, I had to learn early on to answer by e-mail to git-ml rather\n> than by nntp-reply because proper nntp-replies somehow didn't meet the\n> expectations of the e-mail users (double copies because of the cc-policy\n> or such, I don't remember).\n\nAt least some people's workflows seem to send two copies to the list.\nFor instance, Jakub's <2bfd9cf5-a9fa-7650-21e9-9ceb9cc34d8b@gmail.com>\ngot delivered to me via the list twice. Once directly from gmail with:\n\n  To: Oleg Taranenko <olegtaranenko@gmail.com>,\n      Junio C Hamano <gitster@pobox.com>\n  Cc: git@vger.kernel.org\n\nand once via gmane with:\n\n  To: git@vger.kernel.org\n  Cc: git@vger.kernel.org\n\nIt's like this with all of his messages (sorry I can't point to the\nduplicates in an archive; they have the same message-id, so public-inbox\ntreats them as a single unit).\n\nReplying to the second one breaks the usual \"cc-everybody\" rule. Sending\nduplicates means everybody sees it twice (3 times if they're on the cc\nlist!), and the second copy still has the bogus headers (so people\nreplying need to pick the right one).\n\n> I use git sendemail even for small throw-in patches because the git-ml\n> does not allow attachments but wants patches (files) as in-line text,\n> and Thunderbird possibly reformats text (because it's text, you know).\n\nI wonder if this is something we could change. I do not personally have\nany problem with attached patches. \"git am\" knows how to apply them, and\nmutt is smart enough to show text/* by default, and to include it in\nquoted text on reply. So the output of \"git format-patch --attach\" works\nfine for me. But it may not be as nice in other MUAs, and we have to\ncare about all of the other reviewers.\n\n> When I want to try out a proposed patch I have to \"save message\" and run\n> git-am because patches don't come as file attachments on the git-ml\n> (can't use \"save/open attachment\"+ git-apply) nor a PR (can't use git\n> fetch nor view in browser). If it's a series, I have to do that for each\n> invididual patch, which usually means I simply don't (or rely on Junio\n> doing it and fetch his xy/topic branch).\n\nSo you would like the opposite of my dream tool, I think: something that\ntakes mailing list conversations and turns them into PRs.\n\n(My real dream is actually to have a bidirectional version of the tool,\nso that everybody can use whatever interface they like, and nobody has\nto care about somebody else's preferences).\n\n> And more often than not, patches from series do not appear in sequence,\n> not threaded on top of the cover letter (in the gmane nntp version of\n> git-ml), and it usually happens for the same people again and again,\n> which tells me it's a git sendemail config issue and not gmane.\n\nJust a guess, but I suspect this is caused by people who use \"rebase -i\"\nto rearrange patches. When format-patch writes out the patches, it uses\nthe author date as the \"Date\" field, which means it may be out of order.\nI think send-email will always write out a new, monotonically increasing\ndate. But I suspect other workflows (e.g., imap-send and then mailing\nfrom a MUA) blindly re-use that date.\n\n> Suggestion\n> ==========\n> \n> Maybe the current gmane woes are a good reason to try out something\n> different for a month or two, with copies to the git-ml, and with the\n> default being to revert back to git-ml after that and discuss what we've\n> learned. As a result we may improve our workflow here, get GitHub to\n> improve, and maybe switch or not. Either way we could profit from that.\n\nI think public-inbox is a nice step forward on the reading side (it's a\nlot easier to get raw patches out of it, for example). But it doesn't\nhelp much with sending (and sending is a tricky subject; anytime you\npromise to send mail on behalf of somebody, you're going to attract\nspammers).\n\n-Peff\n"},{"id":"293477","messageId":"20160809113703.57irthzzpg6j3dmv@sigill.intra.peff.net","threadId":"42743","inReplyTo":"3055f063-c9c1-0bf5-99bd-08256c253d33@alum.mit.edu","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-08-09T11:37:03Z","receivedAt":"2016-08-09T11:37:11Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Aug 09, 2016 at 01:20:05AM +0200, Michael Haggerty wrote:\n\n> > but I\n> > do not think it is sane to expect that the same quality and quantity\n> > of reviews as we do here can be maintained with that thing.\n> \n> Could you elaborate why you would expect quality and/or quantity of\n> reviews to suffer? I'm really curious, and I'd be happy to pass your\n> feedback along to my colleagues.\n\nHaving done a lot of review here on the mailing list, as well as in\nGitHub PRs, I vastly prefer the mailing list.\n\nHere's a random list of things that I think I would miss:\n\n - I really like the flow of having the commit message and diff dumped\n   in my editor. I'm very efficient at slicing and dicing text, omitting\n   uninteresting quoted bits, etc.\n\n   Web text boxes feel like a straitjacket. I do have a browser plugin\n   that opens them in vim. That helps, but it breaks the flow (I make a\n   comment, save the file, click \"comment\", then read to the next place,\n   click \"+\", then start a new vim instance for that comment).  Besides\n   the tedium of clicking around, it loses the \"unit\" size of a single\n   email, where I may make many comments, go back and revise earlier\n   comments after reading more of the patch, etc.\n\n - I really like the flow of having conversations next to patches. I can\n   look at the index of the mailing list folder and see what people are\n   talking about, how big the threads are, etc, at a glance. Moving\n   between messages and threads involve single keystrokes.\n\n   Similarly, having local storage is _fast_. I think GitHub is fine for\n   a web app. But when I'm reading a high-volume mailing list, I really\n   want to flip around quickly. If there's even 500ms to get to the next\n   message or thread, it feels clunky and slow to me. Obviously I spend\n   more than 500ms _reading_ most messages (though for some I see the\n   first paragraph and immediately jump away). It's just the latency\n   when I've decided I'm done with one thing and want to move to the\n   next.\n\n - For that matter, GitHub doesn't really have a good tool for random\n   conversations. There are issues, which you can vaguely use like a\n   thread, but it doesn't quite feel the same.\n\n   I think part of it is that I can view the mailing list both as a\n   series of threads _and_ as a stream of messages. So sometimes I mark\n   a thread as \"read\", and then see the next day that there are a ton of\n   new messages on it. Maybe those are uninteresting (and it's a single\n   keystroke to mark the thread again), but maybe that's a hint that\n   there's interesting discussion going on.\n\n   The threading in GitHub comments and pull requests is also not great.\n   Each PR or issue is its own thread, but it's totally flat inside.\n   There are no sub-threads to organize discussion, and it's sometimes\n   hard to see what people are replying to.\n\n - When I move between a discussion and patches on the list and my local\n   git checkout, it's important to do so with minimal fuss. Which means\n   I want to use _context_ in my workflow. If I'm reading a thread, I\n   want there to be a keystroke for \"jump to this thread in my\n   checkout\". That's (relatively) easy for me to script via mutt (grab\n   these patches, apply them). It's a bit harder in the browser (the\n   best I've got is to copy-paste the URL to a script that pulls out the\n   PR number, then fetches and checks it out).\n\n - A sort-of feature: the mailing list is actually fairly decentralized,\n   because of the \"reply-to-all\" convention. I don't know if anybody\n   else noticed, but vger seemed to be down Friday evening and Saturday\n   morning (at least my messages to the list got 400 SMTP codes, and no\n   new messages were delivered to me). But I still had some\n   conversations going with people, because our messages were mailed\n   directly (and the list eventually caught up).\n\n   Now that probably doesn't matter for GitHub, which seems to have\n   fairly reasonable uptime. It would matter if we picked a centralized\n   tool that didn't.\n\nThere are probably more, but I've run out of ranting steam for now. :)\n\n> Here are some factors that I think will *improve* reviews:\n\nI was going to respond point-by-point to a few of these, but I think I\ncovered most of it above. In short, I agree with many of the benefits\nyou list. In most cases, I've already reaped those benefits for my own\nworkflow (e.g., my \"git am\" workflow is pretty efficient now). But not\neverybody has done so, and it's a lot to ask of casual contributors.\n\n> Given that I work for GitHub, I'm uncomfortable doing any more advocacy\n> here. If people have concrete questions, I'd be happy to answer them, on\n> the list or in private.\n\nHopefully I provided some counterpoint. ;)\n\n-Peff\n"},{"id":"293479","messageId":"alpine.DEB.2.20.1608091339590.5786@virtualbox","threadId":"42743","inReplyTo":"xmqqr39zf9tt.fsf@gitster.mtv.corp.google.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-09T11:41:18Z","receivedAt":"2016-08-09T11:41:42Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Mon, 8 Aug 2016, Junio C Hamano wrote:\n\n> Lars Schneider <larsxschneider@gmail.com> writes:\n> \n> > 4.) Reviewing patches is super hard for me because my email client\n> > does not support patch color highlighting and I can't easily expand\n> > context or look at the history of code touched by the patch (e.g via\n> > git blame). I tried to setup Alpine but I wasn't happy with the\n> > interface either. I like patches with a GitHub URL for review but then\n> > I need to find the right line in the original email to write a\n> > comment.\n> \n> Unless a patch is about an area you are super familiar with so that you\n> know what is beyond the context of the patch to be able to judge if the\n> change is good in the context of the file being touched, it is always\n> hard to review from inside a mail reader.\n> \n> Running \"git am\" is a good first step to review such a patch, as that\n> lets you view the resulting code with the full power of Git.  As you\n> gain experience on the codebase, you'll be able to spot more problems\n> while in your mail reader.\n\nI am glad that you agree that the requirement to manually transform the\npatches back into Git (where they had been originally to begin with) is\ncumbersome. This is the first time that I see you admit it ;-)\n\nCiao,\nDscho\n"},{"id":"293480","messageId":"alpine.DEB.2.20.1608091342190.5786@virtualbox","threadId":"42743","inReplyTo":"xmqqvazbfa5v.fsf@gitster.mtv.corp.google.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-09T11:48:14Z","receivedAt":"2016-08-09T11:48:40Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Mon, 8 Aug 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > I think you both got it wrong. The original citizens were the mail\n> > clients that allowed you to communicate with other human beings.\n> > ... It is our usage to transport machine-readable content (and not\n> > as an attachment!) that is the intruder.\n> \n> It is not \"its is our usage\".\n> \n> You are too young to remember or too old to remember the history, or\n> you are knowingly distorting it.  The original users of \"patch\" and\n> \"diff\" expected that e-mail to be a medium to safely exchange\n> changes to programs among themselves.\n\nIf you are saying that transporting patches via email was the original\npurpose of email, then it is not exactly I who is misremembering history.\n\nBut that is not what you meant, I believe. You probably wanted to point\nout that the Git developers are not the first ones to abuse the medium\nknown as email that way. And you are correct, of course. And I never\nclaimed anything else. I just said that the problem is our usage of emails\nas a means to transport byte-exact content intended primarily to be\nconsumed by a program instead of a human. It does not matter whether\nothers did that before us. It is the problem we face right now, that is\nthe important part of my message.\n\nAnd even if it seems as if you are eagerly defending this system, I do not\nbelieve even a microsecond that you think it is a good system. I believe\nthat you, too, would welcome a better review/contribution system that is\neasier to use, more welcoming to new users, less error-prone and less\ntime-wasting than the current, email-based one, just like you jumped on\nGit as a better SCM when it came around, from whatever inadequate system\nyou came from.\n\nCiao,\nDscho\n"},{"id":"293484","messageId":"alpine.DEB.2.20.1608091402540.5786@virtualbox","threadId":"42743","inReplyTo":"6c937f79-2b82-619d-51fe-adccbe09bd66@alum.mit.edu","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-09T12:07:41Z","receivedAt":"2016-08-09T12:08:07Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Michael,\n\nOn Tue, 9 Aug 2016, Michael Haggerty wrote:\n\n> On 08/04/2016 05:58 PM, Johannes Schindelin wrote:\n> > [...]\n> > Even requiring every contributor to register with GitHub would be too\n> > much of a limitation, I would wager.\n> > [...]\n> \n> Is it *really* so insane to consider moving collaboration on the Git\n> project to GitHub or some other similar platform?\n\nSpeaking for myself, I do prefer GitHub's UI to mail, by a lot. Not only\nbecause it is more focused on the code, but because it integrates so\nnicely with Git, which email distinctly does not.\n\nSo I personally would not have the least bit of a problem to switch to\nGitHub (that's indeed what Git for Windows did, getting substantially more\ncontributions than we would otherwise have).\n\nAnd of course I use the email notifications quite a bit. They are really\nconvenient: I get my updates via my mail program, still, and the\ndiscussion I want to participate in is just one click away.\n\nThe reason why I stated that GitHub is out of the question is that I\nexpected resistance against it. But you are right: I should not have ruled\nit out so categorically, it is not at all my call to make.\n\nCiao,\nDscho\n"},{"id":"293502","messageId":"xmqqlh05c0sj.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"alpine.DEB.2.20.1608091339590.5786@virtualbox","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-09T17:25:16Z","receivedAt":"2016-08-09T17:25:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> On Mon, 8 Aug 2016, Junio C Hamano wrote:\n>\n>> Unless a patch is about an area you are super familiar with so that you\n>> know what is beyond the context of the patch to be able to judge if the\n>> change is good in the context of the file being touched, it is always\n>> hard to review from inside a mail reader.\n>> \n>> Running \"git am\" is a good first step to review such a patch, as that\n>> lets you view the resulting code with the full power of Git.  As you\n>> gain experience on the codebase, you'll be able to spot more problems\n>> while in your mail reader.\n>\n> I am glad that you agree that the requirement to manually transform the\n> patches back into Git (where they had been originally to begin with) is\n> cumbersome. This is the first time that I see you admit it ;-)\n\nI was about to apologize for writing a statement that can be\nmisread, but I do not think what I wrote can be misinterpreted, even\nif a reader deliberately tries to twist the words s/he reads, to\nlead to such a conclusion, so I won't.\n\nI merely said that reviewing a change in an unfamiliar area is\nharder (not \"cumbersome\", but \"needs understanding first\") with a\npatch, and it is easier to see changes in context by applying (which\nis an easy, not \"cumbersome\", process).\n\n"},{"id":"293507","messageId":"xmqqh9atc0do.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"20160809113703.57irthzzpg6j3dmv@sigill.intra.peff.net","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-09T17:34:11Z","receivedAt":"2016-08-09T17:40:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes all what I wanted to say, and a lot\nmore, so I don't have to say much ;-)\n\n>  - I really like the flow of having conversations next to patches. I can\n>    look at the index of the mailing list folder and see what people are\n>    talking about, how big the threads are, etc, at a glance. Moving\n>    between messages and threads involve single keystrokes.\n>\n>    Similarly, having local storage is _fast_. I think GitHub is fine for\n>    a web app. But when I'm reading a high-volume mailing list, I really\n>    want to flip around quickly. If there's even 500ms to get to the next\n>    message or thread, it feels clunky and slow to me. Obviously I spend\n>    more than 500ms _reading_ most messages (though for some I see the\n>    first paragraph and immediately jump away). It's just the latency\n>    when I've decided I'm done with one thing and want to move to the\n>    next.\n\nViewing threads in a threaded mail client to help prioritizing\nvarious topics being discussed is what I value the most and I am\nnot sure how I can be as efficient with the pull-request page.\n\n>    The threading in GitHub comments and pull requests is also not great.\n>    Each PR or issue is its own thread, but it's totally flat inside.\n>    There are no sub-threads to organize discussion, and it's sometimes\n>    hard to see what people are replying to.\n\nIt may be a good UI that is optimized for drive-by contributors.  It\nis just that it is not very well suited (compared to mailing list\ndiscussions) to conduct high-volume exchange of ideas and changes\nefficiently.\n\n"},{"id":"293510","messageId":"20160809175018.p3bwnqjwz44t2xnb@sigill.intra.peff.net","threadId":"42743","inReplyTo":"xmqqh9atc0do.fsf@gitster.mtv.corp.google.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-08-09T17:50:18Z","receivedAt":"2016-08-09T17:50:25Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Aug 09, 2016 at 10:34:11AM -0700, Junio C Hamano wrote:\n\n> >    The threading in GitHub comments and pull requests is also not great.\n> >    Each PR or issue is its own thread, but it's totally flat inside.\n> >    There are no sub-threads to organize discussion, and it's sometimes\n> >    hard to see what people are replying to.\n> \n> It may be a good UI that is optimized for drive-by contributors.  It\n> is just that it is not very well suited (compared to mailing list\n> discussions) to conduct high-volume exchange of ideas and changes\n> efficiently.\n\nI think that's something to ponder; can we have a workflow where\ndrive-by contributors can use something that has a lower learning/setup\ncurve, but long-term contributors might opt for something more powerful?\n\nI think SubmitGit is a step in that direction. It does still require\nswitching to the mailing list for subsequent conversation, though. It\nwould be interesting to see something like SubmitGit that puts its own\nemail in the \"From\", and that processes email replies into PR comments,\nand then subsequent PR comments into emails (i.e., part of my \"dream tool\"\nfrom earlier). It's not clear to me whether the result would just end up\nbeing irritating for both sides to use (because it doesn't _quite_\nconform to the norms of each format). But it would be fun to find out.\n\n-Peff\n"},{"id":"293517","messageId":"20160809182800.GA19044@dcvr","threadId":"42743","inReplyTo":"6c937f79-2b82-619d-51fe-adccbe09bd66@alum.mit.edu","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2016-08-09T18:28:00Z","receivedAt":"2016-08-09T18:28:16Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Michael Haggerty <mhagger@alum.mit.edu> wrote:\n> On 08/04/2016 05:58 PM, Johannes Schindelin wrote:\n> > [...]\n> > Even requiring every contributor to register with GitHub would be too much\n> > of a limitation, I would wager.\n> > [...]\n\n> * Discussion of pull requests can be done either\n>   * via the website (super easy for beginners but powerful for\n> experienced users),\n>   * by setting up email notifications for your account and replying to\n> those emails, or\n>   * via an API.\n>   Such discussion is all in markdown, which supports light formatting,\n> hyperlinks, and @-mentions.\n\n<snip>\n\n> Disclaimer: I work for GitHub, but in this email I'm speaking for myself.\n> \n> Michael\n> \n> [1] I concede that people who refuse on ideological grounds to use\n> proprietary software will find this step insurmountable. Perhaps we\n> could come up with a workaround for such people.\n\nI'm one of those ideological people and I don't see an\nacceptable workaround.  GitHub already has misfeatures designed\nto lock people in into centralized messaging:\n\n* pull request feature doesn't work for self-hosted repos\n  (this disincentivizes people from running and improving\n   git-daemon/git-http-backend/etc...)\n\n* \"noreply\" email addresses\n\n* @-mentions you wrote about\n\n* custom email notifications\n\nThis is a problem with Gitlab, Redmine, etc, too:\nthey cannot interoperate with each other.\n\nAt least for now, large proprietary mail providers like Gmail\nstill interoperate with whatever Free Software SMTP software I\nrun.  I dread the day when that is no longer true.\n\nSome of these problems I hope public-inbox (or something like\nit) can fix and turn the tide towards email, again.  In\ncontrast, public-inbox is designed to push decentralization:\n\n* \"reply\" links are instructions for \"git send-email\" which\n  encourage reply-to-all (this applies to what Jeff said\n  about vger going down, I noticed it, too)\n\n* anybody can clone the code + repo, replicate the\n  instances, and tweak it to their needs.\n\n* public-inbox.org/git/$MESSAGE_ID/t.atom allows subscriptions\n  to Atom feeds without any registration or user-tracking\n\n* Message-IDs are exposed for proper threading and interop\n\n* low-bandwidth, Tor-friendly design to encourage deployments\n  even behind NATs and firewalls.\n\nAnyways, my optimistic side might interpret your advocacy as\nGitHub already feeling threatened by public-inbox.  I certainly\nwouldn't expect it at this stage, but I certainly hope it will\nbe the case one day :)\n\n\nDisclaimer: I've always been willing to risk a lifetime of\nunemployment for ideology.\n"},{"id":"293522","messageId":"CACsJy8DA7b9EYTDUMA5+kfk2Xg6hQGAuNTa5ghKH3zMiuvTRbw@mail.gmail.com","threadId":"42743","inReplyTo":"3055f063-c9c1-0bf5-99bd-08256c253d33@alum.mit.edu","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-08-09T18:36:06Z","receivedAt":"2016-08-09T18:36:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Aug 9, 2016 at 1:20 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n> Could you elaborate why you would expect quality and/or quantity of\n> reviews to suffer? I'm really curious, and I'd be happy to pass your\n> feedback along to my colleagues.\n\nSince I have been using github at work for a couple months, I do have\na few complaints if it will ever become the way of reviewing things in\ngit. Some of these may be covered by other people already (I haven't\nread all new mails in this thread yet)\n\n - Github PRs seem to encourage seeing changes as a whole, not a\nseparate commits. Or at least it's not so convenient to view separate\ncommits (e.g. not easy to go to the next commit)\n\n - The ability to show all outdated comments, in case I want to search\nthrough them.\n\n - I have a feeling that commits in PRs are sorted by authordate, not\nin topological order. The order of commits being committed is\nimportant sometimes.\n\n - Not showing leading spaces mixing with TABs, or trailing spaces\n\n - I would love to have all patches numbered so can refer to them as\n1/7, 2/5... instead of just short sha1 (and I think you have the\nability to refer to \"1/7 of iteration 2\", see next bullet point)\n\n -I guess you can manage multiple iterations of a topic with one\niteration per PR, then linking them together. It would be nicer to\nsomehow bake the iteration concept directly to a PR so we can switch\nbetween them, or do interdiff. I know, this is more of a improvement\nrequest than complaint because ML is not really better.\n\n - Offline support would be very nice. I'm only most of the time, but\nsometimes I do work on git stuff offline.\n\n - We lose the integration with ML, I think. Sometimes the user\nreports a bug here, then we reply back with a patch. With github, I\nguess we reply back with a PR number, then further discussion may go\nthere, some discussion may still be on ML.\n\n> * It is easy to summon somebody else into the review conversation by\n> @-mentioning them. That person immediately can see the whole history of\n> the PR. (This is an improvement on the status quo, where a new entrant\n> to a conversation might have to dig through the email trash or an email\n> archive to see emails that they overlooked before they were added to the\n> CC list.)\n\nOn the other hand, we can't just CC anybody anymore because we don't\nknow if they have a github account (or the account name for that\nmatter). Or does github allow @-ing email addresses too? We record\npeople's email address, not github account names.\n\n> * It is possible to search old issues and PRs for additional context.\n> (Granted, the history of the project from its ML era would have to be\n> searched separately.)\n\nTo me searching in email is still better. Maybe I haven't fully\nexplored github search capabilities\n-- \nDuy\n"},{"id":"293523","messageId":"CACsJy8A4xwiUZ50hH9srRkmmBfxhHCQWzN70sLWo5TqZBOPQrw@mail.gmail.com","threadId":"42743","inReplyTo":"CACsJy8DA7b9EYTDUMA5+kfk2Xg6hQGAuNTa5ghKH3zMiuvTRbw@mail.gmail.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-08-09T18:38:44Z","receivedAt":"2016-08-09T18:39:20Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Aug 9, 2016 at 8:36 PM, Duy Nguyen <pclouds@gmail.com> wrote:\n> On Tue, Aug 9, 2016 at 1:20 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n>> Could you elaborate why you would expect quality and/or quantity of\n>> reviews to suffer? I'm really curious, and I'd be happy to pass your\n>> feedback along to my colleagues.\n>\n> Since I have been using github at work for a couple months, I do have\n> a few complaints if it will ever become the way of reviewing things in\n> git. Some of these may be covered by other people already (I haven't\n> read all new mails in this thread yet)\n\nAnother super nit thing: use monospace font for commit messages, or at\nleast have an option for that.\n-- \nDuy\n"},{"id":"293525","messageId":"CACsJy8ARtg5KUceogNaeB+Fgh-u-TxfkAWdOk68_sEA-c0y6vg@mail.gmail.com","threadId":"42743","inReplyTo":"20160809113703.57irthzzpg6j3dmv@sigill.intra.peff.net","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-08-09T18:43:59Z","receivedAt":"2016-08-09T18:44:34Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Aug 9, 2016 at 1:37 PM, Jeff King <peff@peff.net> wrote:\n>    That's (relatively) easy for me to script via mutt (grab\n>    these patches, apply them).\n\nCould you share your mutt set up pleaaase? I've been wanting this for\na long time, but never used mutt long enough to bother with a proper\nsetup like this (I blame gmail).\n-- \nDuy\n"},{"id":"293527","messageId":"CAGZ79kZ=kNCq3uM5WGdmZRfPGaT1ZUqa2WkQdq5C2inF154sew@mail.gmail.com","threadId":"42743","inReplyTo":"CACsJy8ARtg5KUceogNaeB+Fgh-u-TxfkAWdOk68_sEA-c0y6vg@mail.gmail.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-08-09T18:50:51Z","receivedAt":"2016-08-09T18:50:58Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Tue, Aug 9, 2016 at 11:43 AM, Duy Nguyen <pclouds@gmail.com> wrote:\n> On Tue, Aug 9, 2016 at 1:37 PM, Jeff King <peff@peff.net> wrote:\n>>    That's (relatively) easy for me to script via mutt (grab\n>>    these patches, apply them).\n>\n> Could you share your mutt set up pleaaase? I've been wanting this for\n> a long time, but never used mutt long enough to bother with a proper\n> setup like this (I blame gmail).\n\n\nThat is my complaint^H^H^H^H position, too.\nI always wanted to switch to a more powerful\nsetup than git-send-email for sending /gmail for reading,\nbut I could not convince myself the steep learning/setup curve\nis worth it eventually as it is \"not broken enough\" to do the change\nright now.\n\nMy experiments with mutts, have left these lines in my\n~/.muttrc\n\n> # use shift + A to apply a patch in the working dir\n> # macro index A \":unset pipe_decode\\n|git am -3\\n:set pipe_decode\\n\"\n> # macro pager A \":unset pipe_decode\\n|git am -3\\n:set pipe_decode\\n\"\n>\n> macro index A \":set folder='.'\\n:copy-message\\n\"\n\n(IIRC they were broken for many patches, but I got applying\none patch to work. Which sucks for long email series.)\n"},{"id":"293529","messageId":"20160809185505.fjrbid4rxatxrqfl@sigill.intra.peff.net","threadId":"42743","inReplyTo":"CACsJy8ARtg5KUceogNaeB+Fgh-u-TxfkAWdOk68_sEA-c0y6vg@mail.gmail.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-08-09T18:55:05Z","receivedAt":"2016-08-09T18:55:13Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Aug 09, 2016 at 08:43:59PM +0200, Duy Nguyen wrote:\n\n> On Tue, Aug 9, 2016 at 1:37 PM, Jeff King <peff@peff.net> wrote:\n> >    That's (relatively) easy for me to script via mutt (grab\n> >    these patches, apply them).\n> \n> Could you share your mutt set up pleaaase? I've been wanting this for\n> a long time, but never used mutt long enough to bother with a proper\n> setup like this (I blame gmail).\n\nIt's actually pretty simple. The relevant config from my .muttrc is:\n\n   macro pager,index D '<shell-escape>rm -f $HOME/patch<enter>'\n   macro pager,index A '<copy-message>~/patch<enter><enter>'\n\nI use \"~/patch\" as a rendezvous point, and then \"git am ~/patch\" from my\nother terminal. That avoids mutt having to know which repo to apply to,\nand keeps the \"am\" process in its own terminal (which is handy if it\nruns into conflicts, for example).\n\nSo generally I would \"D\" to clear out the contents of ~/patch, and then\n\"A\" whichever patches I want to apply. I often use mutt's aggregate\nselection for that. My bindings are:\n\n  bind index \\; tag-pattern\n  bind index a tag-prefix\n\nwhich I think come from pine (which I used for many years before\nswitching to mutt probably 15 years ago). I don't recall the default\nkeybindings.\n\nAnyway, you can either tag using a pattern (with \";\"), or tag mails\nindividually (using \"t\", the default), and then \"a-A\" to apply the \"A\"\nto all of them (if you are in the habit of tagging all of them and then\ndoing \"A\" in one swoop, you could also get rid of the separate \"D\"\ncommand and just make \"A\" imply it).\n\n-Peff\n"},{"id":"293530","messageId":"20160809185809.qyh2gwabxgotvvgk@sigill.intra.peff.net","threadId":"42743","inReplyTo":"CAGZ79kZ=kNCq3uM5WGdmZRfPGaT1ZUqa2WkQdq5C2inF154sew@mail.gmail.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-08-09T18:58:09Z","receivedAt":"2016-08-09T18:59:26Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Aug 09, 2016 at 11:50:51AM -0700, Stefan Beller wrote:\n\n> > Could you share your mutt set up pleaaase? I've been wanting this for\n> > a long time, but never used mutt long enough to bother with a proper\n> > setup like this (I blame gmail).\n> \n> \n> That is my complaint^H^H^H^H position, too.\n> I always wanted to switch to a more powerful\n> setup than git-send-email for sending /gmail for reading,\n> but I could not convince myself the steep learning/setup curve\n> is worth it eventually as it is \"not broken enough\" to do the change\n> right now.\n\nI think I may have shared it before, but here is the script I use to\nsend emails. It dumps you in mutt, and then I have:\n\n  macro index,pager b \":set edit_headers=yes<enter><resend-message>:set edit_headers=no<enter>\"\n\nto send the message (\"b\" is for \"bounce\", which I think may be another\nPine-ism).\n\n-- >8 --\n#!/bin/sh\n\nupstream_branch() {\n  current=`git symbolic-ref HEAD`\n  upstream=`git for-each-ref --format='%(upstream)' \"$current\"`\n  if test -n \"$upstream\"; then\n    echo $upstream\n  else\n    echo origin\n  fi\n}\n\nget_reply_headers() {\n  perl -ne '\n    if (defined $opt) {\n      if (/^\\s+(.*)/) {\n        $val .= \" $1\";\n        next;\n      }\n      print \"--$opt=\", quotemeta($val), \" \";\n      $opt = $val = undef;\n    }\n    if (/^(cc|to):\\s*(.*)/i) {\n      $opt = lc($1);\n      $val = $2;\n    }\n    elsif (/^message-id:\\s*(.*)/i) {\n      $opt = \"in-reply-to\";\n      $val = $1;\n    }\n    elsif (/^subject:\\s*\\[PATCH v(\\d+)/i) {\n      print \"-v$1 \";\n    }\n    elsif (/^$/) {\n      last;\n    }\n  '\n}\n\nhas_nonoption=\nfor i in \"$@\"; do\n  case \"$i\" in\n    -[0-9]) has_nonoption=yes ;;\n    -*) ;;\n     *) has_nonoption=yes\n  esac\ndone\n\ngit rev-parse || exit 1\n\n: ${REPLY:=$HOME/patch}\ntest -e \"$REPLY\" && eval \"set -- `get_reply_headers <\\\"$REPLY\\\"` \\\"\\$@\\\"\"\ntest \"$has_nonoption\" = \"yes\" || set -- \"$@\" `upstream_branch`\n\ngit format-patch -s --stdout --from \"$@\" >.mbox\nif test -t 1; then\n  mutt -e 'set sort=mailbox-order' -f .mbox\nelse\n  perl -lne '\n    if (/^Subject: (.*)/) {\n      $subject = $1;\n    }\n    elsif ($subject && /^\\s+(.*)/) {\n      $subject .= \" $1\";\n    }\n    elsif ($subject) {\n      print $subject;\n      $subject = undef;\n    }\n  ' .mbox |\n  sed -e 's/\\[PATCH /[/' \\\n      -e 's/]/]:/' \\\n      -e 's/^/  /'\nfi\nrm -f .mbox\n"},{"id":"293532","messageId":"xmqqziol92cw.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"20160809175018.p3bwnqjwz44t2xnb@sigill.intra.peff.net","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-09T19:19:43Z","receivedAt":"2016-08-09T19:20:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Tue, Aug 09, 2016 at 10:34:11AM -0700, Junio C Hamano wrote:\n>\n>> It may be a good UI that is optimized for drive-by contributors.  It\n>> is just that it is not very well suited (compared to mailing list\n>> discussions) to conduct high-volume exchange of ideas and changes\n>> efficiently.\n>\n> I think that's something to ponder; can we have a workflow where\n> drive-by contributors can use something that has a lower learning/setup\n> curve, but long-term contributors might opt for something more powerful?\n>\n> I think SubmitGit is a step in that direction.\n\nYes, agreed 100% with that.  The author of the tool must be praised\nby getting added to the Cc: line in this discussion ;-)\n\n> It does still require\n> switching to the mailing list for subsequent conversation, though. It\n> would be interesting to see something like SubmitGit that puts its own\n> email in the \"From\", and that processes email replies into PR comments,\n> and then subsequent PR comments into emails (i.e., part of my \"dream tool\"\n> from earlier). It's not clear to me whether the result would just end up\n> being irritating for both sides to use (because it doesn't _quite_\n> conform to the norms of each format). But it would be fun to find out.\n\nPerhaps.  I do not know if I like that second and subsequent steps\nfor SubmitGit, but its first step as currently deployed I am very\nhappy with.\n\n"},{"id":"293554","messageId":"20160810004601.galhjhge3kld6ebe@x","threadId":"42743","inReplyTo":"20160809105705.pyzr7be3gvzw2pid@sigill.intra.peff.net","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Josh Triplett","fromEmail":"josh@joshtriplett.org","sentAt":"2016-08-10T00:46:01Z","receivedAt":"2016-08-10T00:46:16Z","isPatch":true,"sender":{"key":"josh@joshtriplett.org","avatar":"https://avatars.githubusercontent.com/u/162737?v=4"},"body":"On Tue, Aug 09, 2016 at 06:57:05AM -0400, Jeff King wrote:\n> On Tue, Aug 09, 2016 at 10:11:30AM +0200, Michael J Gruber wrote:\n> \n> > Maybe two more points of input for the discussion:\n> > \n> > off-line capabilities\n> > =====================\n> > \n> > The current workflow here seems to work best when you are subscribed to\n> > the git-ml and have your own (local, maybe selective) copy of git-ml in\n> > your (text-based) MUA (mutt, (al)pine, emacs, ...) that can jump right\n> > into git-am and such directly. I'm not sure how important the \"off-line\"\n> > aspect of that is for some of us, and how that could be replicated on\n> > GitHub - while PRs and such may be Git based behind the scenes there\n> > seems to be no way to clone that info and work from a local clone.\n> > (Don't know if GitLab is more open.)\n> \n> You can pull it all out via GitHub's HTTP API, but the question is what\n> format you would use to store it locally (and which tools you would then\n> use to play with it).\n> \n> I haven't really tried this lately, though, so I don't know if there is\n> information that the API would be missing.\n> \n> I do have a dream of writing a tool that sucks in GitHub PRs to a fake\n> email thread, lets me make my responses inline in an editor, and then\n> pushes it back up as PR comments (finding the right positions based on\n> the quoted context).\n\nYou might try https://github.com/joeyh/github-backup\n"},{"id":"293556","messageId":"20160810005548.gee6ontd33ck5vej@x","threadId":"42743","inReplyTo":"20160809182800.GA19044@dcvr","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Josh Triplett","fromEmail":"josh@joshtriplett.org","sentAt":"2016-08-10T00:55:49Z","receivedAt":"2016-08-10T00:56:04Z","isPatch":true,"sender":{"key":"josh@joshtriplett.org","avatar":"https://avatars.githubusercontent.com/u/162737?v=4"},"body":"On Tue, Aug 09, 2016 at 06:28:00PM +0000, Eric Wong wrote:\n> Some of these problems I hope public-inbox (or something like\n> it) can fix and turn the tide towards email, again.\n\nThis really seems like the dichotomy that drives people towards central\nservices like GitHub or GitLab.  We need an alternative that doesn't\ninvolve email, or at the very least, doesn't require people to use email\ndirectly.  Half of the pain in the process comes from coaxing email\nclients that don't treat mail text as sacrosanct to leave it alone and\nnot mangle it.  (Some of that would go away if we accepted attachments\nwith inline disposition, but not all of it.  All of it would go away if\nthe submission process just involved \"git push\" to an appropriate\nlocation.)\n\n- Josh Triplett\n"},{"id":"293557","messageId":"20160810015736.GA17898@starla","threadId":"42743","inReplyTo":"20160810005548.gee6ontd33ck5vej@x","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Eric Wong","fromEmail":"e@80x24.org","sentAt":"2016-08-10T01:57:36Z","receivedAt":"2016-08-10T01:58:34Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Josh Triplett <josh@joshtriplett.org> wrote:\n> On Tue, Aug 09, 2016 at 06:28:00PM +0000, Eric Wong wrote:\n> > Some of these problems I hope public-inbox (or something like\n> > it) can fix and turn the tide towards email, again.\n> \n> This really seems like the dichotomy that drives people towards central\n> services like GitHub or GitLab.  We need an alternative that doesn't\n> involve email, or at the very least, doesn't require people to use email\n> directly.  Half of the pain in the process comes from coaxing email\n> clients that don't treat mail text as sacrosanct to leave it alone and\n> not mangle it.  (Some of that would go away if we accepted attachments\n> with inline disposition, but not all of it.  All of it would go away if\n> the submission process just involved \"git push\" to an appropriate\n> location.)\n\nI don't mind patches as attachments and did some work a few\nmonths ago to ensure they're individually downloadable in the\npublic-inbox WWW interface (along with full mboxrd messages)[1].\n\nFwiw, attachments are preferred in perl5-porters, and it might\nbe acceptable on LKML, even.  Not my call, though.\n\nHaving a push/pull-only workflow would still require some sort\nof messaging system to notify others.  Ideally that message\nwould have the output of \"git request-pull\" to ensure people are\non the same page; but I'd prefer patches (either attachments or\ninline) continue to be sent anyways in case the server is down\nor the reader is offline or on a machine without git.\n\n[1] see Brian's (who is new, here) initial email for diff-highlight:\n    https://public-inbox.org/git/20160728162712.GA29220@tci.corp.yp.com/\n"},{"id":"293615","messageId":"20160810193057.s36wfcivlfm3xmh2@x","threadId":"42743","inReplyTo":"CANQwDwcL0etdZiiroAStwtpYurYEhJ7vcM52BUGUXh_ey+P9Kw@mail.gmail.com","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Josh Triplett","fromEmail":"josh@joshtriplett.org","sentAt":"2016-08-10T19:30:58Z","receivedAt":"2016-08-10T19:31:19Z","isPatch":true,"sender":{"key":"josh@joshtriplett.org","avatar":"https://avatars.githubusercontent.com/u/162737?v=4"},"body":"On Wed, Aug 10, 2016 at 09:30:01AM +0200, Jakub Narębski wrote:\n> On 10 August 2016 at 02:55, Josh Triplett <josh@joshtriplett.org> wrote:\n> > On Tue, Aug 09, 2016 at 06:28:00PM +0000, Eric Wong wrote:\n> >> Some of these problems I hope public-inbox (or something like\n> >> it) can fix and turn the tide towards email, again.\n> >\n> > This really seems like the dichotomy that drives people towards central\n> > services like GitHub or GitLab.  We need an alternative that doesn't\n> > involve email, or at the very least, doesn't require people to use email\n> > directly.  Half of the pain in the process comes from coaxing email\n> > clients that don't treat mail text as sacrosanct to leave it alone and\n> > not mangle it.  (Some of that would go away if we accepted attachments\n> > with inline disposition, but not all of it.  All of it would go away if\n> > the submission process just involved \"git push\" to an appropriate\n> > location.)\n> \n> But submission is less important than review. And for review it is\n> usually better (except gigantic series) to have patch text for review\n> with the review.\n\nAgreed.  However, submission typically requires more work than review,\nbecause the patch text must remain applicable.  For review, as long as\nthe email client you use to respond doesn't do something horrible like\n*re-wrap* the quoted patch text, the result will work as a review.\n\nIdeally, I'd love to see 1) a review UI that stores line-by-line reviews\ninto a common format and can translate those to email, and 2) a\nmechanism to translate reviews written by email and quoting into the\nreview format and store them with the repository.\n\n> And (meta)-versioning of series.\n\nI've got a documented format for that. :)\n\n> And place for proof-of-concept / weather-balon patches...\n\nSame place as all other patches, just with an \"RFC\" tag on them.\n"},{"id":"293629","messageId":"CANQwDwcL0etdZiiroAStwtpYurYEhJ7vcM52BUGUXh_ey+P9Kw@mail.gmail.com","threadId":"42743","inReplyTo":"20160810005548.gee6ontd33ck5vej@x","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Jakub Narębski","fromEmail":"jnareb@gmail.com","sentAt":"2016-08-10T07:30:01Z","receivedAt":"2016-08-10T19:52:14Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"On 10 August 2016 at 02:55, Josh Triplett <josh@joshtriplett.org> wrote:\n> On Tue, Aug 09, 2016 at 06:28:00PM +0000, Eric Wong wrote:\n>> Some of these problems I hope public-inbox (or something like\n>> it) can fix and turn the tide towards email, again.\n>\n> This really seems like the dichotomy that drives people towards central\n> services like GitHub or GitLab.  We need an alternative that doesn't\n> involve email, or at the very least, doesn't require people to use email\n> directly.  Half of the pain in the process comes from coaxing email\n> clients that don't treat mail text as sacrosanct to leave it alone and\n> not mangle it.  (Some of that would go away if we accepted attachments\n> with inline disposition, but not all of it.  All of it would go away if\n> the submission process just involved \"git push\" to an appropriate\n> location.)\n\nBut submission is less important than review. And for review it is\nusually better (except gigantic series) to have patch text for review\nwith the review. And threading. And (meta)-versioning of series.\nAnd place for proof-of-concept / weather-balon patches...\n\n-- \nJakub Narebski\n"},{"id":"293647","messageId":"xmqqd1lg498x.fsf@gitster.mtv.corp.google.com","threadId":"42743","inReplyTo":"20160810193057.s36wfcivlfm3xmh2@x","subject":"Re: patch submission process, was Re: [PATCH v6 06/16] merge_recursive: abort properly upon errors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-08-10T21:14:22Z","receivedAt":"2016-08-10T21:16:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Triplett <josh@joshtriplett.org> writes:\n\n>> But submission is less important than review. And for review it is\n>> usually better (except gigantic series) to have patch text for review\n>> with the review.\n>\n> Agreed.  However, submission typically requires more work than review,\n> because the patch text must remain applicable.  For review, as long as\n> the email client you use to respond doesn't do something horrible like\n> *re-wrap* the quoted patch text, the result will work as a review.\n\nYup.  That is why we say \"please send patch inline; when asked to\nsend it as an attachment, please do so\".\n"}]}