2011-08-13 05:43:04 +08:00
|
|
|
/*
|
|
|
|
* Handle git attributes. See gitattributes(5) for a description of
|
2019-11-18 05:04:47 +08:00
|
|
|
* the file syntax, and attr.h for a description of the API.
|
2011-08-13 05:43:04 +08:00
|
|
|
*
|
|
|
|
* One basic design decision here is that we are not going to support
|
|
|
|
* an insanely large number of attributes.
|
|
|
|
*/
|
|
|
|
|
2023-05-16 14:33:57 +08:00
|
|
|
#include "git-compat-util.h"
|
2017-06-15 02:07:36 +08:00
|
|
|
#include "config.h"
|
2023-03-21 14:26:03 +08:00
|
|
|
#include "environment.h"
|
2018-04-11 05:26:18 +08:00
|
|
|
#include "exec-cmd.h"
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
#include "attr.h"
|
2011-10-11 23:53:31 +08:00
|
|
|
#include "dir.h"
|
2023-03-21 14:25:54 +08:00
|
|
|
#include "gettext.h"
|
2023-05-16 14:33:59 +08:00
|
|
|
#include "path.h"
|
2015-04-17 01:48:58 +08:00
|
|
|
#include "utf8.h"
|
2017-01-28 10:01:50 +08:00
|
|
|
#include "quote.h"
|
2023-05-16 14:33:56 +08:00
|
|
|
#include "read-cache-ll.h"
|
2023-11-16 13:44:37 +08:00
|
|
|
#include "refs.h"
|
2023-01-14 16:30:38 +08:00
|
|
|
#include "revision.h"
|
2023-05-16 14:34:06 +08:00
|
|
|
#include "object-store-ll.h"
|
2023-03-21 14:26:05 +08:00
|
|
|
#include "setup.h"
|
2017-01-28 10:02:01 +08:00
|
|
|
#include "thread-utils.h"
|
2023-04-23 04:17:28 +08:00
|
|
|
#include "tree-walk.h"
|
2023-05-06 12:15:29 +08:00
|
|
|
#include "object-name.h"
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
|
2024-05-27 19:46:39 +08:00
|
|
|
char *git_attr_tree;
|
2023-10-14 01:39:30 +08:00
|
|
|
|
2007-04-19 07:16:37 +08:00
|
|
|
const char git_attr__true[] = "(builtin)true";
|
|
|
|
const char git_attr__false[] = "\0(builtin)false";
|
|
|
|
static const char git_attr__unknown[] = "(builtin)unknown";
|
|
|
|
#define ATTR__TRUE git_attr__true
|
|
|
|
#define ATTR__FALSE git_attr__false
|
|
|
|
#define ATTR__UNSET NULL
|
|
|
|
#define ATTR__UNKNOWN git_attr__unknown
|
2007-04-17 12:33:31 +08:00
|
|
|
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
struct git_attr {
|
2022-12-01 22:45:36 +08:00
|
|
|
unsigned int attr_nr; /* unique attribute number */
|
2017-01-28 10:02:01 +08:00
|
|
|
char name[FLEX_ARRAY]; /* attribute name */
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
};
|
2017-01-28 10:01:53 +08:00
|
|
|
|
2017-01-28 10:01:48 +08:00
|
|
|
const char *git_attr_name(const struct git_attr *attr)
|
2011-08-04 12:36:17 +08:00
|
|
|
{
|
|
|
|
return attr->name;
|
|
|
|
}
|
|
|
|
|
2017-01-28 10:02:01 +08:00
|
|
|
struct attr_hashmap {
|
|
|
|
struct hashmap map;
|
|
|
|
pthread_mutex_t mutex;
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline void hashmap_lock(struct attr_hashmap *map)
|
|
|
|
{
|
|
|
|
pthread_mutex_lock(&map->mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void hashmap_unlock(struct attr_hashmap *map)
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
{
|
2017-01-28 10:02:01 +08:00
|
|
|
pthread_mutex_unlock(&map->mutex);
|
|
|
|
}
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
|
2017-01-28 10:02:01 +08:00
|
|
|
/* The container for objects stored in "struct attr_hashmap" */
|
|
|
|
struct attr_hash_entry {
|
2019-10-07 07:30:43 +08:00
|
|
|
struct hashmap_entry ent;
|
2017-01-28 10:02:01 +08:00
|
|
|
const char *key; /* the key; memory should be owned by value */
|
|
|
|
size_t keylen; /* length of the key */
|
|
|
|
void *value; /* the stored value */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* attr_hashmap comparison function */
|
2022-08-26 01:09:48 +08:00
|
|
|
static int attr_hash_entry_cmp(const void *cmp_data UNUSED,
|
2019-10-07 07:30:37 +08:00
|
|
|
const struct hashmap_entry *eptr,
|
|
|
|
const struct hashmap_entry *entry_or_key,
|
2022-08-26 01:09:48 +08:00
|
|
|
const void *keydata UNUSED)
|
2017-01-28 10:02:01 +08:00
|
|
|
{
|
2019-10-07 07:30:37 +08:00
|
|
|
const struct attr_hash_entry *a, *b;
|
|
|
|
|
|
|
|
a = container_of(eptr, const struct attr_hash_entry, ent);
|
|
|
|
b = container_of(entry_or_key, const struct attr_hash_entry, ent);
|
2017-01-28 10:02:01 +08:00
|
|
|
return (a->keylen != b->keylen) || strncmp(a->key, b->key, a->keylen);
|
|
|
|
}
|
|
|
|
|
2020-11-12 04:02:20 +08:00
|
|
|
/*
|
|
|
|
* The global dictionary of all interned attributes. This
|
|
|
|
* is a singleton object which is shared between threads.
|
|
|
|
* Access to this dictionary must be surrounded with a mutex.
|
|
|
|
*/
|
|
|
|
static struct attr_hashmap g_attr_hashmap = {
|
2022-03-18 01:27:17 +08:00
|
|
|
.map = HASHMAP_INIT(attr_hash_entry_cmp, NULL),
|
2020-11-12 04:02:20 +08:00
|
|
|
};
|
2017-01-28 10:02:01 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Retrieve the 'value' stored in a hashmap given the provided 'key'.
|
|
|
|
* If there is no matching entry, return NULL.
|
|
|
|
*/
|
|
|
|
static void *attr_hashmap_get(struct attr_hashmap *map,
|
|
|
|
const char *key, size_t keylen)
|
|
|
|
{
|
|
|
|
struct attr_hash_entry k;
|
|
|
|
struct attr_hash_entry *e;
|
|
|
|
|
2019-10-07 07:30:27 +08:00
|
|
|
hashmap_entry_init(&k.ent, memhash(key, keylen));
|
2017-01-28 10:02:01 +08:00
|
|
|
k.key = key;
|
|
|
|
k.keylen = keylen;
|
2019-10-07 07:30:42 +08:00
|
|
|
e = hashmap_get_entry(&map->map, &k, ent, NULL);
|
2017-01-28 10:02:01 +08:00
|
|
|
|
|
|
|
return e ? e->value : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add 'value' to a hashmap based on the provided 'key'. */
|
|
|
|
static void attr_hashmap_add(struct attr_hashmap *map,
|
|
|
|
const char *key, size_t keylen,
|
|
|
|
void *value)
|
|
|
|
{
|
|
|
|
struct attr_hash_entry *e;
|
|
|
|
|
|
|
|
e = xmalloc(sizeof(struct attr_hash_entry));
|
2019-10-07 07:30:27 +08:00
|
|
|
hashmap_entry_init(&e->ent, memhash(key, keylen));
|
2017-01-28 10:02:01 +08:00
|
|
|
e->key = key;
|
|
|
|
e->keylen = keylen;
|
|
|
|
e->value = value;
|
|
|
|
|
2019-10-07 07:30:29 +08:00
|
|
|
hashmap_add(&map->map, &e->ent);
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
}
|
|
|
|
|
2017-01-28 10:02:02 +08:00
|
|
|
struct all_attrs_item {
|
|
|
|
const struct git_attr *attr;
|
|
|
|
const char *value;
|
2017-01-28 10:02:03 +08:00
|
|
|
/*
|
|
|
|
* If 'macro' is non-NULL, indicates that 'attr' is a macro based on
|
|
|
|
* the current attribute stack and contains a pointer to the match_attr
|
|
|
|
* definition of the macro
|
|
|
|
*/
|
|
|
|
const struct match_attr *macro;
|
2017-01-28 10:02:02 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reallocate and reinitialize the array of all attributes (which is used in
|
|
|
|
* the attribute collection process) in 'check' based on the global dictionary
|
|
|
|
* of attributes.
|
|
|
|
*/
|
|
|
|
static void all_attrs_init(struct attr_hashmap *map, struct attr_check *check)
|
|
|
|
{
|
|
|
|
int i;
|
2017-09-06 23:43:48 +08:00
|
|
|
unsigned int size;
|
2017-01-28 10:02:02 +08:00
|
|
|
|
|
|
|
hashmap_lock(map);
|
|
|
|
|
2017-09-06 23:43:48 +08:00
|
|
|
size = hashmap_get_size(&map->map);
|
|
|
|
if (size < check->all_attrs_nr)
|
2018-05-02 17:38:39 +08:00
|
|
|
BUG("interned attributes shouldn't be deleted");
|
2017-01-28 10:02:02 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the number of attributes in the global dictionary has increased
|
|
|
|
* (or this attr_check instance doesn't have an initialized all_attrs
|
|
|
|
* field), reallocate the provided attr_check instance's all_attrs
|
|
|
|
* field and fill each entry with its corresponding git_attr.
|
|
|
|
*/
|
2017-09-06 23:43:48 +08:00
|
|
|
if (size != check->all_attrs_nr) {
|
2017-01-28 10:02:02 +08:00
|
|
|
struct attr_hash_entry *e;
|
|
|
|
struct hashmap_iter iter;
|
|
|
|
|
2017-09-06 23:43:48 +08:00
|
|
|
REALLOC_ARRAY(check->all_attrs, size);
|
|
|
|
check->all_attrs_nr = size;
|
2017-01-28 10:02:02 +08:00
|
|
|
|
2019-10-07 07:30:38 +08:00
|
|
|
hashmap_for_each_entry(&map->map, &iter, e,
|
|
|
|
ent /* member name */) {
|
2017-01-28 10:02:02 +08:00
|
|
|
const struct git_attr *a = e->value;
|
|
|
|
check->all_attrs[a->attr_nr].attr = a;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
hashmap_unlock(map);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Re-initialize every entry in check->all_attrs.
|
|
|
|
* This re-initialization can live outside of the locked region since
|
|
|
|
* the attribute dictionary is no longer being accessed.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < check->all_attrs_nr; i++) {
|
|
|
|
check->all_attrs[i].value = ATTR__UNKNOWN;
|
2017-01-28 10:02:03 +08:00
|
|
|
check->all_attrs[i].macro = NULL;
|
2017-01-28 10:02:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-16 13:44:37 +08:00
|
|
|
/*
|
|
|
|
* Atribute name cannot begin with "builtin_" which
|
|
|
|
* is a reserved namespace for built in attributes values.
|
|
|
|
*/
|
|
|
|
static int attr_name_reserved(const char *name)
|
|
|
|
{
|
|
|
|
return starts_with(name, "builtin_");
|
|
|
|
}
|
|
|
|
|
2017-01-28 10:02:00 +08:00
|
|
|
static int attr_name_valid(const char *name, size_t namelen)
|
2007-04-16 05:56:09 +08:00
|
|
|
{
|
|
|
|
/*
|
2011-08-04 12:36:13 +08:00
|
|
|
* Attribute name cannot begin with '-' and must consist of
|
|
|
|
* characters from [-A-Za-z0-9_.].
|
2007-04-16 05:56:09 +08:00
|
|
|
*/
|
2011-08-04 12:36:14 +08:00
|
|
|
if (namelen <= 0 || *name == '-')
|
2017-01-28 10:02:00 +08:00
|
|
|
return 0;
|
2007-04-16 05:56:09 +08:00
|
|
|
while (namelen--) {
|
|
|
|
char ch = *name++;
|
|
|
|
if (! (ch == '-' || ch == '.' || ch == '_' ||
|
|
|
|
('0' <= ch && ch <= '9') ||
|
|
|
|
('a' <= ch && ch <= 'z') ||
|
|
|
|
('A' <= ch && ch <= 'Z')) )
|
2017-01-28 10:02:00 +08:00
|
|
|
return 0;
|
2007-04-16 05:56:09 +08:00
|
|
|
}
|
2017-01-28 10:02:00 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void report_invalid_attr(const char *name, size_t len,
|
|
|
|
const char *src, int lineno)
|
|
|
|
{
|
|
|
|
struct strbuf err = STRBUF_INIT;
|
|
|
|
strbuf_addf(&err, _("%.*s is not a valid attribute name"),
|
|
|
|
(int) len, name);
|
|
|
|
fprintf(stderr, "%s: %s:%d\n", err.buf, src, lineno);
|
|
|
|
strbuf_release(&err);
|
2007-04-16 05:56:09 +08:00
|
|
|
}
|
|
|
|
|
2017-01-28 10:02:01 +08:00
|
|
|
/*
|
|
|
|
* Given a 'name', lookup and return the corresponding attribute in the global
|
|
|
|
* dictionary. If no entry is found, create a new attribute and store it in
|
|
|
|
* the dictionary.
|
|
|
|
*/
|
2022-12-01 22:45:15 +08:00
|
|
|
static const struct git_attr *git_attr_internal(const char *name, size_t namelen)
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
{
|
|
|
|
struct git_attr *a;
|
|
|
|
|
2017-01-28 10:02:01 +08:00
|
|
|
if (!attr_name_valid(name, namelen))
|
2007-04-16 05:56:09 +08:00
|
|
|
return NULL;
|
|
|
|
|
2017-01-28 10:02:01 +08:00
|
|
|
hashmap_lock(&g_attr_hashmap);
|
|
|
|
|
|
|
|
a = attr_hashmap_get(&g_attr_hashmap, name, namelen);
|
|
|
|
|
|
|
|
if (!a) {
|
|
|
|
FLEX_ALLOC_MEM(a, name, name, namelen);
|
2017-09-06 23:43:48 +08:00
|
|
|
a->attr_nr = hashmap_get_size(&g_attr_hashmap.map);
|
2017-01-28 10:02:01 +08:00
|
|
|
|
|
|
|
attr_hashmap_add(&g_attr_hashmap, a->name, namelen, a);
|
2022-12-01 22:45:36 +08:00
|
|
|
if (a->attr_nr != hashmap_get_size(&g_attr_hashmap.map) - 1)
|
|
|
|
die(_("unable to add additional attribute"));
|
2017-01-28 10:02:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
hashmap_unlock(&g_attr_hashmap);
|
attribute macro support
This adds "attribute macros" (for lack of better name). So far,
we have low-level attributes such as crlf and diff, which are
defined in operational terms --- setting or unsetting them on a
particular path directly affects what is done to the path. For
example, in order to decline diffs or crlf conversions on a
binary blob, no diffs on PostScript files, and treat all other
files normally, you would have something like these:
* diff crlf
*.ps !diff
proprietary.o !diff !crlf
That is fine as the operation goes, but gets unwieldy rather
rapidly, when we start adding more low-level attributes that are
defined in operational terms. A near-term example of such an
attribute would be 'merge-3way' which would control if git
should attempt the usual 3-way file-level merge internally, or
leave merging to a specialized external program of user's
choice. When it is added, we do _not_ want to force the users
to update the above to:
* diff crlf merge-3way
*.ps !diff
proprietary.o !diff !crlf !merge-3way
The way this patch solves this issue is to realize that the
attributes the user is assigning to paths are not defined in
terms of operations but in terms of what they are.
All of the three low-level attributes usually make sense for
most of the files that sane SCM users have git operate on (these
files are typically called "text'). Only a few cases, such as
binary blob, need exception to decline the "usual treatment
given to text files" -- and people mark them as "binary".
So this allows the $GIT_DIR/info/alternates and .gitattributes
at the toplevel of the project to also specify attributes that
assigns other attributes. The syntax is '[attr]' followed by an
attribute name followed by a list of attribute names:
[attr] binary !diff !crlf !merge-3way
When "binary" attribute is set to a path, if the path has not
got diff/crlf/merge-3way attribute set or unset by other rules,
this rule unsets the three low-level attributes.
It is expected that the user level .gitattributes will be
expressed mostly in terms of attributes based on what the files
are, and the above sample would become like this:
(built-in attribute configuration)
[attr] binary !diff !crlf !merge-3way
* diff crlf merge-3way
(project specific .gitattributes)
proprietary.o binary
(user preference $GIT_DIR/info/attributes)
*.ps !diff
There are a few caveats.
* As described above, you can define these macros only in
$GIT_DIR/info/attributes and toplevel .gitattributes.
* There is no attempt to detect circular definition of macro
attributes, and definitions are evaluated from bottom to top
as usual to fill in other attributes that have not yet got
values. The following would work as expected:
[attr] text diff crlf
[attr] ps text !diff
*.ps ps
while this would most likely not (I haven't tried):
[attr] ps text !diff
[attr] text diff crlf
*.ps ps
* When a macro says "[attr] A B !C", saying that a path does
not have attribute A does not let you tell anything about
attributes B or C. That is, given this:
[attr] text diff crlf
[attr] ps text !diff
*.txt !ps
path hello.txt, which would match "*.txt" pattern, would have
"ps" attribute set to zero, but that does not make text
attribute of hello.txt set to false (nor diff attribute set to
true).
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-14 23:54:37 +08:00
|
|
|
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
2017-01-28 10:02:04 +08:00
|
|
|
const struct git_attr *git_attr(const char *name)
|
2010-01-17 12:39:59 +08:00
|
|
|
{
|
|
|
|
return git_attr_internal(name, strlen(name));
|
|
|
|
}
|
|
|
|
|
2007-04-17 12:33:31 +08:00
|
|
|
/* What does a matched pattern decide? */
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
struct attr_state {
|
2017-01-28 10:02:04 +08:00
|
|
|
const struct git_attr *attr;
|
2007-04-19 07:16:37 +08:00
|
|
|
const char *setto;
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
};
|
|
|
|
|
2012-10-15 14:24:39 +08:00
|
|
|
struct pattern {
|
|
|
|
const char *pattern;
|
|
|
|
int patternlen;
|
|
|
|
int nowildcardlen;
|
2019-09-04 02:04:56 +08:00
|
|
|
unsigned flags; /* PATTERN_FLAG_* */
|
2012-10-15 14:24:39 +08:00
|
|
|
};
|
|
|
|
|
2011-08-13 05:43:05 +08:00
|
|
|
/*
|
|
|
|
* One rule, as from a .gitattributes file.
|
|
|
|
*
|
|
|
|
* If is_macro is true, then u.attr is a pointer to the git_attr being
|
|
|
|
* defined.
|
|
|
|
*
|
2017-01-28 10:01:43 +08:00
|
|
|
* If is_macro is false, then u.pat is the filename pattern to which the
|
|
|
|
* rule applies.
|
2011-08-13 05:43:05 +08:00
|
|
|
*
|
|
|
|
* In either case, num_attr is the number of attributes affected by
|
|
|
|
* this rule, and state is an array listing them. The attributes are
|
|
|
|
* listed as they appear in the file (macros unexpanded).
|
|
|
|
*/
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
struct match_attr {
|
attribute macro support
This adds "attribute macros" (for lack of better name). So far,
we have low-level attributes such as crlf and diff, which are
defined in operational terms --- setting or unsetting them on a
particular path directly affects what is done to the path. For
example, in order to decline diffs or crlf conversions on a
binary blob, no diffs on PostScript files, and treat all other
files normally, you would have something like these:
* diff crlf
*.ps !diff
proprietary.o !diff !crlf
That is fine as the operation goes, but gets unwieldy rather
rapidly, when we start adding more low-level attributes that are
defined in operational terms. A near-term example of such an
attribute would be 'merge-3way' which would control if git
should attempt the usual 3-way file-level merge internally, or
leave merging to a specialized external program of user's
choice. When it is added, we do _not_ want to force the users
to update the above to:
* diff crlf merge-3way
*.ps !diff
proprietary.o !diff !crlf !merge-3way
The way this patch solves this issue is to realize that the
attributes the user is assigning to paths are not defined in
terms of operations but in terms of what they are.
All of the three low-level attributes usually make sense for
most of the files that sane SCM users have git operate on (these
files are typically called "text'). Only a few cases, such as
binary blob, need exception to decline the "usual treatment
given to text files" -- and people mark them as "binary".
So this allows the $GIT_DIR/info/alternates and .gitattributes
at the toplevel of the project to also specify attributes that
assigns other attributes. The syntax is '[attr]' followed by an
attribute name followed by a list of attribute names:
[attr] binary !diff !crlf !merge-3way
When "binary" attribute is set to a path, if the path has not
got diff/crlf/merge-3way attribute set or unset by other rules,
this rule unsets the three low-level attributes.
It is expected that the user level .gitattributes will be
expressed mostly in terms of attributes based on what the files
are, and the above sample would become like this:
(built-in attribute configuration)
[attr] binary !diff !crlf !merge-3way
* diff crlf merge-3way
(project specific .gitattributes)
proprietary.o binary
(user preference $GIT_DIR/info/attributes)
*.ps !diff
There are a few caveats.
* As described above, you can define these macros only in
$GIT_DIR/info/attributes and toplevel .gitattributes.
* There is no attempt to detect circular definition of macro
attributes, and definitions are evaluated from bottom to top
as usual to fill in other attributes that have not yet got
values. The following would work as expected:
[attr] text diff crlf
[attr] ps text !diff
*.ps ps
while this would most likely not (I haven't tried):
[attr] ps text !diff
[attr] text diff crlf
*.ps ps
* When a macro says "[attr] A B !C", saying that a path does
not have attribute A does not let you tell anything about
attributes B or C. That is, given this:
[attr] text diff crlf
[attr] ps text !diff
*.txt !ps
path hello.txt, which would match "*.txt" pattern, would have
"ps" attribute set to zero, but that does not make text
attribute of hello.txt set to false (nor diff attribute set to
true).
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-14 23:54:37 +08:00
|
|
|
union {
|
2012-10-15 14:24:39 +08:00
|
|
|
struct pattern pat;
|
2017-01-28 10:02:04 +08:00
|
|
|
const struct git_attr *attr;
|
attribute macro support
This adds "attribute macros" (for lack of better name). So far,
we have low-level attributes such as crlf and diff, which are
defined in operational terms --- setting or unsetting them on a
particular path directly affects what is done to the path. For
example, in order to decline diffs or crlf conversions on a
binary blob, no diffs on PostScript files, and treat all other
files normally, you would have something like these:
* diff crlf
*.ps !diff
proprietary.o !diff !crlf
That is fine as the operation goes, but gets unwieldy rather
rapidly, when we start adding more low-level attributes that are
defined in operational terms. A near-term example of such an
attribute would be 'merge-3way' which would control if git
should attempt the usual 3-way file-level merge internally, or
leave merging to a specialized external program of user's
choice. When it is added, we do _not_ want to force the users
to update the above to:
* diff crlf merge-3way
*.ps !diff
proprietary.o !diff !crlf !merge-3way
The way this patch solves this issue is to realize that the
attributes the user is assigning to paths are not defined in
terms of operations but in terms of what they are.
All of the three low-level attributes usually make sense for
most of the files that sane SCM users have git operate on (these
files are typically called "text'). Only a few cases, such as
binary blob, need exception to decline the "usual treatment
given to text files" -- and people mark them as "binary".
So this allows the $GIT_DIR/info/alternates and .gitattributes
at the toplevel of the project to also specify attributes that
assigns other attributes. The syntax is '[attr]' followed by an
attribute name followed by a list of attribute names:
[attr] binary !diff !crlf !merge-3way
When "binary" attribute is set to a path, if the path has not
got diff/crlf/merge-3way attribute set or unset by other rules,
this rule unsets the three low-level attributes.
It is expected that the user level .gitattributes will be
expressed mostly in terms of attributes based on what the files
are, and the above sample would become like this:
(built-in attribute configuration)
[attr] binary !diff !crlf !merge-3way
* diff crlf merge-3way
(project specific .gitattributes)
proprietary.o binary
(user preference $GIT_DIR/info/attributes)
*.ps !diff
There are a few caveats.
* As described above, you can define these macros only in
$GIT_DIR/info/attributes and toplevel .gitattributes.
* There is no attempt to detect circular definition of macro
attributes, and definitions are evaluated from bottom to top
as usual to fill in other attributes that have not yet got
values. The following would work as expected:
[attr] text diff crlf
[attr] ps text !diff
*.ps ps
while this would most likely not (I haven't tried):
[attr] ps text !diff
[attr] text diff crlf
*.ps ps
* When a macro says "[attr] A B !C", saying that a path does
not have attribute A does not let you tell anything about
attributes B or C. That is, given this:
[attr] text diff crlf
[attr] ps text !diff
*.txt !ps
path hello.txt, which would match "*.txt" pattern, would have
"ps" attribute set to zero, but that does not make text
attribute of hello.txt set to false (nor diff attribute set to
true).
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-14 23:54:37 +08:00
|
|
|
} u;
|
|
|
|
char is_macro;
|
2022-12-01 22:45:27 +08:00
|
|
|
size_t num_attr;
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
struct attr_state state[FLEX_ARRAY];
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char blank[] = " \t\r\n";
|
|
|
|
|
2021-02-16 22:44:25 +08:00
|
|
|
/* Flags usable in read_attr() and parse_attr_line() family of functions. */
|
|
|
|
#define READ_ATTR_MACRO_OK (1<<0)
|
attr: do not respect symlinks for in-tree .gitattributes
The attributes system may sometimes read in-tree files from the
filesystem, and sometimes from the index. In the latter case, we do not
resolve symbolic links (and are not likely to ever start doing so).
Let's open filesystem links with O_NOFOLLOW so that the two cases behave
consistently.
As a bonus, this means that git will not follow such symlinks to read
and parse out-of-tree paths. In some cases this could have security
implications, as a malicious repository can cause Git to open and read
arbitrary files. It could already feed arbitrary content to the parser,
but in certain setups it might be able to exfiltrate data from those
paths (e.g., if an automated service operating on the malicious repo
reveals its stderr to an attacker).
Note that O_NOFOLLOW only prevents following links for the path itself,
not intermediate directories in the path. At first glance, it seems
like
ln -s /some/path in-repo
might still look at "in-repo/.gitattributes", following the symlink to
"/some/path/.gitattributes". However, if "in-repo" is a symbolic link,
then we know that it has no git paths below it, and will never look at
its .gitattributes file.
We will continue to support out-of-tree symbolic links (e.g., in
$GIT_DIR/info/attributes); this just affects in-tree links. When a
symbolic link is encountered, the contents are ignored and a warning is
printed. POSIX specifies ELOOP in this case, so the user would generally
see something like:
warning: unable to access '.gitattributes': Too many levels of symbolic links
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-02-16 22:44:32 +08:00
|
|
|
#define READ_ATTR_NOFOLLOW (1<<1)
|
2021-02-16 22:44:25 +08:00
|
|
|
|
2011-08-13 05:43:07 +08:00
|
|
|
/*
|
|
|
|
* Parse a whitespace-delimited attribute state (i.e., "attr",
|
|
|
|
* "-attr", "!attr", or "attr=value") from the string starting at src.
|
|
|
|
* If e is not NULL, write the results to *e. Return a pointer to the
|
|
|
|
* remainder of the string (with leading whitespace removed), or NULL
|
|
|
|
* if there was an error.
|
|
|
|
*/
|
2007-04-17 12:33:31 +08:00
|
|
|
static const char *parse_attr(const char *src, int lineno, const char *cp,
|
2011-08-13 05:43:07 +08:00
|
|
|
struct attr_state *e)
|
2007-04-17 12:33:31 +08:00
|
|
|
{
|
|
|
|
const char *ep, *equals;
|
2022-12-01 22:45:23 +08:00
|
|
|
size_t len;
|
2007-04-17 12:33:31 +08:00
|
|
|
|
|
|
|
ep = cp + strcspn(cp, blank);
|
|
|
|
equals = strchr(cp, '=');
|
|
|
|
if (equals && ep < equals)
|
|
|
|
equals = NULL;
|
|
|
|
if (equals)
|
|
|
|
len = equals - cp;
|
|
|
|
else
|
|
|
|
len = ep - cp;
|
2011-08-13 05:43:07 +08:00
|
|
|
if (!e) {
|
2007-04-17 12:33:31 +08:00
|
|
|
if (*cp == '-' || *cp == '!') {
|
|
|
|
cp++;
|
|
|
|
len--;
|
|
|
|
}
|
2023-11-16 13:44:37 +08:00
|
|
|
if (!attr_name_valid(cp, len) || attr_name_reserved(cp)) {
|
2017-01-28 10:02:00 +08:00
|
|
|
report_invalid_attr(cp, len, src, lineno);
|
2007-04-17 12:33:31 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
} else {
|
2017-01-28 10:01:44 +08:00
|
|
|
/*
|
|
|
|
* As this function is always called twice, once with
|
|
|
|
* e == NULL in the first pass and then e != NULL in
|
2017-01-28 10:02:00 +08:00
|
|
|
* the second pass, no need for attr_name_valid()
|
2017-01-28 10:01:44 +08:00
|
|
|
* check here.
|
|
|
|
*/
|
2007-04-17 12:33:31 +08:00
|
|
|
if (*cp == '-' || *cp == '!') {
|
|
|
|
e->setto = (*cp == '-') ? ATTR__FALSE : ATTR__UNSET;
|
|
|
|
cp++;
|
|
|
|
len--;
|
|
|
|
}
|
|
|
|
else if (!equals)
|
|
|
|
e->setto = ATTR__TRUE;
|
|
|
|
else {
|
2007-09-16 06:32:36 +08:00
|
|
|
e->setto = xmemdupz(equals + 1, ep - equals - 1);
|
2007-04-17 12:33:31 +08:00
|
|
|
}
|
2010-01-17 12:39:59 +08:00
|
|
|
e->attr = git_attr_internal(cp, len);
|
2007-04-17 12:33:31 +08:00
|
|
|
}
|
|
|
|
return ep + strspn(ep, blank);
|
|
|
|
}
|
|
|
|
|
attribute macro support
This adds "attribute macros" (for lack of better name). So far,
we have low-level attributes such as crlf and diff, which are
defined in operational terms --- setting or unsetting them on a
particular path directly affects what is done to the path. For
example, in order to decline diffs or crlf conversions on a
binary blob, no diffs on PostScript files, and treat all other
files normally, you would have something like these:
* diff crlf
*.ps !diff
proprietary.o !diff !crlf
That is fine as the operation goes, but gets unwieldy rather
rapidly, when we start adding more low-level attributes that are
defined in operational terms. A near-term example of such an
attribute would be 'merge-3way' which would control if git
should attempt the usual 3-way file-level merge internally, or
leave merging to a specialized external program of user's
choice. When it is added, we do _not_ want to force the users
to update the above to:
* diff crlf merge-3way
*.ps !diff
proprietary.o !diff !crlf !merge-3way
The way this patch solves this issue is to realize that the
attributes the user is assigning to paths are not defined in
terms of operations but in terms of what they are.
All of the three low-level attributes usually make sense for
most of the files that sane SCM users have git operate on (these
files are typically called "text'). Only a few cases, such as
binary blob, need exception to decline the "usual treatment
given to text files" -- and people mark them as "binary".
So this allows the $GIT_DIR/info/alternates and .gitattributes
at the toplevel of the project to also specify attributes that
assigns other attributes. The syntax is '[attr]' followed by an
attribute name followed by a list of attribute names:
[attr] binary !diff !crlf !merge-3way
When "binary" attribute is set to a path, if the path has not
got diff/crlf/merge-3way attribute set or unset by other rules,
this rule unsets the three low-level attributes.
It is expected that the user level .gitattributes will be
expressed mostly in terms of attributes based on what the files
are, and the above sample would become like this:
(built-in attribute configuration)
[attr] binary !diff !crlf !merge-3way
* diff crlf merge-3way
(project specific .gitattributes)
proprietary.o binary
(user preference $GIT_DIR/info/attributes)
*.ps !diff
There are a few caveats.
* As described above, you can define these macros only in
$GIT_DIR/info/attributes and toplevel .gitattributes.
* There is no attempt to detect circular definition of macro
attributes, and definitions are evaluated from bottom to top
as usual to fill in other attributes that have not yet got
values. The following would work as expected:
[attr] text diff crlf
[attr] ps text !diff
*.ps ps
while this would most likely not (I haven't tried):
[attr] ps text !diff
[attr] text diff crlf
*.ps ps
* When a macro says "[attr] A B !C", saying that a path does
not have attribute A does not let you tell anything about
attributes B or C. That is, given this:
[attr] text diff crlf
[attr] ps text !diff
*.txt !ps
path hello.txt, which would match "*.txt" pattern, would have
"ps" attribute set to zero, but that does not make text
attribute of hello.txt set to false (nor diff attribute set to
true).
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-14 23:54:37 +08:00
|
|
|
static struct match_attr *parse_attr_line(const char *line, const char *src,
|
2021-02-16 22:44:25 +08:00
|
|
|
int lineno, unsigned flags)
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
{
|
2022-12-01 22:45:27 +08:00
|
|
|
size_t namelen, num_attr, i;
|
2011-08-13 05:43:08 +08:00
|
|
|
const char *cp, *name, *states;
|
2007-04-17 12:33:31 +08:00
|
|
|
struct match_attr *res = NULL;
|
attribute macro support
This adds "attribute macros" (for lack of better name). So far,
we have low-level attributes such as crlf and diff, which are
defined in operational terms --- setting or unsetting them on a
particular path directly affects what is done to the path. For
example, in order to decline diffs or crlf conversions on a
binary blob, no diffs on PostScript files, and treat all other
files normally, you would have something like these:
* diff crlf
*.ps !diff
proprietary.o !diff !crlf
That is fine as the operation goes, but gets unwieldy rather
rapidly, when we start adding more low-level attributes that are
defined in operational terms. A near-term example of such an
attribute would be 'merge-3way' which would control if git
should attempt the usual 3-way file-level merge internally, or
leave merging to a specialized external program of user's
choice. When it is added, we do _not_ want to force the users
to update the above to:
* diff crlf merge-3way
*.ps !diff
proprietary.o !diff !crlf !merge-3way
The way this patch solves this issue is to realize that the
attributes the user is assigning to paths are not defined in
terms of operations but in terms of what they are.
All of the three low-level attributes usually make sense for
most of the files that sane SCM users have git operate on (these
files are typically called "text'). Only a few cases, such as
binary blob, need exception to decline the "usual treatment
given to text files" -- and people mark them as "binary".
So this allows the $GIT_DIR/info/alternates and .gitattributes
at the toplevel of the project to also specify attributes that
assigns other attributes. The syntax is '[attr]' followed by an
attribute name followed by a list of attribute names:
[attr] binary !diff !crlf !merge-3way
When "binary" attribute is set to a path, if the path has not
got diff/crlf/merge-3way attribute set or unset by other rules,
this rule unsets the three low-level attributes.
It is expected that the user level .gitattributes will be
expressed mostly in terms of attributes based on what the files
are, and the above sample would become like this:
(built-in attribute configuration)
[attr] binary !diff !crlf !merge-3way
* diff crlf merge-3way
(project specific .gitattributes)
proprietary.o binary
(user preference $GIT_DIR/info/attributes)
*.ps !diff
There are a few caveats.
* As described above, you can define these macros only in
$GIT_DIR/info/attributes and toplevel .gitattributes.
* There is no attempt to detect circular definition of macro
attributes, and definitions are evaluated from bottom to top
as usual to fill in other attributes that have not yet got
values. The following would work as expected:
[attr] text diff crlf
[attr] ps text !diff
*.ps ps
while this would most likely not (I haven't tried):
[attr] ps text !diff
[attr] text diff crlf
*.ps ps
* When a macro says "[attr] A B !C", saying that a path does
not have attribute A does not let you tell anything about
attributes B or C. That is, given this:
[attr] text diff crlf
[attr] ps text !diff
*.txt !ps
path hello.txt, which would match "*.txt" pattern, would have
"ps" attribute set to zero, but that does not make text
attribute of hello.txt set to false (nor diff attribute set to
true).
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-14 23:54:37 +08:00
|
|
|
int is_macro;
|
2017-01-28 10:01:50 +08:00
|
|
|
struct strbuf pattern = STRBUF_INIT;
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
|
|
|
|
cp = line + strspn(line, blank);
|
|
|
|
if (!*cp || *cp == '#')
|
|
|
|
return NULL;
|
|
|
|
name = cp;
|
2017-01-28 10:01:50 +08:00
|
|
|
|
2022-12-01 22:45:48 +08:00
|
|
|
if (strlen(line) >= ATTR_MAX_LINE_LENGTH) {
|
|
|
|
warning(_("ignoring overly long attributes line %d"), lineno);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-01-28 10:01:50 +08:00
|
|
|
if (*cp == '"' && !unquote_c_style(&pattern, name, &states)) {
|
|
|
|
name = pattern.buf;
|
|
|
|
namelen = pattern.len;
|
|
|
|
} else {
|
|
|
|
namelen = strcspn(name, blank);
|
|
|
|
states = name + namelen;
|
|
|
|
}
|
|
|
|
|
attribute macro support
This adds "attribute macros" (for lack of better name). So far,
we have low-level attributes such as crlf and diff, which are
defined in operational terms --- setting or unsetting them on a
particular path directly affects what is done to the path. For
example, in order to decline diffs or crlf conversions on a
binary blob, no diffs on PostScript files, and treat all other
files normally, you would have something like these:
* diff crlf
*.ps !diff
proprietary.o !diff !crlf
That is fine as the operation goes, but gets unwieldy rather
rapidly, when we start adding more low-level attributes that are
defined in operational terms. A near-term example of such an
attribute would be 'merge-3way' which would control if git
should attempt the usual 3-way file-level merge internally, or
leave merging to a specialized external program of user's
choice. When it is added, we do _not_ want to force the users
to update the above to:
* diff crlf merge-3way
*.ps !diff
proprietary.o !diff !crlf !merge-3way
The way this patch solves this issue is to realize that the
attributes the user is assigning to paths are not defined in
terms of operations but in terms of what they are.
All of the three low-level attributes usually make sense for
most of the files that sane SCM users have git operate on (these
files are typically called "text'). Only a few cases, such as
binary blob, need exception to decline the "usual treatment
given to text files" -- and people mark them as "binary".
So this allows the $GIT_DIR/info/alternates and .gitattributes
at the toplevel of the project to also specify attributes that
assigns other attributes. The syntax is '[attr]' followed by an
attribute name followed by a list of attribute names:
[attr] binary !diff !crlf !merge-3way
When "binary" attribute is set to a path, if the path has not
got diff/crlf/merge-3way attribute set or unset by other rules,
this rule unsets the three low-level attributes.
It is expected that the user level .gitattributes will be
expressed mostly in terms of attributes based on what the files
are, and the above sample would become like this:
(built-in attribute configuration)
[attr] binary !diff !crlf !merge-3way
* diff crlf merge-3way
(project specific .gitattributes)
proprietary.o binary
(user preference $GIT_DIR/info/attributes)
*.ps !diff
There are a few caveats.
* As described above, you can define these macros only in
$GIT_DIR/info/attributes and toplevel .gitattributes.
* There is no attempt to detect circular definition of macro
attributes, and definitions are evaluated from bottom to top
as usual to fill in other attributes that have not yet got
values. The following would work as expected:
[attr] text diff crlf
[attr] ps text !diff
*.ps ps
while this would most likely not (I haven't tried):
[attr] ps text !diff
[attr] text diff crlf
*.ps ps
* When a macro says "[attr] A B !C", saying that a path does
not have attribute A does not let you tell anything about
attributes B or C. That is, given this:
[attr] text diff crlf
[attr] ps text !diff
*.txt !ps
path hello.txt, which would match "*.txt" pattern, would have
"ps" attribute set to zero, but that does not make text
attribute of hello.txt set to false (nor diff attribute set to
true).
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-14 23:54:37 +08:00
|
|
|
if (strlen(ATTRIBUTE_MACRO_PREFIX) < namelen &&
|
2013-12-01 04:55:40 +08:00
|
|
|
starts_with(name, ATTRIBUTE_MACRO_PREFIX)) {
|
2021-02-16 22:44:25 +08:00
|
|
|
if (!(flags & READ_ATTR_MACRO_OK)) {
|
2018-11-10 13:16:03 +08:00
|
|
|
fprintf_ln(stderr, _("%s not allowed: %s:%d"),
|
|
|
|
name, src, lineno);
|
2017-01-28 10:01:49 +08:00
|
|
|
goto fail_return;
|
attribute macro support
This adds "attribute macros" (for lack of better name). So far,
we have low-level attributes such as crlf and diff, which are
defined in operational terms --- setting or unsetting them on a
particular path directly affects what is done to the path. For
example, in order to decline diffs or crlf conversions on a
binary blob, no diffs on PostScript files, and treat all other
files normally, you would have something like these:
* diff crlf
*.ps !diff
proprietary.o !diff !crlf
That is fine as the operation goes, but gets unwieldy rather
rapidly, when we start adding more low-level attributes that are
defined in operational terms. A near-term example of such an
attribute would be 'merge-3way' which would control if git
should attempt the usual 3-way file-level merge internally, or
leave merging to a specialized external program of user's
choice. When it is added, we do _not_ want to force the users
to update the above to:
* diff crlf merge-3way
*.ps !diff
proprietary.o !diff !crlf !merge-3way
The way this patch solves this issue is to realize that the
attributes the user is assigning to paths are not defined in
terms of operations but in terms of what they are.
All of the three low-level attributes usually make sense for
most of the files that sane SCM users have git operate on (these
files are typically called "text'). Only a few cases, such as
binary blob, need exception to decline the "usual treatment
given to text files" -- and people mark them as "binary".
So this allows the $GIT_DIR/info/alternates and .gitattributes
at the toplevel of the project to also specify attributes that
assigns other attributes. The syntax is '[attr]' followed by an
attribute name followed by a list of attribute names:
[attr] binary !diff !crlf !merge-3way
When "binary" attribute is set to a path, if the path has not
got diff/crlf/merge-3way attribute set or unset by other rules,
this rule unsets the three low-level attributes.
It is expected that the user level .gitattributes will be
expressed mostly in terms of attributes based on what the files
are, and the above sample would become like this:
(built-in attribute configuration)
[attr] binary !diff !crlf !merge-3way
* diff crlf merge-3way
(project specific .gitattributes)
proprietary.o binary
(user preference $GIT_DIR/info/attributes)
*.ps !diff
There are a few caveats.
* As described above, you can define these macros only in
$GIT_DIR/info/attributes and toplevel .gitattributes.
* There is no attempt to detect circular definition of macro
attributes, and definitions are evaluated from bottom to top
as usual to fill in other attributes that have not yet got
values. The following would work as expected:
[attr] text diff crlf
[attr] ps text !diff
*.ps ps
while this would most likely not (I haven't tried):
[attr] ps text !diff
[attr] text diff crlf
*.ps ps
* When a macro says "[attr] A B !C", saying that a path does
not have attribute A does not let you tell anything about
attributes B or C. That is, given this:
[attr] text diff crlf
[attr] ps text !diff
*.txt !ps
path hello.txt, which would match "*.txt" pattern, would have
"ps" attribute set to zero, but that does not make text
attribute of hello.txt set to false (nor diff attribute set to
true).
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-14 23:54:37 +08:00
|
|
|
}
|
|
|
|
is_macro = 1;
|
|
|
|
name += strlen(ATTRIBUTE_MACRO_PREFIX);
|
|
|
|
name += strspn(name, blank);
|
|
|
|
namelen = strcspn(name, blank);
|
2023-11-16 13:44:37 +08:00
|
|
|
if (!attr_name_valid(name, namelen) || attr_name_reserved(name)) {
|
2017-01-28 10:02:00 +08:00
|
|
|
report_invalid_attr(name, namelen, src, lineno);
|
2017-01-28 10:01:49 +08:00
|
|
|
goto fail_return;
|
2007-04-16 05:56:09 +08:00
|
|
|
}
|
attribute macro support
This adds "attribute macros" (for lack of better name). So far,
we have low-level attributes such as crlf and diff, which are
defined in operational terms --- setting or unsetting them on a
particular path directly affects what is done to the path. For
example, in order to decline diffs or crlf conversions on a
binary blob, no diffs on PostScript files, and treat all other
files normally, you would have something like these:
* diff crlf
*.ps !diff
proprietary.o !diff !crlf
That is fine as the operation goes, but gets unwieldy rather
rapidly, when we start adding more low-level attributes that are
defined in operational terms. A near-term example of such an
attribute would be 'merge-3way' which would control if git
should attempt the usual 3-way file-level merge internally, or
leave merging to a specialized external program of user's
choice. When it is added, we do _not_ want to force the users
to update the above to:
* diff crlf merge-3way
*.ps !diff
proprietary.o !diff !crlf !merge-3way
The way this patch solves this issue is to realize that the
attributes the user is assigning to paths are not defined in
terms of operations but in terms of what they are.
All of the three low-level attributes usually make sense for
most of the files that sane SCM users have git operate on (these
files are typically called "text'). Only a few cases, such as
binary blob, need exception to decline the "usual treatment
given to text files" -- and people mark them as "binary".
So this allows the $GIT_DIR/info/alternates and .gitattributes
at the toplevel of the project to also specify attributes that
assigns other attributes. The syntax is '[attr]' followed by an
attribute name followed by a list of attribute names:
[attr] binary !diff !crlf !merge-3way
When "binary" attribute is set to a path, if the path has not
got diff/crlf/merge-3way attribute set or unset by other rules,
this rule unsets the three low-level attributes.
It is expected that the user level .gitattributes will be
expressed mostly in terms of attributes based on what the files
are, and the above sample would become like this:
(built-in attribute configuration)
[attr] binary !diff !crlf !merge-3way
* diff crlf merge-3way
(project specific .gitattributes)
proprietary.o binary
(user preference $GIT_DIR/info/attributes)
*.ps !diff
There are a few caveats.
* As described above, you can define these macros only in
$GIT_DIR/info/attributes and toplevel .gitattributes.
* There is no attempt to detect circular definition of macro
attributes, and definitions are evaluated from bottom to top
as usual to fill in other attributes that have not yet got
values. The following would work as expected:
[attr] text diff crlf
[attr] ps text !diff
*.ps ps
while this would most likely not (I haven't tried):
[attr] ps text !diff
[attr] text diff crlf
*.ps ps
* When a macro says "[attr] A B !C", saying that a path does
not have attribute A does not let you tell anything about
attributes B or C. That is, given this:
[attr] text diff crlf
[attr] ps text !diff
*.txt !ps
path hello.txt, which would match "*.txt" pattern, would have
"ps" attribute set to zero, but that does not make text
attribute of hello.txt set to false (nor diff attribute set to
true).
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-14 23:54:37 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
is_macro = 0;
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
|
2011-08-13 05:43:08 +08:00
|
|
|
states += strspn(states, blank);
|
|
|
|
|
2011-08-13 05:43:10 +08:00
|
|
|
/* First pass to count the attr_states */
|
|
|
|
for (cp = states, num_attr = 0; *cp; num_attr++) {
|
|
|
|
cp = parse_attr(src, lineno, cp, NULL);
|
|
|
|
if (!cp)
|
2017-01-28 10:01:49 +08:00
|
|
|
goto fail_return;
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
}
|
2011-08-13 05:43:10 +08:00
|
|
|
|
2022-12-01 22:45:40 +08:00
|
|
|
res = xcalloc(1, st_add3(sizeof(*res),
|
|
|
|
st_mult(sizeof(struct attr_state), num_attr),
|
|
|
|
is_macro ? 0 : namelen + 1));
|
2014-12-28 07:39:47 +08:00
|
|
|
if (is_macro) {
|
2011-08-13 05:43:10 +08:00
|
|
|
res->u.attr = git_attr_internal(name, namelen);
|
2014-12-28 07:39:47 +08:00
|
|
|
} else {
|
2012-10-15 14:24:39 +08:00
|
|
|
char *p = (char *)&(res->state[num_attr]);
|
|
|
|
memcpy(p, name, namelen);
|
|
|
|
res->u.pat.pattern = p;
|
2019-09-04 02:04:57 +08:00
|
|
|
parse_path_pattern(&res->u.pat.pattern,
|
2012-10-15 14:24:39 +08:00
|
|
|
&res->u.pat.patternlen,
|
|
|
|
&res->u.pat.flags,
|
|
|
|
&res->u.pat.nowildcardlen);
|
2019-09-04 02:04:56 +08:00
|
|
|
if (res->u.pat.flags & PATTERN_FLAG_NEGATIVE) {
|
2013-03-02 04:06:17 +08:00
|
|
|
warning(_("Negative patterns are ignored in git attributes\n"
|
|
|
|
"Use '\\!' for literal leading exclamation."));
|
2017-01-28 10:01:49 +08:00
|
|
|
goto fail_return;
|
2013-03-02 04:06:17 +08:00
|
|
|
}
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
}
|
2011-08-13 05:43:10 +08:00
|
|
|
res->is_macro = is_macro;
|
|
|
|
res->num_attr = num_attr;
|
|
|
|
|
|
|
|
/* Second pass to fill the attr_states */
|
|
|
|
for (cp = states, i = 0; *cp; i++) {
|
|
|
|
cp = parse_attr(src, lineno, cp, &(res->state[i]));
|
|
|
|
}
|
|
|
|
|
2017-01-28 10:01:50 +08:00
|
|
|
strbuf_release(&pattern);
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
return res;
|
2017-01-28 10:01:49 +08:00
|
|
|
|
|
|
|
fail_return:
|
2017-01-28 10:01:50 +08:00
|
|
|
strbuf_release(&pattern);
|
2017-01-28 10:01:49 +08:00
|
|
|
free(res);
|
|
|
|
return NULL;
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Like info/exclude and .gitignore, the attribute information can
|
|
|
|
* come from many places.
|
|
|
|
*
|
2019-03-06 17:14:44 +08:00
|
|
|
* (1) .gitattributes file of the same directory;
|
|
|
|
* (2) .gitattributes file of the parent directory if (1) does not have
|
2007-04-17 12:33:31 +08:00
|
|
|
* any match; this goes recursively upwards, just like .gitignore.
|
|
|
|
* (3) $GIT_DIR/info/attributes, which overrides both of the above.
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
*
|
|
|
|
* In the same file, later entries override the earlier match, so in the
|
|
|
|
* global list, we would have entries from info/attributes the earliest
|
2019-03-06 17:14:44 +08:00
|
|
|
* (reading the file from top to bottom), .gitattributes of the root
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
* directory (again, reading the file from top to bottom) down to the
|
|
|
|
* current directory, and then scan the list backwards to find the first match.
|
2012-12-27 10:32:25 +08:00
|
|
|
* This is exactly the same as what is_excluded() does in dir.c to deal with
|
2017-01-28 10:01:45 +08:00
|
|
|
* .gitignore file and info/excludes file as a fallback.
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
*/
|
|
|
|
|
2017-01-28 10:02:05 +08:00
|
|
|
struct attr_stack {
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
struct attr_stack *prev;
|
|
|
|
char *origin;
|
2012-10-05 12:41:01 +08:00
|
|
|
size_t originlen;
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
unsigned num_matches;
|
2007-08-14 16:40:45 +08:00
|
|
|
unsigned alloc;
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
struct match_attr **attrs;
|
2017-01-28 10:02:05 +08:00
|
|
|
};
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
|
2017-01-28 10:02:05 +08:00
|
|
|
static void attr_stack_free(struct attr_stack *e)
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
{
|
2022-12-01 22:45:31 +08:00
|
|
|
unsigned i;
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
free(e->origin);
|
2007-04-17 12:33:31 +08:00
|
|
|
for (i = 0; i < e->num_matches; i++) {
|
|
|
|
struct match_attr *a = e->attrs[i];
|
2022-12-01 22:45:27 +08:00
|
|
|
size_t j;
|
|
|
|
|
2007-04-17 12:33:31 +08:00
|
|
|
for (j = 0; j < a->num_attr; j++) {
|
2007-04-19 07:16:37 +08:00
|
|
|
const char *setto = a->state[j].setto;
|
2007-04-17 12:33:31 +08:00
|
|
|
if (setto == ATTR__TRUE ||
|
|
|
|
setto == ATTR__FALSE ||
|
|
|
|
setto == ATTR__UNSET ||
|
|
|
|
setto == ATTR__UNKNOWN)
|
|
|
|
;
|
|
|
|
else
|
2009-05-01 17:06:36 +08:00
|
|
|
free((char *) setto);
|
2007-04-17 12:33:31 +08:00
|
|
|
}
|
|
|
|
free(a);
|
|
|
|
}
|
2012-01-12 11:05:03 +08:00
|
|
|
free(e->attrs);
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
free(e);
|
|
|
|
}
|
|
|
|
|
2017-01-28 10:02:05 +08:00
|
|
|
static void drop_attr_stack(struct attr_stack **stack)
|
|
|
|
{
|
|
|
|
while (*stack) {
|
|
|
|
struct attr_stack *elem = *stack;
|
|
|
|
*stack = elem->prev;
|
|
|
|
attr_stack_free(elem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* List of all attr_check structs; access should be surrounded by mutex */
|
|
|
|
static struct check_vector {
|
|
|
|
size_t nr;
|
|
|
|
size_t alloc;
|
|
|
|
struct attr_check **checks;
|
|
|
|
pthread_mutex_t mutex;
|
|
|
|
} check_vector;
|
|
|
|
|
|
|
|
static inline void vector_lock(void)
|
|
|
|
{
|
|
|
|
pthread_mutex_lock(&check_vector.mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void vector_unlock(void)
|
|
|
|
{
|
|
|
|
pthread_mutex_unlock(&check_vector.mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void check_vector_add(struct attr_check *c)
|
|
|
|
{
|
|
|
|
vector_lock();
|
|
|
|
|
|
|
|
ALLOC_GROW(check_vector.checks,
|
|
|
|
check_vector.nr + 1,
|
|
|
|
check_vector.alloc);
|
|
|
|
check_vector.checks[check_vector.nr++] = c;
|
|
|
|
|
|
|
|
vector_unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void check_vector_remove(struct attr_check *check)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
vector_lock();
|
|
|
|
|
|
|
|
/* Find entry */
|
|
|
|
for (i = 0; i < check_vector.nr; i++)
|
|
|
|
if (check_vector.checks[i] == check)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (i >= check_vector.nr)
|
2018-05-02 17:38:39 +08:00
|
|
|
BUG("no entry found");
|
2017-01-28 10:02:05 +08:00
|
|
|
|
|
|
|
/* shift entries over */
|
|
|
|
for (; i < check_vector.nr - 1; i++)
|
|
|
|
check_vector.checks[i] = check_vector.checks[i + 1];
|
|
|
|
|
|
|
|
check_vector.nr--;
|
|
|
|
|
|
|
|
vector_unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Iterate through all attr_check instances and drop their stacks */
|
|
|
|
static void drop_all_attr_stacks(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
vector_lock();
|
|
|
|
|
|
|
|
for (i = 0; i < check_vector.nr; i++) {
|
|
|
|
drop_attr_stack(&check_vector.checks[i]->stack);
|
|
|
|
}
|
|
|
|
|
|
|
|
vector_unlock();
|
|
|
|
}
|
|
|
|
|
attr: (re)introduce git_check_attr() and struct attr_check
A common pattern to check N attributes for many paths is to
(1) prepare an array A of N attr_check_item items;
(2) call git_attr() to intern the N attribute names and fill A;
(3) repeatedly call git_check_attrs() for path with N and A;
A look-up for these N attributes for a single path P scans the
entire attr_stack, starting from the .git/info/attributes file and
then .gitattributes file in the directory the path P is in, going
upwards to find .gitattributes file found in parent directories.
An earlier commit 06a604e6 (attr: avoid heavy work when we know the
specified attr is not defined, 2014-12-28) tried to optimize out
this scanning for one trivial special case: when the attribute being
sought is known not to exist, we do not have to scan for it. While
this may be a cheap and effective heuristic, it would not work well
when N is (much) more than 1.
What we would want is a more customized way to skip irrelevant
entries in the attribute stack, and the definition of irrelevance
is tied to the set of attributes passed to git_check_attrs() call,
i.e. the set of attributes being sought. The data necessary for
this optimization needs to live alongside the set of attributes, but
a simple array of git_attr_check_elem simply does not have any place
for that.
Introduce "struct attr_check" that contains N, the number of
attributes being sought, and A, the array that holds N
attr_check_item items, and a function git_check_attr() that
takes a path P and this structure as its parameters. This structure
can later be extended to hold extra data necessary for optimization.
Also, to make it easier to write the first two steps in common
cases, introduce git_attr_check_initl() helper function, which takes
a NULL-terminated list of attribute names and initialize this
structure.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Brandon Williams <bmwill@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-01-31 02:05:20 +08:00
|
|
|
struct attr_check *attr_check_alloc(void)
|
|
|
|
{
|
2017-01-28 10:02:05 +08:00
|
|
|
struct attr_check *c = xcalloc(1, sizeof(struct attr_check));
|
|
|
|
|
|
|
|
/* save pointer to the check struct */
|
|
|
|
check_vector_add(c);
|
|
|
|
|
|
|
|
return c;
|
attr: (re)introduce git_check_attr() and struct attr_check
A common pattern to check N attributes for many paths is to
(1) prepare an array A of N attr_check_item items;
(2) call git_attr() to intern the N attribute names and fill A;
(3) repeatedly call git_check_attrs() for path with N and A;
A look-up for these N attributes for a single path P scans the
entire attr_stack, starting from the .git/info/attributes file and
then .gitattributes file in the directory the path P is in, going
upwards to find .gitattributes file found in parent directories.
An earlier commit 06a604e6 (attr: avoid heavy work when we know the
specified attr is not defined, 2014-12-28) tried to optimize out
this scanning for one trivial special case: when the attribute being
sought is known not to exist, we do not have to scan for it. While
this may be a cheap and effective heuristic, it would not work well
when N is (much) more than 1.
What we would want is a more customized way to skip irrelevant
entries in the attribute stack, and the definition of irrelevance
is tied to the set of attributes passed to git_check_attrs() call,
i.e. the set of attributes being sought. The data necessary for
this optimization needs to live alongside the set of attributes, but
a simple array of git_attr_check_elem simply does not have any place
for that.
Introduce "struct attr_check" that contains N, the number of
attributes being sought, and A, the array that holds N
attr_check_item items, and a function git_check_attr() that
takes a path P and this structure as its parameters. This structure
can later be extended to hold extra data necessary for optimization.
Also, to make it easier to write the first two steps in common
cases, introduce git_attr_check_initl() helper function, which takes
a NULL-terminated list of attribute names and initialize this
structure.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Brandon Williams <bmwill@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-01-31 02:05:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
struct attr_check *attr_check_initl(const char *one, ...)
|
|
|
|
{
|
|
|
|
struct attr_check *check;
|
|
|
|
int cnt;
|
|
|
|
va_list params;
|
|
|
|
const char *param;
|
|
|
|
|
|
|
|
va_start(params, one);
|
|
|
|
for (cnt = 1; (param = va_arg(params, const char *)) != NULL; cnt++)
|
|
|
|
;
|
|
|
|
va_end(params);
|
|
|
|
|
|
|
|
check = attr_check_alloc();
|
|
|
|
check->nr = cnt;
|
|
|
|
check->alloc = cnt;
|
2021-03-14 00:17:22 +08:00
|
|
|
CALLOC_ARRAY(check->items, cnt);
|
attr: (re)introduce git_check_attr() and struct attr_check
A common pattern to check N attributes for many paths is to
(1) prepare an array A of N attr_check_item items;
(2) call git_attr() to intern the N attribute names and fill A;
(3) repeatedly call git_check_attrs() for path with N and A;
A look-up for these N attributes for a single path P scans the
entire attr_stack, starting from the .git/info/attributes file and
then .gitattributes file in the directory the path P is in, going
upwards to find .gitattributes file found in parent directories.
An earlier commit 06a604e6 (attr: avoid heavy work when we know the
specified attr is not defined, 2014-12-28) tried to optimize out
this scanning for one trivial special case: when the attribute being
sought is known not to exist, we do not have to scan for it. While
this may be a cheap and effective heuristic, it would not work well
when N is (much) more than 1.
What we would want is a more customized way to skip irrelevant
entries in the attribute stack, and the definition of irrelevance
is tied to the set of attributes passed to git_check_attrs() call,
i.e. the set of attributes being sought. The data necessary for
this optimization needs to live alongside the set of attributes, but
a simple array of git_attr_check_elem simply does not have any place
for that.
Introduce "struct attr_check" that contains N, the number of
attributes being sought, and A, the array that holds N
attr_check_item items, and a function git_check_attr() that
takes a path P and this structure as its parameters. This structure
can later be extended to hold extra data necessary for optimization.
Also, to make it easier to write the first two steps in common
cases, introduce git_attr_check_initl() helper function, which takes
a NULL-terminated list of attribute names and initialize this
structure.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Brandon Williams <bmwill@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-01-31 02:05:20 +08:00
|
|
|
|
|
|
|
check->items[0].attr = git_attr(one);
|
|
|
|
va_start(params, one);
|
|
|
|
for (cnt = 1; cnt < check->nr; cnt++) {
|
|
|
|
const struct git_attr *attr;
|
|
|
|
param = va_arg(params, const char *);
|
|
|
|
if (!param)
|
2018-05-02 17:38:39 +08:00
|
|
|
BUG("counted %d != ended at %d",
|
attr: (re)introduce git_check_attr() and struct attr_check
A common pattern to check N attributes for many paths is to
(1) prepare an array A of N attr_check_item items;
(2) call git_attr() to intern the N attribute names and fill A;
(3) repeatedly call git_check_attrs() for path with N and A;
A look-up for these N attributes for a single path P scans the
entire attr_stack, starting from the .git/info/attributes file and
then .gitattributes file in the directory the path P is in, going
upwards to find .gitattributes file found in parent directories.
An earlier commit 06a604e6 (attr: avoid heavy work when we know the
specified attr is not defined, 2014-12-28) tried to optimize out
this scanning for one trivial special case: when the attribute being
sought is known not to exist, we do not have to scan for it. While
this may be a cheap and effective heuristic, it would not work well
when N is (much) more than 1.
What we would want is a more customized way to skip irrelevant
entries in the attribute stack, and the definition of irrelevance
is tied to the set of attributes passed to git_check_attrs() call,
i.e. the set of attributes being sought. The data necessary for
this optimization needs to live alongside the set of attributes, but
a simple array of git_attr_check_elem simply does not have any place
for that.
Introduce "struct attr_check" that contains N, the number of
attributes being sought, and A, the array that holds N
attr_check_item items, and a function git_check_attr() that
takes a path P and this structure as its parameters. This structure
can later be extended to hold extra data necessary for optimization.
Also, to make it easier to write the first two steps in common
cases, introduce git_attr_check_initl() helper function, which takes
a NULL-terminated list of attribute names and initialize this
structure.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Brandon Williams <bmwill@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-01-31 02:05:20 +08:00
|
|
|
check->nr, cnt);
|
|
|
|
attr = git_attr(param);
|
|
|
|
if (!attr)
|
2018-05-02 17:38:39 +08:00
|
|
|
BUG("%s: not a valid attribute name", param);
|
attr: (re)introduce git_check_attr() and struct attr_check
A common pattern to check N attributes for many paths is to
(1) prepare an array A of N attr_check_item items;
(2) call git_attr() to intern the N attribute names and fill A;
(3) repeatedly call git_check_attrs() for path with N and A;
A look-up for these N attributes for a single path P scans the
entire attr_stack, starting from the .git/info/attributes file and
then .gitattributes file in the directory the path P is in, going
upwards to find .gitattributes file found in parent directories.
An earlier commit 06a604e6 (attr: avoid heavy work when we know the
specified attr is not defined, 2014-12-28) tried to optimize out
this scanning for one trivial special case: when the attribute being
sought is known not to exist, we do not have to scan for it. While
this may be a cheap and effective heuristic, it would not work well
when N is (much) more than 1.
What we would want is a more customized way to skip irrelevant
entries in the attribute stack, and the definition of irrelevance
is tied to the set of attributes passed to git_check_attrs() call,
i.e. the set of attributes being sought. The data necessary for
this optimization needs to live alongside the set of attributes, but
a simple array of git_attr_check_elem simply does not have any place
for that.
Introduce "struct attr_check" that contains N, the number of
attributes being sought, and A, the array that holds N
attr_check_item items, and a function git_check_attr() that
takes a path P and this structure as its parameters. This structure
can later be extended to hold extra data necessary for optimization.
Also, to make it easier to write the first two steps in common
cases, introduce git_attr_check_initl() helper function, which takes
a NULL-terminated list of attribute names and initialize this
structure.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Brandon Williams <bmwill@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-01-31 02:05:20 +08:00
|
|
|
check->items[cnt].attr = attr;
|
|
|
|
}
|
|
|
|
va_end(params);
|
|
|
|
return check;
|
|
|
|
}
|
|
|
|
|
2017-03-14 02:23:21 +08:00
|
|
|
struct attr_check *attr_check_dup(const struct attr_check *check)
|
|
|
|
{
|
|
|
|
struct attr_check *ret;
|
|
|
|
|
|
|
|
if (!check)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
ret = attr_check_alloc();
|
|
|
|
|
|
|
|
ret->nr = check->nr;
|
|
|
|
ret->alloc = check->alloc;
|
2023-01-02 05:16:48 +08:00
|
|
|
DUP_ARRAY(ret->items, check->items, ret->nr);
|
2017-03-14 02:23:21 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
attr: (re)introduce git_check_attr() and struct attr_check
A common pattern to check N attributes for many paths is to
(1) prepare an array A of N attr_check_item items;
(2) call git_attr() to intern the N attribute names and fill A;
(3) repeatedly call git_check_attrs() for path with N and A;
A look-up for these N attributes for a single path P scans the
entire attr_stack, starting from the .git/info/attributes file and
then .gitattributes file in the directory the path P is in, going
upwards to find .gitattributes file found in parent directories.
An earlier commit 06a604e6 (attr: avoid heavy work when we know the
specified attr is not defined, 2014-12-28) tried to optimize out
this scanning for one trivial special case: when the attribute being
sought is known not to exist, we do not have to scan for it. While
this may be a cheap and effective heuristic, it would not work well
when N is (much) more than 1.
What we would want is a more customized way to skip irrelevant
entries in the attribute stack, and the definition of irrelevance
is tied to the set of attributes passed to git_check_attrs() call,
i.e. the set of attributes being sought. The data necessary for
this optimization needs to live alongside the set of attributes, but
a simple array of git_attr_check_elem simply does not have any place
for that.
Introduce "struct attr_check" that contains N, the number of
attributes being sought, and A, the array that holds N
attr_check_item items, and a function git_check_attr() that
takes a path P and this structure as its parameters. This structure
can later be extended to hold extra data necessary for optimization.
Also, to make it easier to write the first two steps in common
cases, introduce git_attr_check_initl() helper function, which takes
a NULL-terminated list of attribute names and initialize this
structure.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Brandon Williams <bmwill@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-01-31 02:05:20 +08:00
|
|
|
struct attr_check_item *attr_check_append(struct attr_check *check,
|
|
|
|
const struct git_attr *attr)
|
|
|
|
{
|
|
|
|
struct attr_check_item *item;
|
|
|
|
|
|
|
|
ALLOC_GROW(check->items, check->nr + 1, check->alloc);
|
|
|
|
item = &check->items[check->nr++];
|
|
|
|
item->attr = attr;
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
|
|
|
void attr_check_reset(struct attr_check *check)
|
|
|
|
{
|
|
|
|
check->nr = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void attr_check_clear(struct attr_check *check)
|
|
|
|
{
|
2017-06-16 07:15:46 +08:00
|
|
|
FREE_AND_NULL(check->items);
|
attr: (re)introduce git_check_attr() and struct attr_check
A common pattern to check N attributes for many paths is to
(1) prepare an array A of N attr_check_item items;
(2) call git_attr() to intern the N attribute names and fill A;
(3) repeatedly call git_check_attrs() for path with N and A;
A look-up for these N attributes for a single path P scans the
entire attr_stack, starting from the .git/info/attributes file and
then .gitattributes file in the directory the path P is in, going
upwards to find .gitattributes file found in parent directories.
An earlier commit 06a604e6 (attr: avoid heavy work when we know the
specified attr is not defined, 2014-12-28) tried to optimize out
this scanning for one trivial special case: when the attribute being
sought is known not to exist, we do not have to scan for it. While
this may be a cheap and effective heuristic, it would not work well
when N is (much) more than 1.
What we would want is a more customized way to skip irrelevant
entries in the attribute stack, and the definition of irrelevance
is tied to the set of attributes passed to git_check_attrs() call,
i.e. the set of attributes being sought. The data necessary for
this optimization needs to live alongside the set of attributes, but
a simple array of git_attr_check_elem simply does not have any place
for that.
Introduce "struct attr_check" that contains N, the number of
attributes being sought, and A, the array that holds N
attr_check_item items, and a function git_check_attr() that
takes a path P and this structure as its parameters. This structure
can later be extended to hold extra data necessary for optimization.
Also, to make it easier to write the first two steps in common
cases, introduce git_attr_check_initl() helper function, which takes
a NULL-terminated list of attribute names and initialize this
structure.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Brandon Williams <bmwill@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-01-31 02:05:20 +08:00
|
|
|
check->alloc = 0;
|
|
|
|
check->nr = 0;
|
2017-01-28 10:02:02 +08:00
|
|
|
|
2017-06-16 07:15:46 +08:00
|
|
|
FREE_AND_NULL(check->all_attrs);
|
2017-01-28 10:02:02 +08:00
|
|
|
check->all_attrs_nr = 0;
|
2017-01-28 10:02:05 +08:00
|
|
|
|
|
|
|
drop_attr_stack(&check->stack);
|
attr: (re)introduce git_check_attr() and struct attr_check
A common pattern to check N attributes for many paths is to
(1) prepare an array A of N attr_check_item items;
(2) call git_attr() to intern the N attribute names and fill A;
(3) repeatedly call git_check_attrs() for path with N and A;
A look-up for these N attributes for a single path P scans the
entire attr_stack, starting from the .git/info/attributes file and
then .gitattributes file in the directory the path P is in, going
upwards to find .gitattributes file found in parent directories.
An earlier commit 06a604e6 (attr: avoid heavy work when we know the
specified attr is not defined, 2014-12-28) tried to optimize out
this scanning for one trivial special case: when the attribute being
sought is known not to exist, we do not have to scan for it. While
this may be a cheap and effective heuristic, it would not work well
when N is (much) more than 1.
What we would want is a more customized way to skip irrelevant
entries in the attribute stack, and the definition of irrelevance
is tied to the set of attributes passed to git_check_attrs() call,
i.e. the set of attributes being sought. The data necessary for
this optimization needs to live alongside the set of attributes, but
a simple array of git_attr_check_elem simply does not have any place
for that.
Introduce "struct attr_check" that contains N, the number of
attributes being sought, and A, the array that holds N
attr_check_item items, and a function git_check_attr() that
takes a path P and this structure as its parameters. This structure
can later be extended to hold extra data necessary for optimization.
Also, to make it easier to write the first two steps in common
cases, introduce git_attr_check_initl() helper function, which takes
a NULL-terminated list of attribute names and initialize this
structure.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Brandon Williams <bmwill@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-01-31 02:05:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void attr_check_free(struct attr_check *check)
|
|
|
|
{
|
2017-01-28 10:02:05 +08:00
|
|
|
if (check) {
|
|
|
|
/* Remove check from the check vector */
|
|
|
|
check_vector_remove(check);
|
|
|
|
|
|
|
|
attr_check_clear(check);
|
|
|
|
free(check);
|
|
|
|
}
|
attr: (re)introduce git_check_attr() and struct attr_check
A common pattern to check N attributes for many paths is to
(1) prepare an array A of N attr_check_item items;
(2) call git_attr() to intern the N attribute names and fill A;
(3) repeatedly call git_check_attrs() for path with N and A;
A look-up for these N attributes for a single path P scans the
entire attr_stack, starting from the .git/info/attributes file and
then .gitattributes file in the directory the path P is in, going
upwards to find .gitattributes file found in parent directories.
An earlier commit 06a604e6 (attr: avoid heavy work when we know the
specified attr is not defined, 2014-12-28) tried to optimize out
this scanning for one trivial special case: when the attribute being
sought is known not to exist, we do not have to scan for it. While
this may be a cheap and effective heuristic, it would not work well
when N is (much) more than 1.
What we would want is a more customized way to skip irrelevant
entries in the attribute stack, and the definition of irrelevance
is tied to the set of attributes passed to git_check_attrs() call,
i.e. the set of attributes being sought. The data necessary for
this optimization needs to live alongside the set of attributes, but
a simple array of git_attr_check_elem simply does not have any place
for that.
Introduce "struct attr_check" that contains N, the number of
attributes being sought, and A, the array that holds N
attr_check_item items, and a function git_check_attr() that
takes a path P and this structure as its parameters. This structure
can later be extended to hold extra data necessary for optimization.
Also, to make it easier to write the first two steps in common
cases, introduce git_attr_check_initl() helper function, which takes
a NULL-terminated list of attribute names and initialize this
structure.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Brandon Williams <bmwill@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-01-31 02:05:20 +08:00
|
|
|
}
|
|
|
|
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
static const char *builtin_attr[] = {
|
2012-09-09 12:28:55 +08:00
|
|
|
"[attr]binary -diff -merge -text",
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
2007-08-14 16:40:45 +08:00
|
|
|
static void handle_attr_line(struct attr_stack *res,
|
|
|
|
const char *line,
|
|
|
|
const char *src,
|
|
|
|
int lineno,
|
2021-02-16 22:44:25 +08:00
|
|
|
unsigned flags)
|
2007-08-14 16:40:45 +08:00
|
|
|
{
|
|
|
|
struct match_attr *a;
|
|
|
|
|
2021-02-16 22:44:25 +08:00
|
|
|
a = parse_attr_line(line, src, lineno, flags);
|
2007-08-14 16:40:45 +08:00
|
|
|
if (!a)
|
|
|
|
return;
|
2022-12-01 22:45:31 +08:00
|
|
|
ALLOC_GROW_BY(res->attrs, res->num_matches, 1, res->alloc);
|
|
|
|
res->attrs[res->num_matches - 1] = a;
|
2007-08-14 16:40:45 +08:00
|
|
|
}
|
|
|
|
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
static struct attr_stack *read_attr_from_array(const char **list)
|
|
|
|
{
|
|
|
|
struct attr_stack *res;
|
|
|
|
const char *line;
|
attribute macro support
This adds "attribute macros" (for lack of better name). So far,
we have low-level attributes such as crlf and diff, which are
defined in operational terms --- setting or unsetting them on a
particular path directly affects what is done to the path. For
example, in order to decline diffs or crlf conversions on a
binary blob, no diffs on PostScript files, and treat all other
files normally, you would have something like these:
* diff crlf
*.ps !diff
proprietary.o !diff !crlf
That is fine as the operation goes, but gets unwieldy rather
rapidly, when we start adding more low-level attributes that are
defined in operational terms. A near-term example of such an
attribute would be 'merge-3way' which would control if git
should attempt the usual 3-way file-level merge internally, or
leave merging to a specialized external program of user's
choice. When it is added, we do _not_ want to force the users
to update the above to:
* diff crlf merge-3way
*.ps !diff
proprietary.o !diff !crlf !merge-3way
The way this patch solves this issue is to realize that the
attributes the user is assigning to paths are not defined in
terms of operations but in terms of what they are.
All of the three low-level attributes usually make sense for
most of the files that sane SCM users have git operate on (these
files are typically called "text'). Only a few cases, such as
binary blob, need exception to decline the "usual treatment
given to text files" -- and people mark them as "binary".
So this allows the $GIT_DIR/info/alternates and .gitattributes
at the toplevel of the project to also specify attributes that
assigns other attributes. The syntax is '[attr]' followed by an
attribute name followed by a list of attribute names:
[attr] binary !diff !crlf !merge-3way
When "binary" attribute is set to a path, if the path has not
got diff/crlf/merge-3way attribute set or unset by other rules,
this rule unsets the three low-level attributes.
It is expected that the user level .gitattributes will be
expressed mostly in terms of attributes based on what the files
are, and the above sample would become like this:
(built-in attribute configuration)
[attr] binary !diff !crlf !merge-3way
* diff crlf merge-3way
(project specific .gitattributes)
proprietary.o binary
(user preference $GIT_DIR/info/attributes)
*.ps !diff
There are a few caveats.
* As described above, you can define these macros only in
$GIT_DIR/info/attributes and toplevel .gitattributes.
* There is no attempt to detect circular definition of macro
attributes, and definitions are evaluated from bottom to top
as usual to fill in other attributes that have not yet got
values. The following would work as expected:
[attr] text diff crlf
[attr] ps text !diff
*.ps ps
while this would most likely not (I haven't tried):
[attr] ps text !diff
[attr] text diff crlf
*.ps ps
* When a macro says "[attr] A B !C", saying that a path does
not have attribute A does not let you tell anything about
attributes B or C. That is, given this:
[attr] text diff crlf
[attr] ps text !diff
*.txt !ps
path hello.txt, which would match "*.txt" pattern, would have
"ps" attribute set to zero, but that does not make text
attribute of hello.txt set to false (nor diff attribute set to
true).
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-14 23:54:37 +08:00
|
|
|
int lineno = 0;
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
|
2021-03-14 00:17:22 +08:00
|
|
|
CALLOC_ARRAY(res, 1);
|
2007-08-14 16:40:45 +08:00
|
|
|
while ((line = *(list++)) != NULL)
|
2021-02-16 22:44:25 +08:00
|
|
|
handle_attr_line(res, line, "[builtin]", ++lineno,
|
|
|
|
READ_ATTR_MACRO_OK);
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2017-01-28 10:01:53 +08:00
|
|
|
/*
|
2017-01-28 10:02:07 +08:00
|
|
|
* Callers into the attribute system assume there is a single, system-wide
|
|
|
|
* global state where attributes are read from and when the state is flipped by
|
|
|
|
* calling git_attr_set_direction(), the stack frames that have been
|
2021-06-11 19:18:50 +08:00
|
|
|
* constructed need to be discarded so that subsequent calls into the
|
2017-01-28 10:02:07 +08:00
|
|
|
* attribute system will lazily read from the right place. Since changing
|
|
|
|
* direction causes a global paradigm shift, it should not ever be called while
|
|
|
|
* another thread could potentially be calling into the attribute system.
|
2017-01-28 10:01:53 +08:00
|
|
|
*/
|
2009-03-14 12:24:08 +08:00
|
|
|
static enum git_attr_direction direction;
|
|
|
|
|
2018-08-14 00:14:33 +08:00
|
|
|
void git_attr_set_direction(enum git_attr_direction new_direction)
|
2017-01-28 10:02:07 +08:00
|
|
|
{
|
|
|
|
if (is_bare_repository() && new_direction != GIT_ATTR_INDEX)
|
2018-05-02 17:38:39 +08:00
|
|
|
BUG("non-INDEX attr direction in a bare repo");
|
2017-01-28 10:02:07 +08:00
|
|
|
|
|
|
|
if (new_direction != direction)
|
|
|
|
drop_all_attr_stacks();
|
|
|
|
|
|
|
|
direction = new_direction;
|
|
|
|
}
|
|
|
|
|
2021-02-16 22:44:25 +08:00
|
|
|
static struct attr_stack *read_attr_from_file(const char *path, unsigned flags)
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
{
|
2022-12-01 22:45:44 +08:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
attr: do not respect symlinks for in-tree .gitattributes
The attributes system may sometimes read in-tree files from the
filesystem, and sometimes from the index. In the latter case, we do not
resolve symbolic links (and are not likely to ever start doing so).
Let's open filesystem links with O_NOFOLLOW so that the two cases behave
consistently.
As a bonus, this means that git will not follow such symlinks to read
and parse out-of-tree paths. In some cases this could have security
implications, as a malicious repository can cause Git to open and read
arbitrary files. It could already feed arbitrary content to the parser,
but in certain setups it might be able to exfiltrate data from those
paths (e.g., if an automated service operating on the malicious repo
reveals its stderr to an attacker).
Note that O_NOFOLLOW only prevents following links for the path itself,
not intermediate directories in the path. At first glance, it seems
like
ln -s /some/path in-repo
might still look at "in-repo/.gitattributes", following the symlink to
"/some/path/.gitattributes". However, if "in-repo" is a symbolic link,
then we know that it has no git paths below it, and will never look at
its .gitattributes file.
We will continue to support out-of-tree symbolic links (e.g., in
$GIT_DIR/info/attributes); this just affects in-tree links. When a
symbolic link is encountered, the contents are ignored and a warning is
printed. POSIX specifies ELOOP in this case, so the user would generally
see something like:
warning: unable to access '.gitattributes': Too many levels of symbolic links
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-02-16 22:44:32 +08:00
|
|
|
int fd;
|
|
|
|
FILE *fp;
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
struct attr_stack *res;
|
attribute macro support
This adds "attribute macros" (for lack of better name). So far,
we have low-level attributes such as crlf and diff, which are
defined in operational terms --- setting or unsetting them on a
particular path directly affects what is done to the path. For
example, in order to decline diffs or crlf conversions on a
binary blob, no diffs on PostScript files, and treat all other
files normally, you would have something like these:
* diff crlf
*.ps !diff
proprietary.o !diff !crlf
That is fine as the operation goes, but gets unwieldy rather
rapidly, when we start adding more low-level attributes that are
defined in operational terms. A near-term example of such an
attribute would be 'merge-3way' which would control if git
should attempt the usual 3-way file-level merge internally, or
leave merging to a specialized external program of user's
choice. When it is added, we do _not_ want to force the users
to update the above to:
* diff crlf merge-3way
*.ps !diff
proprietary.o !diff !crlf !merge-3way
The way this patch solves this issue is to realize that the
attributes the user is assigning to paths are not defined in
terms of operations but in terms of what they are.
All of the three low-level attributes usually make sense for
most of the files that sane SCM users have git operate on (these
files are typically called "text'). Only a few cases, such as
binary blob, need exception to decline the "usual treatment
given to text files" -- and people mark them as "binary".
So this allows the $GIT_DIR/info/alternates and .gitattributes
at the toplevel of the project to also specify attributes that
assigns other attributes. The syntax is '[attr]' followed by an
attribute name followed by a list of attribute names:
[attr] binary !diff !crlf !merge-3way
When "binary" attribute is set to a path, if the path has not
got diff/crlf/merge-3way attribute set or unset by other rules,
this rule unsets the three low-level attributes.
It is expected that the user level .gitattributes will be
expressed mostly in terms of attributes based on what the files
are, and the above sample would become like this:
(built-in attribute configuration)
[attr] binary !diff !crlf !merge-3way
* diff crlf merge-3way
(project specific .gitattributes)
proprietary.o binary
(user preference $GIT_DIR/info/attributes)
*.ps !diff
There are a few caveats.
* As described above, you can define these macros only in
$GIT_DIR/info/attributes and toplevel .gitattributes.
* There is no attempt to detect circular definition of macro
attributes, and definitions are evaluated from bottom to top
as usual to fill in other attributes that have not yet got
values. The following would work as expected:
[attr] text diff crlf
[attr] ps text !diff
*.ps ps
while this would most likely not (I haven't tried):
[attr] ps text !diff
[attr] text diff crlf
*.ps ps
* When a macro says "[attr] A B !C", saying that a path does
not have attribute A does not let you tell anything about
attributes B or C. That is, given this:
[attr] text diff crlf
[attr] ps text !diff
*.txt !ps
path hello.txt, which would match "*.txt" pattern, would have
"ps" attribute set to zero, but that does not make text
attribute of hello.txt set to false (nor diff attribute set to
true).
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-14 23:54:37 +08:00
|
|
|
int lineno = 0;
|
2022-12-01 22:45:53 +08:00
|
|
|
struct stat st;
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
|
attr: do not respect symlinks for in-tree .gitattributes
The attributes system may sometimes read in-tree files from the
filesystem, and sometimes from the index. In the latter case, we do not
resolve symbolic links (and are not likely to ever start doing so).
Let's open filesystem links with O_NOFOLLOW so that the two cases behave
consistently.
As a bonus, this means that git will not follow such symlinks to read
and parse out-of-tree paths. In some cases this could have security
implications, as a malicious repository can cause Git to open and read
arbitrary files. It could already feed arbitrary content to the parser,
but in certain setups it might be able to exfiltrate data from those
paths (e.g., if an automated service operating on the malicious repo
reveals its stderr to an attacker).
Note that O_NOFOLLOW only prevents following links for the path itself,
not intermediate directories in the path. At first glance, it seems
like
ln -s /some/path in-repo
might still look at "in-repo/.gitattributes", following the symlink to
"/some/path/.gitattributes". However, if "in-repo" is a symbolic link,
then we know that it has no git paths below it, and will never look at
its .gitattributes file.
We will continue to support out-of-tree symbolic links (e.g., in
$GIT_DIR/info/attributes); this just affects in-tree links. When a
symbolic link is encountered, the contents are ignored and a warning is
printed. POSIX specifies ELOOP in this case, so the user would generally
see something like:
warning: unable to access '.gitattributes': Too many levels of symbolic links
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-02-16 22:44:32 +08:00
|
|
|
if (flags & READ_ATTR_NOFOLLOW)
|
|
|
|
fd = open_nofollow(path, O_RDONLY);
|
|
|
|
else
|
|
|
|
fd = open(path, O_RDONLY);
|
|
|
|
|
|
|
|
if (fd < 0) {
|
|
|
|
warn_on_fopen_errors(path);
|
2007-08-14 16:40:45 +08:00
|
|
|
return NULL;
|
attr: do not respect symlinks for in-tree .gitattributes
The attributes system may sometimes read in-tree files from the
filesystem, and sometimes from the index. In the latter case, we do not
resolve symbolic links (and are not likely to ever start doing so).
Let's open filesystem links with O_NOFOLLOW so that the two cases behave
consistently.
As a bonus, this means that git will not follow such symlinks to read
and parse out-of-tree paths. In some cases this could have security
implications, as a malicious repository can cause Git to open and read
arbitrary files. It could already feed arbitrary content to the parser,
but in certain setups it might be able to exfiltrate data from those
paths (e.g., if an automated service operating on the malicious repo
reveals its stderr to an attacker).
Note that O_NOFOLLOW only prevents following links for the path itself,
not intermediate directories in the path. At first glance, it seems
like
ln -s /some/path in-repo
might still look at "in-repo/.gitattributes", following the symlink to
"/some/path/.gitattributes". However, if "in-repo" is a symbolic link,
then we know that it has no git paths below it, and will never look at
its .gitattributes file.
We will continue to support out-of-tree symbolic links (e.g., in
$GIT_DIR/info/attributes); this just affects in-tree links. When a
symbolic link is encountered, the contents are ignored and a warning is
printed. POSIX specifies ELOOP in this case, so the user would generally
see something like:
warning: unable to access '.gitattributes': Too many levels of symbolic links
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-02-16 22:44:32 +08:00
|
|
|
}
|
|
|
|
fp = xfdopen(fd, "r");
|
2022-12-01 22:45:53 +08:00
|
|
|
if (fstat(fd, &st)) {
|
|
|
|
warning_errno(_("cannot fstat gitattributes file '%s'"), path);
|
|
|
|
fclose(fp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (st.st_size >= ATTR_MAX_FILE_SIZE) {
|
|
|
|
warning(_("ignoring overly large gitattributes file '%s'"), path);
|
|
|
|
fclose(fp);
|
|
|
|
return NULL;
|
|
|
|
}
|
attr: do not respect symlinks for in-tree .gitattributes
The attributes system may sometimes read in-tree files from the
filesystem, and sometimes from the index. In the latter case, we do not
resolve symbolic links (and are not likely to ever start doing so).
Let's open filesystem links with O_NOFOLLOW so that the two cases behave
consistently.
As a bonus, this means that git will not follow such symlinks to read
and parse out-of-tree paths. In some cases this could have security
implications, as a malicious repository can cause Git to open and read
arbitrary files. It could already feed arbitrary content to the parser,
but in certain setups it might be able to exfiltrate data from those
paths (e.g., if an automated service operating on the malicious repo
reveals its stderr to an attacker).
Note that O_NOFOLLOW only prevents following links for the path itself,
not intermediate directories in the path. At first glance, it seems
like
ln -s /some/path in-repo
might still look at "in-repo/.gitattributes", following the symlink to
"/some/path/.gitattributes". However, if "in-repo" is a symbolic link,
then we know that it has no git paths below it, and will never look at
its .gitattributes file.
We will continue to support out-of-tree symbolic links (e.g., in
$GIT_DIR/info/attributes); this just affects in-tree links. When a
symbolic link is encountered, the contents are ignored and a warning is
printed. POSIX specifies ELOOP in this case, so the user would generally
see something like:
warning: unable to access '.gitattributes': Too many levels of symbolic links
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-02-16 22:44:32 +08:00
|
|
|
|
2021-03-14 00:17:22 +08:00
|
|
|
CALLOC_ARRAY(res, 1);
|
2022-12-01 22:45:44 +08:00
|
|
|
while (strbuf_getline(&buf, fp) != EOF) {
|
|
|
|
if (!lineno && starts_with(buf.buf, utf8_bom))
|
|
|
|
strbuf_remove(&buf, 0, strlen(utf8_bom));
|
2022-12-13 20:09:40 +08:00
|
|
|
handle_attr_line(res, buf.buf, path, ++lineno, flags);
|
2015-04-17 01:48:58 +08:00
|
|
|
}
|
2022-12-01 22:45:44 +08:00
|
|
|
|
2007-08-14 16:40:45 +08:00
|
|
|
fclose(fp);
|
2022-12-01 22:45:44 +08:00
|
|
|
strbuf_release(&buf);
|
2007-08-14 16:40:45 +08:00
|
|
|
return res;
|
|
|
|
}
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
|
2024-05-04 03:12:36 +08:00
|
|
|
static struct attr_stack *read_attr_from_buf(char *buf, size_t length,
|
|
|
|
const char *path, unsigned flags)
|
2007-08-14 16:40:45 +08:00
|
|
|
{
|
|
|
|
struct attr_stack *res;
|
2023-01-14 16:30:38 +08:00
|
|
|
char *sp;
|
2007-08-14 16:41:02 +08:00
|
|
|
int lineno = 0;
|
attribute macro support
This adds "attribute macros" (for lack of better name). So far,
we have low-level attributes such as crlf and diff, which are
defined in operational terms --- setting or unsetting them on a
particular path directly affects what is done to the path. For
example, in order to decline diffs or crlf conversions on a
binary blob, no diffs on PostScript files, and treat all other
files normally, you would have something like these:
* diff crlf
*.ps !diff
proprietary.o !diff !crlf
That is fine as the operation goes, but gets unwieldy rather
rapidly, when we start adding more low-level attributes that are
defined in operational terms. A near-term example of such an
attribute would be 'merge-3way' which would control if git
should attempt the usual 3-way file-level merge internally, or
leave merging to a specialized external program of user's
choice. When it is added, we do _not_ want to force the users
to update the above to:
* diff crlf merge-3way
*.ps !diff
proprietary.o !diff !crlf !merge-3way
The way this patch solves this issue is to realize that the
attributes the user is assigning to paths are not defined in
terms of operations but in terms of what they are.
All of the three low-level attributes usually make sense for
most of the files that sane SCM users have git operate on (these
files are typically called "text'). Only a few cases, such as
binary blob, need exception to decline the "usual treatment
given to text files" -- and people mark them as "binary".
So this allows the $GIT_DIR/info/alternates and .gitattributes
at the toplevel of the project to also specify attributes that
assigns other attributes. The syntax is '[attr]' followed by an
attribute name followed by a list of attribute names:
[attr] binary !diff !crlf !merge-3way
When "binary" attribute is set to a path, if the path has not
got diff/crlf/merge-3way attribute set or unset by other rules,
this rule unsets the three low-level attributes.
It is expected that the user level .gitattributes will be
expressed mostly in terms of attributes based on what the files
are, and the above sample would become like this:
(built-in attribute configuration)
[attr] binary !diff !crlf !merge-3way
* diff crlf merge-3way
(project specific .gitattributes)
proprietary.o binary
(user preference $GIT_DIR/info/attributes)
*.ps !diff
There are a few caveats.
* As described above, you can define these macros only in
$GIT_DIR/info/attributes and toplevel .gitattributes.
* There is no attempt to detect circular definition of macro
attributes, and definitions are evaluated from bottom to top
as usual to fill in other attributes that have not yet got
values. The following would work as expected:
[attr] text diff crlf
[attr] ps text !diff
*.ps ps
while this would most likely not (I haven't tried):
[attr] ps text !diff
[attr] text diff crlf
*.ps ps
* When a macro says "[attr] A B !C", saying that a path does
not have attribute A does not let you tell anything about
attributes B or C. That is, given this:
[attr] text diff crlf
[attr] ps text !diff
*.txt !ps
path hello.txt, which would match "*.txt" pattern, would have
"ps" attribute set to zero, but that does not make text
attribute of hello.txt set to false (nor diff attribute set to
true).
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-14 23:54:37 +08:00
|
|
|
|
2023-01-14 16:30:38 +08:00
|
|
|
if (!buf)
|
|
|
|
return NULL;
|
2024-05-04 03:12:36 +08:00
|
|
|
if (length >= ATTR_MAX_FILE_SIZE) {
|
|
|
|
warning(_("ignoring overly large gitattributes blob '%s'"), path);
|
|
|
|
free(buf);
|
|
|
|
return NULL;
|
|
|
|
}
|
2023-01-14 16:30:38 +08:00
|
|
|
|
|
|
|
CALLOC_ARRAY(res, 1);
|
|
|
|
for (sp = buf; *sp;) {
|
|
|
|
char *ep;
|
|
|
|
int more;
|
|
|
|
|
|
|
|
ep = strchrnul(sp, '\n');
|
|
|
|
more = (*ep == '\n');
|
|
|
|
*ep = '\0';
|
|
|
|
handle_attr_line(res, sp, path, ++lineno, flags);
|
|
|
|
sp = ep + more;
|
|
|
|
}
|
|
|
|
free(buf);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct attr_stack *read_attr_from_blob(struct index_state *istate,
|
|
|
|
const struct object_id *tree_oid,
|
|
|
|
const char *path, unsigned flags)
|
|
|
|
{
|
|
|
|
struct object_id oid;
|
|
|
|
unsigned long sz;
|
|
|
|
enum object_type type;
|
|
|
|
void *buf;
|
|
|
|
unsigned short mode;
|
|
|
|
|
|
|
|
if (!tree_oid)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (get_tree_entry(istate->repo, tree_oid, path, &oid, &mode))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
buf = repo_read_object_file(istate->repo, &oid, &type, &sz);
|
|
|
|
if (!buf || type != OBJ_BLOB) {
|
|
|
|
free(buf);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2024-05-04 03:12:36 +08:00
|
|
|
return read_attr_from_buf(buf, sz, path, flags);
|
2023-01-14 16:30:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct attr_stack *read_attr_from_index(struct index_state *istate,
|
|
|
|
const char *path, unsigned flags)
|
|
|
|
{
|
2023-08-11 22:22:10 +08:00
|
|
|
struct attr_stack *stack = NULL;
|
2023-01-14 16:30:38 +08:00
|
|
|
char *buf;
|
2023-01-12 08:05:02 +08:00
|
|
|
unsigned long size;
|
2023-08-11 22:22:10 +08:00
|
|
|
int sparse_dir_pos = -1;
|
attribute macro support
This adds "attribute macros" (for lack of better name). So far,
we have low-level attributes such as crlf and diff, which are
defined in operational terms --- setting or unsetting them on a
particular path directly affects what is done to the path. For
example, in order to decline diffs or crlf conversions on a
binary blob, no diffs on PostScript files, and treat all other
files normally, you would have something like these:
* diff crlf
*.ps !diff
proprietary.o !diff !crlf
That is fine as the operation goes, but gets unwieldy rather
rapidly, when we start adding more low-level attributes that are
defined in operational terms. A near-term example of such an
attribute would be 'merge-3way' which would control if git
should attempt the usual 3-way file-level merge internally, or
leave merging to a specialized external program of user's
choice. When it is added, we do _not_ want to force the users
to update the above to:
* diff crlf merge-3way
*.ps !diff
proprietary.o !diff !crlf !merge-3way
The way this patch solves this issue is to realize that the
attributes the user is assigning to paths are not defined in
terms of operations but in terms of what they are.
All of the three low-level attributes usually make sense for
most of the files that sane SCM users have git operate on (these
files are typically called "text'). Only a few cases, such as
binary blob, need exception to decline the "usual treatment
given to text files" -- and people mark them as "binary".
So this allows the $GIT_DIR/info/alternates and .gitattributes
at the toplevel of the project to also specify attributes that
assigns other attributes. The syntax is '[attr]' followed by an
attribute name followed by a list of attribute names:
[attr] binary !diff !crlf !merge-3way
When "binary" attribute is set to a path, if the path has not
got diff/crlf/merge-3way attribute set or unset by other rules,
this rule unsets the three low-level attributes.
It is expected that the user level .gitattributes will be
expressed mostly in terms of attributes based on what the files
are, and the above sample would become like this:
(built-in attribute configuration)
[attr] binary !diff !crlf !merge-3way
* diff crlf merge-3way
(project specific .gitattributes)
proprietary.o binary
(user preference $GIT_DIR/info/attributes)
*.ps !diff
There are a few caveats.
* As described above, you can define these macros only in
$GIT_DIR/info/attributes and toplevel .gitattributes.
* There is no attempt to detect circular definition of macro
attributes, and definitions are evaluated from bottom to top
as usual to fill in other attributes that have not yet got
values. The following would work as expected:
[attr] text diff crlf
[attr] ps text !diff
*.ps ps
while this would most likely not (I haven't tried):
[attr] ps text !diff
[attr] text diff crlf
*.ps ps
* When a macro says "[attr] A B !C", saying that a path does
not have attribute A does not let you tell anything about
attributes B or C. That is, given this:
[attr] text diff crlf
[attr] ps text !diff
*.txt !ps
path hello.txt, which would match "*.txt" pattern, would have
"ps" attribute set to zero, but that does not make text
attribute of hello.txt set to false (nor diff attribute set to
true).
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-14 23:54:37 +08:00
|
|
|
|
2018-08-14 00:14:33 +08:00
|
|
|
if (!istate)
|
2018-08-14 00:14:20 +08:00
|
|
|
return NULL;
|
|
|
|
|
2021-09-08 09:42:31 +08:00
|
|
|
/*
|
2023-08-11 22:22:10 +08:00
|
|
|
* When handling sparse-checkouts, .gitattributes files
|
|
|
|
* may reside within a sparse directory. We distinguish
|
|
|
|
* whether a path exists directly in the index or not by
|
|
|
|
* evaluating if 'pos' is negative.
|
|
|
|
* If 'pos' is negative, the path is not directly present
|
|
|
|
* in the index and is likely within a sparse directory.
|
|
|
|
* For paths not in the index, The absolute value of 'pos'
|
|
|
|
* minus 1 gives us the position where the path would be
|
|
|
|
* inserted in lexicographic order within the index.
|
|
|
|
* We then subtract another 1 from this value
|
|
|
|
* (sparse_dir_pos = -pos - 2) to find the position of the
|
|
|
|
* last index entry which is lexicographically smaller than
|
|
|
|
* the path. This would be the sparse directory containing
|
|
|
|
* the path. By identifying the sparse directory containing
|
|
|
|
* the path, we can correctly read the attributes specified
|
|
|
|
* in the .gitattributes file from the tree object of the
|
|
|
|
* sparse directory.
|
2021-09-08 09:42:31 +08:00
|
|
|
*/
|
2023-08-11 22:22:10 +08:00
|
|
|
if (!path_in_cone_mode_sparse_checkout(path, istate)) {
|
|
|
|
int pos = index_name_pos_sparse(istate, path, strlen(path));
|
2021-09-08 09:42:31 +08:00
|
|
|
|
2023-08-11 22:22:10 +08:00
|
|
|
if (pos < 0)
|
|
|
|
sparse_dir_pos = -pos - 2;
|
2022-12-01 22:45:53 +08:00
|
|
|
}
|
2007-08-14 16:41:02 +08:00
|
|
|
|
2023-08-11 22:22:10 +08:00
|
|
|
if (sparse_dir_pos >= 0 &&
|
|
|
|
S_ISSPARSEDIR(istate->cache[sparse_dir_pos]->ce_mode) &&
|
|
|
|
!strncmp(istate->cache[sparse_dir_pos]->name, path, ce_namelen(istate->cache[sparse_dir_pos]))) {
|
|
|
|
const char *relative_path = path + ce_namelen(istate->cache[sparse_dir_pos]);
|
|
|
|
stack = read_attr_from_blob(istate, &istate->cache[sparse_dir_pos]->oid, relative_path, flags);
|
|
|
|
} else {
|
|
|
|
buf = read_blob_data_from_index(istate, path, &size);
|
2024-05-04 03:12:36 +08:00
|
|
|
stack = read_attr_from_buf(buf, size, path, flags);
|
2023-08-11 22:22:10 +08:00
|
|
|
}
|
|
|
|
return stack;
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
}
|
|
|
|
|
2021-04-01 09:49:39 +08:00
|
|
|
static struct attr_stack *read_attr(struct index_state *istate,
|
2023-01-14 16:30:38 +08:00
|
|
|
const struct object_id *tree_oid,
|
2021-02-16 22:44:25 +08:00
|
|
|
const char *path, unsigned flags)
|
2009-03-14 12:24:08 +08:00
|
|
|
{
|
2017-01-28 10:02:06 +08:00
|
|
|
struct attr_stack *res = NULL;
|
2009-03-14 12:24:08 +08:00
|
|
|
|
2017-01-28 10:02:06 +08:00
|
|
|
if (direction == GIT_ATTR_INDEX) {
|
2021-02-16 22:44:25 +08:00
|
|
|
res = read_attr_from_index(istate, path, flags);
|
2023-01-14 16:30:38 +08:00
|
|
|
} else if (tree_oid) {
|
|
|
|
res = read_attr_from_blob(istate, tree_oid, path, flags);
|
2017-01-28 10:02:06 +08:00
|
|
|
} else if (!is_bare_repository()) {
|
|
|
|
if (direction == GIT_ATTR_CHECKOUT) {
|
2021-02-16 22:44:25 +08:00
|
|
|
res = read_attr_from_index(istate, path, flags);
|
2017-01-28 10:02:06 +08:00
|
|
|
if (!res)
|
2021-02-16 22:44:25 +08:00
|
|
|
res = read_attr_from_file(path, flags);
|
2017-01-28 10:02:06 +08:00
|
|
|
} else if (direction == GIT_ATTR_CHECKIN) {
|
2021-02-16 22:44:25 +08:00
|
|
|
res = read_attr_from_file(path, flags);
|
2017-01-28 10:02:06 +08:00
|
|
|
if (!res)
|
|
|
|
/*
|
|
|
|
* There is no checked out .gitattributes file
|
|
|
|
* there, but we might have it in the index.
|
|
|
|
* We allow operation in a sparsely checked out
|
|
|
|
* work tree, so read from it.
|
|
|
|
*/
|
2021-02-16 22:44:25 +08:00
|
|
|
res = read_attr_from_index(istate, path, flags);
|
2017-01-28 10:02:06 +08:00
|
|
|
}
|
2009-03-14 12:24:08 +08:00
|
|
|
}
|
2017-01-28 10:02:06 +08:00
|
|
|
|
2009-03-14 12:24:08 +08:00
|
|
|
if (!res)
|
2021-03-14 00:17:22 +08:00
|
|
|
CALLOC_ARRAY(res, 1);
|
2009-03-14 12:24:08 +08:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2023-06-28 00:19:00 +08:00
|
|
|
const char *git_attr_system_file(void)
|
2010-09-01 06:42:43 +08:00
|
|
|
{
|
|
|
|
static const char *system_wide;
|
|
|
|
if (!system_wide)
|
|
|
|
system_wide = system_path(ETC_GITATTRIBUTES);
|
|
|
|
return system_wide;
|
|
|
|
}
|
|
|
|
|
2023-06-28 00:19:00 +08:00
|
|
|
const char *git_attr_global_file(void)
|
2017-01-28 10:02:05 +08:00
|
|
|
{
|
|
|
|
if (!git_attributes_file)
|
|
|
|
git_attributes_file = xdg_config_home("attributes");
|
|
|
|
|
|
|
|
return git_attributes_file;
|
|
|
|
}
|
|
|
|
|
2023-06-28 00:19:00 +08:00
|
|
|
int git_attr_system_is_enabled(void)
|
2010-09-01 06:42:43 +08:00
|
|
|
{
|
|
|
|
return !git_env_bool("GIT_ATTR_NOSYSTEM", 0);
|
|
|
|
}
|
|
|
|
|
memoize common git-path "constant" files
One of the most common uses of git_path() is to pass a
constant, like git_path("MERGE_MSG"). This has two
drawbacks:
1. The return value is a static buffer, and the lifetime
is dependent on other calls to git_path, etc.
2. There's no compile-time checking of the pathname. This
is OK for a one-off (after all, we have to spell it
correctly at least once), but many of these constant
strings appear throughout the code.
This patch introduces a series of functions to "memoize"
these strings, which are essentially globals for the
lifetime of the program. We compute the value once, take
ownership of the buffer, and return the cached value for
subsequent calls. cache.h provides a helper macro for
defining these functions as one-liners, and defines a few
common ones for global use.
Using a macro is a little bit gross, but it does nicely
document the purpose of the functions. If we need to touch
them all later (e.g., because we learned how to change the
git_dir variable at runtime, and need to invalidate all of
the stored values), it will be much easier to have the
complete list.
Note that the shared-global functions have separate, manual
declarations. We could do something clever with the macros
(e.g., expand it to a declaration in some places, and a
declaration _and_ a definition in path.c). But there aren't
that many, and it's probably better to stay away from
too-magical macros.
Likewise, if we abandon the C preprocessor in favor of
generating these with a script, we could get much fancier.
E.g., normalizing "FOO/BAR-BAZ" into "git_path_foo_bar_baz".
But the small amount of saved typing is probably not worth
the resulting confusion to readers who want to grep for the
function's definition.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-08-10 17:38:57 +08:00
|
|
|
static GIT_PATH_FUNC(git_path_info_attributes, INFOATTRIBUTES_FILE)
|
|
|
|
|
2017-01-28 10:01:51 +08:00
|
|
|
static void push_stack(struct attr_stack **attr_stack_p,
|
|
|
|
struct attr_stack *elem, char *origin, size_t originlen)
|
|
|
|
{
|
|
|
|
if (elem) {
|
|
|
|
elem->origin = origin;
|
|
|
|
if (origin)
|
|
|
|
elem->originlen = originlen;
|
|
|
|
elem->prev = *attr_stack_p;
|
|
|
|
*attr_stack_p = elem;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-01 09:49:39 +08:00
|
|
|
static void bootstrap_attr_stack(struct index_state *istate,
|
2023-01-14 16:30:38 +08:00
|
|
|
const struct object_id *tree_oid,
|
2018-08-14 00:14:20 +08:00
|
|
|
struct attr_stack **stack)
|
attribute macro support
This adds "attribute macros" (for lack of better name). So far,
we have low-level attributes such as crlf and diff, which are
defined in operational terms --- setting or unsetting them on a
particular path directly affects what is done to the path. For
example, in order to decline diffs or crlf conversions on a
binary blob, no diffs on PostScript files, and treat all other
files normally, you would have something like these:
* diff crlf
*.ps !diff
proprietary.o !diff !crlf
That is fine as the operation goes, but gets unwieldy rather
rapidly, when we start adding more low-level attributes that are
defined in operational terms. A near-term example of such an
attribute would be 'merge-3way' which would control if git
should attempt the usual 3-way file-level merge internally, or
leave merging to a specialized external program of user's
choice. When it is added, we do _not_ want to force the users
to update the above to:
* diff crlf merge-3way
*.ps !diff
proprietary.o !diff !crlf !merge-3way
The way this patch solves this issue is to realize that the
attributes the user is assigning to paths are not defined in
terms of operations but in terms of what they are.
All of the three low-level attributes usually make sense for
most of the files that sane SCM users have git operate on (these
files are typically called "text'). Only a few cases, such as
binary blob, need exception to decline the "usual treatment
given to text files" -- and people mark them as "binary".
So this allows the $GIT_DIR/info/alternates and .gitattributes
at the toplevel of the project to also specify attributes that
assigns other attributes. The syntax is '[attr]' followed by an
attribute name followed by a list of attribute names:
[attr] binary !diff !crlf !merge-3way
When "binary" attribute is set to a path, if the path has not
got diff/crlf/merge-3way attribute set or unset by other rules,
this rule unsets the three low-level attributes.
It is expected that the user level .gitattributes will be
expressed mostly in terms of attributes based on what the files
are, and the above sample would become like this:
(built-in attribute configuration)
[attr] binary !diff !crlf !merge-3way
* diff crlf merge-3way
(project specific .gitattributes)
proprietary.o binary
(user preference $GIT_DIR/info/attributes)
*.ps !diff
There are a few caveats.
* As described above, you can define these macros only in
$GIT_DIR/info/attributes and toplevel .gitattributes.
* There is no attempt to detect circular definition of macro
attributes, and definitions are evaluated from bottom to top
as usual to fill in other attributes that have not yet got
values. The following would work as expected:
[attr] text diff crlf
[attr] ps text !diff
*.ps ps
while this would most likely not (I haven't tried):
[attr] ps text !diff
[attr] text diff crlf
*.ps ps
* When a macro says "[attr] A B !C", saying that a path does
not have attribute A does not let you tell anything about
attributes B or C. That is, given this:
[attr] text diff crlf
[attr] ps text !diff
*.txt !ps
path hello.txt, which would match "*.txt" pattern, would have
"ps" attribute set to zero, but that does not make text
attribute of hello.txt set to false (nor diff attribute set to
true).
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-14 23:54:37 +08:00
|
|
|
{
|
2017-01-28 10:02:05 +08:00
|
|
|
struct attr_stack *e;
|
2021-02-16 22:44:25 +08:00
|
|
|
unsigned flags = READ_ATTR_MACRO_OK;
|
attribute macro support
This adds "attribute macros" (for lack of better name). So far,
we have low-level attributes such as crlf and diff, which are
defined in operational terms --- setting or unsetting them on a
particular path directly affects what is done to the path. For
example, in order to decline diffs or crlf conversions on a
binary blob, no diffs on PostScript files, and treat all other
files normally, you would have something like these:
* diff crlf
*.ps !diff
proprietary.o !diff !crlf
That is fine as the operation goes, but gets unwieldy rather
rapidly, when we start adding more low-level attributes that are
defined in operational terms. A near-term example of such an
attribute would be 'merge-3way' which would control if git
should attempt the usual 3-way file-level merge internally, or
leave merging to a specialized external program of user's
choice. When it is added, we do _not_ want to force the users
to update the above to:
* diff crlf merge-3way
*.ps !diff
proprietary.o !diff !crlf !merge-3way
The way this patch solves this issue is to realize that the
attributes the user is assigning to paths are not defined in
terms of operations but in terms of what they are.
All of the three low-level attributes usually make sense for
most of the files that sane SCM users have git operate on (these
files are typically called "text'). Only a few cases, such as
binary blob, need exception to decline the "usual treatment
given to text files" -- and people mark them as "binary".
So this allows the $GIT_DIR/info/alternates and .gitattributes
at the toplevel of the project to also specify attributes that
assigns other attributes. The syntax is '[attr]' followed by an
attribute name followed by a list of attribute names:
[attr] binary !diff !crlf !merge-3way
When "binary" attribute is set to a path, if the path has not
got diff/crlf/merge-3way attribute set or unset by other rules,
this rule unsets the three low-level attributes.
It is expected that the user level .gitattributes will be
expressed mostly in terms of attributes based on what the files
are, and the above sample would become like this:
(built-in attribute configuration)
[attr] binary !diff !crlf !merge-3way
* diff crlf merge-3way
(project specific .gitattributes)
proprietary.o binary
(user preference $GIT_DIR/info/attributes)
*.ps !diff
There are a few caveats.
* As described above, you can define these macros only in
$GIT_DIR/info/attributes and toplevel .gitattributes.
* There is no attempt to detect circular definition of macro
attributes, and definitions are evaluated from bottom to top
as usual to fill in other attributes that have not yet got
values. The following would work as expected:
[attr] text diff crlf
[attr] ps text !diff
*.ps ps
while this would most likely not (I haven't tried):
[attr] ps text !diff
[attr] text diff crlf
*.ps ps
* When a macro says "[attr] A B !C", saying that a path does
not have attribute A does not let you tell anything about
attributes B or C. That is, given this:
[attr] text diff crlf
[attr] ps text !diff
*.txt !ps
path hello.txt, which would match "*.txt" pattern, would have
"ps" attribute set to zero, but that does not make text
attribute of hello.txt set to false (nor diff attribute set to
true).
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-14 23:54:37 +08:00
|
|
|
|
2017-01-28 10:02:05 +08:00
|
|
|
if (*stack)
|
2012-01-11 04:27:37 +08:00
|
|
|
return;
|
attribute macro support
This adds "attribute macros" (for lack of better name). So far,
we have low-level attributes such as crlf and diff, which are
defined in operational terms --- setting or unsetting them on a
particular path directly affects what is done to the path. For
example, in order to decline diffs or crlf conversions on a
binary blob, no diffs on PostScript files, and treat all other
files normally, you would have something like these:
* diff crlf
*.ps !diff
proprietary.o !diff !crlf
That is fine as the operation goes, but gets unwieldy rather
rapidly, when we start adding more low-level attributes that are
defined in operational terms. A near-term example of such an
attribute would be 'merge-3way' which would control if git
should attempt the usual 3-way file-level merge internally, or
leave merging to a specialized external program of user's
choice. When it is added, we do _not_ want to force the users
to update the above to:
* diff crlf merge-3way
*.ps !diff
proprietary.o !diff !crlf !merge-3way
The way this patch solves this issue is to realize that the
attributes the user is assigning to paths are not defined in
terms of operations but in terms of what they are.
All of the three low-level attributes usually make sense for
most of the files that sane SCM users have git operate on (these
files are typically called "text'). Only a few cases, such as
binary blob, need exception to decline the "usual treatment
given to text files" -- and people mark them as "binary".
So this allows the $GIT_DIR/info/alternates and .gitattributes
at the toplevel of the project to also specify attributes that
assigns other attributes. The syntax is '[attr]' followed by an
attribute name followed by a list of attribute names:
[attr] binary !diff !crlf !merge-3way
When "binary" attribute is set to a path, if the path has not
got diff/crlf/merge-3way attribute set or unset by other rules,
this rule unsets the three low-level attributes.
It is expected that the user level .gitattributes will be
expressed mostly in terms of attributes based on what the files
are, and the above sample would become like this:
(built-in attribute configuration)
[attr] binary !diff !crlf !merge-3way
* diff crlf merge-3way
(project specific .gitattributes)
proprietary.o binary
(user preference $GIT_DIR/info/attributes)
*.ps !diff
There are a few caveats.
* As described above, you can define these macros only in
$GIT_DIR/info/attributes and toplevel .gitattributes.
* There is no attempt to detect circular definition of macro
attributes, and definitions are evaluated from bottom to top
as usual to fill in other attributes that have not yet got
values. The following would work as expected:
[attr] text diff crlf
[attr] ps text !diff
*.ps ps
while this would most likely not (I haven't tried):
[attr] ps text !diff
[attr] text diff crlf
*.ps ps
* When a macro says "[attr] A B !C", saying that a path does
not have attribute A does not let you tell anything about
attributes B or C. That is, given this:
[attr] text diff crlf
[attr] ps text !diff
*.txt !ps
path hello.txt, which would match "*.txt" pattern, would have
"ps" attribute set to zero, but that does not make text
attribute of hello.txt set to false (nor diff attribute set to
true).
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-14 23:54:37 +08:00
|
|
|
|
2017-01-28 10:02:05 +08:00
|
|
|
/* builtin frame */
|
|
|
|
e = read_attr_from_array(builtin_attr);
|
|
|
|
push_stack(stack, e, NULL, 0);
|
2010-09-01 06:42:43 +08:00
|
|
|
|
2017-01-28 10:02:05 +08:00
|
|
|
/* system-wide frame */
|
2023-06-28 00:19:00 +08:00
|
|
|
if (git_attr_system_is_enabled()) {
|
|
|
|
e = read_attr_from_file(git_attr_system_file(), flags);
|
2017-01-28 10:02:05 +08:00
|
|
|
push_stack(stack, e, NULL, 0);
|
|
|
|
}
|
attribute macro support
This adds "attribute macros" (for lack of better name). So far,
we have low-level attributes such as crlf and diff, which are
defined in operational terms --- setting or unsetting them on a
particular path directly affects what is done to the path. For
example, in order to decline diffs or crlf conversions on a
binary blob, no diffs on PostScript files, and treat all other
files normally, you would have something like these:
* diff crlf
*.ps !diff
proprietary.o !diff !crlf
That is fine as the operation goes, but gets unwieldy rather
rapidly, when we start adding more low-level attributes that are
defined in operational terms. A near-term example of such an
attribute would be 'merge-3way' which would control if git
should attempt the usual 3-way file-level merge internally, or
leave merging to a specialized external program of user's
choice. When it is added, we do _not_ want to force the users
to update the above to:
* diff crlf merge-3way
*.ps !diff
proprietary.o !diff !crlf !merge-3way
The way this patch solves this issue is to realize that the
attributes the user is assigning to paths are not defined in
terms of operations but in terms of what they are.
All of the three low-level attributes usually make sense for
most of the files that sane SCM users have git operate on (these
files are typically called "text'). Only a few cases, such as
binary blob, need exception to decline the "usual treatment
given to text files" -- and people mark them as "binary".
So this allows the $GIT_DIR/info/alternates and .gitattributes
at the toplevel of the project to also specify attributes that
assigns other attributes. The syntax is '[attr]' followed by an
attribute name followed by a list of attribute names:
[attr] binary !diff !crlf !merge-3way
When "binary" attribute is set to a path, if the path has not
got diff/crlf/merge-3way attribute set or unset by other rules,
this rule unsets the three low-level attributes.
It is expected that the user level .gitattributes will be
expressed mostly in terms of attributes based on what the files
are, and the above sample would become like this:
(built-in attribute configuration)
[attr] binary !diff !crlf !merge-3way
* diff crlf merge-3way
(project specific .gitattributes)
proprietary.o binary
(user preference $GIT_DIR/info/attributes)
*.ps !diff
There are a few caveats.
* As described above, you can define these macros only in
$GIT_DIR/info/attributes and toplevel .gitattributes.
* There is no attempt to detect circular definition of macro
attributes, and definitions are evaluated from bottom to top
as usual to fill in other attributes that have not yet got
values. The following would work as expected:
[attr] text diff crlf
[attr] ps text !diff
*.ps ps
while this would most likely not (I haven't tried):
[attr] ps text !diff
[attr] text diff crlf
*.ps ps
* When a macro says "[attr] A B !C", saying that a path does
not have attribute A does not let you tell anything about
attributes B or C. That is, given this:
[attr] text diff crlf
[attr] ps text !diff
*.txt !ps
path hello.txt, which would match "*.txt" pattern, would have
"ps" attribute set to zero, but that does not make text
attribute of hello.txt set to false (nor diff attribute set to
true).
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-14 23:54:37 +08:00
|
|
|
|
2017-01-28 10:02:05 +08:00
|
|
|
/* home directory */
|
2023-06-28 00:19:00 +08:00
|
|
|
if (git_attr_global_file()) {
|
|
|
|
e = read_attr_from_file(git_attr_global_file(), flags);
|
2017-01-28 10:02:05 +08:00
|
|
|
push_stack(stack, e, NULL, 0);
|
attribute macro support
This adds "attribute macros" (for lack of better name). So far,
we have low-level attributes such as crlf and diff, which are
defined in operational terms --- setting or unsetting them on a
particular path directly affects what is done to the path. For
example, in order to decline diffs or crlf conversions on a
binary blob, no diffs on PostScript files, and treat all other
files normally, you would have something like these:
* diff crlf
*.ps !diff
proprietary.o !diff !crlf
That is fine as the operation goes, but gets unwieldy rather
rapidly, when we start adding more low-level attributes that are
defined in operational terms. A near-term example of such an
attribute would be 'merge-3way' which would control if git
should attempt the usual 3-way file-level merge internally, or
leave merging to a specialized external program of user's
choice. When it is added, we do _not_ want to force the users
to update the above to:
* diff crlf merge-3way
*.ps !diff
proprietary.o !diff !crlf !merge-3way
The way this patch solves this issue is to realize that the
attributes the user is assigning to paths are not defined in
terms of operations but in terms of what they are.
All of the three low-level attributes usually make sense for
most of the files that sane SCM users have git operate on (these
files are typically called "text'). Only a few cases, such as
binary blob, need exception to decline the "usual treatment
given to text files" -- and people mark them as "binary".
So this allows the $GIT_DIR/info/alternates and .gitattributes
at the toplevel of the project to also specify attributes that
assigns other attributes. The syntax is '[attr]' followed by an
attribute name followed by a list of attribute names:
[attr] binary !diff !crlf !merge-3way
When "binary" attribute is set to a path, if the path has not
got diff/crlf/merge-3way attribute set or unset by other rules,
this rule unsets the three low-level attributes.
It is expected that the user level .gitattributes will be
expressed mostly in terms of attributes based on what the files
are, and the above sample would become like this:
(built-in attribute configuration)
[attr] binary !diff !crlf !merge-3way
* diff crlf merge-3way
(project specific .gitattributes)
proprietary.o binary
(user preference $GIT_DIR/info/attributes)
*.ps !diff
There are a few caveats.
* As described above, you can define these macros only in
$GIT_DIR/info/attributes and toplevel .gitattributes.
* There is no attempt to detect circular definition of macro
attributes, and definitions are evaluated from bottom to top
as usual to fill in other attributes that have not yet got
values. The following would work as expected:
[attr] text diff crlf
[attr] ps text !diff
*.ps ps
while this would most likely not (I haven't tried):
[attr] ps text !diff
[attr] text diff crlf
*.ps ps
* When a macro says "[attr] A B !C", saying that a path does
not have attribute A does not let you tell anything about
attributes B or C. That is, given this:
[attr] text diff crlf
[attr] ps text !diff
*.txt !ps
path hello.txt, which would match "*.txt" pattern, would have
"ps" attribute set to zero, but that does not make text
attribute of hello.txt set to false (nor diff attribute set to
true).
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-14 23:54:37 +08:00
|
|
|
}
|
2012-01-11 04:27:37 +08:00
|
|
|
|
2017-01-28 10:02:05 +08:00
|
|
|
/* root directory */
|
2023-01-14 16:30:38 +08:00
|
|
|
e = read_attr(istate, tree_oid, GITATTRIBUTES_FILE, flags | READ_ATTR_NOFOLLOW);
|
2017-01-28 10:02:05 +08:00
|
|
|
push_stack(stack, e, xstrdup(""), 0);
|
2016-10-20 14:16:41 +08:00
|
|
|
|
2017-01-28 10:02:05 +08:00
|
|
|
/* info frame */
|
|
|
|
if (startup_info->have_repository)
|
2021-02-16 22:44:25 +08:00
|
|
|
e = read_attr_from_file(git_path_info_attributes(), flags);
|
2017-01-28 10:02:05 +08:00
|
|
|
else
|
|
|
|
e = NULL;
|
|
|
|
if (!e)
|
2021-03-14 00:17:22 +08:00
|
|
|
CALLOC_ARRAY(e, 1);
|
2017-01-28 10:02:05 +08:00
|
|
|
push_stack(stack, e, NULL, 0);
|
attribute macro support
This adds "attribute macros" (for lack of better name). So far,
we have low-level attributes such as crlf and diff, which are
defined in operational terms --- setting or unsetting them on a
particular path directly affects what is done to the path. For
example, in order to decline diffs or crlf conversions on a
binary blob, no diffs on PostScript files, and treat all other
files normally, you would have something like these:
* diff crlf
*.ps !diff
proprietary.o !diff !crlf
That is fine as the operation goes, but gets unwieldy rather
rapidly, when we start adding more low-level attributes that are
defined in operational terms. A near-term example of such an
attribute would be 'merge-3way' which would control if git
should attempt the usual 3-way file-level merge internally, or
leave merging to a specialized external program of user's
choice. When it is added, we do _not_ want to force the users
to update the above to:
* diff crlf merge-3way
*.ps !diff
proprietary.o !diff !crlf !merge-3way
The way this patch solves this issue is to realize that the
attributes the user is assigning to paths are not defined in
terms of operations but in terms of what they are.
All of the three low-level attributes usually make sense for
most of the files that sane SCM users have git operate on (these
files are typically called "text'). Only a few cases, such as
binary blob, need exception to decline the "usual treatment
given to text files" -- and people mark them as "binary".
So this allows the $GIT_DIR/info/alternates and .gitattributes
at the toplevel of the project to also specify attributes that
assigns other attributes. The syntax is '[attr]' followed by an
attribute name followed by a list of attribute names:
[attr] binary !diff !crlf !merge-3way
When "binary" attribute is set to a path, if the path has not
got diff/crlf/merge-3way attribute set or unset by other rules,
this rule unsets the three low-level attributes.
It is expected that the user level .gitattributes will be
expressed mostly in terms of attributes based on what the files
are, and the above sample would become like this:
(built-in attribute configuration)
[attr] binary !diff !crlf !merge-3way
* diff crlf merge-3way
(project specific .gitattributes)
proprietary.o binary
(user preference $GIT_DIR/info/attributes)
*.ps !diff
There are a few caveats.
* As described above, you can define these macros only in
$GIT_DIR/info/attributes and toplevel .gitattributes.
* There is no attempt to detect circular definition of macro
attributes, and definitions are evaluated from bottom to top
as usual to fill in other attributes that have not yet got
values. The following would work as expected:
[attr] text diff crlf
[attr] ps text !diff
*.ps ps
while this would most likely not (I haven't tried):
[attr] ps text !diff
[attr] text diff crlf
*.ps ps
* When a macro says "[attr] A B !C", saying that a path does
not have attribute A does not let you tell anything about
attributes B or C. That is, given this:
[attr] text diff crlf
[attr] ps text !diff
*.txt !ps
path hello.txt, which would match "*.txt" pattern, would have
"ps" attribute set to zero, but that does not make text
attribute of hello.txt set to false (nor diff attribute set to
true).
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-14 23:54:37 +08:00
|
|
|
}
|
|
|
|
|
2021-04-01 09:49:39 +08:00
|
|
|
static void prepare_attr_stack(struct index_state *istate,
|
2023-01-14 16:30:38 +08:00
|
|
|
const struct object_id *tree_oid,
|
2018-08-14 00:14:20 +08:00
|
|
|
const char *path, int dirlen,
|
2017-01-28 10:02:05 +08:00
|
|
|
struct attr_stack **stack)
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
{
|
2017-01-28 10:02:05 +08:00
|
|
|
struct attr_stack *info;
|
2017-01-28 10:02:06 +08:00
|
|
|
struct strbuf pathbuf = STRBUF_INIT;
|
2011-08-04 12:36:19 +08:00
|
|
|
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
/*
|
|
|
|
* At the bottom of the attribute stack is the built-in
|
2010-09-01 06:42:43 +08:00
|
|
|
* set of attribute definitions, followed by the contents
|
|
|
|
* of $(prefix)/etc/gitattributes and a file specified by
|
|
|
|
* core.attributesfile. Then, contents from
|
2019-03-06 17:14:44 +08:00
|
|
|
* .gitattributes files from directories closer to the
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
* root to the ones in deeper directories are pushed
|
|
|
|
* to the stack. Finally, at the very top of the stack
|
|
|
|
* we always keep the contents of $GIT_DIR/info/attributes.
|
|
|
|
*
|
|
|
|
* When checking, we use entries from near the top of the
|
|
|
|
* stack, preferring $GIT_DIR/info/attributes, then
|
|
|
|
* .gitattributes in deeper directories to shallower ones,
|
|
|
|
* and finally use the built-in set as the default.
|
|
|
|
*/
|
2023-01-14 16:30:38 +08:00
|
|
|
bootstrap_attr_stack(istate, tree_oid, stack);
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Pop the "info" one that is always at the top of the stack.
|
|
|
|
*/
|
2017-01-28 10:02:05 +08:00
|
|
|
info = *stack;
|
|
|
|
*stack = info->prev;
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Pop the ones from directories that are not the prefix of
|
2012-01-11 04:28:38 +08:00
|
|
|
* the path we are checking. Break out of the loop when we see
|
|
|
|
* the root one (whose origin is an empty string "") or the builtin
|
|
|
|
* one (whose origin is NULL) without popping it.
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
*/
|
2017-01-28 10:02:05 +08:00
|
|
|
while ((*stack)->origin) {
|
|
|
|
int namelen = (*stack)->originlen;
|
|
|
|
struct attr_stack *elem;
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
|
2017-01-28 10:02:05 +08:00
|
|
|
elem = *stack;
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
if (namelen <= dirlen &&
|
attr: don't confuse prefixes with leading directories
When we prepare the attribute stack for a lookup on a path,
we start with the cached stack from the previous lookup
(because it is common to do several lookups in the same
directory hierarchy). So the first thing we must do in
preparing the stack is to pop any entries that point to
directories we are no longer interested in.
For example, if our stack contains gitattributes for:
foo/bar/baz
foo/bar
foo
but we want to do a lookup in "foo/bar/bleep", then we want
to pop the top element, but retain the others.
To do this we walk down the stack from the top, popping
elements that do not match our lookup directory. However,
the test do this simply checked strncmp, meaning we would
mistake "foo/bar/baz" as a leading directory of
"foo/bar/baz_plus". We must also check that the character
after our match is '/', meaning we matched the whole path
component.
There are two special cases to consider:
1. The top of our attr stack has the empty path. So we
must not check for '/', but rather special-case the
empty path, which always matches.
2. Typically when matching paths in this way, you would
also need to check for a full string match (i.e., the
character after is '\0'). We don't need to do so in
this case, though, because our path string is actually
just the directory component of the path to a file
(i.e., we know that it terminates with "/", because the
filename comes after that).
Helped-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-01-11 02:08:21 +08:00
|
|
|
!strncmp(elem->origin, path, namelen) &&
|
|
|
|
(!namelen || path[namelen] == '/'))
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
break;
|
|
|
|
|
2017-01-28 10:02:05 +08:00
|
|
|
*stack = elem->prev;
|
|
|
|
attr_stack_free(elem);
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-01-28 10:02:06 +08:00
|
|
|
* bootstrap_attr_stack() should have added, and the
|
|
|
|
* above loop should have stopped before popping, the
|
|
|
|
* root element whose attr_stack->origin is set to an
|
|
|
|
* empty string.
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
*/
|
2017-01-28 10:02:06 +08:00
|
|
|
assert((*stack)->origin);
|
|
|
|
|
|
|
|
strbuf_addstr(&pathbuf, (*stack)->origin);
|
|
|
|
/* Build up to the directory 'path' is in */
|
|
|
|
while (pathbuf.len < dirlen) {
|
|
|
|
size_t len = pathbuf.len;
|
|
|
|
struct attr_stack *next;
|
|
|
|
char *origin;
|
|
|
|
|
|
|
|
/* Skip path-separator */
|
|
|
|
if (len < dirlen && is_dir_sep(path[len]))
|
|
|
|
len++;
|
|
|
|
/* Find the end of the next component */
|
|
|
|
while (len < dirlen && !is_dir_sep(path[len]))
|
|
|
|
len++;
|
|
|
|
|
|
|
|
if (pathbuf.len > 0)
|
|
|
|
strbuf_addch(&pathbuf, '/');
|
|
|
|
strbuf_add(&pathbuf, path + pathbuf.len, (len - pathbuf.len));
|
|
|
|
strbuf_addf(&pathbuf, "/%s", GITATTRIBUTES_FILE);
|
|
|
|
|
2023-01-14 16:30:38 +08:00
|
|
|
next = read_attr(istate, tree_oid, pathbuf.buf, READ_ATTR_NOFOLLOW);
|
2017-01-28 10:02:06 +08:00
|
|
|
|
|
|
|
/* reset the pathbuf to not include "/.gitattributes" */
|
|
|
|
strbuf_setlen(&pathbuf, len);
|
|
|
|
|
|
|
|
origin = xstrdup(pathbuf.buf);
|
|
|
|
push_stack(stack, next, origin, len);
|
2011-10-07 02:22:21 +08:00
|
|
|
}
|
2009-07-01 06:30:00 +08:00
|
|
|
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
/*
|
|
|
|
* Finally push the "info" one at the top of the stack.
|
|
|
|
*/
|
2017-01-28 10:02:05 +08:00
|
|
|
push_stack(stack, info, NULL, 0);
|
2017-01-28 10:02:06 +08:00
|
|
|
|
|
|
|
strbuf_release(&pathbuf);
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int path_matches(const char *pathname, int pathlen,
|
2013-03-27 01:28:07 +08:00
|
|
|
int basename_offset,
|
2012-10-15 14:24:39 +08:00
|
|
|
const struct pattern *pat,
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
const char *base, int baselen)
|
|
|
|
{
|
2012-10-15 14:24:39 +08:00
|
|
|
const char *pattern = pat->pattern;
|
|
|
|
int prefix = pat->nowildcardlen;
|
2013-03-29 05:49:13 +08:00
|
|
|
int isdir = (pathlen && pathname[pathlen - 1] == '/');
|
2012-10-15 14:24:39 +08:00
|
|
|
|
2019-09-04 02:04:56 +08:00
|
|
|
if ((pat->flags & PATTERN_FLAG_MUSTBEDIR) && !isdir)
|
2012-12-09 04:04:39 +08:00
|
|
|
return 0;
|
|
|
|
|
2019-09-04 02:04:56 +08:00
|
|
|
if (pat->flags & PATTERN_FLAG_NODIR) {
|
2013-03-27 01:28:07 +08:00
|
|
|
return match_basename(pathname + basename_offset,
|
2013-03-29 05:49:13 +08:00
|
|
|
pathlen - basename_offset - isdir,
|
2012-10-15 14:24:39 +08:00
|
|
|
pattern, prefix,
|
|
|
|
pat->patternlen, pat->flags);
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
}
|
2013-03-29 05:49:13 +08:00
|
|
|
return match_pathname(pathname, pathlen - isdir,
|
2012-10-15 14:24:39 +08:00
|
|
|
base, baselen,
|
2022-08-19 16:50:54 +08:00
|
|
|
pattern, prefix, pat->patternlen);
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
}
|
|
|
|
|
2017-01-28 10:02:02 +08:00
|
|
|
static int macroexpand_one(struct all_attrs_item *all_attrs, int nr, int rem);
|
2010-04-06 20:46:44 +08:00
|
|
|
|
attr: drop DEBUG_ATTR code
Since its inception in d0bfd026a8 (Add basic infrastructure to assign
attributes to paths, 2007-04-12), the attribute code carries a little
bit of debug code that is conditionally compiled only when DEBUG_ATTR is
set. But since you have to know about it and make a special build of Git
to use it, it's not clear that it's helping anyone (and there are very
few mentions of it on the list over the years).
Meanwhile, it causes slight headaches. Since it's not built as part of a
regular compile, it's subject to bitrot. E.g., this was dealt with in
712efb1a42 (attr: make it build with DEBUG_ATTR again, 2013-01-15), and
it currently fails to build with DEVELOPER=1 since e810e06357 (attr:
tighten const correctness with git_attr and match_attr, 2017-01-27).
And it causes confusion with -Wunused-parameter; the "what" parameter of
fill_one() is unused in a normal build, but needed in a debug build.
Let's just get rid of this code (and the now-useless parameter).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-10-06 21:23:19 +08:00
|
|
|
static int fill_one(struct all_attrs_item *all_attrs,
|
2017-01-28 10:02:03 +08:00
|
|
|
const struct match_attr *a, int rem)
|
2007-04-17 12:33:31 +08:00
|
|
|
{
|
2022-12-01 22:45:27 +08:00
|
|
|
size_t i;
|
2007-04-17 12:33:31 +08:00
|
|
|
|
2022-12-01 22:45:27 +08:00
|
|
|
for (i = a->num_attr; rem > 0 && i > 0; i--) {
|
|
|
|
const struct git_attr *attr = a->state[i - 1].attr;
|
2017-01-28 10:02:02 +08:00
|
|
|
const char **n = &(all_attrs[attr->attr_nr].value);
|
2022-12-01 22:45:27 +08:00
|
|
|
const char *v = a->state[i - 1].setto;
|
2007-04-17 12:33:31 +08:00
|
|
|
|
|
|
|
if (*n == ATTR__UNKNOWN) {
|
|
|
|
*n = v;
|
|
|
|
rem--;
|
2017-01-28 10:02:02 +08:00
|
|
|
rem = macroexpand_one(all_attrs, attr->attr_nr, rem);
|
2007-04-17 12:33:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return rem;
|
|
|
|
}
|
|
|
|
|
2013-03-27 01:28:07 +08:00
|
|
|
static int fill(const char *path, int pathlen, int basename_offset,
|
2017-01-28 10:02:05 +08:00
|
|
|
const struct attr_stack *stack,
|
|
|
|
struct all_attrs_item *all_attrs, int rem)
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
{
|
2017-01-28 10:02:05 +08:00
|
|
|
for (; rem > 0 && stack; stack = stack->prev) {
|
2022-12-01 22:45:31 +08:00
|
|
|
unsigned i;
|
2017-01-28 10:02:05 +08:00
|
|
|
const char *base = stack->origin ? stack->origin : "";
|
|
|
|
|
2022-12-01 22:45:31 +08:00
|
|
|
for (i = stack->num_matches; 0 < rem && 0 < i; i--) {
|
|
|
|
const struct match_attr *a = stack->attrs[i - 1];
|
2017-01-28 10:02:05 +08:00
|
|
|
if (a->is_macro)
|
|
|
|
continue;
|
|
|
|
if (path_matches(path, pathlen, basename_offset,
|
|
|
|
&a->u.pat, base, stack->originlen))
|
attr: drop DEBUG_ATTR code
Since its inception in d0bfd026a8 (Add basic infrastructure to assign
attributes to paths, 2007-04-12), the attribute code carries a little
bit of debug code that is conditionally compiled only when DEBUG_ATTR is
set. But since you have to know about it and make a special build of Git
to use it, it's not clear that it's helping anyone (and there are very
few mentions of it on the list over the years).
Meanwhile, it causes slight headaches. Since it's not built as part of a
regular compile, it's subject to bitrot. E.g., this was dealt with in
712efb1a42 (attr: make it build with DEBUG_ATTR again, 2013-01-15), and
it currently fails to build with DEVELOPER=1 since e810e06357 (attr:
tighten const correctness with git_attr and match_attr, 2017-01-27).
And it causes confusion with -Wunused-parameter; the "what" parameter of
fill_one() is unused in a normal build, but needed in a debug build.
Let's just get rid of this code (and the now-useless parameter).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-10-06 21:23:19 +08:00
|
|
|
rem = fill_one(all_attrs, a, rem);
|
2017-01-28 10:02:05 +08:00
|
|
|
}
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
}
|
2017-01-28 10:02:05 +08:00
|
|
|
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
return rem;
|
|
|
|
}
|
|
|
|
|
2017-01-28 10:02:02 +08:00
|
|
|
static int macroexpand_one(struct all_attrs_item *all_attrs, int nr, int rem)
|
attribute macro support
This adds "attribute macros" (for lack of better name). So far,
we have low-level attributes such as crlf and diff, which are
defined in operational terms --- setting or unsetting them on a
particular path directly affects what is done to the path. For
example, in order to decline diffs or crlf conversions on a
binary blob, no diffs on PostScript files, and treat all other
files normally, you would have something like these:
* diff crlf
*.ps !diff
proprietary.o !diff !crlf
That is fine as the operation goes, but gets unwieldy rather
rapidly, when we start adding more low-level attributes that are
defined in operational terms. A near-term example of such an
attribute would be 'merge-3way' which would control if git
should attempt the usual 3-way file-level merge internally, or
leave merging to a specialized external program of user's
choice. When it is added, we do _not_ want to force the users
to update the above to:
* diff crlf merge-3way
*.ps !diff
proprietary.o !diff !crlf !merge-3way
The way this patch solves this issue is to realize that the
attributes the user is assigning to paths are not defined in
terms of operations but in terms of what they are.
All of the three low-level attributes usually make sense for
most of the files that sane SCM users have git operate on (these
files are typically called "text'). Only a few cases, such as
binary blob, need exception to decline the "usual treatment
given to text files" -- and people mark them as "binary".
So this allows the $GIT_DIR/info/alternates and .gitattributes
at the toplevel of the project to also specify attributes that
assigns other attributes. The syntax is '[attr]' followed by an
attribute name followed by a list of attribute names:
[attr] binary !diff !crlf !merge-3way
When "binary" attribute is set to a path, if the path has not
got diff/crlf/merge-3way attribute set or unset by other rules,
this rule unsets the three low-level attributes.
It is expected that the user level .gitattributes will be
expressed mostly in terms of attributes based on what the files
are, and the above sample would become like this:
(built-in attribute configuration)
[attr] binary !diff !crlf !merge-3way
* diff crlf merge-3way
(project specific .gitattributes)
proprietary.o binary
(user preference $GIT_DIR/info/attributes)
*.ps !diff
There are a few caveats.
* As described above, you can define these macros only in
$GIT_DIR/info/attributes and toplevel .gitattributes.
* There is no attempt to detect circular definition of macro
attributes, and definitions are evaluated from bottom to top
as usual to fill in other attributes that have not yet got
values. The following would work as expected:
[attr] text diff crlf
[attr] ps text !diff
*.ps ps
while this would most likely not (I haven't tried):
[attr] ps text !diff
[attr] text diff crlf
*.ps ps
* When a macro says "[attr] A B !C", saying that a path does
not have attribute A does not let you tell anything about
attributes B or C. That is, given this:
[attr] text diff crlf
[attr] ps text !diff
*.txt !ps
path hello.txt, which would match "*.txt" pattern, would have
"ps" attribute set to zero, but that does not make text
attribute of hello.txt set to false (nor diff attribute set to
true).
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-14 23:54:37 +08:00
|
|
|
{
|
2017-01-28 10:02:03 +08:00
|
|
|
const struct all_attrs_item *item = &all_attrs[nr];
|
attribute macro support
This adds "attribute macros" (for lack of better name). So far,
we have low-level attributes such as crlf and diff, which are
defined in operational terms --- setting or unsetting them on a
particular path directly affects what is done to the path. For
example, in order to decline diffs or crlf conversions on a
binary blob, no diffs on PostScript files, and treat all other
files normally, you would have something like these:
* diff crlf
*.ps !diff
proprietary.o !diff !crlf
That is fine as the operation goes, but gets unwieldy rather
rapidly, when we start adding more low-level attributes that are
defined in operational terms. A near-term example of such an
attribute would be 'merge-3way' which would control if git
should attempt the usual 3-way file-level merge internally, or
leave merging to a specialized external program of user's
choice. When it is added, we do _not_ want to force the users
to update the above to:
* diff crlf merge-3way
*.ps !diff
proprietary.o !diff !crlf !merge-3way
The way this patch solves this issue is to realize that the
attributes the user is assigning to paths are not defined in
terms of operations but in terms of what they are.
All of the three low-level attributes usually make sense for
most of the files that sane SCM users have git operate on (these
files are typically called "text'). Only a few cases, such as
binary blob, need exception to decline the "usual treatment
given to text files" -- and people mark them as "binary".
So this allows the $GIT_DIR/info/alternates and .gitattributes
at the toplevel of the project to also specify attributes that
assigns other attributes. The syntax is '[attr]' followed by an
attribute name followed by a list of attribute names:
[attr] binary !diff !crlf !merge-3way
When "binary" attribute is set to a path, if the path has not
got diff/crlf/merge-3way attribute set or unset by other rules,
this rule unsets the three low-level attributes.
It is expected that the user level .gitattributes will be
expressed mostly in terms of attributes based on what the files
are, and the above sample would become like this:
(built-in attribute configuration)
[attr] binary !diff !crlf !merge-3way
* diff crlf merge-3way
(project specific .gitattributes)
proprietary.o binary
(user preference $GIT_DIR/info/attributes)
*.ps !diff
There are a few caveats.
* As described above, you can define these macros only in
$GIT_DIR/info/attributes and toplevel .gitattributes.
* There is no attempt to detect circular definition of macro
attributes, and definitions are evaluated from bottom to top
as usual to fill in other attributes that have not yet got
values. The following would work as expected:
[attr] text diff crlf
[attr] ps text !diff
*.ps ps
while this would most likely not (I haven't tried):
[attr] ps text !diff
[attr] text diff crlf
*.ps ps
* When a macro says "[attr] A B !C", saying that a path does
not have attribute A does not let you tell anything about
attributes B or C. That is, given this:
[attr] text diff crlf
[attr] ps text !diff
*.txt !ps
path hello.txt, which would match "*.txt" pattern, would have
"ps" attribute set to zero, but that does not make text
attribute of hello.txt set to false (nor diff attribute set to
true).
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-14 23:54:37 +08:00
|
|
|
|
2017-01-28 10:02:03 +08:00
|
|
|
if (item->macro && item->value == ATTR__TRUE)
|
attr: drop DEBUG_ATTR code
Since its inception in d0bfd026a8 (Add basic infrastructure to assign
attributes to paths, 2007-04-12), the attribute code carries a little
bit of debug code that is conditionally compiled only when DEBUG_ATTR is
set. But since you have to know about it and make a special build of Git
to use it, it's not clear that it's helping anyone (and there are very
few mentions of it on the list over the years).
Meanwhile, it causes slight headaches. Since it's not built as part of a
regular compile, it's subject to bitrot. E.g., this was dealt with in
712efb1a42 (attr: make it build with DEBUG_ATTR again, 2013-01-15), and
it currently fails to build with DEVELOPER=1 since e810e06357 (attr:
tighten const correctness with git_attr and match_attr, 2017-01-27).
And it causes confusion with -Wunused-parameter; the "what" parameter of
fill_one() is unused in a normal build, but needed in a debug build.
Let's just get rid of this code (and the now-useless parameter).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-10-06 21:23:19 +08:00
|
|
|
return fill_one(all_attrs, item->macro, rem);
|
2017-01-28 10:02:03 +08:00
|
|
|
else
|
2010-04-06 20:46:44 +08:00
|
|
|
return rem;
|
2017-01-28 10:02:03 +08:00
|
|
|
}
|
2010-04-06 20:46:44 +08:00
|
|
|
|
2017-01-28 10:02:03 +08:00
|
|
|
/*
|
|
|
|
* Marks the attributes which are macros based on the attribute stack.
|
|
|
|
* This prevents having to search through the attribute stack each time
|
|
|
|
* a macro needs to be expanded during the fill stage.
|
|
|
|
*/
|
|
|
|
static void determine_macros(struct all_attrs_item *all_attrs,
|
|
|
|
const struct attr_stack *stack)
|
|
|
|
{
|
|
|
|
for (; stack; stack = stack->prev) {
|
2022-12-01 22:45:31 +08:00
|
|
|
unsigned i;
|
|
|
|
for (i = stack->num_matches; i > 0; i--) {
|
|
|
|
const struct match_attr *ma = stack->attrs[i - 1];
|
2017-01-28 10:02:03 +08:00
|
|
|
if (ma->is_macro) {
|
2022-12-01 22:45:36 +08:00
|
|
|
unsigned int n = ma->u.attr->attr_nr;
|
2017-01-28 10:02:03 +08:00
|
|
|
if (!all_attrs[n].macro) {
|
|
|
|
all_attrs[n].macro = ma;
|
|
|
|
}
|
|
|
|
}
|
2010-04-06 20:46:44 +08:00
|
|
|
}
|
2017-01-28 10:01:47 +08:00
|
|
|
}
|
attribute macro support
This adds "attribute macros" (for lack of better name). So far,
we have low-level attributes such as crlf and diff, which are
defined in operational terms --- setting or unsetting them on a
particular path directly affects what is done to the path. For
example, in order to decline diffs or crlf conversions on a
binary blob, no diffs on PostScript files, and treat all other
files normally, you would have something like these:
* diff crlf
*.ps !diff
proprietary.o !diff !crlf
That is fine as the operation goes, but gets unwieldy rather
rapidly, when we start adding more low-level attributes that are
defined in operational terms. A near-term example of such an
attribute would be 'merge-3way' which would control if git
should attempt the usual 3-way file-level merge internally, or
leave merging to a specialized external program of user's
choice. When it is added, we do _not_ want to force the users
to update the above to:
* diff crlf merge-3way
*.ps !diff
proprietary.o !diff !crlf !merge-3way
The way this patch solves this issue is to realize that the
attributes the user is assigning to paths are not defined in
terms of operations but in terms of what they are.
All of the three low-level attributes usually make sense for
most of the files that sane SCM users have git operate on (these
files are typically called "text'). Only a few cases, such as
binary blob, need exception to decline the "usual treatment
given to text files" -- and people mark them as "binary".
So this allows the $GIT_DIR/info/alternates and .gitattributes
at the toplevel of the project to also specify attributes that
assigns other attributes. The syntax is '[attr]' followed by an
attribute name followed by a list of attribute names:
[attr] binary !diff !crlf !merge-3way
When "binary" attribute is set to a path, if the path has not
got diff/crlf/merge-3way attribute set or unset by other rules,
this rule unsets the three low-level attributes.
It is expected that the user level .gitattributes will be
expressed mostly in terms of attributes based on what the files
are, and the above sample would become like this:
(built-in attribute configuration)
[attr] binary !diff !crlf !merge-3way
* diff crlf merge-3way
(project specific .gitattributes)
proprietary.o binary
(user preference $GIT_DIR/info/attributes)
*.ps !diff
There are a few caveats.
* As described above, you can define these macros only in
$GIT_DIR/info/attributes and toplevel .gitattributes.
* There is no attempt to detect circular definition of macro
attributes, and definitions are evaluated from bottom to top
as usual to fill in other attributes that have not yet got
values. The following would work as expected:
[attr] text diff crlf
[attr] ps text !diff
*.ps ps
while this would most likely not (I haven't tried):
[attr] ps text !diff
[attr] text diff crlf
*.ps ps
* When a macro says "[attr] A B !C", saying that a path does
not have attribute A does not let you tell anything about
attributes B or C. That is, given this:
[attr] text diff crlf
[attr] ps text !diff
*.txt !ps
path hello.txt, which would match "*.txt" pattern, would have
"ps" attribute set to zero, but that does not make text
attribute of hello.txt set to false (nor diff attribute set to
true).
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-14 23:54:37 +08:00
|
|
|
}
|
|
|
|
|
2011-08-04 12:36:20 +08:00
|
|
|
/*
|
2017-01-28 10:02:02 +08:00
|
|
|
* Collect attributes for path into the array pointed to by check->all_attrs.
|
|
|
|
* If check->check_nr is non-zero, only attributes in check[] are collected.
|
|
|
|
* Otherwise all attributes are collected.
|
2011-08-04 12:36:20 +08:00
|
|
|
*/
|
2021-04-01 09:49:39 +08:00
|
|
|
static void collect_some_attrs(struct index_state *istate,
|
2023-01-14 16:30:38 +08:00
|
|
|
const struct object_id *tree_oid,
|
|
|
|
const char *path, struct attr_check *check)
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
{
|
attr: do not mark queried macros as unset
Since 60a12722ac (attr: remove maybe-real, maybe-macro from git_attr,
2017-01-27), we will always mark an attribute macro (e.g., "binary")
that is specifically queried for as "unspecified", even though listing
_all_ attributes would display it at set. E.g.:
$ echo "* binary" >.gitattributes
$ git check-attr -a file
file: binary: set
file: diff: unset
file: merge: unset
file: text: unset
$ git check-attr binary file
file: binary: unspecified
The problem stems from an incorrect conversion of the optimization from
06a604e670 (attr: avoid heavy work when we know the specified attr is
not defined, 2014-12-28). There we tried in collect_some_attrs() to
avoid even looking at the attr_stack when the user has asked for "foo"
and we know that "foo" did not ever appear in any .gitattributes file.
It used a flag "maybe_real" in each attribute struct, where "real" meant
that the attribute appeared in an actual file (we have to make this
distinction because we also create an attribute struct for any names
that are being queried). But as explained in that commit message, the
meaning of "real" was tangled with some special cases around macros.
When 60a12722ac later refactored the macro code, it dropped maybe_real
entirely. This missed the fact that "maybe_real" could be unset for two
reasons: because of a macro, or because it was never found during
parsing. This had two results:
- the optimization in collect_some_attrs() ceased doing anything
meaningful, since it no longer kept track of "was it found during
parsing"
- worse, it actually kicked in when the caller _did_ ask about a macro
by name, causing us to mark it as unspecified
It should be possible to salvage this optimization, but let's start with
just removing the remnants. It hasn't been doing anything (except
creating bugs) since 60a12722ac, and nobody seems to have noticed the
performance regression. It's more important to fix the correctness
problem clearly first.
I've added two tests here. The second one actually shows off the bug.
The test of "check-attr -a" is not strictly necessary, but we currently
do not test attribute macros much, and the builtin "binary" not at all.
So this increases our general test coverage, as well as making sure we
didn't mess up this related case.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-01-19 05:34:58 +08:00
|
|
|
int pathlen, rem, dirlen;
|
2013-03-27 01:28:07 +08:00
|
|
|
const char *cp, *last_slash = NULL;
|
|
|
|
int basename_offset;
|
2013-01-16 14:02:38 +08:00
|
|
|
|
|
|
|
for (cp = path; *cp; cp++) {
|
|
|
|
if (*cp == '/' && cp[1])
|
|
|
|
last_slash = cp;
|
|
|
|
}
|
|
|
|
pathlen = cp - path;
|
|
|
|
if (last_slash) {
|
2013-03-27 01:28:07 +08:00
|
|
|
basename_offset = last_slash + 1 - path;
|
2013-01-16 14:02:38 +08:00
|
|
|
dirlen = last_slash - path;
|
|
|
|
} else {
|
2013-03-27 01:28:07 +08:00
|
|
|
basename_offset = 0;
|
2013-01-16 14:02:38 +08:00
|
|
|
dirlen = 0;
|
|
|
|
}
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
|
2023-01-14 16:30:38 +08:00
|
|
|
prepare_attr_stack(istate, tree_oid, path, dirlen, &check->stack);
|
2017-01-28 10:02:02 +08:00
|
|
|
all_attrs_init(&g_attr_hashmap, check);
|
2017-01-28 10:02:05 +08:00
|
|
|
determine_macros(check->all_attrs, check->stack);
|
2017-01-28 10:02:02 +08:00
|
|
|
|
|
|
|
rem = check->all_attrs_nr;
|
2017-01-28 10:02:05 +08:00
|
|
|
fill(path, pathlen, basename_offset, check->stack, check->all_attrs, rem);
|
2011-08-04 12:36:20 +08:00
|
|
|
}
|
|
|
|
|
2023-05-06 12:15:29 +08:00
|
|
|
static const char *default_attr_source_tree_object_name;
|
|
|
|
|
|
|
|
void set_git_attr_source(const char *tree_object_name)
|
|
|
|
{
|
|
|
|
default_attr_source_tree_object_name = xstrdup(tree_object_name);
|
|
|
|
}
|
|
|
|
|
2024-05-07 12:53:01 +08:00
|
|
|
static int compute_default_attr_source(struct object_id *attr_source)
|
2023-05-06 12:15:29 +08:00
|
|
|
{
|
2024-05-07 12:53:01 +08:00
|
|
|
int ignore_bad_attr_tree = 0;
|
|
|
|
|
2023-05-06 12:15:29 +08:00
|
|
|
if (!default_attr_source_tree_object_name)
|
|
|
|
default_attr_source_tree_object_name = getenv(GIT_ATTR_SOURCE_ENVIRONMENT);
|
|
|
|
|
2023-10-14 01:39:30 +08:00
|
|
|
if (!default_attr_source_tree_object_name && git_attr_tree) {
|
|
|
|
default_attr_source_tree_object_name = git_attr_tree;
|
|
|
|
ignore_bad_attr_tree = 1;
|
|
|
|
}
|
|
|
|
|
2024-05-07 12:53:01 +08:00
|
|
|
if (!default_attr_source_tree_object_name)
|
|
|
|
return 0;
|
2023-05-06 12:15:29 +08:00
|
|
|
|
2024-05-07 12:53:05 +08:00
|
|
|
if (!startup_info->have_repository) {
|
|
|
|
if (!ignore_bad_attr_tree)
|
|
|
|
die(_("cannot use --attr-source or GIT_ATTR_SOURCE without repo"));
|
|
|
|
return 0;
|
|
|
|
}
|
2023-05-06 12:15:29 +08:00
|
|
|
|
2023-10-14 01:39:29 +08:00
|
|
|
if (repo_get_oid_treeish(the_repository,
|
|
|
|
default_attr_source_tree_object_name,
|
2024-05-07 12:53:01 +08:00
|
|
|
attr_source)) {
|
|
|
|
if (!ignore_bad_attr_tree)
|
|
|
|
die(_("bad --attr-source or GIT_ATTR_SOURCE"));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
2023-05-06 12:15:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct object_id *default_attr_source(void)
|
|
|
|
{
|
|
|
|
static struct object_id attr_source;
|
2024-05-07 12:53:01 +08:00
|
|
|
static int has_attr_source = -1;
|
2023-05-06 12:15:29 +08:00
|
|
|
|
2024-05-07 12:53:01 +08:00
|
|
|
if (has_attr_source < 0)
|
|
|
|
has_attr_source = compute_default_attr_source(&attr_source);
|
|
|
|
if (!has_attr_source)
|
2023-05-06 12:15:29 +08:00
|
|
|
return NULL;
|
|
|
|
return &attr_source;
|
|
|
|
}
|
|
|
|
|
2023-11-16 13:44:37 +08:00
|
|
|
static const char *interned_mode_string(unsigned int mode)
|
|
|
|
{
|
|
|
|
static struct {
|
|
|
|
unsigned int val;
|
|
|
|
char str[7];
|
|
|
|
} mode_string[] = {
|
|
|
|
{ .val = 0040000 },
|
|
|
|
{ .val = 0100644 },
|
|
|
|
{ .val = 0100755 },
|
|
|
|
{ .val = 0120000 },
|
|
|
|
{ .val = 0160000 },
|
|
|
|
};
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(mode_string); i++) {
|
|
|
|
if (mode_string[i].val != mode)
|
|
|
|
continue;
|
|
|
|
if (!*mode_string[i].str)
|
|
|
|
snprintf(mode_string[i].str, sizeof(mode_string[i].str),
|
|
|
|
"%06o", mode);
|
|
|
|
return mode_string[i].str;
|
|
|
|
}
|
|
|
|
BUG("Unsupported mode 0%o", mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *builtin_object_mode_attr(struct index_state *istate, const char *path)
|
|
|
|
{
|
|
|
|
unsigned int mode;
|
|
|
|
|
|
|
|
if (direction == GIT_ATTR_CHECKIN) {
|
|
|
|
struct object_id oid;
|
|
|
|
struct stat st;
|
|
|
|
if (lstat(path, &st))
|
|
|
|
die_errno(_("unable to stat '%s'"), path);
|
|
|
|
mode = canon_mode(st.st_mode);
|
|
|
|
if (S_ISDIR(mode)) {
|
|
|
|
/*
|
|
|
|
*`path` is either a directory or it is a submodule,
|
|
|
|
* in which case it is already indexed as submodule
|
|
|
|
* or it does not exist in the index yet and we need to
|
|
|
|
* check if we can resolve to a ref.
|
|
|
|
*/
|
|
|
|
int pos = index_name_pos(istate, path, strlen(path));
|
|
|
|
if (pos >= 0) {
|
|
|
|
if (S_ISGITLINK(istate->cache[pos]->ce_mode))
|
|
|
|
mode = istate->cache[pos]->ce_mode;
|
2024-05-17 16:18:39 +08:00
|
|
|
} else if (repo_resolve_gitlink_ref(the_repository, path,
|
|
|
|
"HEAD", &oid) == 0) {
|
2023-11-16 13:44:37 +08:00
|
|
|
mode = S_IFGITLINK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* For GIT_ATTR_CHECKOUT and GIT_ATTR_INDEX we only check
|
|
|
|
* for mode in the index.
|
|
|
|
*/
|
|
|
|
int pos = index_name_pos(istate, path, strlen(path));
|
|
|
|
if (pos >= 0)
|
|
|
|
mode = istate->cache[pos]->ce_mode;
|
|
|
|
else
|
|
|
|
return ATTR__UNSET;
|
|
|
|
}
|
|
|
|
|
|
|
|
return interned_mode_string(mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static const char *compute_builtin_attr(struct index_state *istate,
|
|
|
|
const char *path,
|
|
|
|
const struct git_attr *attr) {
|
|
|
|
static const struct git_attr *object_mode_attr;
|
|
|
|
|
|
|
|
if (!object_mode_attr)
|
|
|
|
object_mode_attr = git_attr("builtin_objectmode");
|
|
|
|
|
|
|
|
if (attr == object_mode_attr)
|
|
|
|
return builtin_object_mode_attr(istate, path);
|
|
|
|
return ATTR__UNSET;
|
|
|
|
}
|
|
|
|
|
2021-04-01 09:49:39 +08:00
|
|
|
void git_check_attr(struct index_state *istate,
|
2023-05-06 12:15:29 +08:00
|
|
|
const char *path,
|
2018-09-13 03:32:02 +08:00
|
|
|
struct attr_check *check)
|
2011-08-04 12:36:20 +08:00
|
|
|
{
|
|
|
|
int i;
|
2023-05-06 12:15:29 +08:00
|
|
|
const struct object_id *tree_oid = default_attr_source();
|
2011-08-04 12:36:20 +08:00
|
|
|
|
2023-01-14 16:30:38 +08:00
|
|
|
collect_some_attrs(istate, tree_oid, path, check);
|
attribute macro support
This adds "attribute macros" (for lack of better name). So far,
we have low-level attributes such as crlf and diff, which are
defined in operational terms --- setting or unsetting them on a
particular path directly affects what is done to the path. For
example, in order to decline diffs or crlf conversions on a
binary blob, no diffs on PostScript files, and treat all other
files normally, you would have something like these:
* diff crlf
*.ps !diff
proprietary.o !diff !crlf
That is fine as the operation goes, but gets unwieldy rather
rapidly, when we start adding more low-level attributes that are
defined in operational terms. A near-term example of such an
attribute would be 'merge-3way' which would control if git
should attempt the usual 3-way file-level merge internally, or
leave merging to a specialized external program of user's
choice. When it is added, we do _not_ want to force the users
to update the above to:
* diff crlf merge-3way
*.ps !diff
proprietary.o !diff !crlf !merge-3way
The way this patch solves this issue is to realize that the
attributes the user is assigning to paths are not defined in
terms of operations but in terms of what they are.
All of the three low-level attributes usually make sense for
most of the files that sane SCM users have git operate on (these
files are typically called "text'). Only a few cases, such as
binary blob, need exception to decline the "usual treatment
given to text files" -- and people mark them as "binary".
So this allows the $GIT_DIR/info/alternates and .gitattributes
at the toplevel of the project to also specify attributes that
assigns other attributes. The syntax is '[attr]' followed by an
attribute name followed by a list of attribute names:
[attr] binary !diff !crlf !merge-3way
When "binary" attribute is set to a path, if the path has not
got diff/crlf/merge-3way attribute set or unset by other rules,
this rule unsets the three low-level attributes.
It is expected that the user level .gitattributes will be
expressed mostly in terms of attributes based on what the files
are, and the above sample would become like this:
(built-in attribute configuration)
[attr] binary !diff !crlf !merge-3way
* diff crlf merge-3way
(project specific .gitattributes)
proprietary.o binary
(user preference $GIT_DIR/info/attributes)
*.ps !diff
There are a few caveats.
* As described above, you can define these macros only in
$GIT_DIR/info/attributes and toplevel .gitattributes.
* There is no attempt to detect circular definition of macro
attributes, and definitions are evaluated from bottom to top
as usual to fill in other attributes that have not yet got
values. The following would work as expected:
[attr] text diff crlf
[attr] ps text !diff
*.ps ps
while this would most likely not (I haven't tried):
[attr] ps text !diff
[attr] text diff crlf
*.ps ps
* When a macro says "[attr] A B !C", saying that a path does
not have attribute A does not let you tell anything about
attributes B or C. That is, given this:
[attr] text diff crlf
[attr] ps text !diff
*.txt !ps
path hello.txt, which would match "*.txt" pattern, would have
"ps" attribute set to zero, but that does not make text
attribute of hello.txt set to false (nor diff attribute set to
true).
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-14 23:54:37 +08:00
|
|
|
|
2017-01-28 10:01:59 +08:00
|
|
|
for (i = 0; i < check->nr; i++) {
|
2022-12-01 22:45:36 +08:00
|
|
|
unsigned int n = check->items[i].attr->attr_nr;
|
2017-01-28 10:02:02 +08:00
|
|
|
const char *value = check->all_attrs[n].value;
|
2007-04-17 12:33:31 +08:00
|
|
|
if (value == ATTR__UNKNOWN)
|
2023-11-16 13:44:37 +08:00
|
|
|
value = compute_builtin_attr(istate, path, check->all_attrs[n].attr);
|
2017-01-28 10:01:59 +08:00
|
|
|
check->items[i].value = value;
|
2007-04-17 12:33:31 +08:00
|
|
|
}
|
Add basic infrastructure to assign attributes to paths
This adds the basic infrastructure to assign attributes to
paths, in a way similar to what the exclusion mechanism does
based on $GIT_DIR/info/exclude and .gitignore files.
An attribute is just a simple string that does not contain any
whitespace. They can be specified in $GIT_DIR/info/attributes
file, and .gitattributes file in each directory.
Each line in these files defines a pattern matching rule.
Similar to the exclusion mechanism, a later match overrides an
earlier match in the same file, and entries from .gitattributes
file in the same directory takes precedence over the ones from
parent directories. Lines in $GIT_DIR/info/attributes file are
used as the lowest precedence default rules.
A line is either a comment (an empty line, or a line that begins
with a '#'), or a rule, which is a whitespace separated list of
tokens. The first token on the line is a shell glob pattern.
The rest are names of attributes, each of which can optionally
be prefixed with '!'. Such a line means "if a path matches this
glob, this attribute is set (or unset -- if the attribute name
is prefixed with '!'). For glob matching, the same "if the
pattern does not have a slash in it, the basename of the path is
matched with fnmatch(3) against the pattern, otherwise, the path
is matched with the pattern with FNM_PATHNAME" rule as the
exclusion mechanism is used.
This does not define what an attribute means. Tying an
attribute to various effects it has on git operation for paths
that have it will be specified separately.
Signed-off-by: Junio C Hamano <junkio@cox.net>
2007-04-12 16:07:32 +08:00
|
|
|
}
|
2009-03-14 12:24:08 +08:00
|
|
|
|
2023-05-06 12:15:29 +08:00
|
|
|
void git_all_attrs(struct index_state *istate,
|
2018-08-14 00:14:20 +08:00
|
|
|
const char *path, struct attr_check *check)
|
2011-08-04 12:36:23 +08:00
|
|
|
{
|
2017-01-31 02:06:08 +08:00
|
|
|
int i;
|
2023-05-06 12:15:29 +08:00
|
|
|
const struct object_id *tree_oid = default_attr_source();
|
2011-08-04 12:36:23 +08:00
|
|
|
|
2017-01-31 02:06:08 +08:00
|
|
|
attr_check_reset(check);
|
2023-01-14 16:30:38 +08:00
|
|
|
collect_some_attrs(istate, tree_oid, path, check);
|
2011-08-04 12:36:23 +08:00
|
|
|
|
2017-01-28 10:02:02 +08:00
|
|
|
for (i = 0; i < check->all_attrs_nr; i++) {
|
|
|
|
const char *name = check->all_attrs[i].attr->name;
|
|
|
|
const char *value = check->all_attrs[i].value;
|
2017-01-31 02:06:08 +08:00
|
|
|
struct attr_check_item *item;
|
|
|
|
if (value == ATTR__UNSET || value == ATTR__UNKNOWN)
|
|
|
|
continue;
|
|
|
|
item = attr_check_append(check, git_attr(name));
|
|
|
|
item->value = value;
|
2011-08-04 12:36:23 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-28 10:02:01 +08:00
|
|
|
void attr_start(void)
|
|
|
|
{
|
|
|
|
pthread_mutex_init(&g_attr_hashmap.mutex, NULL);
|
2017-01-28 10:02:05 +08:00
|
|
|
pthread_mutex_init(&check_vector.mutex, NULL);
|
2017-01-28 10:02:01 +08:00
|
|
|
}
|