Merge branch 'bw/attr'

The gitattributes machinery is being taught to work better in a
multi-threaded environment.

* bw/attr: (27 commits)
  attr: reformat git_attr_set_direction() function
  attr: push the bare repo check into read_attr()
  attr: store attribute stack in attr_check structure
  attr: tighten const correctness with git_attr and match_attr
  attr: remove maybe-real, maybe-macro from git_attr
  attr: eliminate global check_all_attr array
  attr: use hashmap for attribute dictionary
  attr: change validity check for attribute names to use positive logic
  attr: pass struct attr_check to collect_some_attrs
  attr: retire git_check_attrs() API
  attr: convert git_check_attrs() callers to use the new API
  attr: convert git_all_attrs() to use "struct attr_check"
  attr: (re)introduce git_check_attr() and struct attr_check
  attr: rename function and struct related to checking attributes
  attr.c: outline the future plans by heavily commenting
  Documentation: fix a typo
  attr.c: add push_stack() helper
  attr: support quoting pathname patterns in C style
  attr.c: plug small leak in parse_attr_line()
  attr.c: tighten constness around "git_attr" structure
  ...
This commit is contained in:
Junio C Hamano
2017-02-27 13:57:14 -08:00
14 changed files with 830 additions and 458 deletions

View File

@ -21,9 +21,11 @@ Each line in `gitattributes` file is of form:
pattern attr1 attr2 ... pattern attr1 attr2 ...
That is, a pattern followed by an attributes list, That is, a pattern followed by an attributes list,
separated by whitespaces. When the pattern matches the separated by whitespaces. Leading and trailing whitespaces are
path in question, the attributes listed on the line are given to ignored. Lines that begin with '#' are ignored. Patterns
the path. that begin with a double quote are quoted in C style.
When the pattern matches the path in question, the attributes
listed on the line are given to the path.
Each attribute can be in one of these states for a given path: Each attribute can be in one of these states for a given path:
@ -86,7 +88,7 @@ is either not set or empty, $HOME/.config/git/attributes is used instead.
Attributes for all users on a system should be placed in the Attributes for all users on a system should be placed in the
`$(prefix)/etc/gitattributes` file. `$(prefix)/etc/gitattributes` file.
Sometimes you would need to override an setting of an attribute Sometimes you would need to override a setting of an attribute
for a path to `Unspecified` state. This can be done by listing for a path to `Unspecified` state. This can be done by listing
the name of the attribute prefixed with an exclamation point `!`. the name of the attribute prefixed with an exclamation point `!`.

View File

@ -16,10 +16,15 @@ Data Structure
of no interest to the calling programs. The name of the of no interest to the calling programs. The name of the
attribute can be retrieved by calling `git_attr_name()`. attribute can be retrieved by calling `git_attr_name()`.
`struct git_attr_check`:: `struct attr_check_item`::
This structure represents a set of attributes to check in a call This structure represents one attribute and its value.
to `git_check_attr()` function, and receives the results.
`struct attr_check`::
This structure represents a collection of `attr_check_item`.
It is passed to `git_check_attr()` function, specifying the
attributes to check, and receives their values.
Attribute Values Attribute Values
@ -27,7 +32,7 @@ Attribute Values
An attribute for a path can be in one of four states: Set, Unset, An attribute for a path can be in one of four states: Set, Unset,
Unspecified or set to a string, and `.value` member of `struct Unspecified or set to a string, and `.value` member of `struct
git_attr_check` records it. There are three macros to check these: attr_check_item` records it. There are three macros to check these:
`ATTR_TRUE()`:: `ATTR_TRUE()`::
@ -48,49 +53,51 @@ value of the attribute for the path.
Querying Specific Attributes Querying Specific Attributes
---------------------------- ----------------------------
* Prepare an array of `struct git_attr_check` to define the list of * Prepare `struct attr_check` using attr_check_initl()
attributes you would want to check. To populate this array, you would function, enumerating the names of attributes whose values you are
need to define necessary attributes by calling `git_attr()` function. interested in, terminated with a NULL pointer. Alternatively, an
empty `struct attr_check` can be prepared by calling
`attr_check_alloc()` function and then attributes you want to
ask about can be added to it with `attr_check_append()`
function.
* Call `git_check_attr()` to check the attributes for the path. * Call `git_check_attr()` to check the attributes for the path.
* Inspect `git_attr_check` structure to see how each of the attribute in * Inspect `attr_check` structure to see how each of the
the array is defined for the path. attribute in the array is defined for the path.
Example Example
------- -------
To see how attributes "crlf" and "indent" are set for different paths. To see how attributes "crlf" and "ident" are set for different paths.
. Prepare an array of `struct git_attr_check` with two elements (because . Prepare a `struct attr_check` with two elements (because
we are checking two attributes). Initialize their `attr` member with we are checking two attributes):
pointers to `struct git_attr` obtained by calling `git_attr()`:
------------ ------------
static struct git_attr_check check[2]; static struct attr_check *check;
static void setup_check(void) static void setup_check(void)
{ {
if (check[0].attr) if (check)
return; /* already done */ return; /* already done */
check[0].attr = git_attr("crlf"); check = attr_check_initl("crlf", "ident", NULL);
check[1].attr = git_attr("ident");
} }
------------ ------------
. Call `git_check_attr()` with the prepared array of `struct git_attr_check`: . Call `git_check_attr()` with the prepared `struct attr_check`:
------------ ------------
const char *path; const char *path;
setup_check(); setup_check();
git_check_attr(path, ARRAY_SIZE(check), check); git_check_attr(path, check);
------------ ------------
. Act on `.value` member of the result, left in `check[]`: . Act on `.value` member of the result, left in `check->items[]`:
------------ ------------
const char *value = check[0].value; const char *value = check->items[0].value;
if (ATTR_TRUE(value)) { if (ATTR_TRUE(value)) {
The attribute is Set, by listing only the name of the The attribute is Set, by listing only the name of the
@ -109,20 +116,39 @@ static void setup_check(void)
} }
------------ ------------
To see how attributes in argv[] are set for different paths, only
the first step in the above would be different.
------------
static struct attr_check *check;
static void setup_check(const char **argv)
{
check = attr_check_alloc();
while (*argv) {
struct git_attr *attr = git_attr(*argv);
attr_check_append(check, attr);
argv++;
}
}
------------
Querying All Attributes Querying All Attributes
----------------------- -----------------------
To get the values of all attributes associated with a file: To get the values of all attributes associated with a file:
* Call `git_all_attrs()`, which returns an array of `git_attr_check` * Prepare an empty `attr_check` structure by calling
structures. `attr_check_alloc()`.
* Iterate over the `git_attr_check` array to examine the attribute * Call `git_all_attrs()`, which populates the `attr_check`
names and values. The name of the attribute described by a with the attributes attached to the path.
`git_attr_check` object can be retrieved via
`git_attr_name(check[i].attr)`. (Please note that no items will be
returned for unset attributes, so `ATTR_UNSET()` will return false
for all returned `git_array_check` objects.)
* Free the `git_array_check` array. * Iterate over the `attr_check.items[]` array to examine
the attribute names and values. The name of the attribute
described by a `attr_check.items[]` object can be retrieved via
`git_attr_name(check->items[i].attr)`. (Please note that no items
will be returned for unset attributes, so `ATTR_UNSET()` will return
false for all returned `attr_check.items[]` objects.)
* Free the `attr_check` struct by calling `attr_check_free()`.

View File

@ -87,19 +87,6 @@ void *sha1_file_to_archive(const struct archiver_args *args,
return buffer; return buffer;
} }
static void setup_archive_check(struct git_attr_check *check)
{
static struct git_attr *attr_export_ignore;
static struct git_attr *attr_export_subst;
if (!attr_export_ignore) {
attr_export_ignore = git_attr("export-ignore");
attr_export_subst = git_attr("export-subst");
}
check[0].attr = attr_export_ignore;
check[1].attr = attr_export_subst;
}
struct directory { struct directory {
struct directory *up; struct directory *up;
struct object_id oid; struct object_id oid;
@ -120,10 +107,10 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
void *context) void *context)
{ {
static struct strbuf path = STRBUF_INIT; static struct strbuf path = STRBUF_INIT;
static struct attr_check *check;
struct archiver_context *c = context; struct archiver_context *c = context;
struct archiver_args *args = c->args; struct archiver_args *args = c->args;
write_archive_entry_fn_t write_entry = c->write_entry; write_archive_entry_fn_t write_entry = c->write_entry;
struct git_attr_check check[2];
const char *path_without_prefix; const char *path_without_prefix;
int err; int err;
@ -137,11 +124,12 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
strbuf_addch(&path, '/'); strbuf_addch(&path, '/');
path_without_prefix = path.buf + args->baselen; path_without_prefix = path.buf + args->baselen;
setup_archive_check(check); if (!check)
if (!git_check_attr(path_without_prefix, ARRAY_SIZE(check), check)) { check = attr_check_initl("export-ignore", "export-subst", NULL);
if (ATTR_TRUE(check[0].value)) if (!git_check_attr(path_without_prefix, check)) {
if (ATTR_TRUE(check->items[0].value))
return 0; return 0;
args->convert = ATTR_TRUE(check[1].value); args->convert = ATTR_TRUE(check->items[1].value);
} }
if (S_ISDIR(mode) || S_ISGITLINK(mode)) { if (S_ISDIR(mode) || S_ISGITLINK(mode)) {

852
attr.c

File diff suppressed because it is too large Load Diff

49
attr.h
View File

@ -4,11 +4,15 @@
/* An attribute is a pointer to this opaque structure */ /* An attribute is a pointer to this opaque structure */
struct git_attr; struct git_attr;
/* opaque structures used internally for attribute collection */
struct all_attrs_item;
struct attr_stack;
/* /*
* Given a string, return the gitattribute object that * Given a string, return the gitattribute object that
* corresponds to it. * corresponds to it.
*/ */
struct git_attr *git_attr(const char *); const struct git_attr *git_attr(const char *);
/* Internal use */ /* Internal use */
extern const char git_attr__true[]; extern const char git_attr__true[];
@ -20,38 +24,57 @@ extern const char git_attr__false[];
#define ATTR_UNSET(v) ((v) == NULL) #define ATTR_UNSET(v) ((v) == NULL)
/* /*
* Send one or more git_attr_check to git_check_attr(), and * Send one or more git_attr_check to git_check_attrs(), and
* each 'value' member tells what its value is. * each 'value' member tells what its value is.
* Unset one is returned as NULL. * Unset one is returned as NULL.
*/ */
struct git_attr_check { struct attr_check_item {
struct git_attr *attr; const struct git_attr *attr;
const char *value; const char *value;
}; };
struct attr_check {
int nr;
int alloc;
struct attr_check_item *items;
int all_attrs_nr;
struct all_attrs_item *all_attrs;
struct attr_stack *stack;
};
extern struct attr_check *attr_check_alloc(void);
extern struct attr_check *attr_check_initl(const char *, ...);
extern struct attr_check_item *attr_check_append(struct attr_check *check,
const struct git_attr *attr);
extern void attr_check_reset(struct attr_check *check);
extern void attr_check_clear(struct attr_check *check);
extern void attr_check_free(struct attr_check *check);
/* /*
* Return the name of the attribute represented by the argument. The * Return the name of the attribute represented by the argument. The
* return value is a pointer to a null-delimited string that is part * return value is a pointer to a null-delimited string that is part
* of the internal data structure; it should not be modified or freed. * of the internal data structure; it should not be modified or freed.
*/ */
char *git_attr_name(struct git_attr *); extern const char *git_attr_name(const struct git_attr *);
int git_check_attr(const char *path, int, struct git_attr_check *); extern int git_check_attr(const char *path, struct attr_check *check);
/* /*
* Retrieve all attributes that apply to the specified path. *num * Retrieve all attributes that apply to the specified path.
* will be set to the number of attributes on the path; **check will * check holds the attributes and their values.
* be set to point at a newly-allocated array of git_attr_check
* objects describing the attributes and their values. *check must be
* free()ed by the caller.
*/ */
int git_all_attrs(const char *path, int *num, struct git_attr_check **check); extern void git_all_attrs(const char *path, struct attr_check *check);
enum git_attr_direction { enum git_attr_direction {
GIT_ATTR_CHECKIN, GIT_ATTR_CHECKIN,
GIT_ATTR_CHECKOUT, GIT_ATTR_CHECKOUT,
GIT_ATTR_INDEX GIT_ATTR_INDEX
}; };
void git_attr_set_direction(enum git_attr_direction, struct index_state *); void git_attr_set_direction(enum git_attr_direction new_direction,
struct index_state *istate);
extern void attr_start(void);
#endif /* ATTR_H */ #endif /* ATTR_H */

View File

@ -24,12 +24,13 @@ static const struct option check_attr_options[] = {
OPT_END() OPT_END()
}; };
static void output_attr(int cnt, struct git_attr_check *check, static void output_attr(struct attr_check *check, const char *file)
const char *file)
{ {
int j; int j;
int cnt = check->nr;
for (j = 0; j < cnt; j++) { for (j = 0; j < cnt; j++) {
const char *value = check[j].value; const char *value = check->items[j].value;
if (ATTR_TRUE(value)) if (ATTR_TRUE(value))
value = "set"; value = "set";
@ -42,35 +43,38 @@ static void output_attr(int cnt, struct git_attr_check *check,
printf("%s%c" /* path */ printf("%s%c" /* path */
"%s%c" /* attrname */ "%s%c" /* attrname */
"%s%c" /* attrvalue */, "%s%c" /* attrvalue */,
file, 0, git_attr_name(check[j].attr), 0, value, 0); file, 0,
git_attr_name(check->items[j].attr), 0, value, 0);
} else { } else {
quote_c_style(file, NULL, stdout, 0); quote_c_style(file, NULL, stdout, 0);
printf(": %s: %s\n", git_attr_name(check[j].attr), value); printf(": %s: %s\n",
git_attr_name(check->items[j].attr), value);
} }
} }
} }
static void check_attr(const char *prefix, int cnt, static void check_attr(const char *prefix,
struct git_attr_check *check, const char *file) struct attr_check *check,
int collect_all,
const char *file)
{ {
char *full_path = char *full_path =
prefix_path(prefix, prefix ? strlen(prefix) : 0, file); prefix_path(prefix, prefix ? strlen(prefix) : 0, file);
if (check != NULL) {
if (git_check_attr(full_path, cnt, check)) if (collect_all) {
die("git_check_attr died"); git_all_attrs(full_path, check);
output_attr(cnt, check, file);
} else { } else {
if (git_all_attrs(full_path, &cnt, &check)) if (git_check_attr(full_path, check))
die("git_all_attrs died"); die("git_check_attr died");
output_attr(cnt, check, file);
free(check);
} }
output_attr(check, file);
free(full_path); free(full_path);
} }
static void check_attr_stdin_paths(const char *prefix, int cnt, static void check_attr_stdin_paths(const char *prefix,
struct git_attr_check *check) struct attr_check *check,
int collect_all)
{ {
struct strbuf buf = STRBUF_INIT; struct strbuf buf = STRBUF_INIT;
struct strbuf unquoted = STRBUF_INIT; struct strbuf unquoted = STRBUF_INIT;
@ -84,7 +88,7 @@ static void check_attr_stdin_paths(const char *prefix, int cnt,
die("line is badly quoted"); die("line is badly quoted");
strbuf_swap(&buf, &unquoted); strbuf_swap(&buf, &unquoted);
} }
check_attr(prefix, cnt, check, buf.buf); check_attr(prefix, check, collect_all, buf.buf);
maybe_flush_or_die(stdout, "attribute to stdout"); maybe_flush_or_die(stdout, "attribute to stdout");
} }
strbuf_release(&buf); strbuf_release(&buf);
@ -99,7 +103,7 @@ static NORETURN void error_with_usage(const char *msg)
int cmd_check_attr(int argc, const char **argv, const char *prefix) int cmd_check_attr(int argc, const char **argv, const char *prefix)
{ {
struct git_attr_check *check; struct attr_check *check;
int cnt, i, doubledash, filei; int cnt, i, doubledash, filei;
if (!is_bare_repository()) if (!is_bare_repository())
@ -159,28 +163,26 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)
error_with_usage("No file specified"); error_with_usage("No file specified");
} }
if (all_attrs) { check = attr_check_alloc();
check = NULL; if (!all_attrs) {
} else {
check = xcalloc(cnt, sizeof(*check));
for (i = 0; i < cnt; i++) { for (i = 0; i < cnt; i++) {
const char *name; const struct git_attr *a = git_attr(argv[i]);
struct git_attr *a;
name = argv[i];
a = git_attr(name);
if (!a) if (!a)
return error("%s: not a valid attribute name", return error("%s: not a valid attribute name",
name); argv[i]);
check[i].attr = a; attr_check_append(check, a);
} }
} }
if (stdin_paths) if (stdin_paths)
check_attr_stdin_paths(prefix, cnt, check); check_attr_stdin_paths(prefix, check, all_attrs);
else { else {
for (i = filei; i < argc; i++) for (i = filei; i < argc; i++)
check_attr(prefix, cnt, check, argv[i]); check_attr(prefix, check, all_attrs, argv[i]);
maybe_flush_or_die(stdout, "attribute to stdout"); maybe_flush_or_die(stdout, "attribute to stdout");
} }
attr_check_free(check);
return 0; return 0;
} }

View File

@ -894,24 +894,15 @@ static void write_pack_file(void)
written, nr_result); written, nr_result);
} }
static void setup_delta_attr_check(struct git_attr_check *check)
{
static struct git_attr *attr_delta;
if (!attr_delta)
attr_delta = git_attr("delta");
check[0].attr = attr_delta;
}
static int no_try_delta(const char *path) static int no_try_delta(const char *path)
{ {
struct git_attr_check check[1]; static struct attr_check *check;
setup_delta_attr_check(check); if (!check)
if (git_check_attr(path, ARRAY_SIZE(check), check)) check = attr_check_initl("delta", NULL);
if (git_check_attr(path, check))
return 0; return 0;
if (ATTR_FALSE(check->value)) if (ATTR_FALSE(check->items[0].value))
return 1; return 1;
return 0; return 0;
} }

View File

@ -415,8 +415,7 @@ int find_commit_subject(const char *commit_buffer, const char **subject)
p++; p++;
if (*p) { if (*p) {
p = skip_blank_lines(p + 2); p = skip_blank_lines(p + 2);
for (eol = p; *eol && *eol != '\n'; eol++) eol = strchrnul(p, '\n');
; /* do nothing */
} else } else
eol = p; eol = p;

View File

@ -1,5 +1,6 @@
#include "cache.h" #include "cache.h"
#include "exec_cmd.h" #include "exec_cmd.h"
#include "attr.h"
/* /*
* Many parts of Git have subprograms communicate via pipe, expect the * Many parts of Git have subprograms communicate via pipe, expect the
@ -33,6 +34,8 @@ int main(int argc, const char **argv)
git_setup_gettext(); git_setup_gettext();
attr_start();
git_extract_argv0_path(argv[0]); git_extract_argv0_path(argv[0]);
restore_sigpipe_to_default(); restore_sigpipe_to_default();

View File

@ -1028,7 +1028,7 @@ static int ident_to_worktree(const char *path, const char *src, size_t len,
return 1; return 1;
} }
static enum crlf_action git_path_check_crlf(struct git_attr_check *check) static enum crlf_action git_path_check_crlf(struct attr_check_item *check)
{ {
const char *value = check->value; const char *value = check->value;
@ -1045,7 +1045,7 @@ static enum crlf_action git_path_check_crlf(struct git_attr_check *check)
return CRLF_UNDEFINED; return CRLF_UNDEFINED;
} }
static enum eol git_path_check_eol(struct git_attr_check *check) static enum eol git_path_check_eol(struct attr_check_item *check)
{ {
const char *value = check->value; const char *value = check->value;
@ -1058,7 +1058,7 @@ static enum eol git_path_check_eol(struct git_attr_check *check)
return EOL_UNSET; return EOL_UNSET;
} }
static struct convert_driver *git_path_check_convert(struct git_attr_check *check) static struct convert_driver *git_path_check_convert(struct attr_check_item *check)
{ {
const char *value = check->value; const char *value = check->value;
struct convert_driver *drv; struct convert_driver *drv;
@ -1071,7 +1071,7 @@ static struct convert_driver *git_path_check_convert(struct git_attr_check *chec
return NULL; return NULL;
} }
static int git_path_check_ident(struct git_attr_check *check) static int git_path_check_ident(struct attr_check_item *check)
{ {
const char *value = check->value; const char *value = check->value;
@ -1085,24 +1085,19 @@ struct conv_attrs {
int ident; int ident;
}; };
static const char *conv_attr_name[] = {
"crlf", "ident", "filter", "eol", "text",
};
#define NUM_CONV_ATTRS ARRAY_SIZE(conv_attr_name)
static void convert_attrs(struct conv_attrs *ca, const char *path) static void convert_attrs(struct conv_attrs *ca, const char *path)
{ {
int i; static struct attr_check *check;
static struct git_attr_check ccheck[NUM_CONV_ATTRS];
if (!ccheck[0].attr) { if (!check) {
for (i = 0; i < NUM_CONV_ATTRS; i++) check = attr_check_initl("crlf", "ident", "filter",
ccheck[i].attr = git_attr(conv_attr_name[i]); "eol", "text", NULL);
user_convert_tail = &user_convert; user_convert_tail = &user_convert;
git_config(read_convert_config, NULL); git_config(read_convert_config, NULL);
} }
if (!git_check_attr(path, NUM_CONV_ATTRS, ccheck)) { if (!git_check_attr(path, check)) {
struct attr_check_item *ccheck = check->items;
ca->crlf_action = git_path_check_crlf(ccheck + 4); ca->crlf_action = git_path_check_crlf(ccheck + 4);
if (ca->crlf_action == CRLF_UNDEFINED) if (ca->crlf_action == CRLF_UNDEFINED)
ca->crlf_action = git_path_check_crlf(ccheck + 0); ca->crlf_action = git_path_check_crlf(ccheck + 0);

View File

@ -336,15 +336,6 @@ static const struct ll_merge_driver *find_ll_merge_driver(const char *merge_attr
return &ll_merge_drv[LL_TEXT_MERGE]; return &ll_merge_drv[LL_TEXT_MERGE];
} }
static int git_path_check_merge(const char *path, struct git_attr_check check[2])
{
if (!check[0].attr) {
check[0].attr = git_attr("merge");
check[1].attr = git_attr("conflict-marker-size");
}
return git_check_attr(path, 2, check);
}
static void normalize_file(mmfile_t *mm, const char *path) static void normalize_file(mmfile_t *mm, const char *path)
{ {
struct strbuf strbuf = STRBUF_INIT; struct strbuf strbuf = STRBUF_INIT;
@ -362,7 +353,7 @@ int ll_merge(mmbuffer_t *result_buf,
mmfile_t *theirs, const char *their_label, mmfile_t *theirs, const char *their_label,
const struct ll_merge_options *opts) const struct ll_merge_options *opts)
{ {
static struct git_attr_check check[2]; static struct attr_check *check;
static const struct ll_merge_options default_opts; static const struct ll_merge_options default_opts;
const char *ll_driver_name = NULL; const char *ll_driver_name = NULL;
int marker_size = DEFAULT_CONFLICT_MARKER_SIZE; int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
@ -376,10 +367,14 @@ int ll_merge(mmbuffer_t *result_buf,
normalize_file(ours, path); normalize_file(ours, path);
normalize_file(theirs, path); normalize_file(theirs, path);
} }
if (!git_path_check_merge(path, check)) {
ll_driver_name = check[0].value; if (!check)
if (check[1].value) { check = attr_check_initl("merge", "conflict-marker-size", NULL);
marker_size = atoi(check[1].value);
if (!git_check_attr(path, check)) {
ll_driver_name = check->items[0].value;
if (check->items[1].value) {
marker_size = atoi(check->items[1].value);
if (marker_size <= 0) if (marker_size <= 0)
marker_size = DEFAULT_CONFLICT_MARKER_SIZE; marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
} }
@ -398,13 +393,13 @@ int ll_merge(mmbuffer_t *result_buf,
int ll_merge_marker_size(const char *path) int ll_merge_marker_size(const char *path)
{ {
static struct git_attr_check check; static struct attr_check *check;
int marker_size = DEFAULT_CONFLICT_MARKER_SIZE; int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
if (!check.attr) if (!check)
check.attr = git_attr("conflict-marker-size"); check = attr_check_initl("conflict-marker-size", NULL);
if (!git_check_attr(path, 1, &check) && check.value) { if (!git_check_attr(path, check) && check->items[0].value) {
marker_size = atoi(check.value); marker_size = atoi(check->items[0].value);
if (marker_size <= 0) if (marker_size <= 0)
marker_size = DEFAULT_CONFLICT_MARKER_SIZE; marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
} }

View File

@ -13,10 +13,31 @@ attr_check () {
test_line_count = 0 err test_line_count = 0 err
} }
attr_check_quote () {
path="$1"
quoted_path="$2"
expect="$3"
git check-attr test -- "$path" >actual &&
echo "\"$quoted_path\": test: $expect" >expect &&
test_cmp expect actual
}
test_expect_success 'open-quoted pathname' '
echo "\"a test=a" >.gitattributes &&
test_must_fail attr_check a a
'
test_expect_success 'setup' ' test_expect_success 'setup' '
mkdir -p a/b/d a/c b && mkdir -p a/b/d a/c b &&
( (
echo "[attr]notest !test" echo "[attr]notest !test"
echo "\" d \" test=d"
echo " e test=e"
echo " e\" test=e"
echo "f test=f" echo "f test=f"
echo "a/i test=a/i" echo "a/i test=a/i"
echo "onoff test -test" echo "onoff test -test"
@ -69,6 +90,11 @@ test_expect_success 'command line checks' '
' '
test_expect_success 'attribute test' ' test_expect_success 'attribute test' '
attr_check " d " d &&
attr_check e e &&
attr_check_quote e\" e\\\" e &&
attr_check f f && attr_check f f &&
attr_check a/f f && attr_check a/f f &&
attr_check a/c/f f && attr_check a/c/f f &&

View File

@ -262,25 +262,22 @@ struct userdiff_driver *userdiff_find_by_name(const char *name) {
struct userdiff_driver *userdiff_find_by_path(const char *path) struct userdiff_driver *userdiff_find_by_path(const char *path)
{ {
static struct git_attr *attr; static struct attr_check *check;
struct git_attr_check check;
if (!attr)
attr = git_attr("diff");
check.attr = attr;
if (!check)
check = attr_check_initl("diff", NULL);
if (!path) if (!path)
return NULL; return NULL;
if (git_check_attr(path, 1, &check)) if (git_check_attr(path, check))
return NULL; return NULL;
if (ATTR_TRUE(check.value)) if (ATTR_TRUE(check->items[0].value))
return &driver_true; return &driver_true;
if (ATTR_FALSE(check.value)) if (ATTR_FALSE(check->items[0].value))
return &driver_false; return &driver_false;
if (ATTR_UNSET(check.value)) if (ATTR_UNSET(check->items[0].value))
return NULL; return NULL;
return userdiff_find_by_name(check.value); return userdiff_find_by_name(check->items[0].value);
} }
struct userdiff_driver *userdiff_get_textconv(struct userdiff_driver *driver) struct userdiff_driver *userdiff_get_textconv(struct userdiff_driver *driver)

19
ws.c
View File

@ -71,24 +71,17 @@ unsigned parse_whitespace_rule(const char *string)
return rule; return rule;
} }
static void setup_whitespace_attr_check(struct git_attr_check *check)
{
static struct git_attr *attr_whitespace;
if (!attr_whitespace)
attr_whitespace = git_attr("whitespace");
check[0].attr = attr_whitespace;
}
unsigned whitespace_rule(const char *pathname) unsigned whitespace_rule(const char *pathname)
{ {
struct git_attr_check attr_whitespace_rule; static struct attr_check *attr_whitespace_rule;
setup_whitespace_attr_check(&attr_whitespace_rule); if (!attr_whitespace_rule)
if (!git_check_attr(pathname, 1, &attr_whitespace_rule)) { attr_whitespace_rule = attr_check_initl("whitespace", NULL);
if (!git_check_attr(pathname, attr_whitespace_rule)) {
const char *value; const char *value;
value = attr_whitespace_rule.value; value = attr_whitespace_rule->items[0].value;
if (ATTR_TRUE(value)) { if (ATTR_TRUE(value)) {
/* true (whitespace) */ /* true (whitespace) */
unsigned all_rule = ws_tab_width(whitespace_rule_cfg); unsigned all_rule = ws_tab_width(whitespace_rule_cfg);