{"thread":{"id":"34085","subject":"[PATCH 1/2] rm: better error message on failure for multiple files","startedAt":"2013-06-10T12:51:39Z","lastAt":"2013-06-10T17:17:07Z","messageCount":6,"participants":["Mathieu Lienard--Mayor","Mathieu Liénard--Mayor","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"220278","messageId":"1370868700-27189-1-git-send-email-Mathieu.Lienard--Mayor@ensimag.imag.fr","threadId":"34085","inReplyTo":null,"subject":"[PATCH 1/2] rm: better error message on failure for multiple files","fromName":"Mathieu Lienard--Mayor","fromEmail":"mathieu.lienard--mayor@ensimag.imag.fr","sentAt":"2013-06-10T12:51:39Z","receivedAt":"2013-06-10T12:51:39Z","isPatch":true,"sender":{"key":"mathieu.lienard--mayor@ensimag.imag.fr","avatar":null},"body":"When 'git rm' fails, it now displays a single message\nwith the list of files involved, instead of displaying\na list of messages with one file each.\n\nAs an example, the old message:\n\terror: 'foo.txt' has changes staged in the index\n\t(use --cached to keep the file, or -f to force removal)\n\terror: 'bar.txt' has changes staged in the index\n\t(use --cached to keep the file, or -f to force removal)\n\nwould now be displayed as:\n\terror: the following files have changes staged in the index:\n\t\tfoo.txt\n\t\tbar.txt\n\t(use --cached to keep the file, or -f to force removal)\n\nSigned-off-by: Mathieu Lienard--Mayor <Mathieu.Lienard--Mayor@ensimag.imag.fr>\nSigned-off-by: Jorge Juan Garcia Garcia <Jorge-Juan.Garcia-Garcia@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n---\n builtin/rm.c |   93 +++++++++++++++++++++++++++++++++++++++++++++++++---------\n 1 files changed, 79 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin/rm.c b/builtin/rm.c\nindex 7b91d52..1bff656 100644\n--- a/builtin/rm.c\n+++ b/builtin/rm.c\n@@ -82,6 +82,11 @@ static int check_local_mod(unsigned char *head, int index_only)\n \tint i, no_head;\n \tint errs = 0;\n \n+\tstruct string_list files_staged = STRING_LIST_INIT_NODUP;\n+\tstruct string_list files_cached = STRING_LIST_INIT_NODUP;\n+\tstruct string_list files_submodule = STRING_LIST_INIT_NODUP;\n+\tstruct string_list files_local = STRING_LIST_INIT_NODUP;\n+\n \tno_head = is_null_sha1(head);\n \tfor (i = 0; i < list.nr; i++) {\n \t\tstruct stat st;\n@@ -171,29 +176,89 @@ static int check_local_mod(unsigned char *head, int index_only)\n \t\t */\n \t\tif (local_changes && staged_changes) {\n \t\t\tif (!index_only || !(ce->ce_flags & CE_INTENT_TO_ADD))\n-\t\t\t\terrs = error(_(\"'%s' has staged content different \"\n-\t\t\t\t\t     \"from both the file and the HEAD\\n\"\n-\t\t\t\t\t     \"(use -f to force removal)\"), name);\n+\t\t\t\tstring_list_append(&files_staged, name);\n \t\t}\n \t\telse if (!index_only) {\n \t\t\tif (staged_changes)\n-\t\t\t\terrs = error(_(\"'%s' has changes staged in the index\\n\"\n-\t\t\t\t\t     \"(use --cached to keep the file, \"\n-\t\t\t\t\t     \"or -f to force removal)\"), name);\n+\t\t\t\tstring_list_append(&files_cached, name);\n \t\t\tif (local_changes) {\n \t\t\t\tif (S_ISGITLINK(ce->ce_mode) &&\n \t\t\t\t    !submodule_uses_gitfile(name)) {\n-\t\t\t\t\terrs = error(_(\"submodule '%s' (or one of its nested \"\n-\t\t\t\t\t\t     \"submodules) uses a .git directory\\n\"\n-\t\t\t\t\t\t     \"(use 'rm -rf' if you really want to remove \"\n-\t\t\t\t\t\t     \"it including all of its history)\"), name);\n-\t\t\t\t} else\n-\t\t\t\t\terrs = error(_(\"'%s' has local modifications\\n\"\n-\t\t\t\t\t\t     \"(use --cached to keep the file, \"\n-\t\t\t\t\t\t     \"or -f to force removal)\"), name);\n+\t\t\t\t\tstring_list_append(&files_submodule,\n+\t\t\t\t\t\t\t   name);\n+\t\t\t\t} else {\n+\t\t\t\t\tstring_list_append(&files_local, name);\n+\t\t\t\t}\n \t\t\t}\n \t\t}\n \t}\n+\tif (files_staged.nr) {\n+\t\tstruct strbuf msg_staged = STRBUF_INIT;\n+\t\tint j;\n+\t\tstrbuf_addstr(\n+\t\t\t&msg_staged,\n+\t\t\t\"the following files have staged content different \"\n+\t\t\t\"from both the\\nfile and the HEAD:\");\n+\t\tfor (j = 0; j < files_staged.nr; j++) {\n+\t\t\tstrbuf_addf(&msg_staged,\n+\t\t\t\t    \"\\n\t%s\",\n+\t\t\t\t    files_staged.items[j].string);\n+\t\t}\n+\t\tstrbuf_addstr(&msg_staged,\n+\t\t\t      \"\\n(use -f to force removal)\");\n+\t\terrs = error(_(\"%s\"), msg_staged.buf);\n+\t}\n+\tif (files_cached.nr) {\n+\t\tstruct strbuf msg_cached = STRBUF_INIT;\n+\t\tint j;\n+\t\tstrbuf_addstr(\n+\t\t\t&msg_cached,\n+\t\t\t\"the following files have changes staged \"\n+\t\t\t\"in the index:\");\n+\t\tfor (j = 0; j < files_cached.nr; j++) {\n+\t\t\tstrbuf_addf(&msg_cached,\n+\t\t\t\t    \"\\n\t%s\",\n+\t\t\t\t    files_cached.items[j].string);\n+\t\t}\n+\t\tstrbuf_addstr(&msg_cached,\n+\t\t\t      \"\\n(use --cached to keep the file, \"\n+\t\t\t      \"or -f to force removal)\");\n+\t\terrs = error(_(\"%s\"), msg_cached.buf);\n+\t}\n+\tif (files_submodule.nr) {\n+\t\tstruct strbuf msg_sub = STRBUF_INIT;\n+\t\tint j;\n+\t\tstrbuf_addstr(\n+\t\t\t&msg_sub,\n+\t\t\t\"the following submodules (or one of its nested \"\n+\t\t\t\"submodule) use a .git directory:\");\n+\t\tfor (j = 0; j < files_submodule.nr; j++) {\n+\t\t\tstrbuf_addf(&msg_sub,\n+\t\t\t\t    \"\\n\t%s\",\n+\t\t\t\t    files_submodule.items[j].string);\n+\t\t}\n+\t\tstrbuf_addstr(&msg_sub,\n+\t\t\t      \"\\n(use 'rm -rf' if you really want \"\n+\t\t\t      \"to remove i including all \"\n+\t\t\t      \"of its history)\");\n+\t\terrs = error(_(\"%s\"), msg_sub.buf);\n+\t}\n+\tif (files_local.nr) {\n+\t\tstruct strbuf msg_local = STRBUF_INIT;\n+\t\tint j;\n+\t\tstrbuf_addstr(&msg_local,\n+\t\t\t      \"the following files have local modifications:\");\n+\t\tfor (j = 0; j < files_local.nr; j++) {\n+\t\t\tstrbuf_addf(&msg_local,\n+\t\t\t\t    \"\\n\t%s\",\n+\t\t\t\t    files_local.items[j].string);\n+\t\t}\n+\t\tstrbuf_addstr(&msg_local,\n+\t\t\t      \"\\n(use --cached to keep the file, \"\n+\t\t\t      \"or -f to force removal)\");\n+\t\terrs = error(_(\"%s\"), msg_local.buf);\n+\t}\n+\n \treturn errs;\n }\n \n-- \n1.7.8\n"},{"id":"220279","messageId":"1370868700-27189-2-git-send-email-Mathieu.Lienard--Mayor@ensimag.imag.fr","threadId":"34085","inReplyTo":"1370868700-27189-1-git-send-email-Mathieu.Lienard--Mayor@ensimag.imag.fr","subject":"[PATCH 2/2] rm: introduce advice.rmHints to shorten messages","fromName":"Mathieu Lienard--Mayor","fromEmail":"mathieu.lienard--mayor@ensimag.imag.fr","sentAt":"2013-06-10T12:51:40Z","receivedAt":"2013-06-10T12:51:40Z","isPatch":true,"sender":{"key":"mathieu.lienard--mayor@ensimag.imag.fr","avatar":null},"body":"Introduce advice.rmHints to choose whether to display advice or not\nwhen git rm fails. Defaults to true, in order to preserve current behavior.\n\nAs an example, the message:\n\terror: 'foo.txt' has changes staged in the index\n\t(use --cached to keep the file, or -f to force removal)\n\nwould look like, with advice.rmHints=false:\n\terror: 'foo.txt' has changes staged in the index\n\nSigned-off-by: Mathieu Lienard--Mayor <Mathieu.Lienard--Mayor@ensimag.imag.fr>\nSigned-off-by: Jorge Juan Garcia Garcia <Jorge-Juan.Garcia-Garcia@ensimag.imag.fr>\nSigned-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n---\n Documentation/config.txt |    3 ++\n advice.c                 |    2 +\n advice.h                 |    1 +\n builtin/rm.c             |   36 ++++++++++++---------\n t/t3600-rm.sh            |   77 ++++++++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 104 insertions(+), 15 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 6e53fc5..eb04479 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -199,6 +199,9 @@ advice.*::\n \tamWorkDir::\n \t\tAdvice that shows the location of the patch file when\n \t\tlinkgit:git-am[1] fails to apply it.\n+\trmHints::\n+\t\tIn case of failure in the output of linkgit:git-rm[1],\n+\t\tshow directions on how to proceed from the current state.\n --\n \n core.fileMode::\ndiff --git a/advice.c b/advice.c\nindex a8deee6..a4c169c 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -14,6 +14,7 @@ int advice_resolve_conflict = 1;\n int advice_implicit_identity = 1;\n int advice_detached_head = 1;\n int advice_set_upstream_failure = 1;\n+int advice_rm_hints = 1;\n \n static struct {\n \tconst char *name;\n@@ -33,6 +34,7 @@ static struct {\n \t{ \"implicitidentity\", &advice_implicit_identity },\n \t{ \"detachedhead\", &advice_detached_head },\n \t{ \"setupstreamfailure\", &advice_set_upstream_failure },\n+\t{ \"rmhints\", &advice_rm_hints },\n \n \t/* make this an alias for backward compatibility */\n \t{ \"pushnonfastforward\", &advice_push_update_rejected }\ndiff --git a/advice.h b/advice.h\nindex 94caa32..36104c4 100644\n--- a/advice.h\n+++ b/advice.h\n@@ -17,6 +17,7 @@ extern int advice_resolve_conflict;\n extern int advice_implicit_identity;\n extern int advice_detached_head;\n extern int advice_set_upstream_failure;\n+extern int advice_rm_hints;\n \n int git_default_advice_config(const char *var, const char *value);\n void advise(const char *advice, ...);\ndiff --git a/builtin/rm.c b/builtin/rm.c\nindex 1bff656..c9081cd 100644\n--- a/builtin/rm.c\n+++ b/builtin/rm.c\n@@ -62,9 +62,11 @@ static int check_submodules_use_gitfiles(void)\n \n \t\tif (!submodule_uses_gitfile(name))\n \t\t\terrs = error(_(\"submodule '%s' (or one of its nested \"\n-\t\t\t\t     \"submodules) uses a .git directory\\n\"\n-\t\t\t\t     \"(use 'rm -rf' if you really want to remove \"\n-\t\t\t\t     \"it including all of its history)\"), name);\n+\t\t\t\t       \"submodules) uses a .git directory%s\"), name,\n+\t\t\t\t       advice_rm_hints\n+\t\t\t\t       ? \"\\n(use 'rm -rf' if you really want to remove \"\n+\t\t\t\t       \"it including all of its history)\"\n+\t\t\t\t       : \"\");\n \t}\n \n \treturn errs;\n@@ -204,8 +206,9 @@ static int check_local_mod(unsigned char *head, int index_only)\n \t\t\t\t    \"\\n\t%s\",\n \t\t\t\t    files_staged.items[j].string);\n \t\t}\n-\t\tstrbuf_addstr(&msg_staged,\n-\t\t\t      \"\\n(use -f to force removal)\");\n+\t\tif (advice_rm_hints)\n+\t\t\tstrbuf_addstr(&msg_staged,\n+\t\t\t\t   \"\\n(use -f to force removal)\");\n \t\terrs = error(_(\"%s\"), msg_staged.buf);\n \t}\n \tif (files_cached.nr) {\n@@ -220,9 +223,10 @@ static int check_local_mod(unsigned char *head, int index_only)\n \t\t\t\t    \"\\n\t%s\",\n \t\t\t\t    files_cached.items[j].string);\n \t\t}\n-\t\tstrbuf_addstr(&msg_cached,\n-\t\t\t      \"\\n(use --cached to keep the file, \"\n-\t\t\t      \"or -f to force removal)\");\n+\t\tif (advice_rm_hints)\n+\t\t\tstrbuf_addstr(&msg_cached,\n+\t\t\t\t      \"\\n(use --cached to keep the file, \"\n+\t\t\t\t      \"or -f to force removal)\");\n \t\terrs = error(_(\"%s\"), msg_cached.buf);\n \t}\n \tif (files_submodule.nr) {\n@@ -237,10 +241,11 @@ static int check_local_mod(unsigned char *head, int index_only)\n \t\t\t\t    \"\\n\t%s\",\n \t\t\t\t    files_submodule.items[j].string);\n \t\t}\n-\t\tstrbuf_addstr(&msg_sub,\n-\t\t\t      \"\\n(use 'rm -rf' if you really want \"\n-\t\t\t      \"to remove i including all \"\n-\t\t\t      \"of its history)\");\n+\t\tif (advice_rm_hints)\n+\t\t\tstrbuf_addstr(&msg_sub,\n+\t\t\t\t      \"\\n(use 'rm -rf' if you really want \"\n+\t\t\t\t      \"to remove i including all \"\n+\t\t\t\t      \"of its history)\");\n \t\terrs = error(_(\"%s\"), msg_sub.buf);\n \t}\n \tif (files_local.nr) {\n@@ -253,9 +258,10 @@ static int check_local_mod(unsigned char *head, int index_only)\n \t\t\t\t    \"\\n\t%s\",\n \t\t\t\t    files_local.items[j].string);\n \t\t}\n-\t\tstrbuf_addstr(&msg_local,\n-\t\t\t      \"\\n(use --cached to keep the file, \"\n-\t\t\t      \"or -f to force removal)\");\n+\t\tif (advice_rm_hints)\n+\t\t\tstrbuf_addstr(&msg_local,\n+\t\t\t\t      \"\\n(use --cached to keep the file, \"\n+\t\t\t\t      \"or -f to force removal)\");\n \t\terrs = error(_(\"%s\"), msg_local.buf);\n \t}\n \ndiff --git a/t/t3600-rm.sh b/t/t3600-rm.sh\nindex 0c44e9f..ab10cc6 100755\n--- a/t/t3600-rm.sh\n+++ b/t/t3600-rm.sh\n@@ -687,4 +687,81 @@ test_expect_failure SYMLINKS 'rm across a symlinked leading path (w/ index)' '\n \ttest_path_is_file e/f\n '\n \n+test_expect_success 'setup for testing rm messages' '\n+\t>bar.txt &&\n+\t>foo.txt &&\n+\tgit add bar.txt foo.txt\n+'\n+\n+test_expect_success 'rm files with different staged content' '\n+\tcat >expect << EOF &&\n+error: the following files have staged content different from both the\n+file and the HEAD:\n+\tbar.txt\n+\tfoo.txt\n+(use -f to force removal)\n+EOF\n+\techo content1 >foo.txt &&\n+\techo content1 >bar.txt &&\n+\ttest_must_fail git rm foo.txt bar.txt 2>actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rm files with different staged content without hints' '\n+\tcat >expect << EOF &&\n+error: the following files have staged content different from both the\n+file and the HEAD:\n+\tbar.txt\n+\tfoo.txt\n+EOF\n+\techo content2 >foo.txt &&\n+\techo content2 >bar.txt &&\n+\ttest_must_fail git -c advice.rmhints=false rm foo.txt bar.txt 2>actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rm file with local modification' '\n+\tcat >expect << EOF &&\n+error: the following files have local modifications:\n+\tfoo.txt\n+(use --cached to keep the file, or -f to force removal)\n+EOF\n+\tgit commit -m \"testing rm 3\" &&\n+\techo content3 >foo.txt &&\n+\ttest_must_fail git rm foo.txt 2>actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rm file with local modification without hints' '\n+\tcat >expect << EOF &&\n+error: the following files have local modifications:\n+\tbar.txt\n+EOF\n+\techo content4 >bar.txt &&\n+\ttest_must_fail git -c advice.rmhints=false rm bar.txt 2>actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rm file with changes in the index' '\n+\tcat >expect << EOF &&\n+error: the following files have changes staged in the index:\n+\tfoo.txt\n+(use --cached to keep the file, or -f to force removal)\n+EOF\n+\tgit reset --hard &&\n+\techo content5 >foo.txt &&\n+\tgit add foo.txt &&\n+\ttest_must_fail git rm foo.txt 2>actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rm file with changes in the index without hints' '\n+\tcat >expect << EOF &&\n+error: the following files have changes staged in the index:\n+\tfoo.txt\n+EOF\n+\ttest_must_fail git -c advice.rmhints=false rm foo.txt 2>actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n1.7.8\n"},{"id":"220280","messageId":"d0b036b99a012c5c550f76351f0dcbcb@ensibm.imag.fr","threadId":"34085","inReplyTo":"1370868700-27189-1-git-send-email-Mathieu.Lienard--Mayor@ensimag.imag.fr","subject":"Re: [PATCH 1/2] rm: better error message on failure for multiple files","fromName":"Mathieu Liénard--Mayor","fromEmail":"mathieu.lienard--mayor@ensimag.fr","sentAt":"2013-06-10T12:53:58Z","receivedAt":"2013-06-10T12:53:58Z","isPatch":true,"sender":{"key":"mathieu.lienard--mayor@ensimag.fr","avatar":null},"body":"Please ignore this, manipulation error while in the git send-email \ncommand line.\n\nLe 2013-06-10 14:51, Mathieu Lienard--Mayor a écrit :\n> When 'git rm' fails, it now displays a single message\n> with the list of files involved, instead of displaying\n> a list of messages with one file each.\n>\n> As an example, the old message:\n> \terror: 'foo.txt' has changes staged in the index\n> \t(use --cached to keep the file, or -f to force removal)\n> \terror: 'bar.txt' has changes staged in the index\n> \t(use --cached to keep the file, or -f to force removal)\n>\n> would now be displayed as:\n> \terror: the following files have changes staged in the index:\n> \t\tfoo.txt\n> \t\tbar.txt\n> \t(use --cached to keep the file, or -f to force removal)\n>\n> Signed-off-by: Mathieu Lienard--Mayor \n> <Mathieu.Lienard--Mayor@ensimag.imag.fr>\n> Signed-off-by: Jorge Juan Garcia Garcia\n> <Jorge-Juan.Garcia-Garcia@ensimag.imag.fr>\n> Signed-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n> ---\n>  builtin/rm.c |   93 \n> +++++++++++++++++++++++++++++++++++++++++++++++++---------\n>  1 files changed, 79 insertions(+), 14 deletions(-)\n>\n> diff --git a/builtin/rm.c b/builtin/rm.c\n> index 7b91d52..1bff656 100644\n> --- a/builtin/rm.c\n> +++ b/builtin/rm.c\n> @@ -82,6 +82,11 @@ static int check_local_mod(unsigned char *head,\n> int index_only)\n>  \tint i, no_head;\n>  \tint errs = 0;\n>\n> +\tstruct string_list files_staged = STRING_LIST_INIT_NODUP;\n> +\tstruct string_list files_cached = STRING_LIST_INIT_NODUP;\n> +\tstruct string_list files_submodule = STRING_LIST_INIT_NODUP;\n> +\tstruct string_list files_local = STRING_LIST_INIT_NODUP;\n> +\n>  \tno_head = is_null_sha1(head);\n>  \tfor (i = 0; i < list.nr; i++) {\n>  \t\tstruct stat st;\n> @@ -171,29 +176,89 @@ static int check_local_mod(unsigned char *head,\n> int index_only)\n>  \t\t */\n>  \t\tif (local_changes && staged_changes) {\n>  \t\t\tif (!index_only || !(ce->ce_flags & CE_INTENT_TO_ADD))\n> -\t\t\t\terrs = error(_(\"'%s' has staged content different \"\n> -\t\t\t\t\t     \"from both the file and the HEAD\\n\"\n> -\t\t\t\t\t     \"(use -f to force removal)\"), name);\n> +\t\t\t\tstring_list_append(&files_staged, name);\n>  \t\t}\n>  \t\telse if (!index_only) {\n>  \t\t\tif (staged_changes)\n> -\t\t\t\terrs = error(_(\"'%s' has changes staged in the index\\n\"\n> -\t\t\t\t\t     \"(use --cached to keep the file, \"\n> -\t\t\t\t\t     \"or -f to force removal)\"), name);\n> +\t\t\t\tstring_list_append(&files_cached, name);\n>  \t\t\tif (local_changes) {\n>  \t\t\t\tif (S_ISGITLINK(ce->ce_mode) &&\n>  \t\t\t\t    !submodule_uses_gitfile(name)) {\n> -\t\t\t\t\terrs = error(_(\"submodule '%s' (or one of its nested \"\n> -\t\t\t\t\t\t     \"submodules) uses a .git directory\\n\"\n> -\t\t\t\t\t\t     \"(use 'rm -rf' if you really want to remove \"\n> -\t\t\t\t\t\t     \"it including all of its history)\"), name);\n> -\t\t\t\t} else\n> -\t\t\t\t\terrs = error(_(\"'%s' has local modifications\\n\"\n> -\t\t\t\t\t\t     \"(use --cached to keep the file, \"\n> -\t\t\t\t\t\t     \"or -f to force removal)\"), name);\n> +\t\t\t\t\tstring_list_append(&files_submodule,\n> +\t\t\t\t\t\t\t   name);\n> +\t\t\t\t} else {\n> +\t\t\t\t\tstring_list_append(&files_local, name);\n> +\t\t\t\t}\n>  \t\t\t}\n>  \t\t}\n>  \t}\n> +\tif (files_staged.nr) {\n> +\t\tstruct strbuf msg_staged = STRBUF_INIT;\n> +\t\tint j;\n> +\t\tstrbuf_addstr(\n> +\t\t\t&msg_staged,\n> +\t\t\t\"the following files have staged content different \"\n> +\t\t\t\"from both the\\nfile and the HEAD:\");\n> +\t\tfor (j = 0; j < files_staged.nr; j++) {\n> +\t\t\tstrbuf_addf(&msg_staged,\n> +\t\t\t\t    \"\\n\t%s\",\n> +\t\t\t\t    files_staged.items[j].string);\n> +\t\t}\n> +\t\tstrbuf_addstr(&msg_staged,\n> +\t\t\t      \"\\n(use -f to force removal)\");\n> +\t\terrs = error(_(\"%s\"), msg_staged.buf);\n> +\t}\n> +\tif (files_cached.nr) {\n> +\t\tstruct strbuf msg_cached = STRBUF_INIT;\n> +\t\tint j;\n> +\t\tstrbuf_addstr(\n> +\t\t\t&msg_cached,\n> +\t\t\t\"the following files have changes staged \"\n> +\t\t\t\"in the index:\");\n> +\t\tfor (j = 0; j < files_cached.nr; j++) {\n> +\t\t\tstrbuf_addf(&msg_cached,\n> +\t\t\t\t    \"\\n\t%s\",\n> +\t\t\t\t    files_cached.items[j].string);\n> +\t\t}\n> +\t\tstrbuf_addstr(&msg_cached,\n> +\t\t\t      \"\\n(use --cached to keep the file, \"\n> +\t\t\t      \"or -f to force removal)\");\n> +\t\terrs = error(_(\"%s\"), msg_cached.buf);\n> +\t}\n> +\tif (files_submodule.nr) {\n> +\t\tstruct strbuf msg_sub = STRBUF_INIT;\n> +\t\tint j;\n> +\t\tstrbuf_addstr(\n> +\t\t\t&msg_sub,\n> +\t\t\t\"the following submodules (or one of its nested \"\n> +\t\t\t\"submodule) use a .git directory:\");\n> +\t\tfor (j = 0; j < files_submodule.nr; j++) {\n> +\t\t\tstrbuf_addf(&msg_sub,\n> +\t\t\t\t    \"\\n\t%s\",\n> +\t\t\t\t    files_submodule.items[j].string);\n> +\t\t}\n> +\t\tstrbuf_addstr(&msg_sub,\n> +\t\t\t      \"\\n(use 'rm -rf' if you really want \"\n> +\t\t\t      \"to remove i including all \"\n> +\t\t\t      \"of its history)\");\n> +\t\terrs = error(_(\"%s\"), msg_sub.buf);\n> +\t}\n> +\tif (files_local.nr) {\n> +\t\tstruct strbuf msg_local = STRBUF_INIT;\n> +\t\tint j;\n> +\t\tstrbuf_addstr(&msg_local,\n> +\t\t\t      \"the following files have local modifications:\");\n> +\t\tfor (j = 0; j < files_local.nr; j++) {\n> +\t\t\tstrbuf_addf(&msg_local,\n> +\t\t\t\t    \"\\n\t%s\",\n> +\t\t\t\t    files_local.items[j].string);\n> +\t\t}\n> +\t\tstrbuf_addstr(&msg_local,\n> +\t\t\t      \"\\n(use --cached to keep the file, \"\n> +\t\t\t      \"or -f to force removal)\");\n> +\t\terrs = error(_(\"%s\"), msg_local.buf);\n> +\t}\n> +\n>  \treturn errs;\n>  }\n\n-- \nMathieu Liénard--Mayor,\n2nd year at Grenoble INP - ENSIMAG\n(+33)6 80 56 30 02\n"},{"id":"220281","messageId":"18c54938bf29444008aec7b3e48912dc@ensibm.imag.fr","threadId":"34085","inReplyTo":"1370868700-27189-2-git-send-email-Mathieu.Lienard--Mayor@ensimag.imag.fr","subject":"Re: [PATCH 2/2] rm: introduce advice.rmHints to shorten messages","fromName":"Mathieu Liénard--Mayor","fromEmail":"mathieu.lienard--mayor@ensimag.fr","sentAt":"2013-06-10T12:54:23Z","receivedAt":"2013-06-10T12:54:23Z","isPatch":true,"sender":{"key":"mathieu.lienard--mayor@ensimag.fr","avatar":null},"body":"Please ignore this, manipulation error while in the git send-email \ncommand line.\n\nLe 2013-06-10 14:51, Mathieu Lienard--Mayor a écrit :\n> Introduce advice.rmHints to choose whether to display advice or not\n> when git rm fails. Defaults to true, in order to preserve current \n> behavior.\n>\n> As an example, the message:\n> \terror: 'foo.txt' has changes staged in the index\n> \t(use --cached to keep the file, or -f to force removal)\n>\n> would look like, with advice.rmHints=false:\n> \terror: 'foo.txt' has changes staged in the index\n>\n> Signed-off-by: Mathieu Lienard--Mayor \n> <Mathieu.Lienard--Mayor@ensimag.imag.fr>\n> Signed-off-by: Jorge Juan Garcia Garcia\n> <Jorge-Juan.Garcia-Garcia@ensimag.imag.fr>\n> Signed-off-by: Matthieu Moy <Matthieu.Moy@grenoble-inp.fr>\n> ---\n>  Documentation/config.txt |    3 ++\n>  advice.c                 |    2 +\n>  advice.h                 |    1 +\n>  builtin/rm.c             |   36 ++++++++++++---------\n>  t/t3600-rm.sh            |   77 \n> ++++++++++++++++++++++++++++++++++++++++++++++\n>  5 files changed, 104 insertions(+), 15 deletions(-)\n>\n> diff --git a/Documentation/config.txt b/Documentation/config.txt\n> index 6e53fc5..eb04479 100644\n> --- a/Documentation/config.txt\n> +++ b/Documentation/config.txt\n> @@ -199,6 +199,9 @@ advice.*::\n>  \tamWorkDir::\n>  \t\tAdvice that shows the location of the patch file when\n>  \t\tlinkgit:git-am[1] fails to apply it.\n> +\trmHints::\n> +\t\tIn case of failure in the output of linkgit:git-rm[1],\n> +\t\tshow directions on how to proceed from the current state.\n>  --\n>\n>  core.fileMode::\n> diff --git a/advice.c b/advice.c\n> index a8deee6..a4c169c 100644\n> --- a/advice.c\n> +++ b/advice.c\n> @@ -14,6 +14,7 @@ int advice_resolve_conflict = 1;\n>  int advice_implicit_identity = 1;\n>  int advice_detached_head = 1;\n>  int advice_set_upstream_failure = 1;\n> +int advice_rm_hints = 1;\n>\n>  static struct {\n>  \tconst char *name;\n> @@ -33,6 +34,7 @@ static struct {\n>  \t{ \"implicitidentity\", &advice_implicit_identity },\n>  \t{ \"detachedhead\", &advice_detached_head },\n>  \t{ \"setupstreamfailure\", &advice_set_upstream_failure },\n> +\t{ \"rmhints\", &advice_rm_hints },\n>\n>  \t/* make this an alias for backward compatibility */\n>  \t{ \"pushnonfastforward\", &advice_push_update_rejected }\n> diff --git a/advice.h b/advice.h\n> index 94caa32..36104c4 100644\n> --- a/advice.h\n> +++ b/advice.h\n> @@ -17,6 +17,7 @@ extern int advice_resolve_conflict;\n>  extern int advice_implicit_identity;\n>  extern int advice_detached_head;\n>  extern int advice_set_upstream_failure;\n> +extern int advice_rm_hints;\n>\n>  int git_default_advice_config(const char *var, const char *value);\n>  void advise(const char *advice, ...);\n> diff --git a/builtin/rm.c b/builtin/rm.c\n> index 1bff656..c9081cd 100644\n> --- a/builtin/rm.c\n> +++ b/builtin/rm.c\n> @@ -62,9 +62,11 @@ static int check_submodules_use_gitfiles(void)\n>\n>  \t\tif (!submodule_uses_gitfile(name))\n>  \t\t\terrs = error(_(\"submodule '%s' (or one of its nested \"\n> -\t\t\t\t     \"submodules) uses a .git directory\\n\"\n> -\t\t\t\t     \"(use 'rm -rf' if you really want to remove \"\n> -\t\t\t\t     \"it including all of its history)\"), name);\n> +\t\t\t\t       \"submodules) uses a .git directory%s\"), name,\n> +\t\t\t\t       advice_rm_hints\n> +\t\t\t\t       ? \"\\n(use 'rm -rf' if you really want to remove \"\n> +\t\t\t\t       \"it including all of its history)\"\n> +\t\t\t\t       : \"\");\n>  \t}\n>\n>  \treturn errs;\n> @@ -204,8 +206,9 @@ static int check_local_mod(unsigned char *head,\n> int index_only)\n>  \t\t\t\t    \"\\n\t%s\",\n>  \t\t\t\t    files_staged.items[j].string);\n>  \t\t}\n> -\t\tstrbuf_addstr(&msg_staged,\n> -\t\t\t      \"\\n(use -f to force removal)\");\n> +\t\tif (advice_rm_hints)\n> +\t\t\tstrbuf_addstr(&msg_staged,\n> +\t\t\t\t   \"\\n(use -f to force removal)\");\n>  \t\terrs = error(_(\"%s\"), msg_staged.buf);\n>  \t}\n>  \tif (files_cached.nr) {\n> @@ -220,9 +223,10 @@ static int check_local_mod(unsigned char *head,\n> int index_only)\n>  \t\t\t\t    \"\\n\t%s\",\n>  \t\t\t\t    files_cached.items[j].string);\n>  \t\t}\n> -\t\tstrbuf_addstr(&msg_cached,\n> -\t\t\t      \"\\n(use --cached to keep the file, \"\n> -\t\t\t      \"or -f to force removal)\");\n> +\t\tif (advice_rm_hints)\n> +\t\t\tstrbuf_addstr(&msg_cached,\n> +\t\t\t\t      \"\\n(use --cached to keep the file, \"\n> +\t\t\t\t      \"or -f to force removal)\");\n>  \t\terrs = error(_(\"%s\"), msg_cached.buf);\n>  \t}\n>  \tif (files_submodule.nr) {\n> @@ -237,10 +241,11 @@ static int check_local_mod(unsigned char *head,\n> int index_only)\n>  \t\t\t\t    \"\\n\t%s\",\n>  \t\t\t\t    files_submodule.items[j].string);\n>  \t\t}\n> -\t\tstrbuf_addstr(&msg_sub,\n> -\t\t\t      \"\\n(use 'rm -rf' if you really want \"\n> -\t\t\t      \"to remove i including all \"\n> -\t\t\t      \"of its history)\");\n> +\t\tif (advice_rm_hints)\n> +\t\t\tstrbuf_addstr(&msg_sub,\n> +\t\t\t\t      \"\\n(use 'rm -rf' if you really want \"\n> +\t\t\t\t      \"to remove i including all \"\n> +\t\t\t\t      \"of its history)\");\n>  \t\terrs = error(_(\"%s\"), msg_sub.buf);\n>  \t}\n>  \tif (files_local.nr) {\n> @@ -253,9 +258,10 @@ static int check_local_mod(unsigned char *head,\n> int index_only)\n>  \t\t\t\t    \"\\n\t%s\",\n>  \t\t\t\t    files_local.items[j].string);\n>  \t\t}\n> -\t\tstrbuf_addstr(&msg_local,\n> -\t\t\t      \"\\n(use --cached to keep the file, \"\n> -\t\t\t      \"or -f to force removal)\");\n> +\t\tif (advice_rm_hints)\n> +\t\t\tstrbuf_addstr(&msg_local,\n> +\t\t\t\t      \"\\n(use --cached to keep the file, \"\n> +\t\t\t\t      \"or -f to force removal)\");\n>  \t\terrs = error(_(\"%s\"), msg_local.buf);\n>  \t}\n>\n> diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh\n> index 0c44e9f..ab10cc6 100755\n> --- a/t/t3600-rm.sh\n> +++ b/t/t3600-rm.sh\n> @@ -687,4 +687,81 @@ test_expect_failure SYMLINKS 'rm across a\n> symlinked leading path (w/ index)' '\n>  \ttest_path_is_file e/f\n>  '\n>\n> +test_expect_success 'setup for testing rm messages' '\n> +\t>bar.txt &&\n> +\t>foo.txt &&\n> +\tgit add bar.txt foo.txt\n> +'\n> +\n> +test_expect_success 'rm files with different staged content' '\n> +\tcat >expect << EOF &&\n> +error: the following files have staged content different from both \n> the\n> +file and the HEAD:\n> +\tbar.txt\n> +\tfoo.txt\n> +(use -f to force removal)\n> +EOF\n> +\techo content1 >foo.txt &&\n> +\techo content1 >bar.txt &&\n> +\ttest_must_fail git rm foo.txt bar.txt 2>actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n> +test_expect_success 'rm files with different staged content without \n> hints' '\n> +\tcat >expect << EOF &&\n> +error: the following files have staged content different from both \n> the\n> +file and the HEAD:\n> +\tbar.txt\n> +\tfoo.txt\n> +EOF\n> +\techo content2 >foo.txt &&\n> +\techo content2 >bar.txt &&\n> +\ttest_must_fail git -c advice.rmhints=false rm foo.txt bar.txt \n> 2>actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n> +test_expect_success 'rm file with local modification' '\n> +\tcat >expect << EOF &&\n> +error: the following files have local modifications:\n> +\tfoo.txt\n> +(use --cached to keep the file, or -f to force removal)\n> +EOF\n> +\tgit commit -m \"testing rm 3\" &&\n> +\techo content3 >foo.txt &&\n> +\ttest_must_fail git rm foo.txt 2>actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n> +test_expect_success 'rm file with local modification without hints' \n> '\n> +\tcat >expect << EOF &&\n> +error: the following files have local modifications:\n> +\tbar.txt\n> +EOF\n> +\techo content4 >bar.txt &&\n> +\ttest_must_fail git -c advice.rmhints=false rm bar.txt 2>actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n> +test_expect_success 'rm file with changes in the index' '\n> +\tcat >expect << EOF &&\n> +error: the following files have changes staged in the index:\n> +\tfoo.txt\n> +(use --cached to keep the file, or -f to force removal)\n> +EOF\n> +\tgit reset --hard &&\n> +\techo content5 >foo.txt &&\n> +\tgit add foo.txt &&\n> +\ttest_must_fail git rm foo.txt 2>actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n> +test_expect_success 'rm file with changes in the index without \n> hints' '\n> +\tcat >expect << EOF &&\n> +error: the following files have changes staged in the index:\n> +\tfoo.txt\n> +EOF\n> +\ttest_must_fail git -c advice.rmhints=false rm foo.txt 2>actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n>  test_done\n\n-- \nMathieu Liénard--Mayor,\n2nd year at Grenoble INP - ENSIMAG\n(+33)6 80 56 30 02\n"},{"id":"220321","messageId":"7vd2rtkiov.fsf@alter.siamese.dyndns.org","threadId":"34085","inReplyTo":"18c54938bf29444008aec7b3e48912dc@ensibm.imag.fr","subject":"Re: [PATCH 2/2] rm: introduce advice.rmHints to shorten messages","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-06-10T16:57:04Z","receivedAt":"2013-06-10T16:57:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Mathieu Liénard--Mayor  <mathieu.lienard--mayor@ensimag.fr> writes:\n\n> Please ignore this, manipulation error while in the git send-email\n> command line.\n\nHere is what my mailbox looks like (the penultimate one with 252\nlines is what I am responding to).  \n\n  R. [ 146: Mathieu Lienard--Mayor ] [PATCH 1/2] rm: better error messa\n  R. [ 231: Mathieu Lienard--Mayor ] [PATCH 2/2] rm: introduce advice.r\n  R. [ 157: Mathieu Lienard--Mayor ] [PATCH 2/2] rm: introduce advice.r\n  R. [ 198: Mathieu Lienard--Mayor ] [PATCH v2 1/2] rm: better error me\n  R. [ 157: Mathieu Lienard--Mayor ] [PATCH v2 2/2] rm: introduce advic\n   . [ 153: Mathieu Lienard--Mayor ] [PATCH v3 2/2] rm: introduce advic\n   . [ 214: Mathieu Lienard--Mayor ] [PATCH v3 1/2]  rm: better error m\n   . [ 214: Mathieu Lienard--Mayor ] [PATCH v3 1/2]  rm: better error m\n   . [ 153: Mathieu Lienard--Mayor ] [PATCH v3 2/2] rm: introduce advic\n  R  [  33: Mathieu Liénard--Mayor ] Re: [PATCH 1/2] rm: better error m\n  O  [  38: Mathieu Liénard--Mayor ] Re: [PATCH 2/2] rm: introduce advi\n  R. [ 156: Mathieu Liénard--Mayor ] Re: [PATCH 1/2] rm: better error m\n  R. [ 252: Mathieu Liénard--Mayor ] Re: [PATCH 2/2] rm: introduce advi\n   . [  84: Mathieu Liénard--Mayor ] Re: [PATCH v2 1/2] rm: better erro\n\nI am guessing that [v3 1/2] and [v3 2/2] are the final ones but it\nthat is not the case please holler.\n"},{"id":"220327","messageId":"13e4098ead63ece337f065669dcc64ec@ensibm.imag.fr","threadId":"34085","inReplyTo":"7vd2rtkiov.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 2/2] rm: introduce advice.rmHints to shorten messages","fromName":"Mathieu Liénard--Mayor","fromEmail":"mathieu.lienard--mayor@ensimag.fr","sentAt":"2013-06-10T17:17:07Z","receivedAt":"2013-06-10T17:17:07Z","isPatch":true,"sender":{"key":"mathieu.lienard--mayor@ensimag.fr","avatar":null},"body":"Le 2013-06-10 18:57, Junio C Hamano a écrit :\n> Mathieu Liénard--Mayor  <mathieu.lienard--mayor@ensimag.fr> writes:\n>\n>> Please ignore this, manipulation error while in the git send-email\n>> command line.\n>\n> Here is what my mailbox looks like (the penultimate one with 252\n> lines is what I am responding to).\n>\n>   R. [ 146: Mathieu Lienard--Mayor ] [PATCH 1/2] rm: better error \n> messa\n>   R. [ 231: Mathieu Lienard--Mayor ] [PATCH 2/2] rm: introduce \n> advice.r\n>   R. [ 157: Mathieu Lienard--Mayor ] [PATCH 2/2] rm: introduce \n> advice.r\n>   R. [ 198: Mathieu Lienard--Mayor ] [PATCH v2 1/2] rm: better error \n> me\n>   R. [ 157: Mathieu Lienard--Mayor ] [PATCH v2 2/2] rm: introduce \n> advic\n>    . [ 153: Mathieu Lienard--Mayor ] [PATCH v3 2/2] rm: introduce \n> advic\n>    . [ 214: Mathieu Lienard--Mayor ] [PATCH v3 1/2]  rm: better error \n> m\n>    . [ 214: Mathieu Lienard--Mayor ] [PATCH v3 1/2]  rm: better error \n> m\n>    . [ 153: Mathieu Lienard--Mayor ] [PATCH v3 2/2] rm: introduce \n> advic\n>   R  [  33: Mathieu Liénard--Mayor ] Re: [PATCH 1/2] rm: better error \n> m\n>   O  [  38: Mathieu Liénard--Mayor ] Re: [PATCH 2/2] rm: introduce \n> advi\n>   R. [ 156: Mathieu Liénard--Mayor ] Re: [PATCH 1/2] rm: better error \n> m\n>   R. [ 252: Mathieu Liénard--Mayor ] Re: [PATCH 2/2] rm: introduce \n> advi\n>    . [  84: Mathieu Liénard--Mayor ] Re: [PATCH v2 1/2] rm: better \n> erro\n>\n> I am guessing that [v3 1/2] and [v3 2/2] are the final ones but it\n> that is not the case please holler.\nYes, [v3 1/2] and [v3 2/2] are the final ones.\ni'm sorry, i really don't know how i managed to create such a mess, i'm \nstill not familiar with the send-email tool =/\n-- \nMathieu Liénard--Mayor,\n2nd year at Grenoble INP - ENSIMAG\n(+33)6 80 56 30 02\n"}]}