[PATCH 6/6] Implement negative pathspec
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
I really like the mnemonic ^ but it's regex. ":^Documentation" looks
nicer than ":~Documentation". Do we plan on supporting regex in
pathspec too?
We should mention these magic in a less obscure document. Glossary is
mostly for developer discussions. git-diff may be a good place
because it's one of the two frequently used commands (the other one
is grep) that benefit magic the most (with a short reference from
git.txt)
Documentation/glossary-content.txt | 8 +++---
cache.h | 1 +
dir.c | 2 +
setup.c | 1 +
tree-walk.c | 37 +++++++++++++++++++++++++++++++++--
5 files changed, 42 insertions(+), 7 deletions(-)
diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
index 3595b58..9a2765d 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -319,12 +319,12 @@ top `/`;;
The magic word `top` (mnemonic: `/`) makes the pattern match
from the root of the working tree, even when you are running
the command from inside a subdirectory.
+
+exclude `~`;;
+ The magic word `exclude` (mnemonic: `~`) excludes paths that
+ match the pattern.
--
+
-Currently only the slash `/` is recognized as the "magic signature",
-but it is envisioned that we will support more types of magic in later
-versions of git.
-+
A pathspec with only a colon means "there is no pathspec". This form
should not be combined with other pathspec.
diff --git a/cache.h b/cache.h
index 719d4a3..75fe589 100644
--- a/cache.h
+++ b/cache.h
@@ -541,6 +541,7 @@ extern int ie_modified(const struct index_state *, struct cache_entry *, struct
*/
#define PATHSPEC_FROMTOP (1<<0)
#define PATHSPEC_NOGLOB (1<<1)
+#define PATHSPEC_NEGATE (1<<2)
struct pathspec {
const char **raw; /* get_pathspec() result, not freed by free_pathspec() */
diff --git a/dir.c b/dir.c
index d38af0f..46dd35f 100644
--- a/dir.c
+++ b/dir.c
@@ -1305,6 +1305,8 @@ int parse_pathspec(struct pathspec *pathspec, const char *prefix,
pitem->magic |= PATHSPEC_NOGLOB;
else
pathspec->magic &= ~PATHSPEC_NOGLOB;
+ if (pitem->magic & PATHSPEC_NEGATE)
+ pathspec->magic |= PATHSPEC_NEGATE;
pitem++;
dst++;
}
diff --git a/setup.c b/setup.c
index b074210..42beb9b 100644
--- a/setup.c
+++ b/setup.c
@@ -111,6 +111,7 @@ static struct pathspec_magic {
const char *name;
} pathspec_magic[] = {
{ PATHSPEC_FROMTOP, '/', "top" },
+ { PATHSPEC_NEGATE, '~', "exclude" },
};
/*
diff --git a/tree-walk.c b/tree-walk.c
index db07fd6..936b5da 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -580,15 +580,17 @@ static int match_dir_prefix(const char *base, int baselen,
* - zero for no
* - negative for "no, and no subsequent entries will be either"
*/
-int tree_entry_interesting(const struct name_entry *entry,
- struct strbuf *base, int base_offset,
- const struct pathspec *ps)
+static int tree_entry_interesting_1(const struct name_entry *entry,
+ struct strbuf *base, int base_offset,
+ const struct pathspec *ps, int negative_magic)
{
int i;
int pathlen, baselen = base->len - base_offset;
int never_interesting = ps->magic & PATHSPEC_NOGLOB ? -1 : 0;
+ int has_effective_pathspec = 0;
if (!ps->nr) {
+no_pathspec:
if (!ps->recursive || ps->max_depth == -1)
return 2;
return !!within_depth(base->buf + base_offset, baselen,
@@ -604,6 +606,12 @@ int tree_entry_interesting(const struct name_entry *entry,
const char *base_str = base->buf + base_offset;
int matchlen = item->len;
+ if ((!negative_magic && !(item->magic & PATHSPEC_NEGATE)) ||
+ ( negative_magic && (item->magic & PATHSPEC_NEGATE)))
+ has_effective_pathspec = 1;
+ else
+ continue;
+
if (baselen >= matchlen) {
/* If it doesn't match, move along... */
if (!match_dir_prefix(base_str, baselen, match, matchlen))
@@ -663,5 +671,28 @@ match_wildcards:
if (ps->recursive && S_ISDIR(entry->mode))
return 1;
}
+
+ /* the same effect with ps->nr == 0 */
+ if (!has_effective_pathspec)
+ goto no_pathspec;
+
return never_interesting; /* No matches */
}
+
+int tree_entry_interesting(const struct name_entry *entry,
+ struct strbuf *base, int base_offset,
+ const struct pathspec *ps)
+{
+ int next_ret, ret;
+
+ ret = tree_entry_interesting_1(entry, base, base_offset, ps, 0);
+ if (ps->magic & PATHSPEC_NEGATE) {
+ next_ret = tree_entry_interesting_1(entry, base, base_offset, ps, 1);
+ switch (next_ret) {
+ case 2: ret = -1; break;
+ case 1: ret = 0; break;
+ default: break;
+ }
+ }
+ return ret;
+}
--
1.7.3.1.256.g2539c.dirty