Untracked files may be significant for certain repositories, but if run the command "git clean -fdx" by accident, all untracked files will be lost. This hack adds three values in addtion to true/false to "clean.requireforce", which can protect untracked files from cleaning: * true or unset : can not clean without -f/--force option provided. * false : clean untracked files just as -f/--force option provided. * lockignored : can not clean untracked ignored files. * lockunignored : can not clean untracked unignored files. * lockall : can not clean anything. Signed-off-by: Jiang Xin <jiangxin@xxxxxxxxx> --- Documentation/config.txt | 7 +++- builtin/clean.c | 29 ++++++++++++++++++- t/t7300-clean.sh | 69 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 4 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 6b93777..b930f42 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -684,8 +684,11 @@ browser.<tool>.path:: working repository in gitweb (see linkgit:git-instaweb[1]). clean.requireForce:: - A boolean to make git-clean do nothing unless given -f - or -n. Defaults to true. + When set to `LockIgnored`, cleaning untracked ignored files is + denied. When set to `LockUnIgnored`, only allow cleaning untracked + ignored files using -X option. When set to `LockAll`, no files + can be cleaned until unset this variable. A boolean to make + git-clean do nothing unless given -f or -n. Defaults to true. color.branch:: A boolean to enable/disable color in the output of diff --git a/builtin/clean.c b/builtin/clean.c index 75697f7..441f35a 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -13,7 +13,11 @@ #include "string-list.h" #include "quote.h" +#define LOCK_IGNORED 01 +#define LOCK_UNIGNORED 02 + static int force = -1; /* unset */ +static int lock_flag = 0; static const char *const builtin_clean_usage[] = { "git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>...", @@ -22,8 +26,17 @@ static const char *const builtin_clean_usage[] = { static int git_clean_config(const char *var, const char *value, void *cb) { - if (!strcmp(var, "clean.requireforce")) - force = !git_config_bool(var, value); + if (!strcmp(var, "clean.requireforce")) { + if (value && !strcasecmp(value, "LockIgnored")) + lock_flag = LOCK_IGNORED; + else if (value && !strcasecmp(value, "LockUnIgnored")) + lock_flag = LOCK_UNIGNORED; + else if (value && !strcasecmp(value, "LockAll")) + lock_flag = LOCK_IGNORED | LOCK_UNIGNORED; + else + force = !git_config_bool(var, value); + return 0; + } return git_default_config(var, value, cb); } @@ -77,6 +90,18 @@ int cmd_clean(int argc, const char **argv, const char *prefix) if (ignored && ignored_only) die(_("-x and -X cannot be used together")); + if (!show_only && lock_flag) { + if (lock_flag & LOCK_IGNORED && lock_flag & LOCK_UNIGNORED) + die(_("clean.requireForce set to LockAll; " + "refusing to clean until reset clean.requireForce")); + else if (lock_flag & LOCK_IGNORED && (ignored_only || ignored)) + die(_("clean.requireForce set to LockIgnored and conflict with -x or -X; " + "refusing to clean")); + else if (lock_flag & LOCK_UNIGNORED && !ignored_only) + die(_("clean.requireForce set to LockUnIgnored and can only work with -X; " + "refusing to clean")); + } + if (!show_only && !force) { if (config_set) die(_("clean.requireForce set to true and neither -n nor -f given; " diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh index 800b536..b4f38dd 100755 --- a/t/t7300-clean.sh +++ b/t/t7300-clean.sh @@ -460,4 +460,73 @@ test_expect_success SANITY 'git clean -d with an unreadable empty directory' ' ! test -d foo ' +test_expect_success 'clean.requireForce LockIgnored' ' + + git config clean.requireForce LockIgnored && + mkdir -p build docs && + touch a.out src/part3.c docs/manual.txt obj.o build/lib.so && + test_must_fail git clean && + test_must_fail git clean -f -x && + test_must_fail git clean -f -X && + git clean -f && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test ! -f src/part3.c && + test -f docs/manual.txt && + test -f obj.o && + test -f build/lib.so && + git clean -f -d && + test ! -f docs/manual.txt && + test -f build/lib.so && + git config clean.requireForce true +' + +test_expect_success 'clean.requireForce LockUnIgnored' ' + + git config clean.requireForce LockUnIgnored && + mkdir -p build docs && + touch a.out src/part3.c docs/manual.txt obj.o build/lib.so && + test_must_fail git clean && + test_must_fail git clean -f && + test_must_fail git clean -f -x && + git clean -f -X && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test -f src/part3.c && + test -f docs/manual.txt && + test ! -f obj.o && + test -f build/lib.so && + git clean -f -d -X && + test ! -f build/lib.so && + test -f src/part3.c && + git config clean.requireForce true +' + +test_expect_success 'clean.requireForce LockAll' ' + + git config clean.requireForce lockall && + mkdir -p build docs && + touch a.out src/part3.c docs/manual.txt obj.o build/lib.so && + test_must_fail git clean -f && + test_must_fail git clean -f -x && + test_must_fail git clean -f -X && + git clean -ndx && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test -f src/part3.c && + test -f docs/manual.txt && + test -f obj.o && + test -f build/lib.so && + git config clean.requireForce true +' + test_done -- 1.7.5.4.1.g6c49.dirty -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html