Those files that are managed by git, but out of working directory due to sparse-checkout file are subjected to be cleaned by "-S". Files that match the index exactly will be cleaned without "-f". Otherwise '-f' is needed. Signed-off-by: Nguyán ThÃi Ngác Duy <pclouds@xxxxxxxxx> --- Documentation/git-clean.txt | 6 ++- builtin/clean.c | 70 ++++++++++++++++++++++++++++++++ t/t7301-clean-sparse.sh | 92 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+), 1 deletions(-) create mode 100755 t/t7301-clean-sparse.sh diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt index 60e38e6..e0c95b1 100644 --- a/Documentation/git-clean.txt +++ b/Documentation/git-clean.txt @@ -8,7 +8,7 @@ git-clean - Remove untracked files from the working tree SYNOPSIS -------- [verse] -'git clean' [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <path>... +'git clean' [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X | -S] [--] <path>... DESCRIPTION ----------- @@ -61,6 +61,10 @@ OPTIONS Remove only files ignored by git. This may be useful to rebuild everything from scratch, but keep manually created files. +-S:: + Remove files tracked by git but are outside of sparse checkout. + Files that match the index exactly will be removed even when + '-f' is not given and clean.requireForce is no. Author ------ diff --git a/builtin/clean.c b/builtin/clean.c index c8798f5..5827993 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -34,11 +34,71 @@ static int exclude_cb(const struct option *opt, const char *arg, int unset) return 0; } +static int clean_sparse_checkout(const char *prefix, const char **pathspec, + int show_only, int quiet, int force) +{ + struct stat st; + int i, errors = 0; + unsigned char sha1[20]; + const char *qname; + struct strbuf buf = STRBUF_INIT; + + if (read_cache() < 0) + die("index file corrupt"); + for (i = 0; i < the_index.cache_nr; i++) { + struct cache_entry *ce = the_index.cache[i]; + + if (!ce_skip_worktree(ce)) + continue; + if (pathspec && !match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) + continue; + + if (stat(ce->name, &st) < 0) + continue; + qname = quote_path_relative(ce->name, ce_namelen(ce), &buf, prefix); + if (index_path(sha1, ce->name, &st, 0) < 0) { + warning("failed to hash %s", qname); + errors++; + continue; + } + if (!hashcmp(sha1, ce->sha1)) { + if (show_only) { + printf("Would remove %s\n", qname); + continue; + } + if (!quiet) + printf("Removing %s\n", qname); + if (unlink(ce->name) != 0) { + warning("failed to remove %s", qname); + errors++; + } + continue; + } + if (force) { + if (show_only) { + printf("Would remove %s\n", qname); + continue; + } + if (!quiet) + printf("Removing %s\n", qname); + if (unlink(ce->name) != 0) { + warning("failed to remove %s", qname); + errors++; + } + continue; + } + if (show_only) + printf("Would not remove %s\n", qname); + } + return errors != 0; +} + int cmd_clean(int argc, const char **argv, const char *prefix) { int i; int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0; int ignored_only = 0, baselen = 0, config_set = 0, errors = 0; + int sparse = 0; int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT; struct strbuf directory = STRBUF_INIT; struct dir_struct dir; @@ -55,6 +115,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) "remove whole directories"), { OPTION_CALLBACK, 'e', "exclude", &exclude_list, "pattern", "exclude <pattern>", PARSE_OPT_NONEG, exclude_cb }, + OPT_BOOLEAN('S', NULL, &sparse, "remove tracked files outside sparse checkout"), OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"), OPT_BOOLEAN('X', NULL, &ignored_only, "remove only ignored files"), @@ -70,6 +131,15 @@ int cmd_clean(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, builtin_clean_usage, 0); + if (sparse) { + if (ignored || ignored_only) + die("-S, -x and -X cannot be used together"); + if (exclude_list.nr) + die("-S and -e cannot be used together (yet)"); + pathspec = get_pathspec(prefix, argv); + return clean_sparse_checkout(prefix, pathspec, show_only, quiet, force); + } + memset(&dir, 0, sizeof(dir)); if (ignored_only) dir.flags |= DIR_SHOW_IGNORED; diff --git a/t/t7301-clean-sparse.sh b/t/t7301-clean-sparse.sh new file mode 100755 index 0000000..3ac0b0a --- /dev/null +++ b/t/t7301-clean-sparse.sh @@ -0,0 +1,92 @@ +#!/bin/sh + +test_description='git clean -S basic tests' + +. ./test-lib.sh + +git config clean.requireForce yes + +test_expect_success 'setup' ' + mkdir src && + touch file1 file2 && + touch src/file1 src/file2 && + git add . && + git update-index --skip-worktree file1 src/file1 +' + +test_expect_success 'clean -x -S does not work' ' + test_must_fail git clean -x -S && + test_must_fail git clean -X -S +' + +test_expect_success 'clean -n -S' ' + cat >expected <<\EOF +Would remove file1 +Would remove src/file1 +EOF + git clean -n -S >result && + test_cmp expected result +' + +test_expect_success 'clean -n -S src' ' + cat >expected <<\EOF +Would remove src/file1 +EOF + git clean -n -S src >result && + test_cmp expected result +' + +test_expect_success '[src] clean -n -S .' ' + ( + cd src + cat >expected <<\EOF +Would remove file1 +EOF + git clean -n -S . >result && + test_cmp expected result + ) +' + +test_expect_success '[src] clean -n -S ../file1' ' + ( + cd src + cat >expected <<\EOF +Would remove ../file1 +EOF + git clean -n -S ../file1 >result && + test_cmp expected result + ) +' + +test_expect_success 'clean -n -S with dirty worktree' ' + echo dirty >file1 && + cat >expected <<\EOF +Would not remove file1 +Would remove src/file1 +EOF + git clean -n -S >result && + test_cmp expected result +' + +test_expect_success 'clean -f -n -S with dirty worktree' ' + echo dirty >file1 && + cat >expected <<\EOF +Would remove file1 +Would remove src/file1 +EOF + git clean -f -n -S >result && + test_cmp expected result +' + +test_expect_success 'clean -S with dirty worktree' ' + git clean -S && + grep dirty file1 && + test ! -f src/file1 +' + +test_expect_success 'clean -f -S with dirty worktree' ' + git clean -f -S && + test ! -f file1 +' + +test_done -- 1.7.3.2.210.g045198 -- 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