Skip to contentSkip to conversation

Add --exclude to git-clean.j

PATCH In review Jared Hance 5 participants 16 messages Jun 15, 2016 Documentation

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Add --exclude to git-clean.j

With the -e/--exclude option for git-clean, a user can specify files that they haven't yet told git about, but either need for a short amount of time or plan to tell git about them later. This allows one to still use git-clean while these files are around without losing data.

---
 Documentation/git-clean.txt |    6 +++++-
 builtin/clean.c             |   21 +++++++++++++++++++++
 2 files changed, 26 insertions(+), 1 deletions(-)
Documentation/git-clean.txt +5 -1
index a81cb6c..2bf9cf2 100644
@@ -8,7 +8,7 @@ git-clean - Remove untracked files from the working tree
 SYNOPSIS
 --------
 [verse]
-'git clean' [-d] [-f] [-n] [-q] [-x | -X] [--] <path>...
+'git clean' [-d] [-f] [-n] [-q] [-e] [-x | -X] [--] <path>...
 
 DESCRIPTION
 -----------
@@ -45,6 +45,10 @@ OPTIONS
 	Be quiet, only report errors, but not the files that are
 	successfully removed.
 
+-e::
+--exclude::
+	Specify special exceptions to not be cleaned. Separate with colon.
+
 -x::
 	Don't use the ignore rules.  This allows removing all untracked
 	files, including build products.  This can be used (possibly in
builtin/clean.c +21 -0
index fac64e6..aa3eed8 100644
@@ -13,6 +13,7 @@
 #include "quote.h"
 
 static int force = -1; /* unset */
+static char *excludes;
 
 static const char *const builtin_clean_usage[] = {
 	"git clean [-d] [-f] [-n] [-q] [-x | -X] [--] <paths>...",
@@ -36,6 +37,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 	struct dir_struct dir;
 	static const char **pathspec;
 	struct strbuf buf = STRBUF_INIT;
+	struct strbuf excludes_buf = STRBUF_INIT;
+	struct strbuf **excludes_split;
 	const char *qname;
 	char *seen = NULL;
 	struct option options[] = {
@@ -44,6 +47,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 		OPT_BOOLEAN('f', "force", &force, "force"),
 		OPT_BOOLEAN('d', NULL, &remove_directories,
 				"remove whole directories"),
+		OPT_STRING('e', "exclude", &excludes, "EXCLUDES", "specify files not to clean"),
 		OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"),
 		OPT_BOOLEAN('X', NULL, &ignored_only,
 				"remove only ignored files"),
@@ -81,6 +85,17 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 	if (!ignored)
 		setup_standard_excludes(&dir);
 
+	if (excludes) {
+		strbuf_addstr(&excludes_buf, excludes);
+		excludes_split = strbuf_split(&excludes_buf, ':');
+		for (i = 0; excludes_split[i]; i++) {
+			if (excludes_split[i]->buf[excludes_split[i]->len - 1] == ':') {
+				strbuf_remove(excludes_split[i], excludes_split[i]->len - 1, 1);
+			}
+			add_exclude(excludes_split[i]->buf, "", 0, dir.exclude_list);
+		}
+	}
+
 	pathspec = get_pathspec(prefix, argv);
 
 	fill_directory(&dir, pathspec);
@@ -167,5 +182,11 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 	free(seen);
 
 	strbuf_release(&directory);
+	if (excludes) {
+		strbuf_release(&excludes_buf);
+		for (i = 0; excludes_split[i]; i++) {
+			strbuf_release(excludes_split[i]);
+		}
+	}
 	return (errors != 0);
 }
Signature · 1 line
1.7.1.1
#1 lore raw
Quoted 3 lines · Jared Hance

With the -e/--exclude option for git-clean, a user can specify files that they haven't yet told git about, but either need for a short amount of time or plan to tell git about them later.

No thoughts on your patch, but have you looked into “git add -N”?

Jonathan

#2 lore raw
Quoted 1 line · On Mon, Jul 19, 2010 at 11:14:14AM -0500, Jonathan Nieder wrote: Johnathon…

No thoughts on your patch, but have you looked into “git add -N”?

That is interesting. However, it doesn't really help for files that you don't intend to add to the index later. "git clean -e" is nice for when you have a file you are using for some temporary purpose.

On another note, I failed getting my title correct, so I sent another patch with the correct title (I think it affects the description of the commit).

#3 lore raw

PATCH v2 1/1 Add --exclude to git-clean.

In reply to Jared Hance

With the -e/--exclude option for git-clean, a user can specify files that they haven't yet told git about, but either need for a short amount of time or plan to tell git about them later. This allows one to still use git-clean while these files are around without losing data.

---
 Documentation/git-clean.txt |    6 +++++-
 builtin/clean.c             |   21 +++++++++++++++++++++
 2 files changed, 26 insertions(+), 1 deletions(-)
Documentation/git-clean.txt +5 -1
index a81cb6c..2bf9cf2 100644
@@ -8,7 +8,7 @@ git-clean - Remove untracked files from the working tree
 SYNOPSIS
 --------
 [verse]
-'git clean' [-d] [-f] [-n] [-q] [-x | -X] [--] <path>...
+'git clean' [-d] [-f] [-n] [-q] [-e] [-x | -X] [--] <path>...
 
 DESCRIPTION
 -----------
@@ -45,6 +45,10 @@ OPTIONS
 	Be quiet, only report errors, but not the files that are
 	successfully removed.
 
+-e::
+--exclude::
+	Specify special exceptions to not be cleaned. Separate with colon.
+
 -x::
 	Don't use the ignore rules.  This allows removing all untracked
 	files, including build products.  This can be used (possibly in
builtin/clean.c +21 -0
index fac64e6..aa3eed8 100644
@@ -13,6 +13,7 @@
 #include "quote.h"
 
 static int force = -1; /* unset */
+static char *excludes;
 
 static const char *const builtin_clean_usage[] = {
 	"git clean [-d] [-f] [-n] [-q] [-x | -X] [--] <paths>...",
@@ -36,6 +37,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 	struct dir_struct dir;
 	static const char **pathspec;
 	struct strbuf buf = STRBUF_INIT;
+	struct strbuf excludes_buf = STRBUF_INIT;
+	struct strbuf **excludes_split;
 	const char *qname;
 	char *seen = NULL;
 	struct option options[] = {
@@ -44,6 +47,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 		OPT_BOOLEAN('f', "force", &force, "force"),
 		OPT_BOOLEAN('d', NULL, &remove_directories,
 				"remove whole directories"),
+		OPT_STRING('e', "exclude", &excludes, "EXCLUDES", "specify files not to clean"),
 		OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"),
 		OPT_BOOLEAN('X', NULL, &ignored_only,
 				"remove only ignored files"),
@@ -81,6 +85,17 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 	if (!ignored)
 		setup_standard_excludes(&dir);
 
+	if (excludes) {
+		strbuf_addstr(&excludes_buf, excludes);
+		excludes_split = strbuf_split(&excludes_buf, ':');
+		for (i = 0; excludes_split[i]; i++) {
+			if (excludes_split[i]->buf[excludes_split[i]->len - 1] == ':') {
+				strbuf_remove(excludes_split[i], excludes_split[i]->len - 1, 1);
+			}
+			add_exclude(excludes_split[i]->buf, "", 0, dir.exclude_list);
+		}
+	}
+
 	pathspec = get_pathspec(prefix, argv);
 
 	fill_directory(&dir, pathspec);
@@ -167,5 +182,11 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 	free(seen);
 
 	strbuf_release(&directory);
+	if (excludes) {
+		strbuf_release(&excludes_buf);
+		for (i = 0; excludes_split[i]; i++) {
+			strbuf_release(excludes_split[i]);
+		}
+	}
 	return (errors != 0);
 }
Signature · 1 line
1.7.1.1
#4 lore raw

Re: [PATCH v2] Add --exclude to git-clean.

Documentation/git-clean.txt git-clean - Remove untracked files from the working tree · line 11

Quoted 10 lines · Jared Hance
Documentation/git-clean.txt +1 -1
index a81cb6c..2bf9cf2 100644
@@ -8,7 +8,7 @@ git-clean - Remove untracked files from the working tree
 SYNOPSIS
 --------
 [verse]
-'git clean' [-d] [-f] [-n] [-q] [-x | -X] [--] <path>...
+'git clean' [-d] [-f] [-n] [-q] [-e] [-x | -X] [--] <path>...

This and ...

Documentation/git-clean.txt OPTIONS · line 49

Quoted 8 lines
Documentation/git-clean.txt +2 -0
 DESCRIPTION
 -----------
@@ -45,6 +45,10 @@ OPTIONS
 	Be quiet, only report errors, but not the files that are
 	successfully removed.
 
+-e::
+--exclude::

this both look wrong. They do not tell the readers that the option takes a mandatory argument that specifies the "exceptions". Worse yet,

Documentation/git-clean.txt OPTIONS · line 50

Quoted 1 line
Documentation/git-clean.txt +1 -0
+	Specify special exceptions to not be cleaned. Separate with colon.

this does not tell _how_ exceptions are specified.

What should each element on the list look like?

Is it a dot-suffix without dot (e.g. "html") or with dot (e.g. ".html")? Or is it a glob (e.g. "*.html")? Or is it a full path relative to the worktree root (e.g. "Documentation/index.html")?

Using colon as an inter-element separator makes sense only if last one is true (i.e. "concrete path, not glob nor suffix"), so an intelligent reader could probably guess what you meant, but you shouldn't make readers guess in the first place.

If on the other hand you wanted to allow specifying the same kind of patterns used in the gitignore files from the command line:

(1) A list separated with whitespace would be more natural, not a colon; and

(2) I have to wonder why do we give such a command line exclude override to begin with.

(2-a) wouldn't it be easier for the user to add such a local
configuration to $GIT_DIR/info/exclude once and be done with it?

(2-b) if command-line override has benefit, why is it limited to only
_exclude_ and not include (iow, additional ignore patterns)?

#5 lore raw

Re: [PATCH v2] Add --exclude to git-clean.

Quoted 2 lines · Junio C Hamano

this both look wrong. They do not tell the readers that the option takes a mandatory argument that specifies the "exceptions". Worse yet,

Sorry about that. I will get that fixed.

Quoted 15 lines

Documentation/git-clean.txt OPTIONS · line 50

Quoted 1 line
Documentation/git-clean.txt +1 -0
+	Specify special exceptions to not be cleaned. Separate with colon.

this does not tell _how_ exceptions are specified.

What should each element on the list look like?

Is it a dot-suffix without dot (e.g. "html") or with dot (e.g. ".html")? Or is it a glob (e.g. "*.html")? Or is it a full path relative to the worktree root (e.g. "Documentation/index.html")?

Using colon as an inter-element separator makes sense only if last one is true (i.e. "concrete path, not glob nor suffix"), so an intelligent reader could probably guess what you meant, but you shouldn't make readers guess in the first place.

Quoted 5 lines

If on the other hand you wanted to allow specifying the same kind of patterns used in the gitignore files from the command line:

(1) A list separated with whitespace would be more natural, not a colon; and

It is a global (like in the .gitignore file). I will change it to whitespace. At first, I was worried about files with whitespace, and a colon seemed to fit it better (since files don't have colons).

On the other hand, can you think of any character that would fit both a glob and a strict filename (as it allows both)? I had trouble figuring out what to use here.

Quoted 6 lines

(2) I have to wonder why do we give such a command line exclude override to begin with.

(2-a) wouldn't it be easier for the user to add such a local
configuration to $GIT_DIR/info/exclude once and be done with it?

As I said earlier, its generally useful for files you are only going to have for a few minutes and you don't want to have the clutter up $GIT_DIR/info/exclude with them (since they probably won't ever return).

Quoted 2 lines

(2-b) if command-line override has benefit, why is it limited to only
_exclude_ and not include (iow, additional ignore patterns)?

"git clean && git rm <INCLUDES>" has the same effect as including in the clean.

#6 lore raw

PATCH v3 1/1 Add --exclude to git-clean.

In reply to Jared Hance

With the -e/--exclude option for git-clean, a user can specify files that they haven't yet told git about, but either need for a short amount of time or plan to tell git about them later. This allows one to still use git-clean while these files are around without losing data.

---
 Documentation/git-clean.txt |    7 ++++++-
 builtin/clean.c             |   21 +++++++++++++++++++++
 2 files changed, 27 insertions(+), 1 deletions(-)
Documentation/git-clean.txt +6 -1
index a81cb6c..488e103 100644
@@ -8,7 +8,7 @@ git-clean - Remove untracked files from the working tree
 SYNOPSIS
 --------
 [verse]
-'git clean' [-d] [-f] [-n] [-q] [-x | -X] [--] <path>...
+'git clean' [-d] [-f] [-n] [-q] [-e] [-x | -X] [--] <path>...
 
 DESCRIPTION
 -----------
@@ -45,6 +45,11 @@ OPTIONS
 	Be quiet, only report errors, but not the files that are
 	successfully removed.
 
+-e <files>::
+--exclude=<files>::
+	Specify special exceptions to not be cleaned. Separate with a space.
+	Globs, like that in $GIT_DIR/info/excludes, should be used.
+
 -x::
 	Don't use the ignore rules.  This allows removing all untracked
 	files, including build products.  This can be used (possibly in
builtin/clean.c +21 -0
index fac64e6..b1da923 100644
@@ -13,6 +13,7 @@
 #include "quote.h"
 
 static int force = -1; /* unset */
+static const char *excludes;
 
 static const char *const builtin_clean_usage[] = {
 	"git clean [-d] [-f] [-n] [-q] [-x | -X] [--] <paths>...",
@@ -36,6 +37,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 	struct dir_struct dir;
 	static const char **pathspec;
 	struct strbuf buf = STRBUF_INIT;
+	struct strbuf excludes_buf = STRBUF_INIT;
+	struct strbuf **excludes_split = NULL;
 	const char *qname;
 	char *seen = NULL;
 	struct option options[] = {
@@ -44,6 +47,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 		OPT_BOOLEAN('f', "force", &force, "force"),
 		OPT_BOOLEAN('d', NULL, &remove_directories,
 				"remove whole directories"),
+		OPT_STRING('e', "exclude", &excludes, "EXCLUDES", "specify files not to clean"),
 		OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"),
 		OPT_BOOLEAN('X', NULL, &ignored_only,
 				"remove only ignored files"),
@@ -81,6 +85,17 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 	if (!ignored)
 		setup_standard_excludes(&dir);
 
+	if (excludes) {
+		strbuf_addstr(&excludes_buf, excludes);
+		excludes_split = strbuf_split(&excludes_buf, ' ');
+		for (i = 0; excludes_split[i]; i++) {
+			if (excludes_split[i]->buf[excludes_split[i]->len - 1] == ' ') {
+				strbuf_remove(excludes_split[i], excludes_split[i]->len - 1, 1);
+			}
+			add_exclude(excludes_split[i]->buf, "", 0, dir.exclude_list);
+		}
+	}
+
 	pathspec = get_pathspec(prefix, argv);
 
 	fill_directory(&dir, pathspec);
@@ -167,5 +182,11 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 	free(seen);
 
 	strbuf_release(&directory);
+	if (excludes) {
+		strbuf_release(&excludes_buf);
+		for (i = 0; excludes_split[i]; i++) {
+			strbuf_release(excludes_split[i]);
+		}
+	}
 	return (errors != 0);
 }
Signature · 1 line
1.7.1.1
#7 lore raw

v4 cover Add -e/--exclude to git clean.

This is the fourth round of patches for git clean -e.

This round introduces a test in t/t7300-clean.sh.

No other changes were added to builtin/clean.c or
Documentation/git-clean.txt.

Since this patch seems to be somewhat controversial, I've marked it as PATCH/RFC. I would like some ideas on what to use for separators as well as constructive critism on the Documentation and test.

Jared Hance (2):
Add --exclude to git-clean.
Add test for git clean -e.

 Documentation/git-clean.txt |    7 ++++++-
 builtin/clean.c             |   21 +++++++++++++++++++++
 t/t7300-clean.sh            |   16 ++++++++++++++++
 3 files changed, 43 insertions(+), 1 deletions(-)
#8 lore raw

PATCH v4 1/2 Add --exclude to git-clean.

With the -e/--exclude option for git-clean, a user can specify files that they haven't yet told git about, but either need for a short amount of time or plan to tell git about them later. This allows one to still use git-clean while these files are around without losing data.

---
 Documentation/git-clean.txt |    7 ++++++-
 builtin/clean.c             |   21 +++++++++++++++++++++
 2 files changed, 27 insertions(+), 1 deletions(-)
Documentation/git-clean.txt +6 -1
index a81cb6c..488e103 100644
@@ -8,7 +8,7 @@ git-clean - Remove untracked files from the working tree
 SYNOPSIS
 --------
 [verse]
-'git clean' [-d] [-f] [-n] [-q] [-x | -X] [--] <path>...
+'git clean' [-d] [-f] [-n] [-q] [-e] [-x | -X] [--] <path>...
 
 DESCRIPTION
 -----------
@@ -45,6 +45,11 @@ OPTIONS
 	Be quiet, only report errors, but not the files that are
 	successfully removed.
 
+-e <files>::
+--exclude=<files>::
+	Specify special exceptions to not be cleaned. Separate with a space.
+	Globs, like that in $GIT_DIR/info/excludes, should be used.
+
 -x::
 	Don't use the ignore rules.  This allows removing all untracked
 	files, including build products.  This can be used (possibly in
builtin/clean.c +21 -0
index fac64e6..b1da923 100644
@@ -13,6 +13,7 @@
 #include "quote.h"
 
 static int force = -1; /* unset */
+static const char *excludes;
 
 static const char *const builtin_clean_usage[] = {
 	"git clean [-d] [-f] [-n] [-q] [-x | -X] [--] <paths>...",
@@ -36,6 +37,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 	struct dir_struct dir;
 	static const char **pathspec;
 	struct strbuf buf = STRBUF_INIT;
+	struct strbuf excludes_buf = STRBUF_INIT;
+	struct strbuf **excludes_split = NULL;
 	const char *qname;
 	char *seen = NULL;
 	struct option options[] = {
@@ -44,6 +47,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 		OPT_BOOLEAN('f', "force", &force, "force"),
 		OPT_BOOLEAN('d', NULL, &remove_directories,
 				"remove whole directories"),
+		OPT_STRING('e', "exclude", &excludes, "EXCLUDES", "specify files not to clean"),
 		OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"),
 		OPT_BOOLEAN('X', NULL, &ignored_only,
 				"remove only ignored files"),
@@ -81,6 +85,17 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 	if (!ignored)
 		setup_standard_excludes(&dir);
 
+	if (excludes) {
+		strbuf_addstr(&excludes_buf, excludes);
+		excludes_split = strbuf_split(&excludes_buf, ' ');
+		for (i = 0; excludes_split[i]; i++) {
+			if (excludes_split[i]->buf[excludes_split[i]->len - 1] == ' ') {
+				strbuf_remove(excludes_split[i], excludes_split[i]->len - 1, 1);
+			}
+			add_exclude(excludes_split[i]->buf, "", 0, dir.exclude_list);
+		}
+	}
+
 	pathspec = get_pathspec(prefix, argv);
 
 	fill_directory(&dir, pathspec);
@@ -167,5 +182,11 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 	free(seen);
 
 	strbuf_release(&directory);
+	if (excludes) {
+		strbuf_release(&excludes_buf);
+		for (i = 0; excludes_split[i]; i++) {
+			strbuf_release(excludes_split[i]);
+		}
+	}
 	return (errors != 0);
 }
Signature · 1 line
1.7.1.1
#9 lore raw

PATCH v4 2/2 Add test for git clean -e.

In reply to Jared Hance
---
 t/t7300-clean.sh |   16 ++++++++++++++++
 1 files changed, 16 insertions(+), 0 deletions(-)
t/t7300-clean.sh +16 -0
index 7d8ed68..c6df26d 100755
@@ -438,4 +438,20 @@ test_expect_success 'force removal of nested git work tree' '
 	! test -d bar
 '
 
+test_expect_success 'git clean -e' '
+	rm -fr repo &&
+	mkdir repo &&
+	(
+		cd repo &&
+		git init &&
+		touch 1 2 3 known &&
+		git add known &&
+		git clean -f -e "1 2" &&
+		test -e 1 &&
+		test -e 2 &&
+		! (test -e 3) &&
+		test -e known
+	)
+'
+
 test_done
Signature · 1 line
1.7.1.1
#10 lore raw

Re: [PATCH/RFC v4 0/2] Add -e/--exclude to git clean.

In reply to Jared Hance
Quoted 3 lines · Jared Hance

This is the fourth round of patches for git clean -e.
Since this patch seems to be somewhat controversial, I've marked it as PATCH/RFC. I would like some ideas on what to use for separators

Rather than stuffing multiple exclusions into a single option, how about requiring one -e option per exclusion?

git clean -e foo -e bar

Signature · 1 line
Aaron Crane ** http://aaroncrane.co.uk/
#11 lore raw

Re: [PATCH/RFC v4 0/2] Add -e/--exclude to git clean.

Quoted 4 lines · Aaron Crane

Rather than stuffing multiple exclusions into a single option, how about requiring one -e option per exclusion?

git clean -e foo -e bar

I actually considered that - However, Documentation/technical/api-parse-options.txt does not document how to do this type of thing with the the parse options api. I will look into the code for some examples to see if I can figure it out.

#12 lore raw

Re: [PATCH/RFC v4 0/2] Add -e/--exclude to git clean.

In reply to Aaron Crane
Quoted 9 lines · Aaron Crane
Quoted 3 lines · Jared Hance

This is the fourth round of patches for git clean -e.
Since this patch seems to be somewhat controversial, I've marked it as PATCH/RFC. I would like some ideas on what to use for separators

Rather than stuffing multiple exclusions into a single option, how about requiring one -e option per exclusion?

git clean -e foo -e bar

I find it a lot saner.

Sorry, Jared, I should have thought of it and suggested it during the first review round.

#13 lore raw

Re: [PATCH/RFC v4 0/2] Add -e/--exclude to git clean.

Quoted 10 lines · Junio C Hamano
Quoted 8 lines · Aaron Crane
Quoted 3 lines · Jared Hance

This is the fourth round of patches for git clean -e.
Since this patch seems to be somewhat controversial, I've marked it as PATCH/RFC. I would like some ideas on what to use for separators

Rather than stuffing multiple exclusions into a single option, how about requiring one -e option per exclusion?

Quoted 6 lines
Quoted 1 line

git clean -e foo -e bar

I find it a lot saner.

Sorry, Jared, I should have thought of it and suggested it during the first review round.

The fix-up should look something like this, on top of your patch.

Note that I did this on top of applying your patch to 'maint', and you may need to adjust the parameter order of string-list functions if you want to forward port it to 'master'.

Untested, of course ;-)

 Documentation/git-clean.txt |   11 ++++++-----
 builtin/clean.c             |   36 +++++++++++++++---------------------
 2 files changed, 21 insertions(+), 26 deletions(-)
Documentation/git-clean.txt +6 -5
index 488e103..60e38e6 100644
@@ -8,7 +8,7 @@ git-clean - Remove untracked files from the working tree
 SYNOPSIS
 --------
 [verse]
-'git clean' [-d] [-f] [-n] [-q] [-e] [-x | -X] [--] <path>...
+'git clean' [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <path>...
 
 DESCRIPTION
 -----------
@@ -45,10 +45,11 @@ OPTIONS
 	Be quiet, only report errors, but not the files that are
 	successfully removed.
 
--e <files>::
---exclude=<files>::
-	Specify special exceptions to not be cleaned. Separate with a space.
-	Globs, like that in $GIT_DIR/info/excludes, should be used.
+-e <pattern>::
+--exclude=<pattern>::
+	Specify special exceptions to not be cleaned.  Each <pattern> is
+	the same form as in $GIT_DIR/info/excludes and this option can be
+	given multiple times.
 
 -x::
 	Don't use the ignore rules.  This allows removing all untracked
builtin/clean.c +15 -21
index b1da923..58c9c06 100644
@@ -10,13 +10,13 @@
 #include "cache.h"
 #include "dir.h"
 #include "parse-options.h"
+#include "string-list.h"
 #include "quote.h"
 
 static int force = -1; /* unset */
-static const char *excludes;
 
 static const char *const builtin_clean_usage[] = {
-	"git clean [-d] [-f] [-n] [-q] [-x | -X] [--] <paths>...",
+	"git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>...",
 	NULL
 };
 
@@ -27,6 +27,13 @@ static int git_clean_config(const char *var, const char *value, void *cb)
 	return git_default_config(var, value, cb);
 }
 
+static int exclude_cb(const struct option *opt, const char *arg, int unset)
+{
+	struct string_list *exclude_list = opt->value;
+	string_list_append(arg, exclude_list);
+	return 0;
+}
+
 int cmd_clean(int argc, const char **argv, const char *prefix)
 {
 	int i;
@@ -37,8 +44,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 	struct dir_struct dir;
 	static const char **pathspec;
 	struct strbuf buf = STRBUF_INIT;
-	struct strbuf excludes_buf = STRBUF_INIT;
-	struct strbuf **excludes_split = NULL;
+	struct string_list exclude_list = { NULL, 0, 0, 0 };
 	const char *qname;
 	char *seen = NULL;
 	struct option options[] = {
@@ -47,7 +53,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 		OPT_BOOLEAN('f', "force", &force, "force"),
 		OPT_BOOLEAN('d', NULL, &remove_directories,
 				"remove whole directories"),
-		OPT_STRING('e', "exclude", &excludes, "EXCLUDES", "specify files not to clean"),
+		{ OPTION_CALLBACK, 'e', "exclude", &exclude_list, "pattern",
+		  "exclude <pattern>", PARSE_OPT_NONEG, exclude_cb },
 		OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"),
 		OPT_BOOLEAN('X', NULL, &ignored_only,
 				"remove only ignored files"),
@@ -85,16 +92,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 	if (!ignored)
 		setup_standard_excludes(&dir);
 
-	if (excludes) {
-		strbuf_addstr(&excludes_buf, excludes);
-		excludes_split = strbuf_split(&excludes_buf, ' ');
-		for (i = 0; excludes_split[i]; i++) {
-			if (excludes_split[i]->buf[excludes_split[i]->len - 1] == ' ') {
-				strbuf_remove(excludes_split[i], excludes_split[i]->len - 1, 1);
-			}
-			add_exclude(excludes_split[i]->buf, "", 0, dir.exclude_list);
-		}
-	}
+	for (i = 0; i < exclude_list.nr; i++)
+		add_exclude(exclude_list.items[i].string, "", 0, dir.exclude_list);
 
 	pathspec = get_pathspec(prefix, argv);
 
@@ -182,11 +181,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 	free(seen);
 
 	strbuf_release(&directory);
-	if (excludes) {
-		strbuf_release(&excludes_buf);
-		for (i = 0; excludes_split[i]; i++) {
-			strbuf_release(excludes_split[i]);
-		}
-	}
+	string_list_clear(&exclude_list, 0);
 	return (errors != 0);
 }
#14 lore raw

Re: [PATCH/RFC v4 0/2] Add -e/--exclude to git clean.

Quoted 7 lines · Junio C Hamano

The fix-up should look something like this, on top of your patch.

Note that I did this on top of applying your patch to 'maint', and you may need to adjust the parameter order of string-list functions if you want to forward port it to 'master'.

Untested, of course ;-)

Okay. Do you want to me to create a rebased version to combine this and my first patch?

#15 lore raw

Re: [PATCH/RFC v4 0/2] Add -e/--exclude to git clean.

The git clean -e option is more useful than just for stuff that you plan to bring under git control later. It also allows to retain per-user configurations of external tools when using git clean to remove all build results and intermediate files. This leads to the enhancement request described below.

Most IDEs (integrated development environments) allow the user to configure various display and debugger options, and store those options in some configuration file. Such user option files should not be under git control in a multi-user environment, because then all users are forced to use the same options. Yet these user option files should not be deleted when the rest of the untracked files are deleted (through git clean), otherwise the user options are lost.

git clean -x -d -f -e "pattern" removes all untracked files except those matching the pattern. However, it is cumbersome to specify these patterns every time. It would be more convenient if git clean would read such patterns from a file similar to .gitignore (maybe .gitignoreclean?). Then that file itself could be put under git control and shared with the other developers.

I request implementation of this feature.

Best regards,

Louis Strous