* RFC: git bisect should accept "paths-to-be-excluded"
@ 2013-09-16 12:39 Toralf Förster
2013-09-17 7:26 ` Christian Couder
0 siblings, 1 reply; 16+ messages in thread
From: Toralf Förster @ 2013-09-16 12:39 UTC (permalink / raw)
To: git@vger.kernel.org
I'm bisecting a linux kernel issue and want to ignore all commits just
touching something in ./drives/staging.
Currently the only way would be to specify all dir/subdir combination
under ./linux except that particular directory, right ?
--
MfG/Sincerely
Toralf Förster
pgp finger print: 7B1A 07F4 EC82 0F90 D4C2 8936 872A E508 7DB6 9DA3
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: RFC: git bisect should accept "paths-to-be-excluded"
2013-09-16 12:39 RFC: git bisect should accept "paths-to-be-excluded" Toralf Förster
@ 2013-09-17 7:26 ` Christian Couder
2013-09-17 8:21 ` Matthieu Moy
2013-09-17 16:23 ` RFC: git bisect should accept "paths-to-be-excluded" Toralf Förster
0 siblings, 2 replies; 16+ messages in thread
From: Christian Couder @ 2013-09-17 7:26 UTC (permalink / raw)
To: Toralf Förster; +Cc: git@vger.kernel.org
Hi,
On Mon, Sep 16, 2013 at 2:39 PM, Toralf Förster <toralf.foerster@gmx.de> wrote:
> I'm bisecting a linux kernel issue and want to ignore all commits just
> touching something in ./drives/staging.
>
> Currently the only way would be to specify all dir/subdir combination
> under ./linux except that particular directory, right ?
Yeah, you are right, currently the only way would be to specify all
dir/subdir combination
under ./linux except the particular directory you want to exclude.
It might indeed be useful to have a way to exclude some directories or files.
In practice though, as git bisect is a kind of binary search, if what
you want to exclude is exclusively touched by half the commits, it
will only add one more bisection step if you don't exclude it.
Best regards,
Christian.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: RFC: git bisect should accept "paths-to-be-excluded"
2013-09-17 7:26 ` Christian Couder
@ 2013-09-17 8:21 ` Matthieu Moy
2013-09-17 9:03 ` Christian Couder
2013-09-17 16:23 ` RFC: git bisect should accept "paths-to-be-excluded" Toralf Förster
1 sibling, 1 reply; 16+ messages in thread
From: Matthieu Moy @ 2013-09-17 8:21 UTC (permalink / raw)
To: Christian Couder; +Cc: Toralf Förster, git@vger.kernel.org
Christian Couder <christian.couder@gmail.com> writes:
> In practice though, as git bisect is a kind of binary search, if what
> you want to exclude is exclusively touched by half the commits, it
> will only add one more bisection step if you don't exclude it.
Actually, I think the same remark would apply to any other Git command
that deal with a set of revisions. If you want to review code with "git
log -p", but you don't care about a subdirectory, you may want a "git
log -p --ignore-dir foo/" or so, too.
And then, the "it's logarithmic" argument doesn't work anymore ;-).
--
Matthieu Moy
http://www-verimag.imag.fr/~moy/
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: RFC: git bisect should accept "paths-to-be-excluded"
2013-09-17 8:21 ` Matthieu Moy
@ 2013-09-17 9:03 ` Christian Couder
2013-09-17 11:45 ` Duy Nguyen
0 siblings, 1 reply; 16+ messages in thread
From: Christian Couder @ 2013-09-17 9:03 UTC (permalink / raw)
To: Matthieu Moy, Nguyen Thai Ngoc Duy
Cc: Toralf Förster, git@vger.kernel.org
On Tue, Sep 17, 2013 at 10:21 AM, Matthieu Moy
<Matthieu.Moy@grenoble-inp.fr> wrote:
> Christian Couder <christian.couder@gmail.com> writes:
>
>> In practice though, as git bisect is a kind of binary search, if what
>> you want to exclude is exclusively touched by half the commits, it
>> will only add one more bisection step if you don't exclude it.
>
> Actually, I think the same remark would apply to any other Git command
> that deal with a set of revisions. If you want to review code with "git
> log -p", but you don't care about a subdirectory, you may want a "git
> log -p --ignore-dir foo/" or so, too.
Yeah, and there was a patch series about that 2 years ago:
http://thread.gmane.org/gmane.comp.version-control.git/182830/
> And then, the "it's logarithmic" argument doesn't work anymore ;-).
Sure.
Best regards,
Christian.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: RFC: git bisect should accept "paths-to-be-excluded"
2013-09-17 9:03 ` Christian Couder
@ 2013-09-17 11:45 ` Duy Nguyen
2013-09-17 17:02 ` Junio C Hamano
0 siblings, 1 reply; 16+ messages in thread
From: Duy Nguyen @ 2013-09-17 11:45 UTC (permalink / raw)
To: Christian Couder; +Cc: Matthieu Moy, Toralf Förster, git@vger.kernel.org
On Tue, Sep 17, 2013 at 4:03 PM, Christian Couder
<christian.couder@gmail.com> wrote:
> On Tue, Sep 17, 2013 at 10:21 AM, Matthieu Moy
> <Matthieu.Moy@grenoble-inp.fr> wrote:
>> Christian Couder <christian.couder@gmail.com> writes:
>>
>>> In practice though, as git bisect is a kind of binary search, if what
>>> you want to exclude is exclusively touched by half the commits, it
>>> will only add one more bisection step if you don't exclude it.
>>
>> Actually, I think the same remark would apply to any other Git command
>> that deal with a set of revisions. If you want to review code with "git
>> log -p", but you don't care about a subdirectory, you may want a "git
>> log -p --ignore-dir foo/" or so, too.
>
> Yeah, and there was a patch series about that 2 years ago:
>
> http://thread.gmane.org/gmane.comp.version-control.git/182830/
And that's just one of the few attempts if I remember correctly. I
guess it's time revisit it. A few things to sort out before we get to
the implementation:
Support flat or nested negation (i.e.include A, ignore A/B, but
include A/B/C..). Nested thing complicates things so I'm towards the
flat exclusion (exclude B means all inside B, no buts nor excepts) and
probably cover most use cases
Interaction with "git grep --depth"
Syntax. I guess --ignore (or --exclude) is more intuitive than
":(exclude)something" but then it might collide with existing options
(I did not check if --ignore or --exclude is used anywhere though).
The latter also enables combining with other filters, such as
case-insensitive matching..
--
Duy
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: RFC: git bisect should accept "paths-to-be-excluded"
2013-09-17 7:26 ` Christian Couder
2013-09-17 8:21 ` Matthieu Moy
@ 2013-09-17 16:23 ` Toralf Förster
1 sibling, 0 replies; 16+ messages in thread
From: Toralf Förster @ 2013-09-17 16:23 UTC (permalink / raw)
To: Christian Couder; +Cc: git@vger.kernel.org
On 09/17/2013 09:26 AM, Christian Couder wrote:
> Hi,
>
> On Mon, Sep 16, 2013 at 2:39 PM, Toralf Förster <toralf.foerster@gmx.de> wrote:
>> I'm bisecting a linux kernel issue and want to ignore all commits just
>> touching something in ./drives/staging.
>>
>> Currently the only way would be to specify all dir/subdir combination
>> under ./linux except that particular directory, right ?
>
> Yeah, you are right, currently the only way would be to specify all
> dir/subdir combination
> under ./linux except the particular directory you want to exclude.
>
> It might indeed be useful to have a way to exclude some directories or files.
Great to hear
> In practice though, as git bisect is a kind of binary search, if what
> you want to exclude is exclusively touched by half the commits, it
> will only add one more bisection step if you don't exclude it.
Unfortunately not. Linus pulls from Greg's staging tree usually once in
a merge window. It is not uncommon to have hundreds of commits in that
merge. If now (by accident) the merge point is marked as "BAD" and the
base is "GOOD", then git bisect falls into that trap and wastes about
ld(few hundreds) steps - and this happened here for me and each bisect
step took hours ...
> Best regards,
> Christian.
>
--
MfG/Sincerely
Toralf Förster
pgp finger print: 7B1A 07F4 EC82 0F90 D4C2 8936 872A E508 7DB6 9DA3
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: RFC: git bisect should accept "paths-to-be-excluded"
2013-09-17 11:45 ` Duy Nguyen
@ 2013-09-17 17:02 ` Junio C Hamano
2013-09-17 18:12 ` Piotr Krukowiecki
` (2 more replies)
0 siblings, 3 replies; 16+ messages in thread
From: Junio C Hamano @ 2013-09-17 17:02 UTC (permalink / raw)
To: Duy Nguyen
Cc: Christian Couder, Matthieu Moy, Toralf Förster,
git@vger.kernel.org
Duy Nguyen <pclouds@gmail.com> writes:
> On Tue, Sep 17, 2013 at 4:03 PM, Christian Couder
> <christian.couder@gmail.com> wrote:
>> On Tue, Sep 17, 2013 at 10:21 AM, Matthieu Moy
>> <Matthieu.Moy@grenoble-inp.fr> wrote:
>>> Christian Couder <christian.couder@gmail.com> writes:
>>>
>>>> In practice though, as git bisect is a kind of binary search, if what
>>>> you want to exclude is exclusively touched by half the commits, it
>>>> will only add one more bisection step if you don't exclude it.
>>>
>>> Actually, I think the same remark would apply to any other Git command
>>> that deal with a set of revisions. If you want to review code with "git
>>> log -p", but you don't care about a subdirectory, you may want a "git
>>> log -p --ignore-dir foo/" or so, too.
>>
>> Yeah, and there was a patch series about that 2 years ago:
>>
>> http://thread.gmane.org/gmane.comp.version-control.git/182830/
>
> And that's just one of the few attempts if I remember correctly. I
> guess it's time revisit it. A few things to sort out before we get to
> the implementation:
>
> Support flat or nested negation (i.e.include A, ignore A/B, but
> include A/B/C..). Nested thing complicates things so I'm towards the
> flat exclusion (exclude B means all inside B, no buts nor excepts) and
> probably cover most use cases
Yeah, it is easy to say that
git log -- A ':(exclude)A/B' A/B/C
has two positive (A, A/B/C) and one negative (A/B), and then the
most specific one A/B/C matches a path A/B/C/D and hence A/B/C/D is
included.
But to actually _design_ it, there are ambiguities that makes
understanding and explaining the semantics, especially given
pathspecs can have wildcards, icase matches, etc. For example, is
":(exclude,icase)A/B/?" more specific than "A/?/C" or less?
So I tend to agree that we should aim for an easier to explain, if
less capable, approach.
> Interaction with "git grep --depth"
I am not sure how that affects anything. Conceptually, isn't
"--depth" an independent axis to filter out paths that have too many
components after given positive pathspec elements? E.g. given
git grep --depth=2 pattern -- A B/C
we will grab paths from two levels starting at A and B/C (so A/1/2
and B/C/1/2 may hit but not A/1/2/3 nor B/C/1/2/3). Shouldn't
negative pathspecs just filter that depth filtering, i.e. if you
have ":(exclude)*/1/*", even though both "A/1/2" and "A/a/b" may
pass the --depth=2 filter, the former is excluded while the latter
is not.
> Syntax. I guess --ignore (or --exclude) is more intuitive than
> ":(exclude)something" but then it might collide with existing options
> (I did not check if --ignore or --exclude is used anywhere though).
> The latter also enables combining with other filters, such as
> case-insensitive matching..
I do not think it is an option to do this with any mechanism other
than negative pathspecs.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: RFC: git bisect should accept "paths-to-be-excluded"
2013-09-17 17:02 ` Junio C Hamano
@ 2013-09-17 18:12 ` Piotr Krukowiecki
2013-09-17 19:04 ` Junio C Hamano
2013-09-18 2:22 ` Duy Nguyen
2013-11-20 1:41 ` [PATCH] Support pathspec magic :(exclude) and its short form :- Nguyễn Thái Ngọc Duy
2 siblings, 1 reply; 16+ messages in thread
From: Piotr Krukowiecki @ 2013-09-17 18:12 UTC (permalink / raw)
To: Junio C Hamano, Duy Nguyen
Cc: Christian Couder, Matthieu Moy, Toralf Förster,
git@vger.kernel.org
Junio C Hamano <gitster@pobox.com> napisał:
>Yeah, it is easy to say that
>
> git log -- A ':(exclude)A/B' A/B/C
>
>has two positive (A, A/B/C) and one negative (A/B), and then the
>most specific one A/B/C matches a path A/B/C/D and hence A/B/C/D is
>included.
>
>But to actually _design_ it, there are ambiguities that makes
>understanding and explaining the semantics, especially given
>pathspecs can have wildcards, icase matches, etc. For example, is
>":(exclude,icase)A/B/?" more specific than "A/?/C" or less?
What about simply iterating over options in order in which they are specified and the last option that matches specifies the result?
--
Piotr Krukowiecki
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: RFC: git bisect should accept "paths-to-be-excluded"
2013-09-17 18:12 ` Piotr Krukowiecki
@ 2013-09-17 19:04 ` Junio C Hamano
2013-09-17 19:41 ` Piotr Krukowiecki
0 siblings, 1 reply; 16+ messages in thread
From: Junio C Hamano @ 2013-09-17 19:04 UTC (permalink / raw)
To: Piotr Krukowiecki
Cc: Duy Nguyen, Christian Couder, Matthieu Moy, Toralf Förster,
git@vger.kernel.org
Piotr Krukowiecki <piotr.krukowiecki@gmail.com> writes:
> What about simply iterating over options in order in which they
> are specified and the last option that matches specifies the
> result?
But isn't it very inconsistent from the way normal pathspec works?
"git log -- A B" and "git log -- B A" would give the same result.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: RFC: git bisect should accept "paths-to-be-excluded"
2013-09-17 19:04 ` Junio C Hamano
@ 2013-09-17 19:41 ` Piotr Krukowiecki
2013-09-17 20:47 ` Junio C Hamano
0 siblings, 1 reply; 16+ messages in thread
From: Piotr Krukowiecki @ 2013-09-17 19:41 UTC (permalink / raw)
To: Junio C Hamano
Cc: Duy Nguyen, Christian Couder, Matthieu Moy, Toralf Förster,
git@vger.kernel.org
On Tue, Sep 17, 2013 at 9:04 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Piotr Krukowiecki <piotr.krukowiecki@gmail.com> writes:
>
>> What about simply iterating over options in order in which they
>> are specified and the last option that matches specifies the
>> result?
>
> But isn't it very inconsistent from the way normal pathspec works?
> "git log -- A B" and "git log -- B A" would give the same result.
Both are include-type filters. "--include A --include B" will give the
same result as "--include B --include A" too.
Are there existing include/exclude filters where order does not
matter? For example gitattributes(5) says "When more than one pattern
matches the path, a later line overrides an earlier line."
Ignoring (possible) inconsistency thing, I think they are easy to
understand and use.
--
Piotr Krukowiecki
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: RFC: git bisect should accept "paths-to-be-excluded"
2013-09-17 19:41 ` Piotr Krukowiecki
@ 2013-09-17 20:47 ` Junio C Hamano
0 siblings, 0 replies; 16+ messages in thread
From: Junio C Hamano @ 2013-09-17 20:47 UTC (permalink / raw)
To: Piotr Krukowiecki
Cc: Duy Nguyen, Christian Couder, Matthieu Moy, Toralf Förster,
git@vger.kernel.org
Piotr Krukowiecki <piotr.krukowiecki@gmail.com> writes:
> Ignoring (possible) inconsistency thing, I think they are easy to
> understand and use.
Probably you are right (in the sense that I do not offhand think of
a confusing and ambiguous set of positive and negative pathspecs;
others may find holes in my/our thinking).
I am not sure if it will fit well to the current "struct pathspec"
design, though. We could start from "when there is any negative
pathspec, disable the 'optimize away the common leading prefix'
thing", I guess.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: RFC: git bisect should accept "paths-to-be-excluded"
2013-09-17 17:02 ` Junio C Hamano
2013-09-17 18:12 ` Piotr Krukowiecki
@ 2013-09-18 2:22 ` Duy Nguyen
2013-11-20 1:41 ` [PATCH] Support pathspec magic :(exclude) and its short form :- Nguyễn Thái Ngọc Duy
2 siblings, 0 replies; 16+ messages in thread
From: Duy Nguyen @ 2013-09-18 2:22 UTC (permalink / raw)
To: Junio C Hamano
Cc: Christian Couder, Matthieu Moy, Toralf Förster,
git@vger.kernel.org
On Wed, Sep 18, 2013 at 12:02 AM, Junio C Hamano <gitster@pobox.com> wrote:
>> Interaction with "git grep --depth"
>
> I am not sure how that affects anything. Conceptually, isn't
> "--depth" an independent axis to filter out paths that have too many
> components after given positive pathspec elements? E.g. given
>
> git grep --depth=2 pattern -- A B/C
>
> we will grab paths from two levels starting at A and B/C (so A/1/2
> and B/C/1/2 may hit but not A/1/2/3 nor B/C/1/2/3). Shouldn't
> negative pathspecs just filter that depth filtering, i.e. if you
> have ":(exclude)*/1/*", even though both "A/1/2" and "A/a/b" may
> pass the --depth=2 filter, the former is excluded while the latter
> is not.
Implementation details leaked into the design thoughts. I was worried
that the qsort() in pathspec() might make it incompatible with the
:(exclude). Or I was thinking that --depth should be part of this new
filter.. Never mind.
>> Syntax. I guess --ignore (or --exclude) is more intuitive than
>> ":(exclude)something" but then it might collide with existing options
>> (I did not check if --ignore or --exclude is used anywhere though).
>> The latter also enables combining with other filters, such as
>> case-insensitive matching..
>
> I do not think it is an option to do this with any mechanism other
> than negative pathspecs.
Under the hood, a new pathspec magic must be introduced (else we can't
pass them from "git add -u" to git-add--interactive then some other
commands that take pathspec). So --exclude would be transformed to the
pathspec magic, similar to "git grep --depth". But we could add that
later if :(exclude)something is too long to type.
--
Duy
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH] Support pathspec magic :(exclude) and its short form :-
2013-09-17 17:02 ` Junio C Hamano
2013-09-17 18:12 ` Piotr Krukowiecki
2013-09-18 2:22 ` Duy Nguyen
@ 2013-11-20 1:41 ` Nguyễn Thái Ngọc Duy
2013-11-20 23:48 ` Junio C Hamano
2 siblings, 1 reply; 16+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2013-11-20 1:41 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, christian.couder, Matthieu.Moy, toralf.foerster,
Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
This is yet another stab at the negative pathspec thing. It's not
ready yet (there are a few XXXs) but I could use some feedback
regarding the interface, or the behavior. It looks better this time
now that pathspec magic is supported (or maybe I'm just biased).
For :(glob) or :(icase) you're more likely to enable it for all
pathspec, i.e. --glob-pathspecs. But I expect :(exclude) to be typed
more often (it does not make sense to add --exclude-pathspecs to
exclude everything), which is why I add the short form for it.
We don't have many options that say "negative" in short form.
Either '!', '-' or '~'. '!' is already used for bash history expansion.
~ looks more like $HOME expansion. Which left me '-'.
Documentation/glossary-content.txt | 5 ++++
builtin/add.c | 5 +++-
dir.c | 50 +++++++++++++++++++++++++++++++-----
pathspec.c | 9 ++++++-
pathspec.h | 4 ++-
tree-walk.c | 52 +++++++++++++++++++++++++++++++++++---
6 files changed, 112 insertions(+), 13 deletions(-)
diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
index e470661..f7d7d8c 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -377,6 +377,11 @@ full pathname may have special meaning:
- Other consecutive asterisks are considered invalid.
+
Glob magic is incompatible with literal magic.
+
+exclude `-`;;
+ After a path matches any non-exclude pathspec, it will be run
+ through all exclude pathspec. If it matches, the path is
+ ignored.
--
+
Currently only the slash `/` is recognized as the "magic signature",
diff --git a/builtin/add.c b/builtin/add.c
index 226f758..0df73ae 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -540,10 +540,13 @@ int cmd_add(int argc, const char **argv, const char *prefix)
PATHSPEC_FROMTOP |
PATHSPEC_LITERAL |
PATHSPEC_GLOB |
- PATHSPEC_ICASE);
+ PATHSPEC_ICASE |
+ PATHSPEC_EXCLUDE);
for (i = 0; i < pathspec.nr; i++) {
const char *path = pathspec.items[i].match;
+ if (pathspec.items[i].magic & PATHSPEC_EXCLUDE)
+ continue;
if (!seen[i] &&
((pathspec.items[i].magic &
(PATHSPEC_GLOB | PATHSPEC_ICASE)) ||
diff --git a/dir.c b/dir.c
index 23b6de4..e2df82f 100644
--- a/dir.c
+++ b/dir.c
@@ -126,10 +126,13 @@ static size_t common_prefix_len(const struct pathspec *pathspec)
PATHSPEC_MAXDEPTH |
PATHSPEC_LITERAL |
PATHSPEC_GLOB |
- PATHSPEC_ICASE);
+ PATHSPEC_ICASE |
+ PATHSPEC_EXCLUDE);
for (n = 0; n < pathspec->nr; n++) {
size_t i = 0, len = 0, item_len;
+ if (pathspec->items[n].magic & PATHSPEC_EXCLUDE)
+ continue;
if (pathspec->items[n].magic & PATHSPEC_ICASE)
item_len = pathspec->items[n].prefix;
else
@@ -279,9 +282,10 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix,
* pathspec did not match any names, which could indicate that the
* user mistyped the nth pathspec.
*/
-int match_pathspec_depth(const struct pathspec *ps,
- const char *name, int namelen,
- int prefix, char *seen)
+static int match_pathspec_depth_1(const struct pathspec *ps,
+ const char *name, int namelen,
+ int prefix, char *seen,
+ int exclude)
{
int i, retval = 0;
@@ -290,7 +294,8 @@ int match_pathspec_depth(const struct pathspec *ps,
PATHSPEC_MAXDEPTH |
PATHSPEC_LITERAL |
PATHSPEC_GLOB |
- PATHSPEC_ICASE);
+ PATHSPEC_ICASE |
+ PATHSPEC_EXCLUDE);
if (!ps->nr) {
if (!ps->recursive ||
@@ -309,6 +314,11 @@ int match_pathspec_depth(const struct pathspec *ps,
for (i = ps->nr - 1; i >= 0; i--) {
int how;
+
+ if ((!exclude && ps->items[i].magic & PATHSPEC_EXCLUDE) ||
+ ( exclude && !(ps->items[i].magic & PATHSPEC_EXCLUDE)))
+ continue;
+
if (seen && seen[i] == MATCHED_EXACTLY)
continue;
how = match_pathspec_item(ps->items+i, prefix, name, namelen);
@@ -327,6 +337,16 @@ int match_pathspec_depth(const struct pathspec *ps,
if (how) {
if (retval < how)
retval = how;
+ /*
+ * seen[i] is used for detecting unused
+ * pathspec. For excluded pathspec, it's less
+ * obvious if seen[] should be set if the
+ * pathspec matches.
+ *
+ * XXX: perhaps we should also set seen[] for
+ * exclude patterns and stop e.g. "git add"
+ * from complaining?
+ */
if (seen && seen[i] < how)
seen[i] = how;
}
@@ -334,6 +354,18 @@ int match_pathspec_depth(const struct pathspec *ps,
return retval;
}
+int match_pathspec_depth(const struct pathspec *ps,
+ const char *name, int namelen,
+ int prefix, char *seen)
+{
+ int positive, negative;
+ positive = match_pathspec_depth_1(ps, name, namelen, prefix, seen, 0);
+ if (!(ps->magic & PATHSPEC_EXCLUDE) || !positive)
+ return positive;
+ negative = match_pathspec_depth_1(ps, name, namelen, prefix, seen, 1);
+ return negative ? 0 : positive;
+}
+
/*
* Return the length of the "simple" part of a path match limiter.
*/
@@ -1375,11 +1407,17 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const stru
PATHSPEC_MAXDEPTH |
PATHSPEC_LITERAL |
PATHSPEC_GLOB |
- PATHSPEC_ICASE);
+ PATHSPEC_ICASE |
+ PATHSPEC_EXCLUDE);
if (has_symlink_leading_path(path, len))
return dir->nr;
+ /*
+ * XXX: exclude patterns are treated like positive ones in
+ * create_simplify! This is not wrong, but may make path
+ * filtering less efficient.
+ */
simplify = create_simplify(pathspec ? pathspec->_raw : NULL);
if (!len || treat_leading_path(dir, path, len, simplify))
read_directory_recursive(dir, path, len, 0, simplify);
diff --git a/pathspec.c b/pathspec.c
index 4cf2bd3..a021959 100644
--- a/pathspec.c
+++ b/pathspec.c
@@ -71,6 +71,7 @@ static struct pathspec_magic {
{ PATHSPEC_LITERAL, 0, "literal" },
{ PATHSPEC_GLOB, '\0', "glob" },
{ PATHSPEC_ICASE, '\0', "icase" },
+ { PATHSPEC_EXCLUDE, '-', "exclude" },
};
/*
@@ -355,7 +356,7 @@ void parse_pathspec(struct pathspec *pathspec,
{
struct pathspec_item *item;
const char *entry = argv ? *argv : NULL;
- int i, n, prefixlen;
+ int i, n, prefixlen, nr_exclude = 0;
memset(pathspec, 0, sizeof(*pathspec));
@@ -412,6 +413,8 @@ void parse_pathspec(struct pathspec *pathspec,
if ((flags & PATHSPEC_LITERAL_PATH) &&
!(magic_mask & PATHSPEC_LITERAL))
item[i].magic |= PATHSPEC_LITERAL;
+ if (item[i].magic & PATHSPEC_EXCLUDE)
+ nr_exclude++;
if (item[i].magic & magic_mask)
unsupported_magic(entry,
item[i].magic & magic_mask,
@@ -427,6 +430,10 @@ void parse_pathspec(struct pathspec *pathspec,
pathspec->magic |= item[i].magic;
}
+ if (nr_exclude == n)
+ die(_("There is nothing to exclude from by :(exclude) patterns.\n"
+ "Perhaps you forgot to add either ':/' or '.' ?"));
+
if (pathspec->magic & PATHSPEC_MAXDEPTH) {
if (flags & PATHSPEC_KEEP_ORDER)
diff --git a/pathspec.h b/pathspec.h
index a75e924..0c11262 100644
--- a/pathspec.h
+++ b/pathspec.h
@@ -7,12 +7,14 @@
#define PATHSPEC_LITERAL (1<<2)
#define PATHSPEC_GLOB (1<<3)
#define PATHSPEC_ICASE (1<<4)
+#define PATHSPEC_EXCLUDE (1<<5)
#define PATHSPEC_ALL_MAGIC \
(PATHSPEC_FROMTOP | \
PATHSPEC_MAXDEPTH | \
PATHSPEC_LITERAL | \
PATHSPEC_GLOB | \
- PATHSPEC_ICASE)
+ PATHSPEC_ICASE | \
+ PATHSPEC_EXCLUDE)
#define PATHSPEC_ONESTAR 1 /* the pathspec pattern satisfies GFNM_ONESTAR */
diff --git a/tree-walk.c b/tree-walk.c
index 5ece8c3..9011f87 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -662,9 +662,10 @@ static int match_wildcard_base(const struct pathspec_item *item,
* Pre-condition: either baselen == base_offset (i.e. empty path)
* or base[baselen-1] == '/' (i.e. with trailing slash).
*/
-enum interesting tree_entry_interesting(const struct name_entry *entry,
- struct strbuf *base, int base_offset,
- const struct pathspec *ps)
+static enum interesting tree_entry_interesting_1(const struct name_entry *entry,
+ struct strbuf *base, int base_offset,
+ const struct pathspec *ps,
+ int exclude)
{
int i;
int pathlen, baselen = base->len - base_offset;
@@ -676,7 +677,8 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,
PATHSPEC_MAXDEPTH |
PATHSPEC_LITERAL |
PATHSPEC_GLOB |
- PATHSPEC_ICASE);
+ PATHSPEC_ICASE |
+ PATHSPEC_EXCLUDE);
if (!ps->nr) {
if (!ps->recursive ||
@@ -697,6 +699,10 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,
const char *base_str = base->buf + base_offset;
int matchlen = item->len, matched = 0;
+ if ((!exclude && item->magic & PATHSPEC_EXCLUDE) ||
+ ( exclude && !(item->magic & PATHSPEC_EXCLUDE)))
+ continue;
+
if (baselen >= matchlen) {
/* If it doesn't match, move along... */
if (!match_dir_prefix(item, base_str, match, matchlen))
@@ -782,3 +788,41 @@ match_wildcards:
}
return never_interesting; /* No matches */
}
+
+enum interesting tree_entry_interesting(const struct name_entry *entry,
+ struct strbuf *base, int base_offset,
+ const struct pathspec *ps)
+{
+ enum interesting positive, negative;
+ positive = tree_entry_interesting_1(entry, base, base_offset, ps, 0);
+
+ /*
+ * # | positive | negative | result
+ * -----+----------+----------+-------
+ * 1..4 | -1 | * | -1
+ * 5..8 | 0 | * | 0
+ * 9 | 1 | -1 | 1
+ * 10 | 1 | 0 | 1
+ * 11 | 1 | 1 | 0
+ * 12 | 1 | 2 | 0
+ * 13 | 2 | -1 | 2
+ * 14 | 2 | 0 | 2
+ * 15 | 2 | 1 | 0
+ * 16 | 2 | 2 | -1
+ */
+
+ if (!(ps->magic & PATHSPEC_EXCLUDE) ||
+ positive <= entry_not_interesting) /* #1..#8 */
+ return positive;
+
+ negative = tree_entry_interesting_1(entry, base, base_offset, ps, 1);
+
+ if (negative <= entry_not_interesting) /* #9, #10, #13, #14 */
+ return positive;
+ if ((positive == entry_interesting &&
+ negative >= entry_interesting) || /* #11, #12 */
+ (positive == all_entries_interesting &&
+ negative == entry_interesting)) /* #15 */
+ return entry_not_interesting;
+ return all_entries_not_interesting; /* #16 */
+}
--
1.8.2.82.gc24b958
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH] Support pathspec magic :(exclude) and its short form :-
2013-11-20 1:41 ` [PATCH] Support pathspec magic :(exclude) and its short form :- Nguyễn Thái Ngọc Duy
@ 2013-11-20 23:48 ` Junio C Hamano
2013-11-21 2:10 ` Duy Nguyen
0 siblings, 1 reply; 16+ messages in thread
From: Junio C Hamano @ 2013-11-20 23:48 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy
Cc: git, christian.couder, Matthieu.Moy, toralf.foerster
Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
> This is yet another stab at the negative pathspec thing. It's not
> ready yet (there are a few XXXs) but I could use some feedback
> regarding the interface, or the behavior. It looks better this time
> now that pathspec magic is supported (or maybe I'm just biased).
>
> For :(glob) or :(icase) you're more likely to enable it for all
> pathspec, i.e. --glob-pathspecs. But I expect :(exclude) to be typed
> more often (it does not make sense to add --exclude-pathspecs to
> exclude everything), which is why I add the short form for it.
>
> We don't have many options that say "negative" in short form.
> Either '!', '-' or '~'. '!' is already used for bash history expansion.
> ~ looks more like $HOME expansion. Which left me '-'.
I agree with your decision to reject ~, but "!not-this-pattern" is
very much consistent with the patterns used in .gitignore (and the
"--exclude <pattern>" option), so avoiding "!" and introducing an
inconsistent "-" only to appease bash leaves somewhat a funny taste
in my mouth.
> Documentation/glossary-content.txt | 5 ++++
> builtin/add.c | 5 +++-
> dir.c | 50 +++++++++++++++++++++++++++++++-----
> pathspec.c | 9 ++++++-
> pathspec.h | 4 ++-
> tree-walk.c | 52 +++++++++++++++++++++++++++++++++++---
> 6 files changed, 112 insertions(+), 13 deletions(-)
>
> diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
> index e470661..f7d7d8c 100644
> --- a/Documentation/glossary-content.txt
> +++ b/Documentation/glossary-content.txt
> @@ -377,6 +377,11 @@ full pathname may have special meaning:
> - Other consecutive asterisks are considered invalid.
> +
> Glob magic is incompatible with literal magic.
> +
> +exclude `-`;;
> + After a path matches any non-exclude pathspec, it will be run
> + through all exclude pathspec. If it matches, the path is
> + ignored.
> --
> +
> Currently only the slash `/` is recognized as the "magic signature",
No longer, no? "magic signature" is a non-alphanumeric that follows
the ':' introducer, as opposed to "magic words" that are in ":(...)".
> diff --git a/builtin/add.c b/builtin/add.c
> index 226f758..0df73ae 100644
> --- a/builtin/add.c
> +++ b/builtin/add.c
> @@ -540,10 +540,13 @@ int cmd_add(int argc, const char **argv, const char *prefix)
> PATHSPEC_FROMTOP |
> PATHSPEC_LITERAL |
> PATHSPEC_GLOB |
> - PATHSPEC_ICASE);
> + PATHSPEC_ICASE |
> + PATHSPEC_EXCLUDE);
>
> for (i = 0; i < pathspec.nr; i++) {
> const char *path = pathspec.items[i].match;
> + if (pathspec.items[i].magic & PATHSPEC_EXCLUDE)
> + continue;
> if (!seen[i] &&
> ((pathspec.items[i].magic &
> (PATHSPEC_GLOB | PATHSPEC_ICASE)) ||
So "git add ':(exclude)junk/' '*.c'" to add all .c files except for
the ones in the 'junk/' directory may find that ':(exclude)junk/'
matched nothing (because there is no .c file in there), and that is
not an error. It makes sense to me.
> diff --git a/dir.c b/dir.c
> index 23b6de4..e2df82f 100644
> --- a/dir.c
> +++ b/dir.c
> @@ -126,10 +126,13 @@ static size_t common_prefix_len(const struct pathspec *pathspec)
> PATHSPEC_MAXDEPTH |
> PATHSPEC_LITERAL |
> PATHSPEC_GLOB |
> - PATHSPEC_ICASE);
> + PATHSPEC_ICASE |
> + PATHSPEC_EXCLUDE);
>
> for (n = 0; n < pathspec->nr; n++) {
> size_t i = 0, len = 0, item_len;
> + if (pathspec->items[n].magic & PATHSPEC_EXCLUDE)
> + continue;
> if (pathspec->items[n].magic & PATHSPEC_ICASE)
> item_len = pathspec->items[n].prefix;
> else
Likewise. Exclusion does not participate in the early culling with
the common prefix.
> @@ -1375,11 +1407,17 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const stru
> PATHSPEC_MAXDEPTH |
> PATHSPEC_LITERAL |
> PATHSPEC_GLOB |
> - PATHSPEC_ICASE);
> + PATHSPEC_ICASE |
> + PATHSPEC_EXCLUDE);
>
> if (has_symlink_leading_path(path, len))
> return dir->nr;
>
> + /*
> + * XXX: exclude patterns are treated like positive ones in
> + * create_simplify! This is not wrong, but may make path
> + * filtering less efficient.
> + */
True, but "git add ':(exclude)a/b/c' a/b" would not suffer. And
those who do "git add ':(exclude)a/b' a/b/c" deserve it, no ;-)?
> @@ -427,6 +430,10 @@ void parse_pathspec(struct pathspec *pathspec,
> pathspec->magic |= item[i].magic;
> }
>
> + if (nr_exclude == n)
> + die(_("There is nothing to exclude from by :(exclude) patterns.\n"
> + "Perhaps you forgot to add either ':/' or '.' ?"));
;-).
> +enum interesting tree_entry_interesting(const struct name_entry *entry,
> + struct strbuf *base, int base_offset,
> + const struct pathspec *ps)
> +{
> + enum interesting positive, negative;
> + positive = tree_entry_interesting_1(entry, base, base_offset, ps, 0);
> +
> + /*
> + * # | positive | negative | result
> + * -----+----------+----------+-------
> + * 1..4 | -1 | * | -1
> + * 5..8 | 0 | * | 0
> + * 9 | 1 | -1 | 1
> + * 10 | 1 | 0 | 1
> + * 11 | 1 | 1 | 0
> + * 12 | 1 | 2 | 0
> + * 13 | 2 | -1 | 2
> + * 14 | 2 | 0 | 2
> + * 15 | 2 | 1 | 0
> + * 16 | 2 | 2 | -1
> + */
Not sure what this case-table means...
> + if (!(ps->magic & PATHSPEC_EXCLUDE) ||
> + positive <= entry_not_interesting) /* #1..#8 */
> + return positive;
> +
> + negative = tree_entry_interesting_1(entry, base, base_offset, ps, 1);
> +
> + if (negative <= entry_not_interesting) /* #9, #10, #13, #14 */
> + return positive;
> + if ((positive == entry_interesting &&
> + negative >= entry_interesting) || /* #11, #12 */
> + (positive == all_entries_interesting &&
> + negative == entry_interesting)) /* #15 */
> + return entry_not_interesting;
> + return all_entries_not_interesting; /* #16 */
> +}
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH] Support pathspec magic :(exclude) and its short form :-
2013-11-20 23:48 ` Junio C Hamano
@ 2013-11-21 2:10 ` Duy Nguyen
2013-11-21 18:43 ` Junio C Hamano
0 siblings, 1 reply; 16+ messages in thread
From: Duy Nguyen @ 2013-11-21 2:10 UTC (permalink / raw)
To: Junio C Hamano
Cc: Git Mailing List, Christian Couder, Matthieu Moy,
Toralf Förster
On Thu, Nov 21, 2013 at 6:48 AM, Junio C Hamano <gitster@pobox.com> wrote:
>> We don't have many options that say "negative" in short form.
>> Either '!', '-' or '~'. '!' is already used for bash history expansion.
>> ~ looks more like $HOME expansion. Which left me '-'.
>
> I agree with your decision to reject ~, but "!not-this-pattern" is
> very much consistent with the patterns used in .gitignore (and the
> "--exclude <pattern>" option), so avoiding "!" and introducing an
> inconsistent "-" only to appease bash leaves somewhat a funny taste
> in my mouth.
The thing about '!' is it's history expansion in bash and I suspect
not many people are aware of it. So "git log -- :!something" may
recall the last command that has "something" in it, which is confusing
for those new people and may potentially be dangerous (multiple
command in one line, separated by semicolon). Compared to ":git log --
(exclude)somethign" the worst that could happen is a syntax error
message from bash.
Other than that I'm fine with '!' being the shortcut.
Btw I'm thinking of extending pathspec magic syntax a bit to allow
path completion. Right now the user has to write
git log -- :-Documentation
which does not play well with path completion. I'm thinking of accepting
git log -- :- Documentation
In other words, if there's no path (or pattern) component after the
magic, then the next argument must contain the path. This enables path
completion and I haven't seen any drawbacks yet..
>> @@ -427,6 +430,10 @@ void parse_pathspec(struct pathspec *pathspec,
>> pathspec->magic |= item[i].magic;
>> }
>>
>> + if (nr_exclude == n)
>> + die(_("There is nothing to exclude from by :(exclude) patterns.\n"
>> + "Perhaps you forgot to add either ':/' or '.' ?"));
>
> ;-).
Hey it was originally not there, then I made a mistake of typing "git
log -- :-po" and wondered why it shows nothing. Intuitively, if "git
log" shows every path, then "git log -- :-po" should show every path
except 'po' and the user should not be required to type "git log -- :/
:-po". parse_pathspec() can do that, but it's more work and I'm lazy
so I push that back to the user until they scream :)
>> +enum interesting tree_entry_interesting(const struct name_entry *entry,
>> + struct strbuf *base, int base_offset,
>> + const struct pathspec *ps)
>> +{
>> + enum interesting positive, negative;
>> + positive = tree_entry_interesting_1(entry, base, base_offset, ps, 0);
>> +
>> + /*
>> + * # | positive | negative | result
>> + * -----+----------+----------+-------
>> + * 1..4 | -1 | * | -1
>> + * 5..8 | 0 | * | 0
>> + * 9 | 1 | -1 | 1
>> + * 10 | 1 | 0 | 1
>> + * 11 | 1 | 1 | 0
>> + * 12 | 1 | 2 | 0
>> + * 13 | 2 | -1 | 2
>> + * 14 | 2 | 0 | 2
>> + * 15 | 2 | 1 | 0
>> + * 16 | 2 | 2 | -1
>> + */
>
> Not sure what this case-table means...
Sorry, because tree_entry_interesting_1() returns more than "match or
not", we need to combine the result from positive pathspec with the
negative one to correctly handle all_not_interesting and
all_interesting. This table sums it up. I'll add more explanation in
the next patch.
--
Duy
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH] Support pathspec magic :(exclude) and its short form :-
2013-11-21 2:10 ` Duy Nguyen
@ 2013-11-21 18:43 ` Junio C Hamano
0 siblings, 0 replies; 16+ messages in thread
From: Junio C Hamano @ 2013-11-21 18:43 UTC (permalink / raw)
To: Duy Nguyen
Cc: Git Mailing List, Christian Couder, Matthieu Moy,
Toralf Förster
Duy Nguyen <pclouds@gmail.com> writes:
> Btw I'm thinking of extending pathspec magic syntax a bit to allow
> path completion. Right now the user has to write
>
> git log -- :-Documentation
>
> which does not play well with path completion. I'm thinking of accepting
>
> git log -- :- Documentation
Please don't. That does not help our users, but actively harm them.
They have to stop and wonder why a single pathspec is spelled as two
tokens on the command line of some other people.
Doing that stupidity only to help those who polish the tool (namely,
"bash completion") to be lazy is doubly wrong (in the meantime, the
users can type your second variant and then edit the result).
For the same reason why I do not think rewriting
echo "hello, world!"
to
echo "hello, world-"
only to work around a pitfall of a particular tool (namely "bash")
makes any sense, I do not think it makes sense to make _our_ tool
inconsistent by using "!excluded" in the files (and --exclude) and
"-not this pattern" only here.
>>> + if (nr_exclude == n)
>>> + die(_("There is nothing to exclude from by :(exclude) patterns.\n"
>>> + "Perhaps you forgot to add either ':/' or '.' ?"));
>>
>> ;-).
>
> Hey it was originally not there,...
I am not objecting. I noticed it and was commending on it as "a nice
touch" ;-)
>>> + /*
>>> + * # | positive | negative | result
>>> + * -----+----------+----------+-------
>>> + * 1..4 | -1 | * | -1
>>> + * 5..8 | 0 | * | 0
>>> + * 9 | 1 | -1 | 1
>>> + * 10 | 1 | 0 | 1
>>> + * 11 | 1 | 1 | 0
>>> + * 12 | 1 | 2 | 0
>>> + * 13 | 2 | -1 | 2
>>> + * 14 | 2 | 0 | 2
>>> + * 15 | 2 | 1 | 0
>>> + * 16 | 2 | 2 | -1
>>> + */
>>
>> Not sure what this case-table means...
>
> Sorry, because tree_entry_interesting_1() returns more than "match
> or not", we need to combine the result from positive pathspec with
> the negative one to correctly handle all_not_interesting and
> all_interesting. This table sums it up. I'll add more explanation
> in the next patch.
I managed to have guessed what the three columns on the right meant;
I was wondering about the meaning of the "#" column and where it is
defined/explained.
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2013-11-21 18:43 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-09-16 12:39 RFC: git bisect should accept "paths-to-be-excluded" Toralf Förster
2013-09-17 7:26 ` Christian Couder
2013-09-17 8:21 ` Matthieu Moy
2013-09-17 9:03 ` Christian Couder
2013-09-17 11:45 ` Duy Nguyen
2013-09-17 17:02 ` Junio C Hamano
2013-09-17 18:12 ` Piotr Krukowiecki
2013-09-17 19:04 ` Junio C Hamano
2013-09-17 19:41 ` Piotr Krukowiecki
2013-09-17 20:47 ` Junio C Hamano
2013-09-18 2:22 ` Duy Nguyen
2013-11-20 1:41 ` [PATCH] Support pathspec magic :(exclude) and its short form :- Nguyễn Thái Ngọc Duy
2013-11-20 23:48 ` Junio C Hamano
2013-11-21 2:10 ` Duy Nguyen
2013-11-21 18:43 ` Junio C Hamano
2013-09-17 16:23 ` RFC: git bisect should accept "paths-to-be-excluded" Toralf Förster
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).