{"thread":{"id":"24680","subject":"[PATCH 0/5] Better error messages for checkout and merge.","startedAt":"2010-08-09T14:19:57Z","lastAt":"2010-08-11T08:38:08Z","messageCount":16,"participants":["Matthieu Moy","Junio C Hamano","Ævar Arnfjörð Bjarmason"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"147497","messageId":"1281363602-27856-1-git-send-email-Matthieu.Moy@imag.fr","threadId":"24680","inReplyTo":null,"subject":"[PATCH 0/5] Better error messages for checkout and merge.","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2010-08-09T14:19:57Z","receivedAt":"2010-08-09T14:19:57Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"This is a resurection of an old patch serie by Diane Gasselin:\n\nhttp://thread.gmane.org/gmane.comp.version-control.git/149173/focus=149186\n\nIn short, when you have several untracked files that conflict with a\nmerge or checkout, Git currently reports just the first. After this\npatch serie, it reports things like:\n\nerror: Your local changes to the following files would be overwritten by checkout:\n\trep/two\n\trep/one\nPlease, commit your changes or stash them before you can switch branches.\n\nmost of the job is done by \"unpack_trees: group error messages by type\",\nbut this needed a bit of preparation to be implementable cleanly.\n\nCompared to previous version, there are many small cleanups, and:\n\n* unpack_trees_options was a struct, it's now an array. This makes the\n  code much cleaner whenever one tries to do clever things with it.\n  That's patch \"Turn unpack_trees_options.msgs into an array + enum\".\n\n* A message was previously a type + an action (\"removed\" or\n  \"overwritten\"). The type now encompasses the action. This duplicates\n  a few lines in the declaration of the error messages, but again\n  makes the rest of the code much simpler. That's patch\n  \"merge-recursive: distinguish \"removed\" and \"overwritten\" messages\"\n\n* The info on whether traverse_trees should stop at the first error\n  was stored in info->data, with a very fragile cast to\n  (unpack_trees_options *). I added one more field in the info\n  structure to get rid of this cast.\n\nDiane Gasselin (2):\n  merge-recursive: porcelain messages for checkout\n  t7609: test merge and checkout error messages\n\nMatthieu Moy (3):\n  Turn unpack_trees_options.msgs into an array + enum\n  merge-recursive: distinguish \"removed\" and \"overwritten\" messages\n  unpack_trees: group error messages by type\n\n Documentation/technical/api-tree-walking.txt |    2 +\n builtin/checkout.c                           |    3 +-\n builtin/merge.c                              |    3 +-\n merge-recursive.c                            |   62 +++++++----\n merge-recursive.h                            |    7 +-\n t/t3030-merge-recursive.sh                   |    2 +-\n t/t3400-rebase.sh                            |    3 +-\n t/t3404-rebase-interactive.sh                |    3 +-\n t/t7609-merge-co-error-msgs.sh               |  125 ++++++++++++++++++++++\n tree-walk.c                                  |   11 ++-\n tree-walk.h                                  |    1 +\n unpack-trees.c                               |  148 ++++++++++++++++++++------\n unpack-trees.h                               |   34 ++++--\n 13 files changed, 332 insertions(+), 72 deletions(-)\n create mode 100755 t/t7609-merge-co-error-msgs.sh\n\n-- \n1.7.2.1.52.g95e25.dirty\n"},{"id":"147500","messageId":"1281363602-27856-2-git-send-email-Matthieu.Moy@imag.fr","threadId":"24680","inReplyTo":"1281363602-27856-1-git-send-email-Matthieu.Moy@imag.fr","subject":"[PATCH 1/5] Turn unpack_trees_options.msgs into an array + enum","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2010-08-09T14:19:58Z","receivedAt":"2010-08-09T14:19:58Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"The list of error messages was introduced as a structure, but an array\nindexed over an enum is more flexible, since it allows one to store a\ntype of error message (index in the array) in a variable.\n\nThis change needs to rename would_lose_untracked ->\nwould_lose_untracked_file to avoid a clash with the function\nwould_lose_untracked in merge-recursive.c.\n\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n builtin/checkout.c |    2 +-\n builtin/merge.c    |    2 +-\n merge-recursive.c  |   29 +++++++++++------------------\n merge-recursive.h  |    4 ++--\n unpack-trees.c     |   16 ++++++++--------\n unpack-trees.h     |   19 ++++++++++---------\n 6 files changed, 33 insertions(+), 39 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 1994be9..8854aab 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -373,7 +373,7 @@ static int merge_working_tree(struct checkout_opts *opts,\n \t\ttopts.src_index = &the_index;\n \t\ttopts.dst_index = &the_index;\n \n-\t\ttopts.msgs.not_uptodate_file = \"You have local changes to '%s'; cannot switch branches.\";\n+\t\ttopts.msgs[not_uptodate_file] = \"You have local changes to '%s'; cannot switch branches.\";\n \n \t\trefresh_cache(REFRESH_QUIET);\n \ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 37ce4f5..115a288 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -704,7 +704,7 @@ int checkout_fast_forward(const unsigned char *head, const unsigned char *remote\n \topts.verbose_update = 1;\n \topts.merge = 1;\n \topts.fn = twoway_merge;\n-\topts.msgs = get_porcelain_error_msgs();\n+\tset_porcelain_error_msgs(opts.msgs);\n \n \ttrees[nr_trees] = parse_tree_indirect(head);\n \tif (!trees[nr_trees++])\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex fb6aa4a..755f530 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -185,7 +185,7 @@ static int git_merge_trees(int index_only,\n \topts.fn = threeway_merge;\n \topts.src_index = &the_index;\n \topts.dst_index = &the_index;\n-\topts.msgs = get_porcelain_error_msgs();\n+\tset_porcelain_error_msgs(opts.msgs);\n \n \tinit_tree_desc_from_tree(t+0, common);\n \tinit_tree_desc_from_tree(t+1, head);\n@@ -1178,26 +1178,19 @@ static int process_entry(struct merge_options *o,\n \treturn clean_merge;\n }\n \n-struct unpack_trees_error_msgs get_porcelain_error_msgs(void)\n+void set_porcelain_error_msgs(const char **msgs)\n {\n-\tstruct unpack_trees_error_msgs msgs = {\n-\t\t/* would_overwrite */\n-\t\t\"Your local changes to '%s' would be overwritten by merge.  Aborting.\",\n-\t\t/* not_uptodate_file */\n-\t\t\"Your local changes to '%s' would be overwritten by merge.  Aborting.\",\n-\t\t/* not_uptodate_dir */\n-\t\t\"Updating '%s' would lose untracked files in it.  Aborting.\",\n-\t\t/* would_lose_untracked */\n-\t\t\"Untracked working tree file '%s' would be %s by merge.  Aborting\",\n-\t\t/* bind_overlap -- will not happen here */\n-\t\tNULL,\n-\t};\n-\tif (advice_commit_before_merge) {\n-\t\tmsgs.would_overwrite = msgs.not_uptodate_file =\n+\tif (advice_commit_before_merge)\n+\t\tmsgs[would_overwrite] = msgs[not_uptodate_file] =\n \t\t\t\"Your local changes to '%s' would be overwritten by merge.  Aborting.\\n\"\n \t\t\t\"Please, commit your changes or stash them before you can merge.\";\n-\t}\n-\treturn msgs;\n+\telse\n+\t\tmsgs[would_overwrite] = msgs[not_uptodate_file] =\n+\t\t\t\"Your local changes to '%s' would be overwritten by merge.  Aborting.\";\n+\tmsgs[not_uptodate_dir] =\n+\t\t\"Updating '%s' would lose untracked files in it.  Aborting.\";\n+\tmsgs[would_lose_untracked_file] =\n+\t\t\"Untracked working tree file '%s' would be %s by merge.  Aborting\";\n }\n \n int merge_trees(struct merge_options *o,\ndiff --git a/merge-recursive.h b/merge-recursive.h\nindex b831293..8412db8 100644\n--- a/merge-recursive.h\n+++ b/merge-recursive.h\n@@ -23,8 +23,8 @@ struct merge_options {\n \tstruct string_list current_directory_set;\n };\n \n-/* Return a list of user-friendly error messages to be used by merge */\n-struct unpack_trees_error_msgs get_porcelain_error_msgs(void);\n+/* Sets the list of user-friendly error messages to be used by merge */\n+void set_porcelain_error_msgs(const char **msgs);\n \n /* merge_trees() but with recursive ancestor consolidation */\n int merge_recursive(struct merge_options *o,\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 8cf0da3..538f228 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -17,7 +17,7 @@\n  * explain why it does not allow switching between branches when you have\n  * local changes, for example.\n  */\n-static struct unpack_trees_error_msgs unpack_plumbing_errors = {\n+const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = {\n \t/* would_overwrite */\n \t\"Entry '%s' would be overwritten by merge. Cannot merge.\",\n \n@@ -27,7 +27,7 @@ static struct unpack_trees_error_msgs unpack_plumbing_errors = {\n \t/* not_uptodate_dir */\n \t\"Updating '%s' would lose untracked files in it\",\n \n-\t/* would_lose_untracked */\n+\t/* would_lose_untracked_file */\n \t\"Untracked working tree file '%s' would be %s by merge.\",\n \n \t/* bind_overlap */\n@@ -40,10 +40,10 @@ static struct unpack_trees_error_msgs unpack_plumbing_errors = {\n \t\"Working tree file '%s' would be %s by sparse checkout update.\",\n };\n \n-#define ERRORMSG(o,fld) \\\n-\t( ((o) && (o)->msgs.fld) \\\n-\t? ((o)->msgs.fld) \\\n-\t: (unpack_plumbing_errors.fld) )\n+#define ERRORMSG(o,type) \\\n+\t( ((o) && (o)->msgs[(type)]) \\\n+\t  ? ((o)->msgs[(type)])      \\\n+\t  : (unpack_plumbing_errors[(type)]) )\n \n static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,\n \tunsigned int set, unsigned int clear)\n@@ -1068,7 +1068,7 @@ static int verify_absent_1(struct cache_entry *ce, const char *action,\n \t\t}\n \n \t\treturn o->gently ? -1 :\n-\t\t\terror(ERRORMSG(o, would_lose_untracked), ce->name, action);\n+\t\t\terror(ERRORMSG(o, would_lose_untracked_file), ce->name, action);\n \t}\n \treturn 0;\n }\n@@ -1077,7 +1077,7 @@ static int verify_absent(struct cache_entry *ce, const char *action,\n {\n \tif (!o->skip_sparse_checkout && will_have_skip_worktree(ce, o))\n \t\treturn 0;\n-\treturn verify_absent_1(ce, action, o, ERRORMSG(o, would_lose_untracked));\n+\treturn verify_absent_1(ce, action, o, ERRORMSG(o, would_lose_untracked_file));\n }\n \n static int verify_absent_sparse(struct cache_entry *ce, const char *action,\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex ef70eab..3a6d2b4 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -9,14 +9,15 @@ struct exclude_list;\n typedef int (*merge_fn_t)(struct cache_entry **src,\n \t\tstruct unpack_trees_options *options);\n \n-struct unpack_trees_error_msgs {\n-\tconst char *would_overwrite;\n-\tconst char *not_uptodate_file;\n-\tconst char *not_uptodate_dir;\n-\tconst char *would_lose_untracked;\n-\tconst char *bind_overlap;\n-\tconst char *sparse_not_uptodate_file;\n-\tconst char *would_lose_orphaned;\n+enum unpack_trees_error_types {\n+\twould_overwrite = 0,\n+\tnot_uptodate_file,\n+\tnot_uptodate_dir,\n+\twould_lose_untracked_file,\n+\tbind_overlap,\n+\tsparse_not_uptodate_file,\n+\twould_lose_orphaned,\n+\tNB_UNPACK_TREES_ERROR_TYPES\n };\n \n struct unpack_trees_options {\n@@ -38,7 +39,7 @@ struct unpack_trees_options {\n \tint cache_bottom;\n \tstruct dir_struct *dir;\n \tmerge_fn_t fn;\n-\tstruct unpack_trees_error_msgs msgs;\n+\tconst char *msgs[NB_UNPACK_TREES_ERROR_TYPES];\n \n \tint head_idx;\n \tint merge_size;\n-- \n1.7.2.1.52.g95e25.dirty\n"},{"id":"147499","messageId":"1281363602-27856-3-git-send-email-Matthieu.Moy@imag.fr","threadId":"24680","inReplyTo":"1281363602-27856-1-git-send-email-Matthieu.Moy@imag.fr","subject":"[PATCH 2/5] merge-recursive: porcelain messages for checkout","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2010-08-09T14:19:59Z","receivedAt":"2010-08-09T14:19:59Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"From: Diane Gasselin <diane.gasselin@ensimag.imag.fr>\n\nA porcelain message was first added in checkout.c in the commit\n8ccba008 (Junio C Hamano, Sat May 17 21:03:49 2008, unpack-trees:\nallow Porcelain to give different error messages) to give better feedback\nin the case of merge errors.\n\nThis patch adapts the porcelain messages for the case of checkout\ninstead. This way, when having a checkout error, \"merge\" no longer\nappears in the error message.\n\nWhile we're there, we add an advice in the case of\nwould_lose_untracked_file.\n\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n builtin/checkout.c |    2 +-\n builtin/merge.c    |    2 +-\n merge-recursive.c  |   31 ++++++++++++++++++++++---------\n merge-recursive.h  |    7 +++++--\n 4 files changed, 29 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 8854aab..22bf47c 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -373,7 +373,7 @@ static int merge_working_tree(struct checkout_opts *opts,\n \t\ttopts.src_index = &the_index;\n \t\ttopts.dst_index = &the_index;\n \n-\t\ttopts.msgs[not_uptodate_file] = \"You have local changes to '%s'; cannot switch branches.\";\n+\t\tset_porcelain_error_msgs(topts.msgs, \"checkout\");\n \n \t\trefresh_cache(REFRESH_QUIET);\n \ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 115a288..de5a0f6 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -704,7 +704,7 @@ int checkout_fast_forward(const unsigned char *head, const unsigned char *remote\n \topts.verbose_update = 1;\n \topts.merge = 1;\n \topts.fn = twoway_merge;\n-\tset_porcelain_error_msgs(opts.msgs);\n+\tset_porcelain_error_msgs(opts.msgs, \"merge\");\n \n \ttrees[nr_trees] = parse_tree_indirect(head);\n \tif (!trees[nr_trees++])\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 755f530..bd26497 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -185,7 +185,7 @@ static int git_merge_trees(int index_only,\n \topts.fn = threeway_merge;\n \topts.src_index = &the_index;\n \topts.dst_index = &the_index;\n-\tset_porcelain_error_msgs(opts.msgs);\n+\tset_porcelain_error_msgs(opts.msgs, \"merge\");\n \n \tinit_tree_desc_from_tree(t+0, common);\n \tinit_tree_desc_from_tree(t+1, head);\n@@ -1178,19 +1178,32 @@ static int process_entry(struct merge_options *o,\n \treturn clean_merge;\n }\n \n-void set_porcelain_error_msgs(const char **msgs)\n+void set_porcelain_error_msgs(const char **msgs, const char *cmd)\n {\n+\tconst char *msg;\n+\tchar *tmp;\n+\tconst char *cmd2 = strcmp(cmd, \"checkout\") ? cmd : \"switch branches\";\n \tif (advice_commit_before_merge)\n-\t\tmsgs[would_overwrite] = msgs[not_uptodate_file] =\n-\t\t\t\"Your local changes to '%s' would be overwritten by merge.  Aborting.\\n\"\n-\t\t\t\"Please, commit your changes or stash them before you can merge.\";\n+\t\tmsg = \"Your local changes to '%%s' would be overwritten by %s.  Aborting.\\n\"\n+\t\t\t\"Please, commit your changes or stash them before you can %s.\";\n \telse\n-\t\tmsgs[would_overwrite] = msgs[not_uptodate_file] =\n-\t\t\t\"Your local changes to '%s' would be overwritten by merge.  Aborting.\";\n+\t\tmsg = \"Your local changes to '%%s' would be overwritten by %s.  Aborting.\";\n+\ttmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 3);\n+\tsprintf(tmp, msg, cmd, cmd2);\n+\tmsgs[would_overwrite] = tmp;\n+\tmsgs[not_uptodate_file] = tmp;\n+\n \tmsgs[not_uptodate_dir] =\n \t\t\"Updating '%s' would lose untracked files in it.  Aborting.\";\n-\tmsgs[would_lose_untracked_file] =\n-\t\t\"Untracked working tree file '%s' would be %s by merge.  Aborting\";\n+\n+\tif (advice_commit_before_merge)\n+\t\tmsg = \"Untracked working tree file '%%s' would be %%s by %s.  Aborting\"\n+\t\t\t\"Please move or remove them before you can %s.\";\n+\telse\n+\t\tmsg = \"Untracked working tree file '%%s' would be %%s by %s.  Aborting\";\n+\ttmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 3);\n+\tsprintf(tmp, msg, cmd, cmd2);\n+\tmsgs[would_lose_untracked_file] = tmp;\n }\n \n int merge_trees(struct merge_options *o,\ndiff --git a/merge-recursive.h b/merge-recursive.h\nindex 8412db8..08f9850 100644\n--- a/merge-recursive.h\n+++ b/merge-recursive.h\n@@ -23,8 +23,11 @@ struct merge_options {\n \tstruct string_list current_directory_set;\n };\n \n-/* Sets the list of user-friendly error messages to be used by merge */\n-void set_porcelain_error_msgs(const char **msgs);\n+/*\n+ * Sets the list of user-friendly error messages to be used by the\n+ * command \"cmd\" (either merge or checkout)\n+ */\n+void set_porcelain_error_msgs(const char **msgs, const char *cmd);\n \n /* merge_trees() but with recursive ancestor consolidation */\n int merge_recursive(struct merge_options *o,\n-- \n1.7.2.1.52.g95e25.dirty\n"},{"id":"147502","messageId":"1281363602-27856-4-git-send-email-Matthieu.Moy@imag.fr","threadId":"24680","inReplyTo":"1281363602-27856-1-git-send-email-Matthieu.Moy@imag.fr","subject":"[PATCH 3/5] merge-recursive: distinguish \"removed\" and \"overwritten\" messages","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2010-08-09T14:20:00Z","receivedAt":"2010-08-09T14:20:00Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"To limit the number of possible error messages, the error messages for\nthe case would_lose_untracked_file and would_lose_orphaned in\nunpack_trees_options.msgs were handled with a single string,\nparameterized by an action string (\"overwritten\" or \"removed\").\n\nInstead, we consider them as two different cases, with unparameterized\nstring. This will make it easier to make separate lists sorted by error\ntypes later.\n\nOnly the bind_overlap case still takes two %s parameters, but that's\nunavoidable.\n\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n merge-recursive.c |   13 ++++++----\n unpack-trees.c    |   64 ++++++++++++++++++++++++++++++++--------------------\n unpack-trees.h    |    6 +++-\n 3 files changed, 51 insertions(+), 32 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex bd26497..8c1b189 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1197,13 +1197,16 @@ void set_porcelain_error_msgs(const char **msgs, const char *cmd)\n \t\t\"Updating '%s' would lose untracked files in it.  Aborting.\";\n \n \tif (advice_commit_before_merge)\n-\t\tmsg = \"Untracked working tree file '%%s' would be %%s by %s.  Aborting\"\n+\t\tmsg = \"Untracked working tree file '%%s' would be %s by %s.  Aborting\"\n \t\t\t\"Please move or remove them before you can %s.\";\n \telse\n-\t\tmsg = \"Untracked working tree file '%%s' would be %%s by %s.  Aborting\";\n-\ttmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 3);\n-\tsprintf(tmp, msg, cmd, cmd2);\n-\tmsgs[would_lose_untracked_file] = tmp;\n+\t\tmsg = \"Untracked working tree file '%%s' would be %s by %s.  Aborting\";\n+\ttmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(\"removed\") + strlen(cmd2) - 4);\n+\tsprintf(tmp, msg, \"removed\", cmd, cmd2);\n+\tmsgs[would_lose_untracked_removed] = tmp;\n+\ttmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(\"overwritten\") + strlen(cmd2) - 4);\n+\tsprintf(tmp, msg, \"overwritten\", cmd, cmd2);\n+\tmsgs[would_lose_untracked_overwritten] = tmp;\n }\n \n int merge_trees(struct merge_options *o,\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 538f228..143f897 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -27,8 +27,11 @@ const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = {\n \t/* not_uptodate_dir */\n \t\"Updating '%s' would lose untracked files in it\",\n \n-\t/* would_lose_untracked_file */\n-\t\"Untracked working tree file '%s' would be %s by merge.\",\n+\t/* would_lose_untracked_overwritten */\n+\t\"Untracked working tree file '%s' would be overwritten by merge.\",\n+\n+\t/* would_lose_untracked_removed */\n+\t\"Untracked working tree file '%s' would be removed by merge.\",\n \n \t/* bind_overlap */\n \t\"Entry '%s' overlaps with '%s'.  Cannot bind.\",\n@@ -36,8 +39,11 @@ const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = {\n \t/* sparse_not_uptodate_file */\n \t\"Entry '%s' not uptodate. Cannot update sparse checkout.\",\n \n-\t/* would_lose_orphaned */\n-\t\"Working tree file '%s' would be %s by sparse checkout update.\",\n+\t/* would_lose_orphaned_overwritten */\n+\t\"Working tree file '%s' would be overwritten by sparse checkout update.\",\n+\n+\t/* would_lose_orphaned_removed */\n+\t\"Working tree file '%s' would be removed by sparse checkout update.\",\n };\n \n #define ERRORMSG(o,type) \\\n@@ -132,7 +138,7 @@ static int check_updates(struct unpack_trees_options *o)\n }\n \n static int verify_uptodate_sparse(struct cache_entry *ce, struct unpack_trees_options *o);\n-static int verify_absent_sparse(struct cache_entry *ce, const char *action, struct unpack_trees_options *o);\n+static int verify_absent_sparse(struct cache_entry *ce, enum unpack_trees_error_types, struct unpack_trees_options *o);\n \n static int will_have_skip_worktree(const struct cache_entry *ce, struct unpack_trees_options *o)\n {\n@@ -175,7 +181,7 @@ static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_opt\n \t\tce->ce_flags |= CE_WT_REMOVE;\n \t}\n \tif (was_skip_worktree && !ce_skip_worktree(ce)) {\n-\t\tif (verify_absent_sparse(ce, \"overwritten\", o))\n+\t\tif (verify_absent_sparse(ce, would_lose_untracked_overwritten, o))\n \t\t\treturn -1;\n \t\tce->ce_flags |= CE_UPDATE;\n \t}\n@@ -860,7 +866,7 @@ static int same(struct cache_entry *a, struct cache_entry *b)\n  */\n static int verify_uptodate_1(struct cache_entry *ce,\n \t\t\t\t   struct unpack_trees_options *o,\n-\t\t\t\t   const char *error_msg)\n+\t\t\t\t   enum unpack_trees_error_types error_type)\n {\n \tstruct stat st;\n \n@@ -885,7 +891,7 @@ static int verify_uptodate_1(struct cache_entry *ce,\n \tif (errno == ENOENT)\n \t\treturn 0;\n \treturn o->gently ? -1 :\n-\t\terror(error_msg, ce->name);\n+\t\terror(ERRORMSG(o, error_type), ce->name);\n }\n \n static int verify_uptodate(struct cache_entry *ce,\n@@ -893,13 +899,13 @@ static int verify_uptodate(struct cache_entry *ce,\n {\n \tif (!o->skip_sparse_checkout && will_have_skip_worktree(ce, o))\n \t\treturn 0;\n-\treturn verify_uptodate_1(ce, o, ERRORMSG(o, not_uptodate_file));\n+\treturn verify_uptodate_1(ce, o, not_uptodate_file);\n }\n \n static int verify_uptodate_sparse(struct cache_entry *ce,\n \t\t\t\t  struct unpack_trees_options *o)\n {\n-\treturn verify_uptodate_1(ce, o, ERRORMSG(o, sparse_not_uptodate_file));\n+\treturn verify_uptodate_1(ce, o, sparse_not_uptodate_file);\n }\n \n static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_options *o)\n@@ -915,13 +921,15 @@ static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_optio\n  * Currently, git does not checkout subprojects during a superproject\n  * checkout, so it is not going to overwrite anything.\n  */\n-static int verify_clean_submodule(struct cache_entry *ce, const char *action,\n+static int verify_clean_submodule(struct cache_entry *ce,\n+\t\t\t\t      enum unpack_trees_error_types error_type,\n \t\t\t\t      struct unpack_trees_options *o)\n {\n \treturn 0;\n }\n \n-static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,\n+static int verify_clean_subdirectory(struct cache_entry *ce,\n+\t\t\t\t      enum unpack_trees_error_types error_type,\n \t\t\t\t      struct unpack_trees_options *o)\n {\n \t/*\n@@ -942,7 +950,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,\n \t\t */\n \t\tif (!hashcmp(sha1, ce->sha1))\n \t\t\treturn 0;\n-\t\treturn verify_clean_submodule(ce, action, o);\n+\t\treturn verify_clean_submodule(ce, error_type, o);\n \t}\n \n \t/*\n@@ -1011,9 +1019,9 @@ static int icase_exists(struct unpack_trees_options *o, struct cache_entry *dst,\n  * We do not want to remove or overwrite a working tree file that\n  * is not tracked, unless it is ignored.\n  */\n-static int verify_absent_1(struct cache_entry *ce, const char *action,\n-\t\t\t\t struct unpack_trees_options *o,\n-\t\t\t\t const char *error_msg)\n+static int verify_absent_1(struct cache_entry *ce,\n+\t\t\t\t enum unpack_trees_error_types error_type,\n+\t\t\t\t struct unpack_trees_options *o)\n {\n \tstruct stat st;\n \n@@ -1051,7 +1059,7 @@ static int verify_absent_1(struct cache_entry *ce, const char *action,\n \t\t\t * files that are in \"foo/\" we would lose\n \t\t\t * them.\n \t\t\t */\n-\t\t\tif (verify_clean_subdirectory(ce, action, o) < 0)\n+\t\t\tif (verify_clean_subdirectory(ce, error_type, o) < 0)\n \t\t\t\treturn -1;\n \t\t\treturn 0;\n \t\t}\n@@ -1068,22 +1076,28 @@ static int verify_absent_1(struct cache_entry *ce, const char *action,\n \t\t}\n \n \t\treturn o->gently ? -1 :\n-\t\t\terror(ERRORMSG(o, would_lose_untracked_file), ce->name, action);\n+\t\t\terror(ERRORMSG(o, error_type), ce->name);\n \t}\n \treturn 0;\n }\n-static int verify_absent(struct cache_entry *ce, const char *action,\n+static int verify_absent(struct cache_entry *ce,\n+\t\t\t enum unpack_trees_error_types error_type,\n \t\t\t struct unpack_trees_options *o)\n {\n \tif (!o->skip_sparse_checkout && will_have_skip_worktree(ce, o))\n \t\treturn 0;\n-\treturn verify_absent_1(ce, action, o, ERRORMSG(o, would_lose_untracked_file));\n+\treturn verify_absent_1(ce, error_type, o);\n }\n \n-static int verify_absent_sparse(struct cache_entry *ce, const char *action,\n+static int verify_absent_sparse(struct cache_entry *ce,\n+\t\t\t enum unpack_trees_error_types error_type,\n \t\t\t struct unpack_trees_options *o)\n {\n-\treturn verify_absent_1(ce, action, o, ERRORMSG(o, would_lose_orphaned));\n+\tenum unpack_trees_error_types orphaned_error = error_type;\n+\tif (orphaned_error == would_lose_untracked_overwritten)\n+\t\torphaned_error = would_lose_orphaned_overwritten;\n+\n+\treturn verify_absent_1(ce, orphaned_error, o);\n }\n \n static int merged_entry(struct cache_entry *merge, struct cache_entry *old,\n@@ -1092,7 +1106,7 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,\n \tint update = CE_UPDATE;\n \n \tif (!old) {\n-\t\tif (verify_absent(merge, \"overwritten\", o))\n+\t\tif (verify_absent(merge, would_lose_untracked_overwritten, o))\n \t\t\treturn -1;\n \t\tinvalidate_ce_path(merge, o);\n \t} else if (!(old->ce_flags & CE_CONFLICTED)) {\n@@ -1130,7 +1144,7 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,\n {\n \t/* Did it exist in the index? */\n \tif (!old) {\n-\t\tif (verify_absent(ce, \"removed\", o))\n+\t\tif (verify_absent(ce, would_lose_untracked_removed, o))\n \t\t\treturn -1;\n \t\treturn 0;\n \t}\n@@ -1279,7 +1293,7 @@ int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)\n \t\t\tif (index)\n \t\t\t\treturn deleted_entry(index, index, o);\n \t\t\tif (ce && !head_deleted) {\n-\t\t\t\tif (verify_absent(ce, \"removed\", o))\n+\t\t\t\tif (verify_absent(ce, would_lose_untracked_removed, o))\n \t\t\t\t\treturn -1;\n \t\t\t}\n \t\t\treturn 0;\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex 3a6d2b4..edda877 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -13,10 +13,12 @@ enum unpack_trees_error_types {\n \twould_overwrite = 0,\n \tnot_uptodate_file,\n \tnot_uptodate_dir,\n-\twould_lose_untracked_file,\n+\twould_lose_untracked_overwritten,\n+\twould_lose_untracked_removed,\n \tbind_overlap,\n \tsparse_not_uptodate_file,\n-\twould_lose_orphaned,\n+\twould_lose_orphaned_overwritten,\n+\twould_lose_orphaned_removed,\n \tNB_UNPACK_TREES_ERROR_TYPES\n };\n \n-- \n1.7.2.1.52.g95e25.dirty\n"},{"id":"147501","messageId":"1281363602-27856-5-git-send-email-Matthieu.Moy@imag.fr","threadId":"24680","inReplyTo":"1281363602-27856-1-git-send-email-Matthieu.Moy@imag.fr","subject":"[PATCH 4/5] unpack_trees: group error messages by type","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2010-08-09T14:20:01Z","receivedAt":"2010-08-09T14:20:01Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"When an error is encountered, it calls add_rejected_file() which either\n- directly displays the error message and stops if in plumbing mode\n  (i.e. if show_all_errors is not initialized at 1)\n- or stores it so that it will be displayed at the end with display_error_msgs(),\n\nStoring the files by error type permits to have a list of files for\nwhich there is the same error instead of having a serie of almost\nidentical errors.\n\nAs each bind_overlap error combines a file and an old file, a list cannot be\ndone, therefore, theses errors are not stored but directly displayed.\n\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n Documentation/technical/api-tree-walking.txt |    2 +\n builtin/checkout.c                           |    1 +\n builtin/merge.c                              |    1 +\n merge-recursive.c                            |   25 ++++++--\n t/t3030-merge-recursive.sh                   |    2 +-\n t/t3400-rebase.sh                            |    3 +-\n t/t3404-rebase-interactive.sh                |    3 +-\n tree-walk.c                                  |   11 +++-\n tree-walk.h                                  |    1 +\n unpack-trees.c                               |   78 ++++++++++++++++++++++++-\n unpack-trees.h                               |   13 ++++-\n 11 files changed, 123 insertions(+), 17 deletions(-)\n\ndiff --git a/Documentation/technical/api-tree-walking.txt b/Documentation/technical/api-tree-walking.txt\nindex 55b7286..14af37c 100644\n--- a/Documentation/technical/api-tree-walking.txt\n+++ b/Documentation/technical/api-tree-walking.txt\n@@ -42,6 +42,8 @@ information.\n \n * `data` can be anything the `fn` callback would want to use.\n \n+* `show_all_errors` tells whether to stop at the first error or not.\n+\n Initializing\n ------------\n \ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 22bf47c..894bb84 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -392,6 +392,7 @@ static int merge_working_tree(struct checkout_opts *opts,\n \t\ttopts.dir = xcalloc(1, sizeof(*topts.dir));\n \t\ttopts.dir->flags |= DIR_SHOW_IGNORED;\n \t\ttopts.dir->exclude_per_dir = \".gitignore\";\n+\t\ttopts.show_all_errors = 1;\n \t\ttree = parse_tree_indirect(old->commit ?\n \t\t\t\t\t   old->commit->object.sha1 :\n \t\t\t\t\t   (unsigned char *)EMPTY_TREE_SHA1_BIN);\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex de5a0f6..1bcf7fd 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -704,6 +704,7 @@ int checkout_fast_forward(const unsigned char *head, const unsigned char *remote\n \topts.verbose_update = 1;\n \topts.merge = 1;\n \topts.fn = twoway_merge;\n+\topts.show_all_errors = 1;\n \tset_porcelain_error_msgs(opts.msgs, \"merge\");\n \n \ttrees[nr_trees] = parse_tree_indirect(head);\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 8c1b189..e292d6e 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1184,29 +1184,42 @@ void set_porcelain_error_msgs(const char **msgs, const char *cmd)\n \tchar *tmp;\n \tconst char *cmd2 = strcmp(cmd, \"checkout\") ? cmd : \"switch branches\";\n \tif (advice_commit_before_merge)\n-\t\tmsg = \"Your local changes to '%%s' would be overwritten by %s.  Aborting.\\n\"\n+\t\tmsg = \"Your local changes to the following files would be overwritten by %s:\\n%%s\"\n \t\t\t\"Please, commit your changes or stash them before you can %s.\";\n \telse\n-\t\tmsg = \"Your local changes to '%%s' would be overwritten by %s.  Aborting.\";\n-\ttmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 3);\n+\t\tmsg = \"Your local changes to the following files would be overwritten by %s:\\n%%s\";\n+\ttmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 2);\n \tsprintf(tmp, msg, cmd, cmd2);\n \tmsgs[would_overwrite] = tmp;\n \tmsgs[not_uptodate_file] = tmp;\n \n \tmsgs[not_uptodate_dir] =\n-\t\t\"Updating '%s' would lose untracked files in it.  Aborting.\";\n+\t\t\"Updating the following directories would lose untracked files in it:\\n%s\";\n \n \tif (advice_commit_before_merge)\n-\t\tmsg = \"Untracked working tree file '%%s' would be %s by %s.  Aborting\"\n+\t\tmsg = \"The following untracked working tree files would be %s by %s:\\n%%s\"\n \t\t\t\"Please move or remove them before you can %s.\";\n \telse\n-\t\tmsg = \"Untracked working tree file '%%s' would be %s by %s.  Aborting\";\n+\t\tmsg = \"The following untracked working tree files would be %s by %s:\\n%%s\";\n \ttmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(\"removed\") + strlen(cmd2) - 4);\n \tsprintf(tmp, msg, \"removed\", cmd, cmd2);\n \tmsgs[would_lose_untracked_removed] = tmp;\n \ttmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(\"overwritten\") + strlen(cmd2) - 4);\n \tsprintf(tmp, msg, \"overwritten\", cmd, cmd2);\n \tmsgs[would_lose_untracked_overwritten] = tmp;\n+\n+\t/*\n+\t * Special case: bind_overlap refers to a pair of paths, we\n+\t * cannot easily display it as a list.\n+\t */\n+\tmsgs[bind_overlap] = \"Entry '%s' overlaps with '%s'.  Cannot bind.\";\n+\n+\tmsgs[sparse_not_uptodate_file] =\n+\t\t\"Cannot update sparse checkout: the following entries are not up-to-date:\\n%s\";\n+\tmsgs[would_lose_orphaned_overwritten] =\n+\t\t\"The following Working tree files would be overwritten by sparse checkout update:\\n%s\";\n+\tmsgs[would_lose_orphaned_removed] =\n+\t\t\"The following Working tree files would be removed by sparse checkout update:\\n%s\";\n }\n \n int merge_trees(struct merge_options *o,\ndiff --git a/t/t3030-merge-recursive.sh b/t/t3030-merge-recursive.sh\nindex d541544..efe2900 100755\n--- a/t/t3030-merge-recursive.sh\n+++ b/t/t3030-merge-recursive.sh\n@@ -294,7 +294,7 @@ test_expect_success 'fail if the index has unresolved entries' '\n \tgrep \"You have not concluded your merge\" out &&\n \trm -f .git/MERGE_HEAD &&\n \ttest_must_fail git merge \"$c5\" 2> out &&\n-\tgrep \"Your local changes to .* would be overwritten by merge.\" out\n+\tgrep \"Your local changes to the following files would be overwritten by merge:\" out\n '\n \n test_expect_success 'merge-recursive remove conflict' '\ndiff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh\nindex d98c7b5..45ef282 100755\n--- a/t/t3400-rebase.sh\n+++ b/t/t3400-rebase.sh\n@@ -129,7 +129,8 @@ test_expect_success 'rebase a single mode change' '\n test_expect_success 'Show verbose error when HEAD could not be detached' '\n      : > B &&\n      test_must_fail git rebase topic 2> output.err > output.out &&\n-     grep \"Untracked working tree file .B. would be overwritten\" output.err\n+     grep \"The following untracked working tree files would be overwritten by checkout:\" output.err &&\n+     grep B output.err\n '\n rm -f B\n \ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 9f03ce6..3af3f60 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -150,8 +150,9 @@ test_expect_success 'abort with error when new base cannot be checked out' '\n \tgit rm --cached file1 &&\n \tgit commit -m \"remove file in base\" &&\n \ttest_must_fail git rebase -i master > output 2>&1 &&\n-\tgrep \"Untracked working tree file .file1. would be overwritten\" \\\n+\tgrep \"The following untracked working tree files would be overwritten by checkout:\" \\\n \t\toutput &&\n+\tgrep \"file1\" output &&\n \t! test -d .git/rebase-merge &&\n \tgit reset --hard HEAD^\n '\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 67a9a0c..a9bbf4e 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -1,5 +1,6 @@\n #include \"cache.h\"\n #include \"tree-walk.h\"\n+#include \"unpack-trees.h\"\n #include \"tree.h\"\n \n static const char *get_mode(const char *str, unsigned int *modep)\n@@ -310,6 +311,7 @@ static void free_extended_entry(struct tree_desc_x *t)\n int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)\n {\n \tint ret = 0;\n+\tint error = 0;\n \tstruct name_entry *entry = xmalloc(n*sizeof(*entry));\n \tint i;\n \tstruct tree_desc_x *tx = xcalloc(n, sizeof(*tx));\n@@ -377,8 +379,11 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)\n \t\tif (!mask)\n \t\t\tbreak;\n \t\tret = info->fn(n, mask, dirmask, entry, info);\n-\t\tif (ret < 0)\n-\t\t\tbreak;\n+\t\tif (ret < 0) {\n+\t\t\terror = ret;\n+\t\t\tif (!info->show_all_errors)\n+\t\t\t\tbreak;\n+\t\t}\n \t\tmask &= ret;\n \t\tret = 0;\n \t\tfor (i = 0; i < n; i++)\n@@ -389,7 +394,7 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)\n \tfor (i = 0; i < n; i++)\n \t\tfree_extended_entry(tx + i);\n \tfree(tx);\n-\treturn ret;\n+\treturn error;\n }\n \n static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode)\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 42110a4..88ea7e9 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -45,6 +45,7 @@ struct traverse_info {\n \tunsigned long conflicts;\n \ttraverse_callback_t fn;\n \tvoid *data;\n+\tint show_all_errors;\n };\n \n int get_tree_entry(const unsigned char *, const char *, unsigned char *, unsigned *);\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 143f897..75249db 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -66,6 +66,73 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,\n }\n \n /*\n+ * add error messages on path <path>\n+ * corresponding to the type <e> with the message <msg>\n+ * indicating if it should be display in porcelain or not\n+ */\n+static int add_rejected_path(struct unpack_trees_options *o,\n+\t\t\t     enum unpack_trees_error_types e,\n+\t\t\t     const char *path)\n+{\n+\tstruct rejected_paths_list *newentry;\n+\tint porcelain = o && (o)->msgs[e];\n+\t/*\n+\t * simply display the given error message if in plumbing mode\n+\t */\n+\tif (!porcelain)\n+\t\to->show_all_errors = 0;\n+\tif (!o->show_all_errors)\n+\t\treturn error(ERRORMSG(o, e), path);\n+\n+\t/*\n+\t * Otherwise, insert in a list for future display by\n+\t * display_error_msgs()\n+\t */\n+\tnewentry = xmalloc(sizeof(struct rejected_paths_list));\n+\tnewentry->path = (char *)path;\n+\tnewentry->next = o->unpack_rejects[e];\n+\to->unpack_rejects[e] = newentry;\n+\treturn -1;\n+}\n+\n+/*\n+ * free all the structures allocated for the error <e>\n+ */\n+static void free_rejected_paths(struct unpack_trees_options *o,\n+\t\t\t\tenum unpack_trees_error_types e)\n+{\n+\twhile (o->unpack_rejects[e]) {\n+\t\tstruct rejected_paths_list *del = o->unpack_rejects[e];\n+\t\to->unpack_rejects[e] = o->unpack_rejects[e]->next;\n+\t\tfree(del);\n+\t}\n+\tfree(o->unpack_rejects[e]);\n+}\n+\n+/*\n+ * display all the error messages stored in a nice way\n+ */\n+static void display_error_msgs(struct unpack_trees_options *o)\n+{\n+\tint e;\n+\tint something_displayed = 0;\n+\tfor (e = 0; e < NB_UNPACK_TREES_ERROR_TYPES; e++) {\n+\t\tif (o->unpack_rejects[e]) {\n+\t\t\tstruct rejected_paths_list *rp;\n+\t\t\tstruct strbuf path = STRBUF_INIT;\n+\t\t\tsomething_displayed = 1;\n+\t\t\tfor (rp = o->unpack_rejects[e]; rp; rp = rp->next)\n+\t\t\t\tstrbuf_addf(&path, \"\\t%s\\n\", rp->path);\n+\t\t\terror(ERRORMSG(o, e), path.buf);\n+\t\t\tstrbuf_release(&path);\n+\t\t\tfree_rejected_paths(o, e);\n+\t\t}\n+\t}\n+\tif (something_displayed)\n+\t\tprintf(\"Aborting\\n\");\n+}\n+\n+/*\n  * Unlink the last component and schedule the leading directories for\n  * removal, such that empty directories get removed.\n  */\n@@ -756,6 +823,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \t\tsetup_traverse_info(&info, prefix);\n \t\tinfo.fn = unpack_callback;\n \t\tinfo.data = o;\n+\t\tinfo.show_all_errors = o->show_all_errors;\n \n \t\tif (o->prefix) {\n \t\t\t/*\n@@ -835,6 +903,8 @@ done:\n \treturn ret;\n \n return_failed:\n+\tif (o->show_all_errors)\n+\t\tdisplay_error_msgs(o);\n \tmark_all_ce_unused(o->src_index);\n \tret = unpack_failed(o, NULL);\n \tgoto done;\n@@ -844,7 +914,7 @@ return_failed:\n \n static int reject_merge(struct cache_entry *ce, struct unpack_trees_options *o)\n {\n-\treturn error(ERRORMSG(o, would_overwrite), ce->name);\n+\treturn add_rejected_path(o, would_overwrite, ce->name);\n }\n \n static int same(struct cache_entry *a, struct cache_entry *b)\n@@ -891,7 +961,7 @@ static int verify_uptodate_1(struct cache_entry *ce,\n \tif (errno == ENOENT)\n \t\treturn 0;\n \treturn o->gently ? -1 :\n-\t\terror(ERRORMSG(o, error_type), ce->name);\n+\t\tadd_rejected_path(o, error_type, ce->name);\n }\n \n static int verify_uptodate(struct cache_entry *ce,\n@@ -994,7 +1064,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce,\n \ti = read_directory(&d, pathbuf, namelen+1, NULL);\n \tif (i)\n \t\treturn o->gently ? -1 :\n-\t\t\terror(ERRORMSG(o, not_uptodate_dir), ce->name);\n+\t\t\tadd_rejected_path(o, not_uptodate_dir, ce->name);\n \tfree(pathbuf);\n \treturn cnt;\n }\n@@ -1076,7 +1146,7 @@ static int verify_absent_1(struct cache_entry *ce,\n \t\t}\n \n \t\treturn o->gently ? -1 :\n-\t\t\terror(ERRORMSG(o, error_type), ce->name);\n+\t\t\tadd_rejected_path(o, error_type, ce->name);\n \t}\n \treturn 0;\n }\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex edda877..785bc10 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -22,6 +22,11 @@ enum unpack_trees_error_types {\n \tNB_UNPACK_TREES_ERROR_TYPES\n };\n \n+struct rejected_paths_list {\n+\tchar *path;\n+\tstruct rejected_paths_list *next;\n+};\n+\n struct unpack_trees_options {\n \tunsigned int reset,\n \t\t     merge,\n@@ -36,12 +41,18 @@ struct unpack_trees_options {\n \t\t     diff_index_cached,\n \t\t     debug_unpack,\n \t\t     skip_sparse_checkout,\n-\t\t     gently;\n+\t\t     gently,\n+\t\t     show_all_errors;\n \tconst char *prefix;\n \tint cache_bottom;\n \tstruct dir_struct *dir;\n \tmerge_fn_t fn;\n \tconst char *msgs[NB_UNPACK_TREES_ERROR_TYPES];\n+\t/*\n+\t * Store error messages in an array, each case\n+\t * corresponding to a error message type\n+\t */\n+\tstruct rejected_paths_list *unpack_rejects[NB_UNPACK_TREES_ERROR_TYPES];\n \n \tint head_idx;\n \tint merge_size;\n-- \n1.7.2.1.52.g95e25.dirty\n"},{"id":"147498","messageId":"1281363602-27856-6-git-send-email-Matthieu.Moy@imag.fr","threadId":"24680","inReplyTo":"1281363602-27856-1-git-send-email-Matthieu.Moy@imag.fr","subject":"[PATCH 5/5] t7609: test merge and checkout error messages","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2010-08-09T14:20:02Z","receivedAt":"2010-08-09T14:20:02Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"From: Diane Gasselin <diane.gasselin@ensimag.imag.fr>\n\nTest porcelain and plumbing error messages for different types of errors\nof merge and checkout.\n\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n t/t7609-merge-co-error-msgs.sh |  125 ++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 125 insertions(+), 0 deletions(-)\n create mode 100755 t/t7609-merge-co-error-msgs.sh\n\ndiff --git a/t/t7609-merge-co-error-msgs.sh b/t/t7609-merge-co-error-msgs.sh\nnew file mode 100755\nindex 0000000..1a109b4\n--- /dev/null\n+++ b/t/t7609-merge-co-error-msgs.sh\n@@ -0,0 +1,125 @@\n+#!/bin/sh\n+\n+test_description='unpack-trees error messages'\n+\n+. ./test-lib.sh\n+\n+\n+test_expect_success 'setup' '\n+\techo one >one &&\n+\tgit add one &&\n+\tgit commit -a -m First &&\n+\n+\tgit checkout -b branch &&\n+\techo two >two &&\n+\techo three >three &&\n+\techo four >four &&\n+\techo five >five &&\n+\tgit add two three four five &&\n+\tgit commit -m Second &&\n+\n+\tgit checkout master &&\n+\techo other >two &&\n+\techo other >three &&\n+\techo other >four &&\n+\techo other >five\n+'\n+\n+cat >expect <<\\EOF\n+error: The following untracked working tree files would be overwritten by merge:\n+\ttwo\n+\tthree\n+\tfour\n+\tfive\n+Please move or remove them before you can merge.\n+EOF\n+\n+test_expect_success 'untracked files overwritten by merge' '\n+\ttest_must_fail git merge branch 2>out &&\n+\ttest_cmp out expect\n+'\n+\n+cat >expect <<\\EOF\n+error: Your local changes to the following files would be overwritten by merge:\n+\ttwo\n+\tthree\n+\tfour\n+Please, commit your changes or stash them before you can merge.\n+error: The following untracked working tree files would be overwritten by merge:\n+\tfive\n+Please move or remove them before you can merge.\n+EOF\n+\n+test_expect_success 'untracked files or local changes ovewritten by merge' '\n+\tgit add two &&\n+\tgit add three &&\n+\tgit add four &&\n+\ttest_must_fail git merge branch 2>out &&\n+\ttest_cmp out expect\n+'\n+\n+cat >expect <<\\EOF\n+error: Your local changes to the following files would be overwritten by checkout:\n+\trep/two\n+\trep/one\n+Please, commit your changes or stash them before you can switch branches.\n+EOF\n+\n+test_expect_success 'cannot switch branches because of local changes' '\n+\tgit add five &&\n+\tmkdir rep &&\n+\techo one >rep/one &&\n+\techo two >rep/two &&\n+\tgit add rep/one rep/two &&\n+\tgit commit -m Fourth &&\n+\tgit checkout master &&\n+\techo uno >rep/one &&\n+\techo dos >rep/two &&\n+\ttest_must_fail git checkout branch 2>out &&\n+\ttest_cmp out expect\n+'\n+\n+cat >expect <<\\EOF\n+error: Your local changes to the following files would be overwritten by checkout:\n+\trep/two\n+\trep/one\n+Please, commit your changes or stash them before you can switch branches.\n+EOF\n+\n+test_expect_success 'not uptodate file porcelain checkout error' '\n+\tgit add rep/one rep/two &&\n+\ttest_must_fail git checkout branch 2>out &&\n+\ttest_cmp out expect\n+'\n+\n+cat >expect <<\\EOF\n+error: Updating the following directories would lose untracked files in it:\n+\trep2\n+\trep\n+\n+EOF\n+\n+test_expect_success 'not_uptodate_dir porcelain checkout error' '\n+\tgit init uptodate &&\n+\tcd uptodate &&\n+\tmkdir rep &&\n+\tmkdir rep2 &&\n+\ttouch rep/foo &&\n+\ttouch rep2/foo &&\n+\tgit add rep/foo rep2/foo &&\n+\tgit commit -m init &&\n+\tgit checkout -b branch &&\n+\tgit rm rep -r &&\n+\tgit rm rep2 -r &&\n+\t>rep &&\n+\t>rep2 &&\n+\tgit add rep rep2&&\n+\tgit commit -m \"added test as a file\" &&\n+\tgit checkout master &&\n+\t>rep/untracked-file &&\n+\t>rep2/untracked-file &&\n+\ttest_must_fail git checkout branch 2>out &&\n+\ttest_cmp out ../expect\n+'\n+\n+test_done\n-- \n1.7.2.1.52.g95e25.dirty\n"},{"id":"147540","messageId":"7vwrrz4lgi.fsf@alter.siamese.dyndns.org","threadId":"24680","inReplyTo":"1281363602-27856-2-git-send-email-Matthieu.Moy@imag.fr","subject":"Re: [PATCH 1/5] Turn unpack_trees_options.msgs into an array + enum","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-08-09T19:47:41Z","receivedAt":"2010-08-09T19:47:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthieu Moy <Matthieu.Moy@imag.fr> writes:\n\n> The list of error messages was introduced as a structure, but an array\n> indexed over an enum is more flexible, since it allows one to store a\n> type of error message (index in the array) in a variable.\n\nHmm, the only example of the advantage \"enum used as array index\" offers\nthat I can think of off the top of my head is that you can more easily\niterate over it.\n\n> This change needs to rename would_lose_untracked ->\n> would_lose_untracked_file to avoid a clash with the function\n> would_lose_untracked in merge-recursive.c.\n\nYes, that shows one upside of \"field names in a structure\" has over\n\"enum used as array index\".  We get a unique namespace.\n\nIf you are to change them to enum, I would actually suggest renaming them\na bit more to make them stand out.  Perhaps spell them all in caps,\nperhaps have them share the same short prefix (UTEM_ - unpack trees error\nmessages), etc.\n\nHaving said all that, I am not against the main idea of this patch.\n"},{"id":"147542","messageId":"7vhbj34kz8.fsf@alter.siamese.dyndns.org","threadId":"24680","inReplyTo":"1281363602-27856-3-git-send-email-Matthieu.Moy@imag.fr","subject":"Re: [PATCH 2/5] merge-recursive: porcelain messages for checkout","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-08-09T19:58:03Z","receivedAt":"2010-08-09T19:58:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthieu Moy <Matthieu.Moy@imag.fr> writes:\n\n> +void set_porcelain_error_msgs(const char **msgs, const char *cmd)\n>  {\n> +\tconst char *msg;\n> +\tchar *tmp;\n> +\tconst char *cmd2 = strcmp(cmd, \"checkout\") ? cmd : \"switch branches\";\n\nThis may have implications to the later i18n effort, but lets ignore it\nfor now.  I don't think it will be too bad.\n"},{"id":"147552","messageId":"vpq8w4fse8w.fsf@bauges.imag.fr","threadId":"24680","inReplyTo":"7vwrrz4lgi.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 1/5] Turn unpack_trees_options.msgs into an array + enum","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2010-08-09T20:49:35Z","receivedAt":"2010-08-09T20:49:35Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Matthieu Moy <Matthieu.Moy@imag.fr> writes:\n>\n>> The list of error messages was introduced as a structure, but an array\n>> indexed over an enum is more flexible, since it allows one to store a\n>> type of error message (index in the array) in a variable.\n>\n> Hmm, the only example of the advantage \"enum used as array index\" offers\n> that I can think of off the top of my head is that you can more easily\n> iterate over it.\n\nThe portion of my message you cite give another: the ability to store\nthe index easily in a variable (or to pass it as argument to a\nfunction). With a struct, you can easily pass field name to a macro,\nbut not to a function.\n\nThe other is that you can have multiple arrays indexed over the same\nenum, which is what we do later: we'll have to manage an array of list\nof rejected paths, with one list per error kind.\n\n(we'll also have to iterate over this second array)\n\nThe previous version of the patch serie was using both a struct and an\nenum + array, and I wanted to get rid of that.\n\n> If you are to change them to enum, I would actually suggest renaming them\n> a bit more to make them stand out.  Perhaps spell them all in caps,\n> perhaps have them share the same short prefix (UTEM_ - unpack trees error\n> messages), etc.\n\nI'd say just \"ERROR_*\" (UTEM seems hard to read, and won't be used\noften enough for people to remember it), but I'm fine with all caps +\nprefix, yes. A quick grep shows this is how the rest of Git does.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"147553","messageId":"vpq4of3se4v.fsf@bauges.imag.fr","threadId":"24680","inReplyTo":"7vhbj34kz8.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 2/5] merge-recursive: porcelain messages for checkout","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2010-08-09T20:52:00Z","receivedAt":"2010-08-09T20:52:00Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Matthieu Moy <Matthieu.Moy@imag.fr> writes:\n>\n>> +void set_porcelain_error_msgs(const char **msgs, const char *cmd)\n>>  {\n>> +\tconst char *msg;\n>> +\tchar *tmp;\n>> +\tconst char *cmd2 = strcmp(cmd, \"checkout\") ? cmd : \"switch branches\";\n>\n> This may have implications to the later i18n effort, but lets ignore it\n> for now.  I don't think it will be too bad.\n\nNote that the ? : construct just allows factoring out two messages,\nbut one can easily distinguish all cases and give the complete error\nmessage in the source (we don't care about performance/memory here,\nit's a one-time thing).\n\nSo, yes, that's easily fixable later if it causes problems to i18n.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"147558","messageId":"AANLkTikQAduZ3=1U6ObzLKdY0pomMBm2BGbq+Xyfmggh@mail.gmail.com","threadId":"24680","inReplyTo":"vpq4of3se4v.fsf@bauges.imag.fr","subject":"Re: [PATCH 2/5] merge-recursive: porcelain messages for checkout","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2010-08-09T21:15:00Z","receivedAt":"2010-08-09T21:15:00Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"On Mon, Aug 9, 2010 at 20:52, Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>> Matthieu Moy <Matthieu.Moy@imag.fr> writes:\n>>\n>>> +void set_porcelain_error_msgs(const char **msgs, const char *cmd)\n>>>  {\n>>> +    const char *msg;\n>>> +    char *tmp;\n>>> +    const char *cmd2 = strcmp(cmd, \"checkout\") ? cmd : \"switch branches\";\n>>\n>> This may have implications to the later i18n effort, but lets ignore it\n>> for now.  I don't think it will be too bad.\n>\n> Note that the ? : construct just allows factoring out two messages,\n> but one can easily distinguish all cases and give the complete error\n> message in the source (we don't care about performance/memory here,\n> it's a one-time thing).\n>\n> So, yes, that's easily fixable later if it causes problems to i18n.\n\nYeah, this is a drop in the lego ocean that is Git's source\ncode. Don't worry about it, it can be fixed later.\n\nYou can stop reading at this point.\n\nFWIW \"lego\" is the i18n term used for printf(\"messages %s assembled\nlike this\", \"that are\"). E.g.:\n\n    printf(_(\"It's a nice %s\"), is_evening() ? _(\"evening\") : _(\"day\"));\n\nThings like that will cause trouble for e.g. French where it's \"C'est\nun soirée formidable.\" or \"C'est un jour formidable.\" (I hope I got\nthat right). You can move the %s around, but it's still hard to\ntranslate stuff like that, especially as translation programs often\ndisplay strings independently.\n\nIt's better to have independent strings like _(\"It's a nice evening\")\nand _(\"It's a nice day\") instead, even if it leads to duplication.\n\nI'll be submitting a lot of patches to fix things like this once the\ni18n series gets merged.\n\nHave fun.\n"},{"id":"147736","messageId":"1281515888-31089-1-git-send-email-Matthieu.Moy@imag.fr","threadId":"24680","inReplyTo":"vpq8w4fse8w.fsf@bauges.imag.fr","subject":"[PATCH 1/5 v2] Turn unpack_trees_options.msgs into an array + enum","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2010-08-11T08:38:04Z","receivedAt":"2010-08-11T08:38:04Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"The list of error messages was introduced as a structure, but an array\nindexed over an enum is more flexible, since it allows one to store a\ntype of error message (index in the array) in a variable.\n\nThis change needs to rename would_lose_untracked ->\nwould_lose_untracked_file to avoid a clash with the function\nwould_lose_untracked in merge-recursive.c.\n\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\nMatthieu Moy <Matthieu.Moy@grenoble-inp.fr> writes:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>> If you are to change them to enum, I would actually suggest renaming them\n>> a bit more to make them stand out.  Perhaps spell them all in caps,\n>> perhaps have them share the same short prefix (UTEM_ - unpack trees error\n>> messages), etc.\n>\n> I'd say just \"ERROR_*\" (UTEM seems hard to read, and won't be used\n> often enough for people to remember it), but I'm fine with all caps +\n> prefix, yes. A quick grep shows this is how the rest of Git does.\n\nSo, here it is. BTW, git filter-branch rocks ;-).\n\nDoing this change, I noticed a comment about \"git checkout\" overriding\nonly the \"not_uptodate_file\" case only, which I fixed, but there's no\nother change compared to v1.\n\n builtin/checkout.c |    2 +-\n builtin/merge.c    |    2 +-\n merge-recursive.c  |   29 +++++++++++------------------\n merge-recursive.h  |    4 ++--\n unpack-trees.c     |   42 +++++++++++++++++++++---------------------\n unpack-trees.h     |   19 ++++++++++---------\n 6 files changed, 46 insertions(+), 52 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 1994be9..7d1706e 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -373,7 +373,7 @@ static int merge_working_tree(struct checkout_opts *opts,\n \t\ttopts.src_index = &the_index;\n \t\ttopts.dst_index = &the_index;\n \n-\t\ttopts.msgs.not_uptodate_file = \"You have local changes to '%s'; cannot switch branches.\";\n+\t\ttopts.msgs[ERROR_NOT_UPTODATE_FILE] = \"You have local changes to '%s'; cannot switch branches.\";\n \n \t\trefresh_cache(REFRESH_QUIET);\n \ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 37ce4f5..115a288 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -704,7 +704,7 @@ int checkout_fast_forward(const unsigned char *head, const unsigned char *remote\n \topts.verbose_update = 1;\n \topts.merge = 1;\n \topts.fn = twoway_merge;\n-\topts.msgs = get_porcelain_error_msgs();\n+\tset_porcelain_error_msgs(opts.msgs);\n \n \ttrees[nr_trees] = parse_tree_indirect(head);\n \tif (!trees[nr_trees++])\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex fb6aa4a..d3bd963 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -185,7 +185,7 @@ static int git_merge_trees(int index_only,\n \topts.fn = threeway_merge;\n \topts.src_index = &the_index;\n \topts.dst_index = &the_index;\n-\topts.msgs = get_porcelain_error_msgs();\n+\tset_porcelain_error_msgs(opts.msgs);\n \n \tinit_tree_desc_from_tree(t+0, common);\n \tinit_tree_desc_from_tree(t+1, head);\n@@ -1178,26 +1178,19 @@ static int process_entry(struct merge_options *o,\n \treturn clean_merge;\n }\n \n-struct unpack_trees_error_msgs get_porcelain_error_msgs(void)\n+void set_porcelain_error_msgs(const char **msgs)\n {\n-\tstruct unpack_trees_error_msgs msgs = {\n-\t\t/* would_overwrite */\n-\t\t\"Your local changes to '%s' would be overwritten by merge.  Aborting.\",\n-\t\t/* not_uptodate_file */\n-\t\t\"Your local changes to '%s' would be overwritten by merge.  Aborting.\",\n-\t\t/* not_uptodate_dir */\n-\t\t\"Updating '%s' would lose untracked files in it.  Aborting.\",\n-\t\t/* would_lose_untracked */\n-\t\t\"Untracked working tree file '%s' would be %s by merge.  Aborting\",\n-\t\t/* bind_overlap -- will not happen here */\n-\t\tNULL,\n-\t};\n-\tif (advice_commit_before_merge) {\n-\t\tmsgs.would_overwrite = msgs.not_uptodate_file =\n+\tif (advice_commit_before_merge)\n+\t\tmsgs[ERROR_WOULD_OVERWRITE] = msgs[ERROR_NOT_UPTODATE_FILE] =\n \t\t\t\"Your local changes to '%s' would be overwritten by merge.  Aborting.\\n\"\n \t\t\t\"Please, commit your changes or stash them before you can merge.\";\n-\t}\n-\treturn msgs;\n+\telse\n+\t\tmsgs[ERROR_WOULD_OVERWRITE] = msgs[ERROR_NOT_UPTODATE_FILE] =\n+\t\t\t\"Your local changes to '%s' would be overwritten by merge.  Aborting.\";\n+\tmsgs[ERROR_NOT_UPTODATE_DIR] =\n+\t\t\"Updating '%s' would lose untracked files in it.  Aborting.\";\n+\tmsgs[ERROR_WOULD_LOSE_UNTRACKED] =\n+\t\t\"Untracked working tree file '%s' would be %s by merge.  Aborting\";\n }\n \n int merge_trees(struct merge_options *o,\ndiff --git a/merge-recursive.h b/merge-recursive.h\nindex b831293..8412db8 100644\n--- a/merge-recursive.h\n+++ b/merge-recursive.h\n@@ -23,8 +23,8 @@ struct merge_options {\n \tstruct string_list current_directory_set;\n };\n \n-/* Return a list of user-friendly error messages to be used by merge */\n-struct unpack_trees_error_msgs get_porcelain_error_msgs(void);\n+/* Sets the list of user-friendly error messages to be used by merge */\n+void set_porcelain_error_msgs(const char **msgs);\n \n /* merge_trees() but with recursive ancestor consolidation */\n int merge_recursive(struct merge_options *o,\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 8cf0da3..304e59a 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -13,37 +13,37 @@\n  * Error messages expected by scripts out of plumbing commands such as\n  * read-tree.  Non-scripted Porcelain is not required to use these messages\n  * and in fact are encouraged to reword them to better suit their particular\n- * situation better.  See how \"git checkout\" replaces not_uptodate_file to\n+ * situation better.  See how \"git checkout\" replaces ERROR_NOT_UPTODATE_FILE to\n  * explain why it does not allow switching between branches when you have\n  * local changes, for example.\n  */\n-static struct unpack_trees_error_msgs unpack_plumbing_errors = {\n-\t/* would_overwrite */\n+const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = {\n+\t/* ERROR_WOULD_OVERWRITE */\n \t\"Entry '%s' would be overwritten by merge. Cannot merge.\",\n \n-\t/* not_uptodate_file */\n+\t/* ERROR_NOT_UPTODATE_FILE */\n \t\"Entry '%s' not uptodate. Cannot merge.\",\n \n-\t/* not_uptodate_dir */\n+\t/* ERROR_NOT_UPTODATE_DIR */\n \t\"Updating '%s' would lose untracked files in it\",\n \n-\t/* would_lose_untracked */\n+\t/* ERROR_WOULD_LOSE_UNTRACKED */\n \t\"Untracked working tree file '%s' would be %s by merge.\",\n \n-\t/* bind_overlap */\n+\t/* ERROR_BIND_OVERLAP */\n \t\"Entry '%s' overlaps with '%s'.  Cannot bind.\",\n \n-\t/* sparse_not_uptodate_file */\n+\t/* ERROR_SPARSE_NOT_UPTODATE_FILE */\n \t\"Entry '%s' not uptodate. Cannot update sparse checkout.\",\n \n-\t/* would_lose_orphaned */\n+\t/* ERROR_WOULD_LOSE_ORPHANED */\n \t\"Working tree file '%s' would be %s by sparse checkout update.\",\n };\n \n-#define ERRORMSG(o,fld) \\\n-\t( ((o) && (o)->msgs.fld) \\\n-\t? ((o)->msgs.fld) \\\n-\t: (unpack_plumbing_errors.fld) )\n+#define ERRORMSG(o,type) \\\n+\t( ((o) && (o)->msgs[(type)]) \\\n+\t  ? ((o)->msgs[(type)])      \\\n+\t  : (unpack_plumbing_errors[(type)]) )\n \n static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,\n \tunsigned int set, unsigned int clear)\n@@ -838,7 +838,7 @@ return_failed:\n \n static int reject_merge(struct cache_entry *ce, struct unpack_trees_options *o)\n {\n-\treturn error(ERRORMSG(o, would_overwrite), ce->name);\n+\treturn error(ERRORMSG(o, ERROR_WOULD_OVERWRITE), ce->name);\n }\n \n static int same(struct cache_entry *a, struct cache_entry *b)\n@@ -893,13 +893,13 @@ static int verify_uptodate(struct cache_entry *ce,\n {\n \tif (!o->skip_sparse_checkout && will_have_skip_worktree(ce, o))\n \t\treturn 0;\n-\treturn verify_uptodate_1(ce, o, ERRORMSG(o, not_uptodate_file));\n+\treturn verify_uptodate_1(ce, o, ERRORMSG(o, ERROR_NOT_UPTODATE_FILE));\n }\n \n static int verify_uptodate_sparse(struct cache_entry *ce,\n \t\t\t\t  struct unpack_trees_options *o)\n {\n-\treturn verify_uptodate_1(ce, o, ERRORMSG(o, sparse_not_uptodate_file));\n+\treturn verify_uptodate_1(ce, o, ERRORMSG(o, ERROR_SPARSE_NOT_UPTODATE_FILE));\n }\n \n static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_options *o)\n@@ -986,7 +986,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,\n \ti = read_directory(&d, pathbuf, namelen+1, NULL);\n \tif (i)\n \t\treturn o->gently ? -1 :\n-\t\t\terror(ERRORMSG(o, not_uptodate_dir), ce->name);\n+\t\t\terror(ERRORMSG(o, ERROR_NOT_UPTODATE_DIR), ce->name);\n \tfree(pathbuf);\n \treturn cnt;\n }\n@@ -1068,7 +1068,7 @@ static int verify_absent_1(struct cache_entry *ce, const char *action,\n \t\t}\n \n \t\treturn o->gently ? -1 :\n-\t\t\terror(ERRORMSG(o, would_lose_untracked), ce->name, action);\n+\t\t\terror(ERRORMSG(o, ERROR_WOULD_LOSE_UNTRACKED), ce->name, action);\n \t}\n \treturn 0;\n }\n@@ -1077,13 +1077,13 @@ static int verify_absent(struct cache_entry *ce, const char *action,\n {\n \tif (!o->skip_sparse_checkout && will_have_skip_worktree(ce, o))\n \t\treturn 0;\n-\treturn verify_absent_1(ce, action, o, ERRORMSG(o, would_lose_untracked));\n+\treturn verify_absent_1(ce, action, o, ERRORMSG(o, ERROR_WOULD_LOSE_UNTRACKED));\n }\n \n static int verify_absent_sparse(struct cache_entry *ce, const char *action,\n \t\t\t struct unpack_trees_options *o)\n {\n-\treturn verify_absent_1(ce, action, o, ERRORMSG(o, would_lose_orphaned));\n+\treturn verify_absent_1(ce, action, o, ERRORMSG(o, ERROR_WOULD_LOSE_ORPHANED));\n }\n \n static int merged_entry(struct cache_entry *merge, struct cache_entry *old,\n@@ -1412,7 +1412,7 @@ int bind_merge(struct cache_entry **src,\n \t\t\t     o->merge_size);\n \tif (a && old)\n \t\treturn o->gently ? -1 :\n-\t\t\terror(ERRORMSG(o, bind_overlap), a->name, old->name);\n+\t\t\terror(ERRORMSG(o, ERROR_BIND_OVERLAP), a->name, old->name);\n \tif (!a)\n \t\treturn keep_entry(old, o);\n \telse\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex ef70eab..09e2252 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -9,14 +9,15 @@ struct exclude_list;\n typedef int (*merge_fn_t)(struct cache_entry **src,\n \t\tstruct unpack_trees_options *options);\n \n-struct unpack_trees_error_msgs {\n-\tconst char *would_overwrite;\n-\tconst char *not_uptodate_file;\n-\tconst char *not_uptodate_dir;\n-\tconst char *would_lose_untracked;\n-\tconst char *bind_overlap;\n-\tconst char *sparse_not_uptodate_file;\n-\tconst char *would_lose_orphaned;\n+enum unpack_trees_error_types {\n+\tERROR_WOULD_OVERWRITE = 0,\n+\tERROR_NOT_UPTODATE_FILE,\n+\tERROR_NOT_UPTODATE_DIR,\n+\tERROR_WOULD_LOSE_UNTRACKED,\n+\tERROR_BIND_OVERLAP,\n+\tERROR_SPARSE_NOT_UPTODATE_FILE,\n+\tERROR_WOULD_LOSE_ORPHANED,\n+\tNB_UNPACK_TREES_ERROR_TYPES\n };\n \n struct unpack_trees_options {\n@@ -38,7 +39,7 @@ struct unpack_trees_options {\n \tint cache_bottom;\n \tstruct dir_struct *dir;\n \tmerge_fn_t fn;\n-\tstruct unpack_trees_error_msgs msgs;\n+\tconst char *msgs[NB_UNPACK_TREES_ERROR_TYPES];\n \n \tint head_idx;\n \tint merge_size;\n-- \n1.7.2.1.52.g95e25.dirty\n"},{"id":"147741","messageId":"1281515888-31089-2-git-send-email-Matthieu.Moy@imag.fr","threadId":"24680","inReplyTo":"vpq8w4fse8w.fsf@bauges.imag.fr","subject":"[PATCH 2/5 v2] merge-recursive: porcelain messages for checkout","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2010-08-11T08:38:05Z","receivedAt":"2010-08-11T08:38:05Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"From: Diane Gasselin <diane.gasselin@ensimag.imag.fr>\n\nA porcelain message was first added in checkout.c in the commit\n8ccba008 (Junio C Hamano, Sat May 17 21:03:49 2008, unpack-trees:\nallow Porcelain to give different error messages) to give better feedback\nin the case of merge errors.\n\nThis patch adapts the porcelain messages for the case of checkout\ninstead. This way, when having a checkout error, \"merge\" no longer\nappears in the error message.\n\nWhile we're there, we add an advice in the case of\nwould_lose_untracked_file.\n\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n builtin/checkout.c |    2 +-\n builtin/merge.c    |    2 +-\n merge-recursive.c  |   31 ++++++++++++++++++++++---------\n merge-recursive.h  |    7 +++++--\n unpack-trees.c     |    5 ++---\n 5 files changed, 31 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 7d1706e..22bf47c 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -373,7 +373,7 @@ static int merge_working_tree(struct checkout_opts *opts,\n \t\ttopts.src_index = &the_index;\n \t\ttopts.dst_index = &the_index;\n \n-\t\ttopts.msgs[ERROR_NOT_UPTODATE_FILE] = \"You have local changes to '%s'; cannot switch branches.\";\n+\t\tset_porcelain_error_msgs(topts.msgs, \"checkout\");\n \n \t\trefresh_cache(REFRESH_QUIET);\n \ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 115a288..de5a0f6 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -704,7 +704,7 @@ int checkout_fast_forward(const unsigned char *head, const unsigned char *remote\n \topts.verbose_update = 1;\n \topts.merge = 1;\n \topts.fn = twoway_merge;\n-\tset_porcelain_error_msgs(opts.msgs);\n+\tset_porcelain_error_msgs(opts.msgs, \"merge\");\n \n \ttrees[nr_trees] = parse_tree_indirect(head);\n \tif (!trees[nr_trees++])\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex d3bd963..b1e526b 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -185,7 +185,7 @@ static int git_merge_trees(int index_only,\n \topts.fn = threeway_merge;\n \topts.src_index = &the_index;\n \topts.dst_index = &the_index;\n-\tset_porcelain_error_msgs(opts.msgs);\n+\tset_porcelain_error_msgs(opts.msgs, \"merge\");\n \n \tinit_tree_desc_from_tree(t+0, common);\n \tinit_tree_desc_from_tree(t+1, head);\n@@ -1178,19 +1178,32 @@ static int process_entry(struct merge_options *o,\n \treturn clean_merge;\n }\n \n-void set_porcelain_error_msgs(const char **msgs)\n+void set_porcelain_error_msgs(const char **msgs, const char *cmd)\n {\n+\tconst char *msg;\n+\tchar *tmp;\n+\tconst char *cmd2 = strcmp(cmd, \"checkout\") ? cmd : \"switch branches\";\n \tif (advice_commit_before_merge)\n-\t\tmsgs[ERROR_WOULD_OVERWRITE] = msgs[ERROR_NOT_UPTODATE_FILE] =\n-\t\t\t\"Your local changes to '%s' would be overwritten by merge.  Aborting.\\n\"\n-\t\t\t\"Please, commit your changes or stash them before you can merge.\";\n+\t\tmsg = \"Your local changes to '%%s' would be overwritten by %s.  Aborting.\\n\"\n+\t\t\t\"Please, commit your changes or stash them before you can %s.\";\n \telse\n-\t\tmsgs[ERROR_WOULD_OVERWRITE] = msgs[ERROR_NOT_UPTODATE_FILE] =\n-\t\t\t\"Your local changes to '%s' would be overwritten by merge.  Aborting.\";\n+\t\tmsg = \"Your local changes to '%%s' would be overwritten by %s.  Aborting.\";\n+\ttmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 3);\n+\tsprintf(tmp, msg, cmd, cmd2);\n+\tmsgs[ERROR_WOULD_OVERWRITE] = tmp;\n+\tmsgs[ERROR_NOT_UPTODATE_FILE] = tmp;\n+\n \tmsgs[ERROR_NOT_UPTODATE_DIR] =\n \t\t\"Updating '%s' would lose untracked files in it.  Aborting.\";\n-\tmsgs[ERROR_WOULD_LOSE_UNTRACKED] =\n-\t\t\"Untracked working tree file '%s' would be %s by merge.  Aborting\";\n+\n+\tif (advice_commit_before_merge)\n+\t\tmsg = \"Untracked working tree file '%%s' would be %%s by %s.  Aborting\"\n+\t\t\t\"Please move or remove them before you can %s.\";\n+\telse\n+\t\tmsg = \"Untracked working tree file '%%s' would be %%s by %s.  Aborting\";\n+\ttmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 3);\n+\tsprintf(tmp, msg, cmd, cmd2);\n+\tmsgs[ERROR_WOULD_LOSE_UNTRACKED] = tmp;\n }\n \n int merge_trees(struct merge_options *o,\ndiff --git a/merge-recursive.h b/merge-recursive.h\nindex 8412db8..08f9850 100644\n--- a/merge-recursive.h\n+++ b/merge-recursive.h\n@@ -23,8 +23,11 @@ struct merge_options {\n \tstruct string_list current_directory_set;\n };\n \n-/* Sets the list of user-friendly error messages to be used by merge */\n-void set_porcelain_error_msgs(const char **msgs);\n+/*\n+ * Sets the list of user-friendly error messages to be used by the\n+ * command \"cmd\" (either merge or checkout)\n+ */\n+void set_porcelain_error_msgs(const char **msgs, const char *cmd);\n \n /* merge_trees() but with recursive ancestor consolidation */\n int merge_recursive(struct merge_options *o,\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 304e59a..e325831 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -13,9 +13,8 @@\n  * Error messages expected by scripts out of plumbing commands such as\n  * read-tree.  Non-scripted Porcelain is not required to use these messages\n  * and in fact are encouraged to reword them to better suit their particular\n- * situation better.  See how \"git checkout\" replaces ERROR_NOT_UPTODATE_FILE to\n- * explain why it does not allow switching between branches when you have\n- * local changes, for example.\n+ * situation better.  See how \"git checkout\" and \"git merge\" replaces\n+ * them using set_porcelain_error_msgs(), for example.\n  */\n const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = {\n \t/* ERROR_WOULD_OVERWRITE */\n-- \n1.7.2.1.52.g95e25.dirty\n"},{"id":"147738","messageId":"1281515888-31089-3-git-send-email-Matthieu.Moy@imag.fr","threadId":"24680","inReplyTo":"vpq8w4fse8w.fsf@bauges.imag.fr","subject":"[PATCH 3/5 v2] merge-recursive: distinguish \"removed\" and \"overwritten\" messages","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2010-08-11T08:38:06Z","receivedAt":"2010-08-11T08:38:06Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"To limit the number of possible error messages, the error messages for\nthe case would_lose_untracked_file and would_lose_orphaned in\nunpack_trees_options.msgs were handled with a single string,\nparameterized by an action string (\"overwritten\" or \"removed\").\n\nInstead, we consider them as two different cases, with unparameterized\nstring. This will make it easier to make separate lists sorted by error\ntypes later.\n\nOnly the bind_overlap case still takes two %s parameters, but that's\nunavoidable.\n\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n merge-recursive.c |   13 ++++++----\n unpack-trees.c    |   64 ++++++++++++++++++++++++++++++++--------------------\n unpack-trees.h    |    6 +++-\n 3 files changed, 51 insertions(+), 32 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex b1e526b..697d948 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1197,13 +1197,16 @@ void set_porcelain_error_msgs(const char **msgs, const char *cmd)\n \t\t\"Updating '%s' would lose untracked files in it.  Aborting.\";\n \n \tif (advice_commit_before_merge)\n-\t\tmsg = \"Untracked working tree file '%%s' would be %%s by %s.  Aborting\"\n+\t\tmsg = \"Untracked working tree file '%%s' would be %s by %s.  Aborting\"\n \t\t\t\"Please move or remove them before you can %s.\";\n \telse\n-\t\tmsg = \"Untracked working tree file '%%s' would be %%s by %s.  Aborting\";\n-\ttmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 3);\n-\tsprintf(tmp, msg, cmd, cmd2);\n-\tmsgs[ERROR_WOULD_LOSE_UNTRACKED] = tmp;\n+\t\tmsg = \"Untracked working tree file '%%s' would be %s by %s.  Aborting\";\n+\ttmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(\"removed\") + strlen(cmd2) - 4);\n+\tsprintf(tmp, msg, \"removed\", cmd, cmd2);\n+\tmsgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = tmp;\n+\ttmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(\"overwritten\") + strlen(cmd2) - 4);\n+\tsprintf(tmp, msg, \"overwritten\", cmd, cmd2);\n+\tmsgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = tmp;\n }\n \n int merge_trees(struct merge_options *o,\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex e325831..342f5ec 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -26,8 +26,11 @@ const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = {\n \t/* ERROR_NOT_UPTODATE_DIR */\n \t\"Updating '%s' would lose untracked files in it\",\n \n-\t/* ERROR_WOULD_LOSE_UNTRACKED */\n-\t\"Untracked working tree file '%s' would be %s by merge.\",\n+\t/* ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN */\n+\t\"Untracked working tree file '%s' would be overwritten by merge.\",\n+\n+\t/* ERROR_WOULD_LOSE_UNTRACKED_REMOVED */\n+\t\"Untracked working tree file '%s' would be removed by merge.\",\n \n \t/* ERROR_BIND_OVERLAP */\n \t\"Entry '%s' overlaps with '%s'.  Cannot bind.\",\n@@ -35,8 +38,11 @@ const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = {\n \t/* ERROR_SPARSE_NOT_UPTODATE_FILE */\n \t\"Entry '%s' not uptodate. Cannot update sparse checkout.\",\n \n-\t/* ERROR_WOULD_LOSE_ORPHANED */\n-\t\"Working tree file '%s' would be %s by sparse checkout update.\",\n+\t/* ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN */\n+\t\"Working tree file '%s' would be overwritten by sparse checkout update.\",\n+\n+\t/* ERROR_WOULD_LOSE_ORPHANED_REMOVED */\n+\t\"Working tree file '%s' would be removed by sparse checkout update.\",\n };\n \n #define ERRORMSG(o,type) \\\n@@ -131,7 +137,7 @@ static int check_updates(struct unpack_trees_options *o)\n }\n \n static int verify_uptodate_sparse(struct cache_entry *ce, struct unpack_trees_options *o);\n-static int verify_absent_sparse(struct cache_entry *ce, const char *action, struct unpack_trees_options *o);\n+static int verify_absent_sparse(struct cache_entry *ce, enum unpack_trees_error_types, struct unpack_trees_options *o);\n \n static int will_have_skip_worktree(const struct cache_entry *ce, struct unpack_trees_options *o)\n {\n@@ -174,7 +180,7 @@ static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_opt\n \t\tce->ce_flags |= CE_WT_REMOVE;\n \t}\n \tif (was_skip_worktree && !ce_skip_worktree(ce)) {\n-\t\tif (verify_absent_sparse(ce, \"overwritten\", o))\n+\t\tif (verify_absent_sparse(ce, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o))\n \t\t\treturn -1;\n \t\tce->ce_flags |= CE_UPDATE;\n \t}\n@@ -859,7 +865,7 @@ static int same(struct cache_entry *a, struct cache_entry *b)\n  */\n static int verify_uptodate_1(struct cache_entry *ce,\n \t\t\t\t   struct unpack_trees_options *o,\n-\t\t\t\t   const char *error_msg)\n+\t\t\t\t   enum unpack_trees_error_types error_type)\n {\n \tstruct stat st;\n \n@@ -884,7 +890,7 @@ static int verify_uptodate_1(struct cache_entry *ce,\n \tif (errno == ENOENT)\n \t\treturn 0;\n \treturn o->gently ? -1 :\n-\t\terror(error_msg, ce->name);\n+\t\terror(ERRORMSG(o, error_type), ce->name);\n }\n \n static int verify_uptodate(struct cache_entry *ce,\n@@ -892,13 +898,13 @@ static int verify_uptodate(struct cache_entry *ce,\n {\n \tif (!o->skip_sparse_checkout && will_have_skip_worktree(ce, o))\n \t\treturn 0;\n-\treturn verify_uptodate_1(ce, o, ERRORMSG(o, ERROR_NOT_UPTODATE_FILE));\n+\treturn verify_uptodate_1(ce, o, ERROR_NOT_UPTODATE_FILE);\n }\n \n static int verify_uptodate_sparse(struct cache_entry *ce,\n \t\t\t\t  struct unpack_trees_options *o)\n {\n-\treturn verify_uptodate_1(ce, o, ERRORMSG(o, ERROR_SPARSE_NOT_UPTODATE_FILE));\n+\treturn verify_uptodate_1(ce, o, ERROR_SPARSE_NOT_UPTODATE_FILE);\n }\n \n static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_options *o)\n@@ -914,13 +920,15 @@ static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_optio\n  * Currently, git does not checkout subprojects during a superproject\n  * checkout, so it is not going to overwrite anything.\n  */\n-static int verify_clean_submodule(struct cache_entry *ce, const char *action,\n+static int verify_clean_submodule(struct cache_entry *ce,\n+\t\t\t\t      enum unpack_trees_error_types error_type,\n \t\t\t\t      struct unpack_trees_options *o)\n {\n \treturn 0;\n }\n \n-static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,\n+static int verify_clean_subdirectory(struct cache_entry *ce,\n+\t\t\t\t      enum unpack_trees_error_types error_type,\n \t\t\t\t      struct unpack_trees_options *o)\n {\n \t/*\n@@ -941,7 +949,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,\n \t\t */\n \t\tif (!hashcmp(sha1, ce->sha1))\n \t\t\treturn 0;\n-\t\treturn verify_clean_submodule(ce, action, o);\n+\t\treturn verify_clean_submodule(ce, error_type, o);\n \t}\n \n \t/*\n@@ -1010,9 +1018,9 @@ static int icase_exists(struct unpack_trees_options *o, struct cache_entry *dst,\n  * We do not want to remove or overwrite a working tree file that\n  * is not tracked, unless it is ignored.\n  */\n-static int verify_absent_1(struct cache_entry *ce, const char *action,\n-\t\t\t\t struct unpack_trees_options *o,\n-\t\t\t\t const char *error_msg)\n+static int verify_absent_1(struct cache_entry *ce,\n+\t\t\t\t enum unpack_trees_error_types error_type,\n+\t\t\t\t struct unpack_trees_options *o)\n {\n \tstruct stat st;\n \n@@ -1050,7 +1058,7 @@ static int verify_absent_1(struct cache_entry *ce, const char *action,\n \t\t\t * files that are in \"foo/\" we would lose\n \t\t\t * them.\n \t\t\t */\n-\t\t\tif (verify_clean_subdirectory(ce, action, o) < 0)\n+\t\t\tif (verify_clean_subdirectory(ce, error_type, o) < 0)\n \t\t\t\treturn -1;\n \t\t\treturn 0;\n \t\t}\n@@ -1067,22 +1075,28 @@ static int verify_absent_1(struct cache_entry *ce, const char *action,\n \t\t}\n \n \t\treturn o->gently ? -1 :\n-\t\t\terror(ERRORMSG(o, ERROR_WOULD_LOSE_UNTRACKED), ce->name, action);\n+\t\t\terror(ERRORMSG(o, error_type), ce->name);\n \t}\n \treturn 0;\n }\n-static int verify_absent(struct cache_entry *ce, const char *action,\n+static int verify_absent(struct cache_entry *ce,\n+\t\t\t enum unpack_trees_error_types error_type,\n \t\t\t struct unpack_trees_options *o)\n {\n \tif (!o->skip_sparse_checkout && will_have_skip_worktree(ce, o))\n \t\treturn 0;\n-\treturn verify_absent_1(ce, action, o, ERRORMSG(o, ERROR_WOULD_LOSE_UNTRACKED));\n+\treturn verify_absent_1(ce, error_type, o);\n }\n \n-static int verify_absent_sparse(struct cache_entry *ce, const char *action,\n+static int verify_absent_sparse(struct cache_entry *ce,\n+\t\t\t enum unpack_trees_error_types error_type,\n \t\t\t struct unpack_trees_options *o)\n {\n-\treturn verify_absent_1(ce, action, o, ERRORMSG(o, ERROR_WOULD_LOSE_ORPHANED));\n+\tenum unpack_trees_error_types orphaned_error = error_type;\n+\tif (orphaned_error == ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN)\n+\t\torphaned_error = ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN;\n+\n+\treturn verify_absent_1(ce, orphaned_error, o);\n }\n \n static int merged_entry(struct cache_entry *merge, struct cache_entry *old,\n@@ -1091,7 +1105,7 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,\n \tint update = CE_UPDATE;\n \n \tif (!old) {\n-\t\tif (verify_absent(merge, \"overwritten\", o))\n+\t\tif (verify_absent(merge, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o))\n \t\t\treturn -1;\n \t\tinvalidate_ce_path(merge, o);\n \t} else if (!(old->ce_flags & CE_CONFLICTED)) {\n@@ -1129,7 +1143,7 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,\n {\n \t/* Did it exist in the index? */\n \tif (!old) {\n-\t\tif (verify_absent(ce, \"removed\", o))\n+\t\tif (verify_absent(ce, ERROR_WOULD_LOSE_UNTRACKED_REMOVED, o))\n \t\t\treturn -1;\n \t\treturn 0;\n \t}\n@@ -1278,7 +1292,7 @@ int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)\n \t\t\tif (index)\n \t\t\t\treturn deleted_entry(index, index, o);\n \t\t\tif (ce && !head_deleted) {\n-\t\t\t\tif (verify_absent(ce, \"removed\", o))\n+\t\t\t\tif (verify_absent(ce, ERROR_WOULD_LOSE_UNTRACKED_REMOVED, o))\n \t\t\t\t\treturn -1;\n \t\t\t}\n \t\t\treturn 0;\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex 09e2252..8be6b3c 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -13,10 +13,12 @@ enum unpack_trees_error_types {\n \tERROR_WOULD_OVERWRITE = 0,\n \tERROR_NOT_UPTODATE_FILE,\n \tERROR_NOT_UPTODATE_DIR,\n-\tERROR_WOULD_LOSE_UNTRACKED,\n+\tERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN,\n+\tERROR_WOULD_LOSE_UNTRACKED_REMOVED,\n \tERROR_BIND_OVERLAP,\n \tERROR_SPARSE_NOT_UPTODATE_FILE,\n-\tERROR_WOULD_LOSE_ORPHANED,\n+\tERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN,\n+\tERROR_WOULD_LOSE_ORPHANED_REMOVED,\n \tNB_UNPACK_TREES_ERROR_TYPES\n };\n \n-- \n1.7.2.1.52.g95e25.dirty\n"},{"id":"147740","messageId":"1281515888-31089-4-git-send-email-Matthieu.Moy@imag.fr","threadId":"24680","inReplyTo":"vpq8w4fse8w.fsf@bauges.imag.fr","subject":"[PATCH 4/5 v2] unpack_trees: group error messages by type","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2010-08-11T08:38:07Z","receivedAt":"2010-08-11T08:38:07Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"When an error is encountered, it calls add_rejected_file() which either\n- directly displays the error message and stops if in plumbing mode\n  (i.e. if show_all_errors is not initialized at 1)\n- or stores it so that it will be displayed at the end with display_error_msgs(),\n\nStoring the files by error type permits to have a list of files for\nwhich there is the same error instead of having a serie of almost\nidentical errors.\n\nAs each bind_overlap error combines a file and an old file, a list cannot be\ndone, therefore, theses errors are not stored but directly displayed.\n\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n Documentation/technical/api-tree-walking.txt |    2 +\n builtin/checkout.c                           |    1 +\n builtin/merge.c                              |    1 +\n merge-recursive.c                            |   25 ++++++--\n t/t3030-merge-recursive.sh                   |    2 +-\n t/t3400-rebase.sh                            |    3 +-\n t/t3404-rebase-interactive.sh                |    3 +-\n tree-walk.c                                  |   11 +++-\n tree-walk.h                                  |    1 +\n unpack-trees.c                               |   78 ++++++++++++++++++++++++-\n unpack-trees.h                               |   13 ++++-\n 11 files changed, 123 insertions(+), 17 deletions(-)\n\ndiff --git a/Documentation/technical/api-tree-walking.txt b/Documentation/technical/api-tree-walking.txt\nindex 55b7286..14af37c 100644\n--- a/Documentation/technical/api-tree-walking.txt\n+++ b/Documentation/technical/api-tree-walking.txt\n@@ -42,6 +42,8 @@ information.\n \n * `data` can be anything the `fn` callback would want to use.\n \n+* `show_all_errors` tells whether to stop at the first error or not.\n+\n Initializing\n ------------\n \ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 22bf47c..894bb84 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -392,6 +392,7 @@ static int merge_working_tree(struct checkout_opts *opts,\n \t\ttopts.dir = xcalloc(1, sizeof(*topts.dir));\n \t\ttopts.dir->flags |= DIR_SHOW_IGNORED;\n \t\ttopts.dir->exclude_per_dir = \".gitignore\";\n+\t\ttopts.show_all_errors = 1;\n \t\ttree = parse_tree_indirect(old->commit ?\n \t\t\t\t\t   old->commit->object.sha1 :\n \t\t\t\t\t   (unsigned char *)EMPTY_TREE_SHA1_BIN);\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex de5a0f6..1bcf7fd 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -704,6 +704,7 @@ int checkout_fast_forward(const unsigned char *head, const unsigned char *remote\n \topts.verbose_update = 1;\n \topts.merge = 1;\n \topts.fn = twoway_merge;\n+\topts.show_all_errors = 1;\n \tset_porcelain_error_msgs(opts.msgs, \"merge\");\n \n \ttrees[nr_trees] = parse_tree_indirect(head);\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 697d948..10392d9 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1184,29 +1184,42 @@ void set_porcelain_error_msgs(const char **msgs, const char *cmd)\n \tchar *tmp;\n \tconst char *cmd2 = strcmp(cmd, \"checkout\") ? cmd : \"switch branches\";\n \tif (advice_commit_before_merge)\n-\t\tmsg = \"Your local changes to '%%s' would be overwritten by %s.  Aborting.\\n\"\n+\t\tmsg = \"Your local changes to the following files would be overwritten by %s:\\n%%s\"\n \t\t\t\"Please, commit your changes or stash them before you can %s.\";\n \telse\n-\t\tmsg = \"Your local changes to '%%s' would be overwritten by %s.  Aborting.\";\n-\ttmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 3);\n+\t\tmsg = \"Your local changes to the following files would be overwritten by %s:\\n%%s\";\n+\ttmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 2);\n \tsprintf(tmp, msg, cmd, cmd2);\n \tmsgs[ERROR_WOULD_OVERWRITE] = tmp;\n \tmsgs[ERROR_NOT_UPTODATE_FILE] = tmp;\n \n \tmsgs[ERROR_NOT_UPTODATE_DIR] =\n-\t\t\"Updating '%s' would lose untracked files in it.  Aborting.\";\n+\t\t\"Updating the following directories would lose untracked files in it:\\n%s\";\n \n \tif (advice_commit_before_merge)\n-\t\tmsg = \"Untracked working tree file '%%s' would be %s by %s.  Aborting\"\n+\t\tmsg = \"The following untracked working tree files would be %s by %s:\\n%%s\"\n \t\t\t\"Please move or remove them before you can %s.\";\n \telse\n-\t\tmsg = \"Untracked working tree file '%%s' would be %s by %s.  Aborting\";\n+\t\tmsg = \"The following untracked working tree files would be %s by %s:\\n%%s\";\n \ttmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(\"removed\") + strlen(cmd2) - 4);\n \tsprintf(tmp, msg, \"removed\", cmd, cmd2);\n \tmsgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = tmp;\n \ttmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(\"overwritten\") + strlen(cmd2) - 4);\n \tsprintf(tmp, msg, \"overwritten\", cmd, cmd2);\n \tmsgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = tmp;\n+\n+\t/*\n+\t * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we\n+\t * cannot easily display it as a list.\n+\t */\n+\tmsgs[ERROR_BIND_OVERLAP] = \"Entry '%s' overlaps with '%s'.  Cannot bind.\";\n+\n+\tmsgs[ERROR_SPARSE_NOT_UPTODATE_FILE] =\n+\t\t\"Cannot update sparse checkout: the following entries are not up-to-date:\\n%s\";\n+\tmsgs[ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN] =\n+\t\t\"The following Working tree files would be overwritten by sparse checkout update:\\n%s\";\n+\tmsgs[ERROR_WOULD_LOSE_ORPHANED_REMOVED] =\n+\t\t\"The following Working tree files would be removed by sparse checkout update:\\n%s\";\n }\n \n int merge_trees(struct merge_options *o,\ndiff --git a/t/t3030-merge-recursive.sh b/t/t3030-merge-recursive.sh\nindex d541544..efe2900 100755\n--- a/t/t3030-merge-recursive.sh\n+++ b/t/t3030-merge-recursive.sh\n@@ -294,7 +294,7 @@ test_expect_success 'fail if the index has unresolved entries' '\n \tgrep \"You have not concluded your merge\" out &&\n \trm -f .git/MERGE_HEAD &&\n \ttest_must_fail git merge \"$c5\" 2> out &&\n-\tgrep \"Your local changes to .* would be overwritten by merge.\" out\n+\tgrep \"Your local changes to the following files would be overwritten by merge:\" out\n '\n \n test_expect_success 'merge-recursive remove conflict' '\ndiff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh\nindex d98c7b5..45ef282 100755\n--- a/t/t3400-rebase.sh\n+++ b/t/t3400-rebase.sh\n@@ -129,7 +129,8 @@ test_expect_success 'rebase a single mode change' '\n test_expect_success 'Show verbose error when HEAD could not be detached' '\n      : > B &&\n      test_must_fail git rebase topic 2> output.err > output.out &&\n-     grep \"Untracked working tree file .B. would be overwritten\" output.err\n+     grep \"The following untracked working tree files would be overwritten by checkout:\" output.err &&\n+     grep B output.err\n '\n rm -f B\n \ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 9f03ce6..3af3f60 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -150,8 +150,9 @@ test_expect_success 'abort with error when new base cannot be checked out' '\n \tgit rm --cached file1 &&\n \tgit commit -m \"remove file in base\" &&\n \ttest_must_fail git rebase -i master > output 2>&1 &&\n-\tgrep \"Untracked working tree file .file1. would be overwritten\" \\\n+\tgrep \"The following untracked working tree files would be overwritten by checkout:\" \\\n \t\toutput &&\n+\tgrep \"file1\" output &&\n \t! test -d .git/rebase-merge &&\n \tgit reset --hard HEAD^\n '\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 67a9a0c..a9bbf4e 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -1,5 +1,6 @@\n #include \"cache.h\"\n #include \"tree-walk.h\"\n+#include \"unpack-trees.h\"\n #include \"tree.h\"\n \n static const char *get_mode(const char *str, unsigned int *modep)\n@@ -310,6 +311,7 @@ static void free_extended_entry(struct tree_desc_x *t)\n int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)\n {\n \tint ret = 0;\n+\tint error = 0;\n \tstruct name_entry *entry = xmalloc(n*sizeof(*entry));\n \tint i;\n \tstruct tree_desc_x *tx = xcalloc(n, sizeof(*tx));\n@@ -377,8 +379,11 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)\n \t\tif (!mask)\n \t\t\tbreak;\n \t\tret = info->fn(n, mask, dirmask, entry, info);\n-\t\tif (ret < 0)\n-\t\t\tbreak;\n+\t\tif (ret < 0) {\n+\t\t\terror = ret;\n+\t\t\tif (!info->show_all_errors)\n+\t\t\t\tbreak;\n+\t\t}\n \t\tmask &= ret;\n \t\tret = 0;\n \t\tfor (i = 0; i < n; i++)\n@@ -389,7 +394,7 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)\n \tfor (i = 0; i < n; i++)\n \t\tfree_extended_entry(tx + i);\n \tfree(tx);\n-\treturn ret;\n+\treturn error;\n }\n \n static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode)\ndiff --git a/tree-walk.h b/tree-walk.h\nindex 42110a4..88ea7e9 100644\n--- a/tree-walk.h\n+++ b/tree-walk.h\n@@ -45,6 +45,7 @@ struct traverse_info {\n \tunsigned long conflicts;\n \ttraverse_callback_t fn;\n \tvoid *data;\n+\tint show_all_errors;\n };\n \n int get_tree_entry(const unsigned char *, const char *, unsigned char *, unsigned *);\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 342f5ec..7b10f92 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -65,6 +65,73 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,\n }\n \n /*\n+ * add error messages on path <path>\n+ * corresponding to the type <e> with the message <msg>\n+ * indicating if it should be display in porcelain or not\n+ */\n+static int add_rejected_path(struct unpack_trees_options *o,\n+\t\t\t     enum unpack_trees_error_types e,\n+\t\t\t     const char *path)\n+{\n+\tstruct rejected_paths_list *newentry;\n+\tint porcelain = o && (o)->msgs[e];\n+\t/*\n+\t * simply display the given error message if in plumbing mode\n+\t */\n+\tif (!porcelain)\n+\t\to->show_all_errors = 0;\n+\tif (!o->show_all_errors)\n+\t\treturn error(ERRORMSG(o, e), path);\n+\n+\t/*\n+\t * Otherwise, insert in a list for future display by\n+\t * display_error_msgs()\n+\t */\n+\tnewentry = xmalloc(sizeof(struct rejected_paths_list));\n+\tnewentry->path = (char *)path;\n+\tnewentry->next = o->unpack_rejects[e];\n+\to->unpack_rejects[e] = newentry;\n+\treturn -1;\n+}\n+\n+/*\n+ * free all the structures allocated for the error <e>\n+ */\n+static void free_rejected_paths(struct unpack_trees_options *o,\n+\t\t\t\tenum unpack_trees_error_types e)\n+{\n+\twhile (o->unpack_rejects[e]) {\n+\t\tstruct rejected_paths_list *del = o->unpack_rejects[e];\n+\t\to->unpack_rejects[e] = o->unpack_rejects[e]->next;\n+\t\tfree(del);\n+\t}\n+\tfree(o->unpack_rejects[e]);\n+}\n+\n+/*\n+ * display all the error messages stored in a nice way\n+ */\n+static void display_error_msgs(struct unpack_trees_options *o)\n+{\n+\tint e;\n+\tint something_displayed = 0;\n+\tfor (e = 0; e < NB_UNPACK_TREES_ERROR_TYPES; e++) {\n+\t\tif (o->unpack_rejects[e]) {\n+\t\t\tstruct rejected_paths_list *rp;\n+\t\t\tstruct strbuf path = STRBUF_INIT;\n+\t\t\tsomething_displayed = 1;\n+\t\t\tfor (rp = o->unpack_rejects[e]; rp; rp = rp->next)\n+\t\t\t\tstrbuf_addf(&path, \"\\t%s\\n\", rp->path);\n+\t\t\terror(ERRORMSG(o, e), path.buf);\n+\t\t\tstrbuf_release(&path);\n+\t\t\tfree_rejected_paths(o, e);\n+\t\t}\n+\t}\n+\tif (something_displayed)\n+\t\tprintf(\"Aborting\\n\");\n+}\n+\n+/*\n  * Unlink the last component and schedule the leading directories for\n  * removal, such that empty directories get removed.\n  */\n@@ -755,6 +822,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \t\tsetup_traverse_info(&info, prefix);\n \t\tinfo.fn = unpack_callback;\n \t\tinfo.data = o;\n+\t\tinfo.show_all_errors = o->show_all_errors;\n \n \t\tif (o->prefix) {\n \t\t\t/*\n@@ -834,6 +902,8 @@ done:\n \treturn ret;\n \n return_failed:\n+\tif (o->show_all_errors)\n+\t\tdisplay_error_msgs(o);\n \tmark_all_ce_unused(o->src_index);\n \tret = unpack_failed(o, NULL);\n \tgoto done;\n@@ -843,7 +913,7 @@ return_failed:\n \n static int reject_merge(struct cache_entry *ce, struct unpack_trees_options *o)\n {\n-\treturn error(ERRORMSG(o, ERROR_WOULD_OVERWRITE), ce->name);\n+\treturn add_rejected_path(o, ERROR_WOULD_OVERWRITE, ce->name);\n }\n \n static int same(struct cache_entry *a, struct cache_entry *b)\n@@ -890,7 +960,7 @@ static int verify_uptodate_1(struct cache_entry *ce,\n \tif (errno == ENOENT)\n \t\treturn 0;\n \treturn o->gently ? -1 :\n-\t\terror(ERRORMSG(o, error_type), ce->name);\n+\t\tadd_rejected_path(o, error_type, ce->name);\n }\n \n static int verify_uptodate(struct cache_entry *ce,\n@@ -993,7 +1063,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce,\n \ti = read_directory(&d, pathbuf, namelen+1, NULL);\n \tif (i)\n \t\treturn o->gently ? -1 :\n-\t\t\terror(ERRORMSG(o, ERROR_NOT_UPTODATE_DIR), ce->name);\n+\t\t\tadd_rejected_path(o, ERROR_NOT_UPTODATE_DIR, ce->name);\n \tfree(pathbuf);\n \treturn cnt;\n }\n@@ -1075,7 +1145,7 @@ static int verify_absent_1(struct cache_entry *ce,\n \t\t}\n \n \t\treturn o->gently ? -1 :\n-\t\t\terror(ERRORMSG(o, error_type), ce->name);\n+\t\t\tadd_rejected_path(o, error_type, ce->name);\n \t}\n \treturn 0;\n }\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex 8be6b3c..6e049b0 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -22,6 +22,11 @@ enum unpack_trees_error_types {\n \tNB_UNPACK_TREES_ERROR_TYPES\n };\n \n+struct rejected_paths_list {\n+\tchar *path;\n+\tstruct rejected_paths_list *next;\n+};\n+\n struct unpack_trees_options {\n \tunsigned int reset,\n \t\t     merge,\n@@ -36,12 +41,18 @@ struct unpack_trees_options {\n \t\t     diff_index_cached,\n \t\t     debug_unpack,\n \t\t     skip_sparse_checkout,\n-\t\t     gently;\n+\t\t     gently,\n+\t\t     show_all_errors;\n \tconst char *prefix;\n \tint cache_bottom;\n \tstruct dir_struct *dir;\n \tmerge_fn_t fn;\n \tconst char *msgs[NB_UNPACK_TREES_ERROR_TYPES];\n+\t/*\n+\t * Store error messages in an array, each case\n+\t * corresponding to a error message type\n+\t */\n+\tstruct rejected_paths_list *unpack_rejects[NB_UNPACK_TREES_ERROR_TYPES];\n \n \tint head_idx;\n \tint merge_size;\n-- \n1.7.2.1.52.g95e25.dirty\n"},{"id":"147742","messageId":"1281515888-31089-5-git-send-email-Matthieu.Moy@imag.fr","threadId":"24680","inReplyTo":"vpq8w4fse8w.fsf@bauges.imag.fr","subject":"[PATCH 5/5 v2] t7609: test merge and checkout error messages","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@imag.fr","sentAt":"2010-08-11T08:38:08Z","receivedAt":"2010-08-11T08:38:08Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"From: Diane Gasselin <diane.gasselin@ensimag.imag.fr>\n\nTest porcelain and plumbing error messages for different types of errors\nof merge and checkout.\n\nSigned-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>\n---\n t/t7609-merge-co-error-msgs.sh |  125 ++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 125 insertions(+), 0 deletions(-)\n create mode 100755 t/t7609-merge-co-error-msgs.sh\n\ndiff --git a/t/t7609-merge-co-error-msgs.sh b/t/t7609-merge-co-error-msgs.sh\nnew file mode 100755\nindex 0000000..1a109b4\n--- /dev/null\n+++ b/t/t7609-merge-co-error-msgs.sh\n@@ -0,0 +1,125 @@\n+#!/bin/sh\n+\n+test_description='unpack-trees error messages'\n+\n+. ./test-lib.sh\n+\n+\n+test_expect_success 'setup' '\n+\techo one >one &&\n+\tgit add one &&\n+\tgit commit -a -m First &&\n+\n+\tgit checkout -b branch &&\n+\techo two >two &&\n+\techo three >three &&\n+\techo four >four &&\n+\techo five >five &&\n+\tgit add two three four five &&\n+\tgit commit -m Second &&\n+\n+\tgit checkout master &&\n+\techo other >two &&\n+\techo other >three &&\n+\techo other >four &&\n+\techo other >five\n+'\n+\n+cat >expect <<\\EOF\n+error: The following untracked working tree files would be overwritten by merge:\n+\ttwo\n+\tthree\n+\tfour\n+\tfive\n+Please move or remove them before you can merge.\n+EOF\n+\n+test_expect_success 'untracked files overwritten by merge' '\n+\ttest_must_fail git merge branch 2>out &&\n+\ttest_cmp out expect\n+'\n+\n+cat >expect <<\\EOF\n+error: Your local changes to the following files would be overwritten by merge:\n+\ttwo\n+\tthree\n+\tfour\n+Please, commit your changes or stash them before you can merge.\n+error: The following untracked working tree files would be overwritten by merge:\n+\tfive\n+Please move or remove them before you can merge.\n+EOF\n+\n+test_expect_success 'untracked files or local changes ovewritten by merge' '\n+\tgit add two &&\n+\tgit add three &&\n+\tgit add four &&\n+\ttest_must_fail git merge branch 2>out &&\n+\ttest_cmp out expect\n+'\n+\n+cat >expect <<\\EOF\n+error: Your local changes to the following files would be overwritten by checkout:\n+\trep/two\n+\trep/one\n+Please, commit your changes or stash them before you can switch branches.\n+EOF\n+\n+test_expect_success 'cannot switch branches because of local changes' '\n+\tgit add five &&\n+\tmkdir rep &&\n+\techo one >rep/one &&\n+\techo two >rep/two &&\n+\tgit add rep/one rep/two &&\n+\tgit commit -m Fourth &&\n+\tgit checkout master &&\n+\techo uno >rep/one &&\n+\techo dos >rep/two &&\n+\ttest_must_fail git checkout branch 2>out &&\n+\ttest_cmp out expect\n+'\n+\n+cat >expect <<\\EOF\n+error: Your local changes to the following files would be overwritten by checkout:\n+\trep/two\n+\trep/one\n+Please, commit your changes or stash them before you can switch branches.\n+EOF\n+\n+test_expect_success 'not uptodate file porcelain checkout error' '\n+\tgit add rep/one rep/two &&\n+\ttest_must_fail git checkout branch 2>out &&\n+\ttest_cmp out expect\n+'\n+\n+cat >expect <<\\EOF\n+error: Updating the following directories would lose untracked files in it:\n+\trep2\n+\trep\n+\n+EOF\n+\n+test_expect_success 'not_uptodate_dir porcelain checkout error' '\n+\tgit init uptodate &&\n+\tcd uptodate &&\n+\tmkdir rep &&\n+\tmkdir rep2 &&\n+\ttouch rep/foo &&\n+\ttouch rep2/foo &&\n+\tgit add rep/foo rep2/foo &&\n+\tgit commit -m init &&\n+\tgit checkout -b branch &&\n+\tgit rm rep -r &&\n+\tgit rm rep2 -r &&\n+\t>rep &&\n+\t>rep2 &&\n+\tgit add rep rep2&&\n+\tgit commit -m \"added test as a file\" &&\n+\tgit checkout master &&\n+\t>rep/untracked-file &&\n+\t>rep2/untracked-file &&\n+\ttest_must_fail git checkout branch 2>out &&\n+\ttest_cmp out ../expect\n+'\n+\n+test_done\n-- \n1.7.2.1.52.g95e25.dirty\n"}]}