{"thread":{"id":"27539","subject":"[PATCH] Use clean.requireforce to protect untracked files.","startedAt":"2011-06-03T11:12:46Z","lastAt":"2011-06-03T16:20:37Z","messageCount":3,"participants":["Jiang Xin","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"169248","messageId":"4DE8C1AE.4000007@gmail.com","threadId":"27539","inReplyTo":null,"subject":"[PATCH] Use clean.requireforce to protect untracked files.","fromName":"Jiang Xin","fromEmail":"worldhello.net@gmail.com","sentAt":"2011-06-03T11:12:46Z","receivedAt":"2011-06-03T11:12:46Z","isPatch":true,"sender":{"key":"worldhello.net@gmail.com","avatar":"https://avatars.githubusercontent.com/u/183860?v=4"},"body":"Untracked files may be significant for certain repositories, but if run the\ncommand \"git clean -fdx\" by accident, all untracked files will be lost.\n\nThis hack adds three values in addtion to true/false to \"clean.requireforce\",\nwhich can protect untracked files from cleaning:\n\n* true or unset : can not clean without -f/--force option provided.\n* false         : clean untracked files just as -f/--force option provided.\n* lockignored   : can not clean untracked ignored files.\n* lockunignored : can not clean untracked unignored files.\n* lockall       : can not clean anything.\n\nSigned-off-by: Jiang Xin <jiangxin@ossxp.com>\n---\n Documentation/config.txt |    7 +++-\n builtin/clean.c          |   29 ++++++++++++++++++-\n t/t7300-clean.sh         |   69 ++++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 101 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 6b93777..b930f42 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -684,8 +684,11 @@ browser.<tool>.path::\n \tworking repository in gitweb (see linkgit:git-instaweb[1]).\n \n clean.requireForce::\n-\tA boolean to make git-clean do nothing unless given -f\n-\tor -n.   Defaults to true.\n+\tWhen set to `LockIgnored`, cleaning untracked ignored files is\n+\tdenied. When set to `LockUnIgnored`, only allow cleaning untracked\n+\tignored files using -X option. When set to `LockAll`, no files\n+\tcan be cleaned until unset this variable. A boolean to make\n+\tgit-clean do nothing unless given -f or -n. Defaults to true.\n \n color.branch::\n \tA boolean to enable/disable color in the output of\ndiff --git a/builtin/clean.c b/builtin/clean.c\nindex 75697f7..441f35a 100644\n--- a/builtin/clean.c\n+++ b/builtin/clean.c\n@@ -13,7 +13,11 @@\n #include \"string-list.h\"\n #include \"quote.h\"\n \n+#define LOCK_IGNORED\t01\n+#define LOCK_UNIGNORED\t02\n+\n static int force = -1; /* unset */\n+static int lock_flag = 0;\n \n static const char *const builtin_clean_usage[] = {\n \t\"git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>...\",\n@@ -22,8 +26,17 @@ static const char *const builtin_clean_usage[] = {\n \n static int git_clean_config(const char *var, const char *value, void *cb)\n {\n-\tif (!strcmp(var, \"clean.requireforce\"))\n-\t\tforce = !git_config_bool(var, value);\n+\tif (!strcmp(var, \"clean.requireforce\")) {\n+\t\tif (value && !strcasecmp(value, \"LockIgnored\"))\n+\t\t\tlock_flag = LOCK_IGNORED;\n+\t\telse if (value && !strcasecmp(value, \"LockUnIgnored\"))\n+\t\t\tlock_flag = LOCK_UNIGNORED;\n+\t\telse if (value && !strcasecmp(value, \"LockAll\"))\n+\t\t\tlock_flag = LOCK_IGNORED | LOCK_UNIGNORED;\n+\t\telse\n+\t\t\tforce = !git_config_bool(var, value);\n+\t\treturn 0;\n+\t}\n \treturn git_default_config(var, value, cb);\n }\n \n@@ -77,6 +90,18 @@ int cmd_clean(int argc, const char **argv, const char *prefix)\n \tif (ignored && ignored_only)\n \t\tdie(_(\"-x and -X cannot be used together\"));\n \n+\tif (!show_only && lock_flag) {\n+\t\tif (lock_flag & LOCK_IGNORED && lock_flag & LOCK_UNIGNORED)\n+\t\t\tdie(_(\"clean.requireForce set to LockAll; \"\n+\t\t\t\t  \"refusing to clean until reset clean.requireForce\"));\n+\t\telse if (lock_flag & LOCK_IGNORED && (ignored_only || ignored))\n+\t\t\tdie(_(\"clean.requireForce set to LockIgnored and conflict with -x or -X; \"\n+\t\t\t\t  \"refusing to clean\"));\n+\t\telse if (lock_flag & LOCK_UNIGNORED && !ignored_only)\n+\t\t\tdie(_(\"clean.requireForce set to LockUnIgnored and can only work with -X; \"\n+\t\t\t\t  \"refusing to clean\"));\n+\t}\n+\n \tif (!show_only && !force) {\n \t\tif (config_set)\n \t\t\tdie(_(\"clean.requireForce set to true and neither -n nor -f given; \"\ndiff --git a/t/t7300-clean.sh b/t/t7300-clean.sh\nindex 800b536..b4f38dd 100755\n--- a/t/t7300-clean.sh\n+++ b/t/t7300-clean.sh\n@@ -460,4 +460,73 @@ test_expect_success SANITY 'git clean -d with an unreadable empty directory' '\n \t! test -d foo\n '\n \n+test_expect_success 'clean.requireForce LockIgnored' '\n+\n+\tgit config clean.requireForce LockIgnored &&\n+\tmkdir -p build docs &&\n+\ttouch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&\n+\ttest_must_fail git clean &&\n+\ttest_must_fail git clean -f -x &&\n+\ttest_must_fail git clean -f -X &&\n+\tgit clean -f &&\n+\ttest -f Makefile &&\n+\ttest -f README &&\n+\ttest -f src/part1.c &&\n+\ttest -f src/part2.c &&\n+\ttest ! -f a.out &&\n+\ttest ! -f src/part3.c &&\n+\ttest -f docs/manual.txt &&\n+\ttest -f obj.o &&\n+\ttest -f build/lib.so &&\n+\tgit clean -f -d &&\n+\ttest ! -f docs/manual.txt &&\n+\ttest -f build/lib.so &&\n+\tgit config clean.requireForce true\n+'\n+\n+test_expect_success 'clean.requireForce LockUnIgnored' '\n+\n+\tgit config clean.requireForce LockUnIgnored &&\n+\tmkdir -p build docs &&\n+\ttouch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&\n+\ttest_must_fail git clean &&\n+\ttest_must_fail git clean -f &&\n+\ttest_must_fail git clean -f -x &&\n+\tgit clean -f -X &&\n+\ttest -f Makefile &&\n+\ttest -f README &&\n+\ttest -f src/part1.c &&\n+\ttest -f src/part2.c &&\n+\ttest -f a.out &&\n+\ttest -f src/part3.c &&\n+\ttest -f docs/manual.txt &&\n+\ttest ! -f obj.o &&\n+\ttest -f build/lib.so &&\n+\tgit clean -f -d -X &&\n+\ttest ! -f build/lib.so &&\n+\ttest -f src/part3.c &&\n+\tgit config clean.requireForce true\n+'\n+\n+test_expect_success 'clean.requireForce LockAll' '\n+\n+\tgit config clean.requireForce lockall &&\n+\tmkdir -p build docs &&\n+\ttouch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&\n+\ttest_must_fail git clean -f &&\n+\ttest_must_fail git clean -f -x &&\n+\ttest_must_fail git clean -f -X &&\n+\tgit clean -ndx &&\n+\ttest -f Makefile &&\n+\ttest -f README &&\n+\ttest -f src/part1.c &&\n+\ttest -f src/part2.c &&\n+\ttest -f a.out &&\n+\ttest -f src/part3.c &&\n+\ttest -f docs/manual.txt &&\n+\ttest -f obj.o &&\n+\ttest -f build/lib.so &&\n+\tgit config clean.requireForce true\n+'\n+\n test_done\n-- \n1.7.5.4.1.g6c49.dirty\n"},{"id":"169255","messageId":"7vipsnar23.fsf@alter.siamese.dyndns.org","threadId":"27539","inReplyTo":"4DE8C1AE.4000007@gmail.com","subject":"Re: [PATCH] Use clean.requireforce to protect untracked files.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-06-03T15:11:00Z","receivedAt":"2011-06-03T15:11:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jiang Xin <worldhello.net@gmail.com> writes:\n\n> Untracked files may be significant for certain repositories, but if run the\n> command \"git clean -fdx\" by accident, all untracked files will be lost.\n\nDon't add -x without thinking, then. It is the way to tell the command \"I\nwant to remove all the untracked files and I REALLY MEAN IT\".  It is often\nused to say \"I do not trust Makefile and I want to remove what 'make\nclean' would leave behind\".\n\nA slightly related tangent is that we only have three classes of paths:\n\n - tracked ones\n - untracked ones, where there are two subclasses\n   - unignored ones (e.g. new source file you haven't added)\n   - ignored ones (e.g. build artifacts like *.o files)\n\nand because of that, the general design is to consider \"ignored\" files\nexpendable during various operations. Sometimes people deliberately \"ignore\"\nfiles that they consider not expendable, which is (by today's definition)\na wrong thing to do, but I think in the longer term we should add a way to\nmark them as \"ignored but precious\".\n\n  http://thread.gmane.org/gmane.comp.version-control.git/172818/focus=172846\n\nNobody has designed how this fourth class should behave (and how the\nbehaviour of the \"ignored\" should change, if any) yet, but a rough outline\nwould probably be:\n\n - precious files are the ones that are ignored (by today's definition,\n   i.e. .gitignore mechanism consideres they are ignored) but marked as\n   \"precious\" in some other way [*1*]. They will\n\n   - not appear in \"Untracked files:\" section in \"git status\" output;\n   - not be added by \"git add\" without \"-f\", just like other ignored files;\n   - not be overwritten or removed to make room while switching branches;\n   - not be removed with \"clean -f -x\" [*2*].\n\n - ignored files will stay to be \"expendable\".\n\nI suspect there may be some codepaths that incorrectly treat them as not\nexpendable, and protect their lossage. We would want to fix them after we\nintroduce the \"precious\" class.\n\n[Footnotes]\n\n*1* We could invent a way to sneak such entries in .gitignore, but I am\ninclined to think it would be cleaner to define \"precious\" attribute and\nlet the attributes mechanism handle this.\n\n*2* This is just off the top of my head without thinking things\nthrough. It might turn out that it makes more sense ot remove them.\n"},{"id":"169258","messageId":"4DE909D5.9050805@gmail.com","threadId":"27539","inReplyTo":"7vipsnar23.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] Use clean.requireforce to protect untracked files.","fromName":"Jiang Xin","fromEmail":"worldhello.net@gmail.com","sentAt":"2011-06-03T16:20:37Z","receivedAt":"2011-06-03T16:20:37Z","isPatch":true,"sender":{"key":"worldhello.net@gmail.com","avatar":"https://avatars.githubusercontent.com/u/183860?v=4"},"body":"I do agree there are ignored-but-precious type of files exist in practice.\nIntroducing such ignored-but-precious class through attributes or .gitignore\nis fine, and the definition of the class can broadcast to others through\n.gitattributes or .gitignore, it's cool. But it sounds a bit complicated.\n\nThis afternoon's hack on config variable \"clean.requestForce\" feets my needs,\nbut it also has drawbacks:\n\n1. Define a global \"clean.requestForce\" variable like this:\n\n    $ git config --global clean.requestForce LockIgnored\n\n2. Then in one repository,\n\n    * You can : git clean -f\n    * But you cannot : git clean -f -x\n\n3. If want to override such global setting, simply\n\n    $ git config clean.requestForce true\n\nThe side-effect of this hack is that an unhacked git will complain:\n\n    fatal: bad config value for 'clean.requireforce' in .git/config\n\n\n于 11-6-3 下午11:11, Junio C Hamano 写道:\n> Jiang Xin <worldhello.net@gmail.com> writes:\n> \n>> Untracked files may be significant for certain repositories, but if run the\n>> command \"git clean -fdx\" by accident, all untracked files will be lost.\n> \n> Don't add -x without thinking, then. It is the way to tell the command \"I\n> want to remove all the untracked files and I REALLY MEAN IT\".  It is often\n> used to say \"I do not trust Makefile and I want to remove what 'make\n> clean' would leave behind\".\n> \n> A slightly related tangent is that we only have three classes of paths:\n> \n>  - tracked ones\n>  - untracked ones, where there are two subclasses\n>    - unignored ones (e.g. new source file you haven't added)\n>    - ignored ones (e.g. build artifacts like *.o files)\n> \n> and because of that, the general design is to consider \"ignored\" files\n> expendable during various operations. Sometimes people deliberately \"ignore\"\n> files that they consider not expendable, which is (by today's definition)\n> a wrong thing to do, but I think in the longer term we should add a way to\n> mark them as \"ignored but precious\".\n> \n>   http://thread.gmane.org/gmane.comp.version-control.git/172818/focus=172846\n> \n> Nobody has designed how this fourth class should behave (and how the\n> behaviour of the \"ignored\" should change, if any) yet, but a rough outline\n> would probably be:\n> \n>  - precious files are the ones that are ignored (by today's definition,\n>    i.e. .gitignore mechanism consideres they are ignored) but marked as\n>    \"precious\" in some other way [*1*]. They will\n> \n>    - not appear in \"Untracked files:\" section in \"git status\" output;\n>    - not be added by \"git add\" without \"-f\", just like other ignored files;\n>    - not be overwritten or removed to make room while switching branches;\n>    - not be removed with \"clean -f -x\" [*2*].\n> \n>  - ignored files will stay to be \"expendable\".\n> \n> I suspect there may be some codepaths that incorrectly treat them as not\n> expendable, and protect their lossage. We would want to fix them after we\n> introduce the \"precious\" class.\n> \n> [Footnotes]\n> \n> *1* We could invent a way to sneak such entries in .gitignore, but I am\n> inclined to think it would be cleaner to define \"precious\" attribute and\n> let the attributes mechanism handle this.\n> \n> *2* This is just off the top of my head without thinking things\n> through. It might turn out that it makes more sense ot remove them.\n\n\n-- \n蒋鑫\n\n北京群英汇信息技术有限公司\n邮件: worldhello.net@gmail.com\n网址: http://www.ossxp.com/\n　　  http://blog.ossxp.com/\n电话: 010-51262007, 13910430470\n传真: 010-51262007\n"}]}