From: "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
To: git@vger.kernel.org
Cc: "Junio C Hamano" <gitster@pobox.com>,
christian.couder@gmail.com, Matthieu.Moy@grenoble-inp.fr,
toralf.foerster@gmx.de,
"Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
Subject: [PATCH] Support pathspec magic :(exclude) and its short form :-
Date: Wed, 20 Nov 2013 08:41:31 +0700 [thread overview]
Message-ID: <1384911691-11664-1-git-send-email-pclouds@gmail.com> (raw)
In-Reply-To: <xmqqsix3z8ie.fsf@gitster.dls.corp.google.com>
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
next prev parent reply other threads:[~2013-11-20 1:41 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
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 ` Nguyễn Thái Ngọc Duy [this message]
2013-11-20 23:48 ` [PATCH] Support pathspec magic :(exclude) and its short form :- 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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1384911691-11664-1-git-send-email-pclouds@gmail.com \
--to=pclouds@gmail.com \
--cc=Matthieu.Moy@grenoble-inp.fr \
--cc=christian.couder@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=toralf.foerster@gmx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).