From: Stefan Beller <sbeller@google.com>
To: gitster@pobox.com, pclouds@gmail.com
Cc: git@vger.kernel.org, Stefan Beller <sbeller@google.com>
Subject: [RFC-PATCHv6 4/4] pathspec: allow querying for attributes
Date: Mon, 16 May 2016 20:13:53 -0700 [thread overview]
Message-ID: <20160517031353.23707-5-sbeller@google.com> (raw)
In-Reply-To: <20160517031353.23707-1-sbeller@google.com>
The pathspec mechanism is extended via the new
":(attr:eol=input)pattern/to/match" syntax to filter paths so that it
requires paths to not just match the given pattern but also have the
specified attrs attached for them to be chosen.
Signed-off-by: Stefan Beller <sbeller@google.com>
---
attr.c | 2 +-
attr.h | 2 ++
dir.c | 49 +++++++++++++++++++++++++++++++++
pathspec.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
pathspec.h | 16 +++++++++++
5 files changed, 157 insertions(+), 5 deletions(-)
diff --git a/attr.c b/attr.c
index 5178eb8..2d68a3c 100644
--- a/attr.c
+++ b/attr.c
@@ -59,7 +59,7 @@ static unsigned hash_name(const char *name, int namelen)
return val;
}
-static int invalid_attr_name(const char *name, int namelen)
+int invalid_attr_name(const char *name, int namelen)
{
/*
* Attribute name cannot begin with '-' and must consist of
diff --git a/attr.h b/attr.h
index 7dc49f8..7fd8b90 100644
--- a/attr.h
+++ b/attr.h
@@ -45,6 +45,8 @@ extern void git_attr_check_append(struct git_attr_check *, const char *);
extern void git_attr_check_clear(struct git_attr_check *);
extern void git_attr_check_free(struct git_attr_check *);
+extern int invalid_attr_name(const char *name, int namelen);
+
/*
* Return the name of the attribute represented by the argument. The
* return value is a pointer to a null-delimited string that is part
diff --git a/dir.c b/dir.c
index 996653b..540ea1f 100644
--- a/dir.c
+++ b/dir.c
@@ -9,6 +9,7 @@
*/
#include "cache.h"
#include "dir.h"
+#include "attr.h"
#include "refs.h"
#include "wildmatch.h"
#include "pathspec.h"
@@ -215,6 +216,51 @@ int within_depth(const char *name, int namelen,
return 1;
}
+static struct git_attr_check *check;
+static int match_attrs(const char *name, int namelen,
+ const struct pathspec_item *item)
+{
+ char *path;
+ int i;
+
+ if (!check) {
+ check = git_attr_check_alloc();
+ for (i = 0; i < item->attr_nr; i++)
+ git_attr_check_append(check, item->attrs[i].attr);
+ }
+
+ path = xmemdupz(name, namelen);
+ git_all_attrs(path, check);
+
+ for (i = 0; i < item->attr_nr; i++) {
+ int matched;
+ const char *value = check->check[i].value;
+
+ if (ATTR_TRUE(value)) {
+ matched = (item->attrs[i].mode == MATCH_SET ||
+ item->attrs[i].mode == MATCH_NOT_UNSPECIFIED);
+ } else if (ATTR_FALSE(value)) {
+ matched = (item->attrs[i].mode == MATCH_UNSET ||
+ item->attrs[i].mode == MATCH_NOT_UNSPECIFIED);
+ } else if (ATTR_UNSET(value)) {
+ matched = (item->attrs[i].mode == MATCH_UNSPECIFIED);
+ } else {
+ if (item->attrs[i].mode == MATCH_NOT_UNSPECIFIED) {
+ matched = 1;
+ } else {
+ /* NEEDSWORK: better value matching */
+ matched = !strcmp(item->attrs[i].value, value);
+ }
+ }
+ if (!matched)
+ return 0;
+ }
+
+ free(path);
+
+ return 1;
+}
+
#define DO_MATCH_EXCLUDE 1
#define DO_MATCH_DIRECTORY 2
@@ -270,6 +316,9 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix,
strncmp(item->match, name - prefix, item->prefix))
return 0;
+ if (item->attr_nr && !match_attrs(name, namelen, item))
+ return 0;
+
/* If the match was just the prefix, we matched */
if (!*match)
return MATCHED_RECURSIVELY;
diff --git a/pathspec.c b/pathspec.c
index 4dff252..523ac8c 100644
--- a/pathspec.c
+++ b/pathspec.c
@@ -1,6 +1,7 @@
#include "cache.h"
#include "dir.h"
#include "pathspec.h"
+#include "attr.h"
/*
* Finds which of the given pathspecs match items in the index.
@@ -88,12 +89,73 @@ static void prefix_short_magic(struct strbuf *sb, int prefixlen,
strbuf_addf(sb, ",prefix:%d)", prefixlen);
}
+/*
+ * Check if the given string is a good specification to search for attributes.
+ * Accepted strings:
+ * [+-!] ATTRIBUTE_NAME [= <space separated list of values> ]
+ *
+ * Examples:
+ * attr:+val to find value set to true
+ * attr:-val to find a value set to false
+ * attr:!val to find a value that is not set
+ * (i.e. it is neither set as "val", "val=<empty>", nor unset as "-val")
+ * attr:val=value: to find value that have at least a and b set.
+ *
+ * This only checks one attr, multiple attrs need to be given as multiple
+ * strings.
+ */
+
+static void parse_attr_item(struct attr_item *attr, const char *value)
+{
+ size_t val_len;
+ const char *val = value;
+
+ if (!val || !strlen(val))
+ goto out;
+
+ if (val[0] == '+')
+ attr->mode = MATCH_SET;
+ else if (val[0] == '-')
+ attr->mode = MATCH_UNSET;
+ else if (val[0] == '!')
+ attr->mode = MATCH_UNSPECIFIED;
+ else
+ attr->mode = NOT_INIT;
+
+ if (attr->mode != NOT_INIT)
+ val++;
+
+ val_len = strcspn(val, "=,)");
+ if (val[val_len] == '=')
+ attr->mode = MATCH_VALUE;
+ else
+ attr->mode = MATCH_NOT_UNSPECIFIED;
+
+ if (invalid_attr_name(val, val_len))
+ goto out;
+
+ attr->attr = xmemdupz(val, val_len);
+
+ if (attr->mode == MATCH_VALUE) {
+ const char *after_equal = val + val_len + 1;
+ size_t after_equal_end = strcspn(after_equal, ",)");
+ attr->value = xmemdupz(after_equal, after_equal_end);
+ } else
+ attr->value = NULL;
+ return;
+out:
+ attr->mode = INVALID_ATTR;
+ warning(_("attr spec '%s': attrs must not start with '-' and "
+ "be composed of [-A-Za-z0-9_.]."), value);
+}
+
static void eat_long_magic(struct pathspec_item *item, const char *elt,
unsigned *magic, int *pathspec_prefix,
const char **copyfrom_, const char **long_magic_end)
{
int i;
const char *copyfrom = *copyfrom_;
+ const char *body;
/* longhand */
const char *nextat;
for (copyfrom = elt + 2;
@@ -108,15 +170,20 @@ static void eat_long_magic(struct pathspec_item *item, const char *elt,
if (!len)
continue;
- if (starts_with(copyfrom, "prefix:")) {
+ if (skip_prefix(copyfrom, "prefix:", &body)) {
char *endptr;
- *pathspec_prefix = strtol(copyfrom + 7,
- &endptr, 10);
+ *pathspec_prefix = strtol(body, &endptr, 10);
if (endptr - copyfrom != len)
die(_("invalid parameter for pathspec magic 'prefix'"));
continue;
}
+ if (skip_prefix(copyfrom, "attr:", &body)) {
+ ALLOC_GROW(item->attrs, item->attr_nr + 1, item->attr_alloc);
+ parse_attr_item(&item->attrs[item->attr_nr++], body);
+ continue;
+ }
+
for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
if (strlen(pathspec_magic[i].name) == len &&
!strncmp(pathspec_magic[i].name, copyfrom, len)) {
@@ -425,7 +492,9 @@ void parse_pathspec(struct pathspec *pathspec,
for (i = 0; i < n; i++) {
unsigned short_magic;
entry = argv[i];
-
+ item[i].attrs = NULL;
+ item[i].attr_nr = 0;
+ item[i].attr_alloc = 0;
item[i].magic = prefix_pathspec(item + i, &short_magic,
argv + i, flags,
prefix, prefixlen, entry);
@@ -447,6 +516,13 @@ void parse_pathspec(struct pathspec *pathspec,
if (item[i].nowildcard_len < item[i].len)
pathspec->has_wildcard = 1;
pathspec->magic |= item[i].magic;
+
+ if (item[i].attr_nr) {
+ int j;
+ for (j = 0; j < item[i].attr_nr; j++)
+ if (item[i].attrs[j].mode == INVALID_ATTR)
+ die(_("attribute spec in the wrong syntax are prohibited."));
+ }
}
if (nr_exclude == n)
@@ -502,6 +578,15 @@ void copy_pathspec(struct pathspec *dst, const struct pathspec *src)
void free_pathspec(struct pathspec *pathspec)
{
+ int i, j;
+ for (i = 0; i < pathspec->nr; i++) {
+ for (j = 0; j < pathspec->items[j].attr_nr; j++) {
+ free(pathspec->items[i].attrs[j].attr);
+ free(pathspec->items[i].attrs[j].value);
+ }
+ free(pathspec->items[i].attrs);
+ }
+
free(pathspec->items);
pathspec->items = NULL;
}
diff --git a/pathspec.h b/pathspec.h
index 0c11262..89d73db 100644
--- a/pathspec.h
+++ b/pathspec.h
@@ -32,6 +32,21 @@ struct pathspec {
int len, prefix;
int nowildcard_len;
int flags;
+ int attr_nr;
+ int attr_alloc;
+ struct attr_item {
+ char *attr;
+ char *value;
+ enum attr_match_mode {
+ NOT_INIT,
+ MATCH_SET,
+ MATCH_UNSET,
+ MATCH_VALUE,
+ MATCH_UNSPECIFIED,
+ MATCH_NOT_UNSPECIFIED,
+ INVALID_ATTR
+ } mode;
+ } *attrs;
} *items;
};
@@ -98,5 +113,6 @@ extern char *find_pathspecs_matching_against_index(const struct pathspec *pathsp
extern void add_pathspec_matches_against_index(const struct pathspec *pathspec, char *seen);
extern const char *check_path_for_gitlink(const char *path);
extern void die_if_path_beyond_symlink(const char *path, const char *prefix);
+extern int validate_label_name(const char *label);
#endif /* PATHSPEC_H */
--
2.8.2.401.g9c0faef
next prev parent reply other threads:[~2016-05-17 3:15 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-05-17 3:13 [RFC-PATCHv6 0/4] pathspec attrs [WAS pathspec labels [WAS submodule groups]] Stefan Beller
2016-05-17 3:13 ` [RFC-PATCHv6 1/4] Documentation: fix a typo Stefan Beller
2016-05-17 3:13 ` [RFC-PATCHv6 2/4] pathspec: move long magic parsing out of prefix_pathspec Stefan Beller
2016-05-17 3:13 ` [RFC-PATCHv6 3/4] pathspec: move prefix check out of the inner loop Stefan Beller
2016-05-17 3:13 ` Stefan Beller [this message]
2016-05-17 4:23 ` [RFC-PATCHv6 4/4] pathspec: allow querying for attributes Junio C Hamano
2016-05-17 16:45 ` Stefan Beller
2016-05-17 5:03 ` Junio C Hamano
2016-05-17 17:03 ` Stefan Beller
2016-05-17 17:34 ` Junio C Hamano
2016-05-17 17:45 ` Stefan Beller
2016-05-17 18:05 ` Junio C Hamano
2016-05-17 18:10 ` Stefan Beller
2016-05-17 18:29 ` Junio C Hamano
2016-05-17 19:23 ` Stefan Beller
2016-05-17 20:25 ` Junio C Hamano
2016-05-18 15:39 ` Junio C Hamano
2016-05-17 4:14 ` [RFC-PATCHv6 0/4] pathspec attrs [WAS pathspec labels [WAS submodule groups]] Junio C Hamano
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=20160517031353.23707-5-sbeller@google.com \
--to=sbeller@google.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=pclouds@gmail.com \
/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