From: Stefan Beller <hidden> Date: 2016-10-22 23:32:37
previous discussion:
http://public-inbox.org/git/20161012224109.23410-1-sbeller@google.comhttp://public-inbox.org/git/20161011002115.23312-1-sbeller@google.com/
This implements the discarded series':
jc/attr
jc/attr-more
sb/pathspec-label
sb/submodule-default-paths
* I rebase to origin master (no merge conflicts)
* I implemented the thread safe attr API in patch 27 (attr: convert to new threadsafe API)
* patch 28 (attr: keep attr stack for each check) makes it actually possible
to run in a multithreaded environment.
* I added a test for the multithreaded when it is introduced in patch 32
(pathspec: allow querying for attributes)
Thanks,
Stefan
Junio C Hamano (24):
commit.c: use strchrnul() to scan for one line
attr.c: use strchrnul() to scan for one line
attr.c: update a stale comment on "struct match_attr"
attr.c: explain the lack of attr-name syntax check in parse_attr()
attr.c: complete a sentence in a comment
attr.c: mark where #if DEBUG ends more clearly
attr.c: simplify macroexpand_one()
attr.c: tighten constness around "git_attr" structure
attr.c: plug small leak in parse_attr_line()
attr: rename function and struct related to checking attributes
attr: (re)introduce git_check_attr() and struct git_attr_check
attr: convert git_all_attrs() to use "struct git_attr_check"
attr: convert git_check_attrs() callers to use the new API
attr: retire git_check_attrs() API
attr: add counted string version of git_check_attr()
attr: add counted string version of git_attr()
attr: expose validity check for attribute names
attr.c: add push_stack() helper
attr.c: pass struct git_attr_check down the callchain
attr.c: rename a local variable check
attr.c: correct ugly hack for git_all_attrs()
attr.c: introduce empty_attr_check_elems()
attr.c: always pass check[] to collect_some_attrs()
attr.c: outline the future plans by heavily commenting
Nguyễn Thái Ngọc Duy (1):
attr: support quoting pathname patterns in C style
Stefan Beller (11):
attr: make git_check_attr_counted static
attr: convert to new threadsafe API
attr: keep attr stack for each check
Documentation: fix a typo
pathspec: move long magic parsing out of prefix_pathspec
pathspec: move prefix check out of the inner loop
pathspec: allow querying for attributes
pathspec: allow escaped query values
submodule update: add `--init-default-path` switch
clone: add --init-submodule=<pathspec> switch
completion: clone can initialize specific submodules
Documentation/config.txt | 5 +
Documentation/git-clone.txt | 23 +-
Documentation/git-submodule.txt | 17 +-
Documentation/gitattributes.txt | 10 +-
Documentation/glossary-content.txt | 20 +
Documentation/technical/api-gitattributes.txt | 117 ++++--
archive.c | 26 +-
attr.c | 530 ++++++++++++++++++--------
attr.h | 74 +++-
builtin/check-attr.c | 65 ++--
builtin/clone.c | 36 +-
builtin/pack-objects.c | 27 +-
commit.c | 3 +-
contrib/completion/git-completion.bash | 1 +
convert.c | 45 +--
dir.c | 35 ++
git-submodule.sh | 21 +-
hashmap.h | 2 +
ll-merge.c | 36 +-
pathspec.c | 225 +++++++++--
pathspec.h | 16 +
t/t0003-attributes.sh | 26 ++
t/t6134-pathspec-with-labels.sh | 180 +++++++++
t/t7400-submodule-basic.sh | 134 +++++++
userdiff.c | 21 +-
ws.c | 26 +-
26 files changed, 1313 insertions(+), 408 deletions(-)
create mode 100755 t/t6134-pathspec-with-labels.sh
--
2.10.1.508.g6572022
From: Stefan Beller <hidden> Date: 2016-10-22 23:32:51
From: Junio C Hamano <redacted>
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
attr.c | 6 ++++++
1 file changed, 6 insertions(+)
From: Stefan Beller <hidden> Date: 2016-10-22 23:32:56
From: Junio C Hamano <redacted>
When 82dce998 (attr: more matching optimizations from .gitignore,
2012-10-15) changed a pointer to a string "*pattern" into an
embedded "struct pattern" in struct match_attr, it forgot to update
the comment that describes the structure.
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
attr.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
From: Stefan Beller <hidden> Date: 2016-10-22 23:32:59
From: Junio C Hamano <redacted>
If any error is noticed after the match_attr structure is allocated,
we shouldn't just return NULL from this function.
Add a fail_return label that frees the allocated structure and
returns NULL, and consistently jump there when we want to return
NULL after cleaning up.
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
attr.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
From: Stefan Beller <hidden> Date: 2016-10-22 23:33:00
From: Junio C Hamano <redacted>
The double-loop wants to do an early return immediately when one
matching macro is found. Eliminate the extra variable 'a' used for
that purpose and rewrite the "assign the found item to 'a' to make
it non-NULL and force the loop(s) to terminate" with a direct return
from there.
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
attr.c | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
@@ -701,24 +701,21 @@ static int fill(const char *path, int pathlen, int basename_offset,staticintmacroexpand_one(intnr,intrem){structattr_stack*stk;-structmatch_attr*a=NULL;inti;if(check_all_attr[nr].value!=ATTR__TRUE||!check_all_attr[nr].attr->maybe_macro)returnrem;-for(stk=attr_stack;!a&&stk;stk=stk->prev)-for(i=stk->num_matches-1;!a&&0<=i;i--){+for(stk=attr_stack;stk;stk=stk->prev){+for(i=stk->num_matches-1;0<=i;i--){structmatch_attr*ma=stk->attrs[i];if(!ma->is_macro)continue;if(ma->u.attr->attr_nr==nr)-a=ma;+returnfill_one("expand",ma,rem);}--if(a)-rem=fill_one("expand",a,rem);+}returnrem;}
From: Stefan Beller <hidden> Date: 2016-10-22 23:33:04
From: Junio C Hamano <redacted>
The traditional API to check attributes is to prepare an N-element
array of "struct git_attr_check" and pass N and the array to the
function "git_check_attr()" as arguments.
In preparation to revamp the API to pass a single structure, in
which these N elements are held, rename the type used for these
individual array elements to "struct git_attr_check_elem" and rename
the function to "git_check_attrs()".
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
archive.c | 6 +++---
attr.c | 12 ++++++------
attr.h | 8 ++++----
builtin/check-attr.c | 19 ++++++++++---------
builtin/pack-objects.c | 6 +++---
convert.c | 12 ++++++------
ll-merge.c | 10 +++++-----
userdiff.c | 4 ++--
ws.c | 6 +++---
9 files changed, 42 insertions(+), 41 deletions(-)
@@ -665,7 +665,7 @@ static int macroexpand_one(int attr_nr, int rem);staticintfill_one(constchar*what,structmatch_attr*a,intrem){-structgit_attr_check*check=check_all_attr;+structgit_attr_check_elem*check=check_all_attr;inti;for(i=a->num_attr-1;0<rem&&0<=i;i--){
@@ -730,7 +730,7 @@ static int macroexpand_one(int nr, int rem)*collected.Otherwiseallattributesarecollected.*/staticvoidcollect_some_attrs(constchar*path,intnum,-structgit_attr_check*check)+structgit_attr_check_elem*check){structattr_stack*stk;
From: Stefan Beller <hidden> Date: 2016-10-22 23:33:05
From: Junio C Hamano <redacted>
It holds an interned string, and git_attr_name() is a way to peek
into it. Make sure the involved pointer types are pointer-to-const.
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
attr.c | 2 +-
attr.h | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
From: Stefan Beller <hidden> Date: 2016-10-22 23:33:08
From: Junio C Hamano <redacted>
This updates the other two ways the attribute check is done via an
array of "struct git_attr_check_elem" elements. These two niches
appear only in "git check-attr".
* The caller does not know offhand what attributes it wants to ask
about and cannot use git_attr_check_initl() to prepare the
git_attr_check structure.
* The caller may not know what attributes it wants to ask at all,
and instead wants to learn everything that the given path has.
Such a caller can call git_attr_check_alloc() to allocate an empty
git_attr_check, and then call git_attr_check_append() to add
attribute names one by one. A new attribute can be appended until
git_attr_check structure is "finalized", which happens when it is
used to ask for attributes for any path by calling git_check_attr()
or git_all_attrs(). A git_attr_check structure that is initialized
by git_attr_check_initl() is already finalized when it is returned.
I am not at all happy with the way git_all_attrs() API turned out to
be, but it is only to support one niche caller ("check-attr --all"),
so I'll stop here for now.
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
attr.c | 75 ++++++++++++++++++++++++++++++++++++++--------------
attr.h | 16 ++++++-----
builtin/check-attr.c | 51 ++++++++++++++++++-----------------
3 files changed, 90 insertions(+), 52 deletions(-)
@@ -724,6 +724,11 @@ static int macroexpand_one(int nr, int rem)returnrem;}+staticintattr_check_is_dynamic(conststructgit_attr_check*check)+{+return(void*)(check->check)!=(void*)(check+1);+}+/**Collectattributesforpathintothearraypointedtoby*check_all_attr.Ifnumisnon-zero,onlyattributesincheck[]are
@@ -789,32 +794,21 @@ int git_check_attrs(const char *path, int num, struct git_attr_check_elem *checkreturn0;}-intgit_all_attrs(constchar*path,int*num,structgit_attr_check_elem**check)+voidgit_all_attrs(constchar*path,structgit_attr_check*check){-inti,count,j;+inti;+git_attr_check_clear(check);collect_some_attrs(path,0,NULL);-/* Count the number of attributes that are set. */-count=0;-for(i=0;i<attr_nr;i++){-constchar*value=check_all_attr[i].value;-if(value!=ATTR__UNSET&&value!=ATTR__UNKNOWN)-++count;-}-*num=count;-ALLOC_ARRAY(*check,count);-j=0;for(i=0;i<attr_nr;i++){+constchar*name=check_all_attr[i].attr->name;constchar*value=check_all_attr[i].value;-if(value!=ATTR__UNSET&&value!=ATTR__UNKNOWN){-(*check)[j].attr=check_all_attr[i].attr;-(*check)[j].value=value;-++j;-}+if(value==ATTR__UNSET||value==ATTR__UNKNOWN)+continue;+git_attr_check_append(check,git_attr(name));+check->check[check->check_nr-1].value=value;}--return0;}voidgit_attr_set_direction(enumgit_attr_directionnew,structindex_state*istate)
@@ -849,17 +844,57 @@ struct git_attr_check *git_attr_check_initl(const char *one, ...)check=xcalloc(1,sizeof(*check)+cnt*sizeof(*(check->check)));check->check_nr=cnt;+check->finalized=1;check->check=(structgit_attr_check_elem*)(check+1);check->check[0].attr=git_attr(one);va_start(params,one);for(cnt=1;cnt<check->check_nr;cnt++){+structgit_attr*attr;param=va_arg(params,constchar*);if(!param)die("BUG: counted %d != ended at %d",check->check_nr,cnt);-check->check[cnt].attr=git_attr(param);+attr=git_attr(param);+if(!attr)+die("BUG: %s: not a valid attribute name",param);+check->check[cnt].attr=attr;}va_end(params);returncheck;}++structgit_attr_check*git_attr_check_alloc(void)+{+returnxcalloc(1,sizeof(structgit_attr_check));+}++structgit_attr_check_elem*git_attr_check_append(structgit_attr_check*check,+conststructgit_attr*attr)+{+structgit_attr_check_elem*elem;+if(check->finalized)+die("BUG: append after git_attr_check structure is finalized");+if(!attr_check_is_dynamic(check))+die("BUG: appending to a statically initialized git_attr_check");+ALLOC_GROW(check->check,check->check_nr+1,check->check_alloc);+elem=&check->check[check->check_nr++];+elem->attr=attr;+returnelem;+}++voidgit_attr_check_clear(structgit_attr_check*check)+{+if(!attr_check_is_dynamic(check))+die("BUG: clearing a statically initialized git_attr_check");+free(check->check);+check->check_nr=0;+check->check_alloc=0;+check->finalized=0;+}++voidgit_attr_check_free(structgit_attr_check*check)+{+git_attr_check_clear(check);+free(check);+}
@@ -163,24 +165,21 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)if(all_attrs){check=NULL;}else{-check=xcalloc(cnt,sizeof(*check));+check=git_attr_check_alloc();for(i=0;i<cnt;i++){-constchar*name;-structgit_attr*a;-name=argv[i];-a=git_attr(name);+structgit_attr*a=git_attr(argv[i]);if(!a)returnerror("%s: not a valid attribute name",-name);-check[i].attr=a;+argv[i]);+git_attr_check_append(check,a);}}if(stdin_paths)-check_attr_stdin_paths(prefix,cnt,check);+check_attr_stdin_paths(prefix,check);else{for(i=filei;i<argc;i++)-check_attr(prefix,cnt,check,argv[i]);+check_attr(prefix,check,argv[i]);maybe_flush_or_die(stdout,"attribute to stdout");}return0;
From: Stefan Beller <hidden> Date: 2016-10-22 23:33:11
From: Junio C Hamano <redacted>
Since nobody uses the old API, make it file-scope static, and update
the documentation to describe the new API.
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
Documentation/technical/api-gitattributes.txt | 82 ++++++++++++++++++---------
attr.c | 3 +-
attr.h | 2 -
3 files changed, 56 insertions(+), 31 deletions(-)
@@ -16,10 +16,15 @@ Data Structure of no interest to the calling programs. The name of the attribute can be retrieved by calling `git_attr_name()`.+`struct git_attr_check_elem`::++ This structure represents one attribute and its value.+ `struct git_attr_check`::- This structure represents a set of attributes to check in a call- to `git_check_attr()` function, and receives the results.+ This structure represents a collection of `git_attr_check_elem`.+ It is passed to `git_check_attr()` function, specifying the+ attributes to check, and receives their values. Attribute Values
@@ -48,49 +53,51 @@ value of the attribute for the path. Querying Specific Attributes -----------------------------* Prepare an array of `struct git_attr_check` to define the list of- attributes you would want to check. To populate this array, you would- need to define necessary attributes by calling `git_attr()` function.+* Prepare `struct git_attr_check` using git_attr_check_initl()+ function, enumerating the names of attributes whose values you are+ interested in, terminated with a NULL pointer. Alternatively, an+ empty `struct git_attr_check` can be prepared by calling+ `git_attr_check_alloc()` function and then attributes you want to+ ask about can be added to it with `git_attr_check_append()`+ function. * Call `git_check_attr()` to check the attributes for the path.-* Inspect `git_attr_check` structure to see how each of the attribute in- the array is defined for the path.+* Inspect `git_attr_check` structure to see how each of the+ attribute in the array is defined for the path. 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- we are checking two attributes). Initialize their `attr` member with- pointers to `struct git_attr` obtained by calling `git_attr()`:+. Prepare a `struct git_attr_check` with two elements (because+ we are checking two attributes): -------------static struct git_attr_check check[2];+static struct git_attr_check *check; static void setup_check(void) {- if (check[0].attr)+ if (check) return; /* already done */- check[0].attr = git_attr("crlf");- check[1].attr = git_attr("ident");+ check = git_attr_check_initl("crlf", "ident", NULL); } -------------. Call `git_check_attr()` with the prepared array of `struct git_attr_check`:+. Call `git_check_attr()` with the prepared `struct git_attr_check`: ------------ const char *path; 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->check[]`: ------------- const char *value = check[0].value;+ const char *value = check->check[0].value; if (ATTR_TRUE(value)) { 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 git_attr_check *check;+static void setup_check(const char **argv)+{+ check = git_attr_check_alloc();+ while (*argv) {+ struct git_attr *attr = git_attr(*argv);+ git_attr_check_append(check, attr);+ argv++;+ }+}+------------+ Querying All Attributes ----------------------- To get the values of all attributes associated with a file:-* Call `git_all_attrs()`, which returns an array of `git_attr_check`- structures.+* Prepare an empty `git_attr_check` structure by calling+ `git_attr_check_alloc()`.++* Call `git_all_attrs()`, which populates the `git_attr_check`+ with the attributes attached to the path.-* Iterate over the `git_attr_check` array to examine the attribute- names and values. The name of the attribute described by a- `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.)+* Iterate over the `git_attr_check.check[]` array to examine+ the attribute names and values. The name of the attribute+ described by a `git_attr_check.check[]` object can be retrieved via+ `git_attr_name(check->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.+* Free the `git_array_check` by calling `git_attr_check_free()`.
From: Stefan Beller <hidden> Date: 2016-10-22 23:33:12
From: Junio C Hamano <redacted>
A common pattern to check N attributes for many paths is to
(1) prepare an array A of N git_attr_check_elem 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 git_attr_check" that contains N, the number of
attributes being sought, and A, the array that holds N
git_attr_check_elem 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.
As an illustration of this new API, convert archive.c that asks for
export-subst and export-ignore attributes for each paths.
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
archive.c | 24 ++++++------------------
attr.c | 34 ++++++++++++++++++++++++++++++++++
attr.h | 9 +++++++++
3 files changed, 49 insertions(+), 18 deletions(-)
From: Stefan Beller <hidden> Date: 2016-10-22 23:33:15
From: Junio C Hamano <redacted>
Often a potential caller has <name, namelen> pair that
represents the name it wants to create an attribute out of.
When name[namelen] is not NUL, the caller has to xmemdupz()
only to call git_attr().
Add git_attr_counted() that takes such a counted string instead of
"const char *name".
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
attr.c | 8 ++++----
attr.h | 5 ++++-
2 files changed, 8 insertions(+), 5 deletions(-)
@@ -78,7 +78,7 @@ static int invalid_attr_name(const char *name, int namelen)return0;}-staticstructgit_attr*git_attr_internal(constchar*name,intlen)+structgit_attr*git_attr_counted(constchar*name,size_tlen){unsignedhval=hash_name(name,len);unsignedpos=hval%HASHSIZE;
@@ -109,7 +109,7 @@ static struct git_attr *git_attr_internal(const char *name, int len)structgit_attr*git_attr(constchar*name){-returngit_attr_internal(name,strlen(name));+returngit_attr_counted(name,strlen(name));}/* What does a matched pattern decide? */
@@ -8,7 +8,10 @@ struct git_attr;*Givenastring,returnthegitattributeobjectthat*correspondstoit.*/-structgit_attr*git_attr(constchar*);+externstructgit_attr*git_attr(constchar*);++/* The same, but with counted string */+externstructgit_attr*git_attr_counted(constchar*,size_t);/* Internal use */externconstchargit_attr__true[];
From: Stefan Beller <hidden> Date: 2016-10-22 23:33:18
From: Junio C Hamano <redacted>
Export attr_name_valid() function, and a helper function that
returns the message to be given when a given <name, len> pair
is not a good name for an attribute.
We could later update the message to exactly spell out what the
rules for a good attribute name are, etc.
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
attr.c | 39 +++++++++++++++++++++++++--------------
attr.h | 3 +++
2 files changed, 28 insertions(+), 14 deletions(-)
@@ -59,23 +59,38 @@ static unsigned hash_name(const char *name, int namelen)returnval;}-staticintinvalid_attr_name(constchar*name,intnamelen)+intattr_name_valid(constchar*name,size_tnamelen){/**Attributenamecannotbeginwith'-'andmustconsistof*charactersfrom[-A-Za-z0-9_.].*/if(namelen<=0||*name=='-')-return-1;+return0;while(namelen--){charch=*name++;if(!(ch=='-'||ch=='.'||ch=='_'||('0'<=ch&&ch<='9')||('a'<=ch&&ch<='z')||('A'<=ch&&ch<='Z')))-return-1;+return0;}-return0;+return1;+}++voidinvalid_attr_name_message(structstrbuf*err,constchar*name,intlen)+{+strbuf_addf(err,_("%.*s is not a valid attribute name"),+len,name);+}++staticvoidreport_invalid_attr(constchar*name,size_tlen,+constchar*src,intlineno)+{+structstrbuferr=STRBUF_INIT;+invalid_attr_name_message(&err,name,len);+fprintf(stderr,"%s: %s:%d\n",err.buf,src,lineno);+strbuf_release(&err);}structgit_attr*git_attr_counted(constchar*name,size_tlen)
@@ -13,6 +13,9 @@ extern struct git_attr *git_attr(const char *);/* The same, but with counted string */externstructgit_attr*git_attr_counted(constchar*,size_t);+externintattr_name_valid(constchar*name,size_tnamelen);+externvoidinvalid_attr_name_message(structstrbuf*,constchar*,int);+/* Internal use */externconstchargit_attr__true[];externconstchargit_attr__false[];
From: Stefan Beller <hidden> Date: 2016-10-22 23:33:20
From: Nguyễn Thái Ngọc Duy <redacted>
Full pattern must be quoted. So 'pat"t"ern attr' will give exactly
'pat"t"ern', not 'pattern'. Also clarify that leading whitespaces are
not part of the pattern and document comment syntax.
Signed-off-by: Nguyễn Thái Ngọc Duy <redacted>
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
Documentation/gitattributes.txt | 8 +++++---
attr.c | 15 +++++++++++++--
t/t0003-attributes.sh | 26 ++++++++++++++++++++++++++
3 files changed, 44 insertions(+), 5 deletions(-)
@@ -21,9 +21,11 @@ Each line in `gitattributes` file is of form: pattern attr1 attr2 ... That is, a pattern followed by an attributes list,-separated by whitespaces. When the pattern matches the-path in question, the attributes listed on the line are given to-the path.+separated by whitespaces. Leading and trailing whitespaces are+ignored. Lines that begin with '#' are ignored. Patterns+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:
From: Stefan Beller <hidden> Date: 2016-10-22 23:33:23
From: Junio C Hamano <redacted>
There are too many repetitious "I have this new attr_stack element;
push it at the top of the stack" sequence. The new helper function
push_stack() gives us a way to express what is going on at these
places, and as a side effect, halves the number of times we mention
the attr_stack global variable.
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
attr.c | 71 +++++++++++++++++++++++++++++++-----------------------------------
1 file changed, 33 insertions(+), 38 deletions(-)
From: Stefan Beller <hidden> Date: 2016-10-22 23:33:26
From: Junio C Hamano <redacted>
Throughout this series, we are trying to use "check" to name an
instance of "git_attr_check" structure; let's rename a "check" that
refers to an array whose elements are git_attr_check_elem to avoid
confusion.
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
attr.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
@@ -682,12 +682,12 @@ static int macroexpand_one(int attr_nr, int rem);staticintfill_one(constchar*what,structmatch_attr*a,intrem){-structgit_attr_check_elem*check=check_all_attr;+structgit_attr_check_elem*celem=check_all_attr;inti;for(i=a->num_attr-1;0<rem&&0<=i;i--){structgit_attr*attr=a->state[i].attr;-constchar**n=&(check[attr->attr_nr].value);+constchar**n=&(celem[attr->attr_nr].value);constchar*v=a->state[i].setto;if(*n==ATTR__UNKNOWN){
From: Stefan Beller <hidden> Date: 2016-10-22 23:33:30
From: Junio C Hamano <redacted>
Often a potential caller has <path, pathlen> pair that
represents the path it wants to ask attributes for; when
path[pathlen] is not NUL, the caller has to xmemdupz()
only to call git_check_attr().
Add git_check_attr_counted() that takes such a counted
string instead of "const char *path".
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
attr.c | 23 ++++++++++++++---------
attr.h | 1 +
2 files changed, 15 insertions(+), 9 deletions(-)
From: Stefan Beller <hidden> Date: 2016-10-22 23:33:32
From: Junio C Hamano <redacted>
The collect_some_attrs() function has an ugly hack since
06a604e6 (attr: avoid heavy work when we know the specified attr is
not defined, 2014-12-28) added an optimization that relies on the
fact that the caller knows what attributes it is interested in, so
that we can leave once we know the final answer for all the
attributes the caller asked.
git_all_attrs() that asks "what attributes are on this path?"
however does not know what attributes it is interested in, other
than the vague "we are interested in all of them", which is not a
very useful thing to say. As a way to disable this optimization
for this caller, the said commit added a code to skip it when
the caller passes a NULL for the check structure.
However, it skipped the optimization not when check is NULL, but
when the number of attributes being checked is 0, which is
unnecessarily pessimistic.
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
attr.c | 24 ++++++++----------------
1 file changed, 8 insertions(+), 16 deletions(-)
From: Stefan Beller <hidden> Date: 2016-10-22 23:33:33
From: Junio C Hamano <redacted>
One codepath needs to just empty the git_attr_check_elem array in
the git_attr_check structure, without releasing the entire resource.
Introduce a helper to do so and rewrite git_attr_check_clear() using
it.
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
attr.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
From: Stefan Beller <hidden> Date: 2016-10-22 23:33:37
From: Junio C Hamano <redacted>
The callchain that starts from git_check_attrs() down to
collect_some_attrs() used to take an array of git_attr_check_elem
as their parameters. Pass the enclosing git_attr_check instance
instead, so that they will have access to new fields we will add to
the data structure.
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
attr.c | 36 ++++++++++++++++++++++++------------
1 file changed, 24 insertions(+), 12 deletions(-)
@@ -845,7 +857,7 @@ int git_check_attr_counted(const char *path, int pathlen,structgit_attr_check*check){check->finalized=1;-returngit_check_attrs(path,pathlen,check->check_nr,check->check);+returngit_check_attrs(path,pathlen,check);}intgit_check_attr(constchar*path,structgit_attr_check*check)
From: Stefan Beller <hidden> Date: 2016-10-22 23:33:40
This revamps the API of the attr subsystem to be thread safe.
Before we had the question and its results in one struct type.
The typical usage of the API was
static struct git_attr_check *check;
if (!check)
check = git_attr_check_initl("text", NULL);
git_check_attr(path, check);
act_on(check->value[0]);
This has a couple of issues when it comes to thread safety:
* the initialization is racy in this implementation. To make it
thread safe, we need to acquire a mutex, such that only one
thread is executing the code in git_attr_check_initl.
As we do not want to introduce a mutex at each call site,
this is best done in the attr code. However to do so, we need
to have access to the `check` variable, i.e. the API has to
look like
git_attr_check_initl(struct git_attr_check*, ...);
Then one of the threads calling git_attr_check_initl will
acquire the mutex and init the `check`, while all other threads
will wait on the mutex just to realize they're late to the
party and they'll return with no work done.
* While the check for attributes to be questioned only need to
be initalized once as that part will be read only after its
initialisation, the answer may be different for each path.
Because of that we need to decouple the check and the answer,
such that each thread can obtain an answer for the path it
is currently processing.
This commit changes the API and adds locking in
git_attr_check_initl that provides the thread safety for constructing
`struct git_attr_check`.
The usage of the new API will be:
/*
* The initl call will thread-safely check whether the
* struct git_attr_check has been initialized. We only
* want to do the initialization work once, hence we do
* that work inside a thread safe environment.
*/
static struct git_attr_check *check;
git_attr_check_initl(&check, "text", NULL);
/*
* Obtain a pointer to a correctly sized result
* statically allocated on the stack; this macro:
*/
GIT_ATTR_RESULT_INIT_FOR(myresult, 1);
/* Perform the check and act on it: */
git_check_attr(path, check, myresult);
act_on(myresult->value[0]);
/*
* No need to free the check as it is static, hence doesn't leak
* memory. The result is also static, so no need to free there either.
*/
Signed-off-by: Stefan Beller <redacted>
---
Documentation/technical/api-gitattributes.txt | 91 +++++++++-------
archive.c | 14 +--
attr.c | 143 ++++++++++++++++++--------
attr.h | 71 ++++++++-----
builtin/check-attr.c | 35 ++++---
builtin/pack-objects.c | 16 +--
convert.c | 39 +++----
ll-merge.c | 25 +++--
userdiff.c | 16 +--
ws.c | 17 ++-
10 files changed, 279 insertions(+), 188 deletions(-)
@@ -16,15 +16,17 @@ Data Structure of no interest to the calling programs. The name of the attribute can be retrieved by calling `git_attr_name()`.-`struct git_attr_check_elem`::-- This structure represents one attribute and its value.- `struct git_attr_check`::- This structure represents a collection of `git_attr_check_elem`.+ This structure represents a collection of `struct git_attrs`. It is passed to `git_check_attr()` function, specifying the- attributes to check, and receives their values.+ attributes to check, and receives their values into a corresponding+ `struct git_attr_result`.++`struct git_attr_result`::++ This structure represents a collection of results to its+ corresponding `struct git_attr_check`, that has the same order. Attribute Values
@@ -32,7 +34,7 @@ Attribute Values 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-git_attr_check` records it. There are three macros to check these:+git_attr_result` records it. There are three macros to check these: `ATTR_TRUE()`::
@@ -53,19 +55,31 @@ value of the attribute for the path. Querying Specific Attributes -----------------------------* Prepare `struct git_attr_check` using git_attr_check_initl()+* Prepare a `struct git_attr_check` using `git_attr_check_initl()` function, enumerating the names of attributes whose values you are interested in, terminated with a NULL pointer. Alternatively, an- empty `struct git_attr_check` can be prepared by calling- `git_attr_check_alloc()` function and then attributes you want to- ask about can be added to it with `git_attr_check_append()`- function.--* Call `git_check_attr()` to check the attributes for the path.--* Inspect `git_attr_check` structure to see how each of the- attribute in the array is defined for the path.-+ empty `struct git_attr_check` as allocated by git_attr_check_alloc()+ can be prepared by calling `git_attr_check_alloc()` function and+ then attributes you want to ask about can be added to it with+ `git_attr_check_append()` function.+ `git_attr_check_initl()` is thread safe, i.e. you can call it+ from different threads at the same time; when check determines+ the initialzisation is still needed, the threads will use a+ single global mutex to perform the initialization just once, the+ others will wait on the the thread to actually perform the+ initialization.++* Prepare a `struct git_attr_result` using `GIT_ATTR_RESULT_INIT_FOR()`+ for the result for static allocations. When the result size is not known+ at compile time, use `git_attr_result_alloc`. The call to initialize+ the result is not thread safe, because different threads need their+ own thread local result anyway.++* Call `git_check_attr()` to check the attributes for the path,+ the given `git_attr_result` will be filled with the result.++* Inspect the returned `git_attr_result` structure to see how+ each of the attribute in the array is defined for the path. Example -------
@@ -76,28 +90,23 @@ To see how attributes "crlf" and "ident" are set for different paths. we are checking two attributes): -------------static struct git_attr_check *check;-static void setup_check(void)-{- if (check)- return; /* already done */- check = git_attr_check_initl("crlf", "ident", NULL);-}+ static struct git_attr_check *check;+ git_attr_check_initl(check, "crlf", "ident", NULL); ------------ . Call `git_check_attr()` with the prepared `struct git_attr_check`: ------------ const char *path;+ GIT_ATTR_RESULT_INIT_FOR(result, 2);- setup_check();- git_check_attr(path, check);+ git_check_attr(path, check, result); -------------. Act on `.value` member of the result, left in `check->check[]`:+. Act on `result->value[]`: ------------- const char *value = check->check[0].value;+ const char *value = result->value[0]; if (ATTR_TRUE(value)) { The attribute is Set, by listing only the name of the
@@ -123,12 +132,15 @@ the first step in the above would be different. static struct git_attr_check *check; static void setup_check(const char **argv) {+ if (check)+ return; /* already done */ check = git_attr_check_alloc(); while (*argv) { struct git_attr *attr = git_attr(*argv); git_attr_check_append(check, attr); argv++; }+ struct git_attr_result *result = git_attr_result_alloc(check); } ------------
@@ -138,17 +150,20 @@ Querying All Attributes To get the values of all attributes associated with a file:-* Prepare an empty `git_attr_check` structure by calling- `git_attr_check_alloc()`.+* Setup a local variables for the question+ `struct git_attr_check` as well as a pointer where the result+ `struct git_attr_result` will be stored.-* Call `git_all_attrs()`, which populates the `git_attr_check`- with the attributes attached to the path.+* Call `git_all_attrs()`.-* Iterate over the `git_attr_check.check[]` array to examine- the attribute names and values. The name of the attribute- described by a `git_attr_check.check[]` object can be retrieved via- `git_attr_name(check->check[i].attr)`. (Please note that no items+* Iterate over the `git_attr_check.attr[]` array to examine the+ attribute names. The name of the attribute described by a+ `git_attr_check.attr[]` object can be retrieved via+ `git_attr_name(check->attr[i])`. (Please note that no items will be returned for unset attributes, so `ATTR_UNSET()` will return false for all returned `git_array_check` objects.)+ The respective value for an attribute can be found in the same+ index position in of `git_attr_result`.-* Free the `git_array_check` by calling `git_attr_check_free()`.+* Clear the variables by calling `git_attr_check_clear()` and+ `git_attr_result_free()`.
@@ -55,6 +69,16 @@ static struct git_attr *(git_attr_hash[HASHSIZE]);*/staticintcannot_trust_maybe_real;+/*+*Sendoneormoregit_attr_checktogit_check_attrs(),and+*each'value'membertellswhatitsvalueis.+*UnsetoneisreturnedasNULL.+*/+structgit_attr_check_elem{+conststructgit_attr*attr;+constchar*value;+};+/* NEEDSWORK: This will become per git_attr_check */staticstructgit_attr_check_elem*check_all_attr;
@@ -781,7 +805,7 @@ static int macroexpand_one(int nr, int rem)staticintattr_check_is_dynamic(conststructgit_attr_check*check){-return(void*)(check->check)!=(void*)(check+1);+return(void*)(check->attr)!=(void*)(check+1);}staticvoidempty_attr_check_elems(structgit_attr_check*check)
@@ -932,29 +974,44 @@ struct git_attr_check *git_attr_check_initl(const char *one, ...)attr=git_attr(param);if(!attr)die("BUG: %s: not a valid attribute name",param);-check->check[cnt].attr=attr;+check->attr[cnt]=attr;}va_end(params);-returncheck;+*check_=check;+attr_unlock();+}++voidgit_attr_check_alloc(structgit_attr_check**check)+{+attr_lock();+if(!*check)+*check=xcalloc(1,sizeof(structgit_attr_check));++attr_unlock();}-structgit_attr_check*git_attr_check_alloc(void)+structgit_attr_result*git_attr_result_alloc(structgit_attr_check*check){-returnxcalloc(1,sizeof(structgit_attr_check));+returnxcalloc(1,sizeof(structgit_attr_result)*check->check_nr);}-structgit_attr_check_elem*git_attr_check_append(structgit_attr_check*check,-conststructgit_attr*attr)+voidgit_attr_check_append(structgit_attr_check*check,+conststructgit_attr*attr){-structgit_attr_check_elem*elem;+inti;if(check->finalized)die("BUG: append after git_attr_check structure is finalized");if(!attr_check_is_dynamic(check))die("BUG: appending to a statically initialized git_attr_check");-ALLOC_GROW(check->check,check->check_nr+1,check->check_alloc);-elem=&check->check[check->check_nr++];-elem->attr=attr;-returnelem;+attr_lock();+for(i=0;i<check->check_nr;i++)+if(check->attr[i]==attr)+break;+if(i==check->check_nr){+ALLOC_GROW(check->attr,check->check_nr+1,check->check_alloc);+check->attr[check->check_nr++]=attr;+}+attr_unlock();}voidgit_attr_check_clear(structgit_attr_check*check)
@@ -962,12 +1019,12 @@ void git_attr_check_clear(struct git_attr_check *check)empty_attr_check_elems(check);if(!attr_check_is_dynamic(check))die("BUG: clearing a statically initialized git_attr_check");-free(check->check);+free(check->attr);check->check_alloc=0;}-voidgit_attr_check_free(structgit_attr_check*check)+voidgit_attr_result_free(structgit_attr_result*result){-git_attr_check_clear(check);-free(check);+/* No need to free values as they are interned. */+free(result);}
@@ -9,10 +9,16 @@ struct git_attr;*correspondstoit.*/externstructgit_attr*git_attr(constchar*);-/* The same, but with counted string */externstructgit_attr*git_attr_counted(constchar*,size_t);+/*+*Returnthenameoftheattributerepresentedbytheargument.The+*returnvalueisapointertoanull-delimitedstringthatispart+*oftheinternaldatastructure;itshouldnotbemodifiedorfreed.+*/+externconstchar*git_attr_name(conststructgit_attr*);+externintattr_name_valid(constchar*name,size_tnamelen);externvoidinvalid_attr_name_message(structstrbuf*,constchar*,int);
@@ -25,44 +31,53 @@ extern const char git_attr__false[];#define ATTR_FALSE(v) ((v) == git_attr__false)#define ATTR_UNSET(v) ((v) == NULL)-/*-*Sendoneormoregit_attr_checktogit_check_attrs(),and-*each'value'membertellswhatitsvalueis.-*UnsetoneisreturnedasNULL.-*/-structgit_attr_check_elem{-conststructgit_attr*attr;-constchar*value;-};-structgit_attr_check{intfinalized;intcheck_nr;intcheck_alloc;-structgit_attr_check_elem*check;+conststructgit_attr**attr;+};+#define GIT_ATTR_CHECK_INIT {0, 0, 0, NULL}++structgit_attr_result{+constchar*value;};-externstructgit_attr_check*git_attr_check_initl(constchar*,...);-externintgit_check_attr(constchar*path,structgit_attr_check*);+/*+*Initializethe`git_attr_check`viaoneofthefollowingthreefunctions:+*+*git_attr_check_allocallocatesanemptycheck,+*git_attr_check_appendaddanattributetothegivengit_attr_check+*+*git_all_attrsallocatesacheckandfillsinallattributesthat+*aresetforthegivenpath.+*git_attr_check_initltakesapointertowherethecheckwillbeinitialized,+*followedbyallattributesthataretobechecked.+*Thismakesitpotentiallythreadsafeasitcould+*internallyhaveamutexforthatmemorylocation.+*Currentlyitisnotthreadsafe!+*/+externvoidgit_attr_check_alloc(structgit_attr_check**);+externstructgit_attr_result*git_attr_result_alloc(structgit_attr_check*check);-externstructgit_attr_check*git_attr_check_alloc(void);-externstructgit_attr_check_elem*git_attr_check_append(structgit_attr_check*,conststructgit_attr*);+externvoidgit_attr_check_append(structgit_attr_check*,+conststructgit_attr*);+externvoidgit_attr_check_initl(structgit_attr_check**,+constchar*,...);++externvoidgit_all_attrs(constchar*path,+structgit_attr_check*,+structgit_attr_result**);++/* Query a path for its attributes */+externintgit_check_attr(constchar*path,+structgit_attr_check*,+structgit_attr_result*result);externvoidgit_attr_check_clear(structgit_attr_check*);-externvoidgit_attr_check_free(structgit_attr_check*);-/*-*Returnthenameoftheattributerepresentedbytheargument.The-*returnvalueisapointertoanull-delimitedstringthatispart-*oftheinternaldatastructure;itshouldnotbemodifiedorfreed.-*/-externconstchar*git_attr_name(conststructgit_attr*);+externvoidgit_attr_result_free(structgit_attr_result*);-/*-*Retrieveallattributesthatapplytothespecifiedpath.-*checkholdstheattributesandtheirvalues.-*/-voidgit_all_attrs(constchar*path,structgit_attr_check*check);enumgit_attr_direction{GIT_ATTR_CHECKIN,
From: Stefan Beller <hidden> Date: 2016-10-22 23:33:44
`prefix_pathspec` is quite a lengthy function and we plan on adding more.
Split it up for better readability. As we want to add code into the
inner loop of the long magic parsing, we also benefit from lower
indentation.
Signed-off-by: Stefan Beller <redacted>
Signed-off-by: Junio C Hamano <redacted>
---
pathspec.c | 84 +++++++++++++++++++++++++++++++++++---------------------------
1 file changed, 47 insertions(+), 37 deletions(-)
@@ -88,6 +88,52 @@ static void prefix_short_magic(struct strbuf *sb, int prefixlen,strbuf_addf(sb,",prefix:%d)",prefixlen);}+staticvoideat_long_magic(structpathspec_item*item,constchar*elt,+unsigned*magic,int*pathspec_prefix,+constchar**copyfrom_,constchar**long_magic_end)+{+inti;+constchar*copyfrom=*copyfrom_;+/* longhand */+constchar*nextat;+for(copyfrom=elt+2;+*copyfrom&&*copyfrom!=')';+copyfrom=nextat){+size_tlen=strcspn(copyfrom,",)");+if(copyfrom[len]==',')+nextat=copyfrom+len+1;+else+/* handle ')' and '\0' */+nextat=copyfrom+len;+if(!len)+continue;+for(i=0;i<ARRAY_SIZE(pathspec_magic);i++){+if(strlen(pathspec_magic[i].name)==len&&+!strncmp(pathspec_magic[i].name,copyfrom,len)){+*magic|=pathspec_magic[i].bit;+break;+}+if(starts_with(copyfrom,"prefix:")){+char*endptr;+*pathspec_prefix=strtol(copyfrom+7,+&endptr,10);+if(endptr-copyfrom!=len)+die(_("invalid parameter for pathspec magic 'prefix'"));+/* "i" would be wrong, but it does not matter */+break;+}+}+if(ARRAY_SIZE(pathspec_magic)<=i)+die(_("Invalid pathspec magic '%.*s' in '%s'"),+(int)len,copyfrom,elt);+}+if(*copyfrom!=')')+die(_("Missing ')' at the end of pathspec magic in '%s'"),elt);+*long_magic_end=copyfrom;+copyfrom++;+*copyfrom_=copyfrom;+}+/**Takeanelementofapathspecandcheckformagicsignatures.*Appendtheresulttotheprefix.Returnthemagicbitmap.
From: Stefan Beller <hidden> Date: 2016-10-22 23:33:46
The prefix check is not related the check of pathspec magic; also there
is no code that is relevant after we'd break the loop on a match for
"prefix:". So move the check before the loop and shortcircuit the outer
loop.
Signed-off-by: Stefan Beller <redacted>
Signed-off-by: Junio C Hamano <redacted>
---
pathspec.c | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
@@ -107,21 +107,22 @@ static void eat_long_magic(struct pathspec_item *item, const char *elt,nextat=copyfrom+len;if(!len)continue;++if(starts_with(copyfrom,"prefix:")){+char*endptr;+*pathspec_prefix=strtol(copyfrom+7,+&endptr,10);+if(endptr-copyfrom!=len)+die(_("invalid parameter for pathspec magic 'prefix'"));+continue;+}+for(i=0;i<ARRAY_SIZE(pathspec_magic);i++){if(strlen(pathspec_magic[i].name)==len&&!strncmp(pathspec_magic[i].name,copyfrom,len)){*magic|=pathspec_magic[i].bit;break;}-if(starts_with(copyfrom,"prefix:")){-char*endptr;-*pathspec_prefix=strtol(copyfrom+7,-&endptr,10);-if(endptr-copyfrom!=len)-die(_("invalid parameter for pathspec magic 'prefix'"));-/* "i" would be wrong, but it does not matter */-break;-}}if(ARRAY_SIZE(pathspec_magic)<=i)die(_("Invalid pathspec magic '%.*s' in '%s'"),
@@ -88,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 `$(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 the name of the attribute prefixed with an exclamation point `!`.
From: Stefan Beller <hidden> Date: 2016-10-22 23:33:54
Instead of having a global attr stack, attach the stack to each check.
This allows to use the attr in a multithreaded way.
Signed-off-by: Stefan Beller <redacted>
---
attr.c | 101 +++++++++++++++++++++++++++++++++++++++-----------------------
attr.h | 4 ++-
hashmap.h | 2 ++
3 files changed, 69 insertions(+), 38 deletions(-)
@@ -372,15 +372,17 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,*.gitignorefileandinfo/excludesfileasafallback.*/-/* NEEDSWORK: This will become per git_attr_check */-staticstructattr_stack{+structattr_stack{structattr_stack*prev;char*origin;size_toriginlen;unsignednum_matches;unsignedalloc;structmatch_attr**attrs;-}*attr_stack;+};++structhashmapall_attr_stacks;+intall_attr_stacks_init;staticvoidfree_attr_elem(structattr_stack*e){
@@ -740,9 +762,10 @@ static int path_matches(const char *pathname, int pathlen,pattern,prefix,pat->patternlen,pat->flags);}-staticintmacroexpand_one(intattr_nr,intrem);+staticintmacroexpand_one(intattr_nr,intrem,structgit_attr_check*check);-staticintfill_one(constchar*what,structmatch_attr*a,intrem)+staticintfill_one(constchar*what,structmatch_attr*a,intrem,+structgit_attr_check*check){structgit_attr_check_elem*celem=check_all_attr;inti;
@@ -758,14 +781,14 @@ static int fill_one(const char *what, struct match_attr *a, int rem)attr,v);*n=v;rem--;-rem=macroexpand_one(attr->attr_nr,rem);+rem=macroexpand_one(attr->attr_nr,rem,check);}}returnrem;}staticintfill(constchar*path,intpathlen,intbasename_offset,-structattr_stack*stk,intrem)+structattr_stack*stk,intrem,structgit_attr_check*check){inti;constchar*base=stk->origin?stk->origin:"";
@@ -776,12 +799,12 @@ static int fill(const char *path, int pathlen, int basename_offset,continue;if(path_matches(path,pathlen,basename_offset,&a->u.pat,base,stk->originlen))-rem=fill_one("fill",a,rem);+rem=fill_one("fill",a,rem,check);}returnrem;}-staticintmacroexpand_one(intnr,intrem)+staticintmacroexpand_one(intnr,intrem,structgit_attr_check*check){structattr_stack*stk;inti;
@@ -790,13 +813,13 @@ static int macroexpand_one(int nr, int rem)!check_all_attr[nr].attr->maybe_macro)returnrem;-for(stk=attr_stack;stk;stk=stk->prev){+for(stk=check->attr_stack;stk;stk=stk->prev){for(i=stk->num_matches-1;0<=i;i--){structmatch_attr*ma=stk->attrs[i];if(!ma->is_macro)continue;if(ma->u.attr->attr_nr==nr)-returnfill_one("expand",ma,rem);+returnfill_one("expand",ma,rem,check);}}
@@ -39,10 +44,19 @@ struct git_attr {charname[FLEX_ARRAY];};staticintattr_nr;+staticstructgit_attr*(git_attr_hash[HASHSIZE]);++/*+*NEEDSWORK:maybe-real,maybe-macroarenotpropertyof+*anattribute,asitdependsonwhat.gitattributesare+*read.Onceweintroducepergit_attr_checkattr_stack+*andcheck_all_attr,theoptimizationbasedonthemwill+*becomeunnecessaryandcangoaway.Soisthisvariable.+*/staticintcannot_trust_maybe_real;+/* NEEDSWORK: This will become per git_attr_check */staticstructgit_attr_check_elem*check_all_attr;-staticstructgit_attr*(git_attr_hash[HASHSIZE]);constchar*git_attr_name(conststructgit_attr*attr){
@@ -329,6 +348,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,*.gitignorefileandinfo/excludesfileasafallback.*/+/* NEEDSWORK: This will become per git_attr_check */staticstructattr_stack{structattr_stack*prev;char*origin;
From: Stefan Beller <hidden> Date: 2016-10-22 23:33:58
From: Junio C Hamano <redacted>
This function used to be called with check=NULL to signal it to
collect all attributes in the global check_all_attr[] array.
Because the longer term plan is to allocate check_all_attr[] and
attr_stack data structures per git_attr_check instance (i.e. "check"
here) to make the attr subsystem thread-safe, it is unacceptable.
Pass "Are we grabbing all attributes defined in the system?" bit as
a separate argument and pass it from the callers.
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
attr.c | 37 +++++++++++++++++++------------------
1 file changed, 19 insertions(+), 18 deletions(-)
From: Stefan Beller <hidden> Date: 2016-10-22 23:34:02
The new switch passes the pathspec to `git submodule update --init`
which is called after the actual clone is done.
Additionally this configures the submodule.defaultUpdatePath to
be the given pathspec, such that any future invocation of
`git submodule update --init-default-paths` will keep up
with the pathspec.
Signed-off-by: Stefan Beller <redacted>
Signed-off-by: Junio C Hamano <redacted>
---
Documentation/git-clone.txt | 23 +++++++++----
builtin/clone.c | 36 ++++++++++++++++++--
t/t7400-submodule-basic.sh | 81 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 131 insertions(+), 9 deletions(-)
@@ -217,12 +218,20 @@ objects from the source repository into a pack in the cloned repository. --recursive:: --recurse-submodules::- After the clone is created, initialize all submodules within,- using their default settings. This is equivalent to running- `git submodule update --init --recursive` immediately after- the clone is finished. This option is ignored if the cloned- repository does not have a worktree/checkout (i.e. if any of- `--no-checkout`/`-n`, `--bare`, or `--mirror` is given)+ After the clone is created, initialize and clone all submodules+ within, using their default settings. This is equivalent to+ running `git submodule update --recursive --init `+ immediately after the clone is finished. This option is ignored+ if the cloned repository does not have a worktree/checkout (i.e.+ if any of `--no-checkout`/`-n`, `--bare`, or `--mirror` is given)++--init-submodule::+ After the clone is created, initialize and clone specified+ submodules within, using their default settings. It is possible+ to give multiple specifications by giving this argument multiple+ times. This is equivalent to configure `submodule.defaultUpdateGroup`+ and then running `git submodule update --init-default-path`+ immediately after the clone is finished. --[no-]shallow-submodules:: All submodules which are cloned will be shallow with a depth of 1.
From: Stefan Beller <hidden> Date: 2016-10-22 23:34:05
The new switch `--init-default-path` initializes the submodules which are
configured in `submodule.defaultUpdatePath` instead of those given as
command line arguments before updating. In the first implementation this
is made incompatible with further command line arguments as it is
unclear what the user means by
git submodule update --init --init-default-path <paths>
This new switch allows to record more complex patterns as it saves
retyping them whenever you invoke update.
Signed-off-by: Stefan Beller <redacted>
Signed-off-by: Junio C Hamano <redacted>
---
Documentation/config.txt | 5 ++++
Documentation/git-submodule.txt | 17 +++++++++----
git-submodule.sh | 21 +++++++++++++---
t/t7400-submodule-basic.sh | 53 +++++++++++++++++++++++++++++++++++++++++
4 files changed, 89 insertions(+), 7 deletions(-)
@@ -2886,6 +2886,11 @@ submodule.alternateErrorStrategy as computed via `submodule.alternateLocation`. Possible values are `ignore`, `info`, `die`. Default is `die`.+submodule.defaultUpdatePath::+ Specifies a set of submodules to initialize when calling+ `git submodule --init-default-group` by using the pathspec+ syntax.+ tag.forceSignAnnotated:: A boolean to specify whether annotated tags created should be GPG signed. If `--annotate` is specified on the command line, it takes
@@ -194,6 +194,10 @@ If the submodule is not yet initialized, and you just want to use the setting as stored in .gitmodules, you can automatically initialize the submodule with the `--init` option.+You can configure a set of submodules using pathspec syntax in+submodule.defaultUpdatePath you can use `--init-default-path` to initialize+those before updating.+ If `--recursive` is specified, this command will recurse into the registered submodules, and update any nested submodules within. --
@@ -361,6 +365,11 @@ the submodule itself. Initialize all submodules for which "git submodule init" has not been called so far before updating.+--init-default-path::+ This option is only valid for the update command.+ Initialize all submodules configured in "`submodule.defaultUpdatePath`"+ that have not been updated before.+ --name:: This option is only valid for the add command. It sets the submodule's name to the given string instead of defaulting to its path. The name
@@ -503,7 +503,12 @@ cmd_update()progress="--progress";;-i|--init)-init=1+test-z$init||test$init=by_args||die"$(gettext"Only one of --init or --init-default-path may be used.")"+init=by_args+;;+--init-default-path)+test-z$init||test$init=by_config||die"$(gettext"Only one of --init or --init-default-path may be used.")"+init=by_config;;--remote)remote=1
@@ -572,7 +577,17 @@ cmd_update()iftest-n"$init"then-cmd_init"--""$@"||return+iftest"$init"="by_config"+then+iftest$#-gt0+then+die"$(gettext"path arguments are incompatible with --init-default-path")"+fi+cmd_init"--"$(gitconfig--get-allsubmodule.defaultUpdatePath)||return+else+cmd_init"--""$@"||return+fi+fi{
@@ -1116,5 +1116,58 @@ test_expect_success 'submodule helper list is not confused by common prefixes' 'test_cmpexpectactual'+test_expect_success'setup superproject with submodules''+mkdirsub1&&+(+cdsub1&&+gitinit&&+test_committest&&+test_committest2+)&&+mkdirmultisuper&&+(+cdmultisuper&&+gitinit&&+gitsubmoduleadd../sub1sub0&&+gitsubmoduleadd../sub1sub1&&+gitsubmoduleadd../sub1sub2&&+gitsubmoduleadd../sub1sub3&&+gitcommit-m"add some submodules"+)+'++cat>expect<<-EOF+-sub0+sub1(test2)+sub2(test2)+sub3(test2)+EOF++test_expect_success'submodule update --init with a specification''+test_when_finished"rm -rf multisuper_clone"&&+pwd=$(pwd)&&+gitclonefile://"$pwd"/multisupermultisuper_clone&&+(+cdmultisuper_clone&&+gitsubmoduleupdate--init.":(exclude)sub0"&&+gitsubmodulestatus|cut-c1,43->../actual+)&&+test_cmpexpectactual+'++test_expect_success'submodule update --init-default-path''+test_when_finished"rm -rf multisuper_clone"&&+pwd=$(pwd)&&+gitclonefile://"$pwd"/multisupermultisuper_clone&&+(+cdmultisuper_clone&&+gitconfigsubmodule.defaultUpdatePath"."&&+gitconfig--addsubmodule.defaultUpdatePath":(exclude)sub0"&&+gitsubmoduleupdate--init-default-path&&+gitsubmodulestatus|cut-c1,43->../actual&&+test_must_failgitsubmoduleupdate--init-default-pathsub0+)&&+test_cmpexpectactual+' test_done
From: Stefan Beller <hidden> Date: 2016-10-22 23:34:08
In our own .gitattributes file we have attributes such as:
*.[ch] whitespace=indent,trail,space
When querying for attributes we want to be able to ask for the exact
value, i.e.
git ls-files :(attr:whitespace=indent,trail,space)
should work, but the commas are used in the attr magic to introduce
the next attr, such that this query currently fails with
fatal: Invalid pathspec magic 'trail' in ':(attr:whitespace=indent,trail,space)'
This change allows escaping characters by a backslash, such that the query
git ls-files :(attr:whitespace=indent\,trail\,space)
will match all path that have the value "indent,trail,space" for the
whitespace attribute. To accomplish this, we need to modify two places.
First `eat_long_magic` needs to not stop early upon seeing a comma or
closing paren that is escaped. As a second step we need to remove any
escaping from the attr value.
Helped-by: Junio C Hamano [off-list ref]
Signed-off-by: Stefan Beller <redacted>
Signed-off-by: Junio C Hamano <redacted>
---
pathspec.c | 53 +++++++++++++++++++++++++++++++++++++----
t/t6134-pathspec-with-labels.sh | 10 ++++++++
2 files changed, 58 insertions(+), 5 deletions(-)
@@ -89,12 +89,56 @@ static void prefix_short_magic(struct strbuf *sb, int prefixlen,strbuf_addf(sb,",prefix:%d)",prefixlen);}+staticsize_tstrcspn_escaped(constchar*s,constchar*stop)+{+constchar*i;++for(i=s;*i;i++){+/* skip the escaped character */+if(i[0]=='\\'&&i[1]){+i++;+continue;+}++if(strchr(stop,*i))+break;+}+returni-s;+}++staticinlineintinvalid_value_char(constcharch)+{+if(isalnum(ch)||strchr(",-_",ch))+return0;+return-1;+}++staticchar*attr_value_unescape(constchar*value)+{+constchar*src;+char*dst,*ret;++ret=xmallocz(strlen(value));+for(src=value,dst=ret;*src;src++,dst++){+if(*src=='\\'){+if(!src[1])+die(_("Escape character '\\' not allowed as "+"last character in attr value"));+src++;+}+if(invalid_value_char(*src))+die("cannot use '%c' for value matching",*src);+*dst=*src;+}+*dst='\0';+returnret;+}+staticvoidparse_pathspec_attr_match(structpathspec_item*item,constchar*value){structstring_list_item*si;structstring_listlist=STRING_LIST_INIT_DUP;-if(!value||!strlen(value))die(_("attr spec must not be empty"));
@@ -131,10 +175,9 @@ static void parse_pathspec_attr_match(struct pathspec_item *item, const char *vaif(attr[attr_len]!='=')am->match_mode=MATCH_SET;else{+constchar*v=&attr[attr_len+1];am->match_mode=MATCH_VALUE;-am->value=xstrdup(&attr[attr_len+1]);-if(strchr(am->value,'\\'))-die(_("attr spec values must not contain backslashes"));+am->value=attr_value_unescape(v);}break;}
From: Stefan Beller <hidden> Date: 2016-10-22 23:34:11
The pathspec mechanism is extended via the new
":(attr:eol=input)pattern/to/match" syntax to filter paths so that it
requires paths to not just match the given pattern but also have the
specified attrs attached for them to be chosen.
Signed-off-by: Stefan Beller <redacted>
Signed-off-by: Junio C Hamano <redacted>
---
Documentation/glossary-content.txt | 20 +++++
dir.c | 35 ++++++++
pathspec.c | 103 +++++++++++++++++++++-
pathspec.h | 16 ++++
t/t6134-pathspec-with-labels.sh | 170 +++++++++++++++++++++++++++++++++++++
5 files changed, 340 insertions(+), 4 deletions(-)
create mode 100755 t/t6134-pathspec-with-labels.sh
@@ -384,6 +384,26 @@ full pathname may have special meaning: + Glob magic is incompatible with literal magic.+attr;;+After `attr:` comes a space separated list of "attribute+requirements", all of which must be met in order for the+path to be considered a match; this is in addition to the+usual non-magic pathspec pattern matching.+++Each of the attribute requirements for the path takes one of+these forms:++- "`ATTR`" requires that the attribute `ATTR` must be set.++- "`-ATTR`" requires that the attribute `ATTR` must be unset.++- "`ATTR=VALUE`" requires that the attribute `ATTR` must be+ set to the string `VALUE`.++- "`!ATTR`" requires that the attribute `ATTR` must be+ unspecified.+++ exclude;; After a path matches any non-exclude pathspec, it will be run through all exclude pathspec (magic signature: `!`). If it
@@ -207,6 +208,37 @@ int within_depth(const char *name, int namelen,return1;}+staticintmatch_attrs(constchar*name,intnamelen,+conststructpathspec_item*item)+{+inti;+structgit_attr_result*res=git_attr_result_alloc(item->attr_check);++git_check_attr(name,item->attr_check,res);+for(i=0;i<item->attr_match_nr;i++){+constchar*value;+intmatched;+enumattr_match_modematch_mode;++value=res[i].value;+match_mode=item->attr_match[i].match_mode;++if(ATTR_TRUE(value))+matched=(match_mode==MATCH_SET);+elseif(ATTR_FALSE(value))+matched=(match_mode==MATCH_UNSET);+elseif(ATTR_UNSET(value))+matched=(match_mode==MATCH_UNSPECIFIED);+else+matched=(match_mode==MATCH_VALUE&&+!strcmp(item->attr_match[i].value,value));+if(!matched)+return0;+}++return1;+}+#define DO_MATCH_EXCLUDE 1#define DO_MATCH_DIRECTORY 2
@@ -262,6 +294,9 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix,strncmp(item->match,name-prefix,item->prefix))return0;+if(item->attr_match_nr&&!match_attrs(name,namelen,item))+return0;+/* If the match was just the prefix, we matched */if(!*match)returnMATCHED_RECURSIVELY;
@@ -88,12 +89,78 @@ static void prefix_short_magic(struct strbuf *sb, int prefixlen,strbuf_addf(sb,",prefix:%d)",prefixlen);}+staticvoidparse_pathspec_attr_match(structpathspec_item*item,constchar*value)+{+structstring_list_item*si;+structstring_listlist=STRING_LIST_INIT_DUP;+++if(!value||!strlen(value))+die(_("attr spec must not be empty"));++string_list_split(&list,value,' ',-1);+string_list_remove_empty_items(&list,0);++if(!item->attr_check)+git_attr_check_alloc(&item->attr_check);+else+die(_("Only one 'attr:' specification is allowed."));++ALLOC_GROW(item->attr_match,item->attr_match_nr+list.nr,item->attr_match_alloc);++for_each_string_list_item(si,&list){+size_tattr_len;++intj=item->attr_match_nr++;+constchar*attr=si->string;+structattr_match*am=&item->attr_match[j];++switch(*attr){+case'!':+am->match_mode=MATCH_UNSPECIFIED;+attr++;+attr_len=strlen(attr);+break;+case'-':+am->match_mode=MATCH_UNSET;+attr++;+attr_len=strlen(attr);+break;+default:+attr_len=strcspn(attr,"=");+if(attr[attr_len]!='=')+am->match_mode=MATCH_SET;+else{+am->match_mode=MATCH_VALUE;+am->value=xstrdup(&attr[attr_len+1]);+if(strchr(am->value,'\\'))+die(_("attr spec values must not contain backslashes"));+}+break;+}++am->attr=git_attr_counted(attr,attr_len);+if(!am->attr){+structstrbufsb=STRBUF_INIT;+am->match_mode=INVALID_ATTR;+invalid_attr_name_message(&sb,attr,attr_len);+die(_("invalid attribute in '%s': '%s'"),value,sb.buf);+}++git_attr_check_append(item->attr_check,am->attr);+}++string_list_clear(&list,0);+return;+}+staticvoideat_long_magic(structpathspec_item*item,constchar*elt,unsigned*magic,int*pathspec_prefix,constchar**copyfrom_,constchar**long_magic_end){inti;constchar*copyfrom=*copyfrom_;+constchar*body;/* longhand */constchar*nextat;for(copyfrom=elt+2;
@@ -447,6 +523,13 @@ void parse_pathspec(struct pathspec *pathspec,if(item[i].nowildcard_len<item[i].len)pathspec->has_wildcard=1;pathspec->magic|=item[i].magic;++if(item[i].attr_match_nr){+intj;+for(j=0;j<item[i].attr_match_nr;j++)+if(item[i].attr_match[j].match_mode==INVALID_ATTR)+die(_("attribute spec in the wrong syntax are prohibited."));+}}if(nr_exclude==n)
@@ -0,0 +1,170 @@+#!/bin/sh++test_description='test labels in pathspecs'+../test-lib.sh++test_expect_success'setup a tree''+cat<<-EOF>expect&&+fileA+fileAB+fileAC+fileB+fileBC+fileC+fileNoLabel+fileSetLabel+fileUnsetLabel+fileValue+fileWrongLabel+sub/fileA+sub/fileAB+sub/fileAC+sub/fileB+sub/fileBC+sub/fileC+sub/fileNoLabel+sub/fileSetLabel+sub/fileUnsetLabel+sub/fileValue+sub/fileWrongLabel+EOF+mkdirsub&&+whilereadpath+do+:>$path&&+gitadd$path||return1+done<expect&&+gitcommit-m"initial commit"&&+gitls-files>actual&&+test_cmpexpectactual+'++test_expect_success'pathspec with no attr''+test_must_failgitls-files":(attr:)"+'++test_expect_success'pathspec with labels and non existent .gitattributes''+gitls-files":(attr:label)">actual&&+test_must_be_emptyactual+'++test_expect_success'setup .gitattributes''+cat<<-EOF>.gitattributes&&+fileAlabelA+fileBlabelB+fileClabelC+fileABlabelAlabelB+fileAClabelAlabelC+fileBClabelBlabelC+fileUnsetLabel-label+fileSetLabellabel+fileValuelabel=foo+fileWrongLabellabel☺+EOF+gitadd.gitattributes&&+gitcommit-m"add attributes"+'++test_expect_success'check specific set attr''+cat<<-EOF>expect&&+fileSetLabel+sub/fileSetLabel+EOF+gitls-files":(attr:label)">actual&&+test_cmpexpectactual+'++test_expect_success'check specific unset attr''+cat<<-EOF>expect&&+fileUnsetLabel+sub/fileUnsetLabel+EOF+gitls-files":(attr:-label)">actual&&+test_cmpexpectactual+'++test_expect_success'check specific value attr''+cat<<-EOF>expect&&+fileValue+sub/fileValue+EOF+gitls-files":(attr:label=foo)">actual&&+test_cmpexpectactual&&+gitls-files":(attr:label=bar)">actual&&+test_must_be_emptyactual+'++test_expect_success'check unspecified attr''+cat<<-EOF>expect&&+.gitattributes+fileA+fileAB+fileAC+fileB+fileBC+fileC+fileNoLabel+fileWrongLabel+sub/fileA+sub/fileAB+sub/fileAC+sub/fileB+sub/fileBC+sub/fileC+sub/fileNoLabel+sub/fileWrongLabel+EOF+gitls-files":(attr:!label)">actual&&+test_cmpexpectactual+'++test_expect_success'check multiple unspecified attr''+cat<<-EOF>expect&&+.gitattributes+fileC+fileNoLabel+fileWrongLabel+sub/fileC+sub/fileNoLabel+sub/fileWrongLabel+EOF+gitls-files":(attr:!labelB !labelA !label)">actual&&+test_cmpexpectactual+'++test_expect_success'check label with more labels but excluded path''+cat<<-EOF>expect&&+fileAB+fileB+fileBC+EOF+gitls-files":(attr:labelB)"":(exclude)sub/">actual&&+test_cmpexpectactual+'++test_expect_success'check label excluding other labels''+cat<<-EOF>expect&&+fileAB+fileB+fileBC+sub/fileAB+sub/fileB+EOF+gitls-files":(attr:labelB)"":(exclude,attr:labelC)sub/">actual&&+test_cmpexpectactual+'++test_expect_success'checking attributes in a multithreaded process''+gitstatus":(attr:labelB)"+'++test_expect_success'abort on giving invalid label on the command line''+test_must_failgitls-files.":(attr:☺)"+'++test_expect_success'abort on asking for wrong magic''+test_must_failgitls-files.":(attr:-label=foo)"&&+test_must_failgitls-files.":(attr:!label=foo)"+'++test_done
From: Stefan Beller <hidden> Date: 2016-10-22 23:34:14
From: Junio C Hamano <redacted>
The remaining callers are all simple "I have N attributes I am
interested in. I'll ask about them with various paths one by one".
After this step, no caller to git_check_attrs() remains. After
removing it, we can extend "struct git_attr_check" struct with data
that can be used in optimizing the query for the specific N
attributes it contains.
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
builtin/pack-objects.c | 19 +++++--------------
convert.c | 18 +++++++-----------
ll-merge.c | 33 ++++++++++++++-------------------
userdiff.c | 19 ++++++++-----------
ws.c | 19 ++++++-------------
5 files changed, 40 insertions(+), 68 deletions(-)
From: Ramsay Jones <hidden> Date: 2016-10-23 15:07:52
On 23/10/16 00:32, Stefan Beller wrote:
From: Junio C Hamano <redacted>
Export attr_name_valid() function, and a helper function that
returns the message to be given when a given <name, len> pair
is not a good name for an attribute.
We could later update the message to exactly spell out what the
rules for a good attribute name are, etc.
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
The symbol 'attr_name_valid()' is not used outside of attr.c, even
by the end of this series. Do you expect this function to be used
in any future series? (The export is deliberate and it certainly
seems like it should be part of the public interface, but ...)
In contrast, the 'invalid_attr_name_message()' function is called
from code in pathspec.c, which relies on 'git_attr_counted()' to
call 'attr_name_valid()' internally to check for validity. :-D
ATB,
Ramsay Jones
From: Ramsay Jones <hidden> Date: 2016-10-23 15:10:45
On 23/10/16 00:32, Stefan Beller wrote:
quoted hunk
Instead of having a global attr stack, attach the stack to each check.
This allows to use the attr in a multithreaded way.
Signed-off-by: Stefan Beller <redacted>
---
attr.c | 101 +++++++++++++++++++++++++++++++++++++++-----------------------
attr.h | 4 ++-
hashmap.h | 2 ++
3 files changed, 69 insertions(+), 38 deletions(-)
@@ -372,15 +372,17 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,*.gitignorefileandinfo/excludesfileasafallback.*/-/* NEEDSWORK: This will become per git_attr_check */-staticstructattr_stack{+structattr_stack{structattr_stack*prev;char*origin;size_toriginlen;unsignednum_matches;unsignedalloc;structmatch_attr**attrs;-}*attr_stack;+};++structhashmapall_attr_stacks;+intall_attr_stacks_init;
Mark symbols 'all_attr_stacks' and 'all_attr_stacks_init' with
the static keyword. (ie. these are file-local symbols).
ATB,
Ramsay Jones
From: Stefan Beller <hidden> Date: 2016-10-24 21:07:16
On Sun, Oct 23, 2016 at 8:07 AM, Ramsay Jones
[off-list ref] wrote:
On 23/10/16 00:32, Stefan Beller wrote:
quoted
From: Junio C Hamano <redacted>
Export attr_name_valid() function, and a helper function that
returns the message to be given when a given <name, len> pair
is not a good name for an attribute.
We could later update the message to exactly spell out what the
rules for a good attribute name are, etc.
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
The symbol 'attr_name_valid()' is not used outside of attr.c, even
by the end of this series. Do you expect this function to be used
in any future series? (The export is deliberate and it certainly
seems like it should be part of the public interface, but ...)
In contrast, the 'invalid_attr_name_message()' function is called
from code in pathspec.c, which relies on 'git_attr_counted()' to
call 'attr_name_valid()' internally to check for validity. :-D
Yeah, I am taking over Junios patches and do not quite implement
what Junio thought I would. ;) So I guess it is a communication mismatch.
git_attr_counted is a wrapper around attr_name_valid in the way that
it either returns NULL when the attr name is invalid or it does extra work
and returns a pointer to an attr.
So I think for API completeness we'd want to keep attr_name_valid around,
as otherwise the API looks strange. But that doesn't seem like a compelling
reason, so I'll drop it from the header file and make it static.
Thanks,
Stefan
This mutex is never initialized. That may work on the system you tested,
but it is incorrect, and it does segfault on Windows. A lot.
I need *at least* something like this to make it stop crashing all over
the test suite:
-- snipsnap --
This may initialize the mutex multiple times during the first
lock (which may happen in parallel).
pthread provides static initializers. To quote the man page:
Variables of type pthread_mutex_t can also be initialized
statically, using the constants PTHREAD_MUTEX_INITIALIZER
(for fast mutexes), PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
(for recursive mutexes), and
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP (for error checking
mutexes).
Regards
Simon
--
+ Privatsphäre ist notwendig
+ Ich verwende GnuPG http://gnupg.org
+ Öffentlicher Schlüssel: 0x92FEFDB7E44C32F9
This may initialize the mutex multiple times during the first
lock (which may happen in parallel).
pthread provides static initializers. To quote the man page:
Variables of type pthread_mutex_t can also be initialized
statically, using the constants PTHREAD_MUTEX_INITIALIZER
(for fast mutexes), PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
(for recursive mutexes), and
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP (for error checking
mutexes).
I seem to recall this does not work on Windows, where the pthread
functions are thin wrappers over CRITICAL_SECTION. Other threaded code
in git does an explicit setup step before entering threaded sections.
E.g., see start_threads() in builtin/grep.c.
-Peff
(sorry if this should have been answered if I went through the series
patch by patch, I wanted to do a proper review but finally have to
admit to myself I won't, so I just skim through a single giant diff
instead)
On Sun, Oct 23, 2016 at 6:32 AM, Stefan Beller [off-list ref] wrote:
+attr;;
+After `attr:` comes a space separated list of "attribute
+requirements", all of which must be met in order for the
+path to be considered a match;
What about (attr=abc def,attr=ghi lkj)? Does it mean (abc && def) ||
(ghi && lkj), or abc && def && ghi && lkj? Or is it forbidden to have
multiple 'attr' attribute in the same pathspec?
--
Duy
On Sun, Oct 23, 2016 at 6:32 AM, Stefan Beller [off-list ref] wrote:
This revamps the API of the attr subsystem to be thread safe.
Before we had the question and its results in one struct type.
The typical usage of the API was
static struct git_attr_check *check;
if (!check)
check = git_attr_check_initl("text", NULL);
Two cents. I read the .txt first and my first thought was "is _initl a
typo, shouldn't it be jsut _init"? I know we have this 'l' variant at
least in argv-array, but there we have many ways of adding arguments.
And here it's just "initl", not "init" nor other "initX" variants,
which looks odd. I wonder if the name git_attr_check_init() would do
the job fine, since we don't have different init variants and the
naming convention is not strong enough to tell me "it's multiple
arguments ended with a NULL one" right away. If you're worried about
people forgetting NULL at the end, how about passing an array of
strings, terminated by NULL, instead?
Just thinking out loud. Maybe _initl _is_ a better name.
--
Duy
This may initialize the mutex multiple times during the first
lock (which may happen in parallel).
pthread provides static initializers. To quote the man page:
Variables of type pthread_mutex_t can also be initialized
statically, using the constants PTHREAD_MUTEX_INITIALIZER
(for fast mutexes), PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
(for recursive mutexes), and
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP (for error checking
mutexes).
I seem to recall this does not work on Windows, where the pthread
functions are thin wrappers over CRITICAL_SECTION. Other threaded code
in git does an explicit setup step before entering threaded sections.
E.g., see start_threads() in builtin/grep.c.
I wonder if we can have a similar thing as
http://stackoverflow.com/a/9490113 in compat/win32/pthread.{h.c} as it is
very convenient to not have to explicitly initialize mutexes?
From: Jeff King <hidden> Date: 2016-10-26 20:20:35
On Wed, Oct 26, 2016 at 12:51:02PM -0700, Stefan Beller wrote:
quoted
I seem to recall this does not work on Windows, where the pthread
functions are thin wrappers over CRITICAL_SECTION. Other threaded code
in git does an explicit setup step before entering threaded sections.
E.g., see start_threads() in builtin/grep.c.
I wonder if we can have a similar thing as
http://stackoverflow.com/a/9490113 in compat/win32/pthread.{h.c} as it is
very convenient to not have to explicitly initialize mutexes?
I agree it would be much more convenient and get rid of some repetitive
boilerplate code. I'll leave it to Windows folks to decide if they are
OK with that approach or not (I do not offhand know of any reason it
would not work).
-Peff
From: Jeff King <hidden> Date: 2016-10-26 20:26:59
On Wed, Oct 26, 2016 at 10:25:38PM +0200, Johannes Sixt wrote:
Am 26.10.2016 um 21:51 schrieb Stefan Beller:
quoted
it is
very convenient to not have to explicitly initialize mutexes?
Not to initialize a mutex is still wrong for pthreads.
I think Stefan was being loose with his wording. There would still be an
initializer, but it would be a constant (and in the case of pthread
emulation on Windows, would just be NULL).
-Peff
From: Johannes Sixt <hidden> Date: 2016-10-26 20:42:01
Am 26.10.2016 um 22:26 schrieb Jeff King:
On Wed, Oct 26, 2016 at 10:25:38PM +0200, Johannes Sixt wrote:
quoted
Am 26.10.2016 um 21:51 schrieb Stefan Beller:
quoted
it is
very convenient to not have to explicitly initialize mutexes?
Not to initialize a mutex is still wrong for pthreads.
I think Stefan was being loose with his wording. There would still be an
initializer, but it would be a constant (and in the case of pthread
emulation on Windows, would just be NULL).
And I was loose, too: Not to initialize a mutex with at least
PTHREAD_MUTEX_INITILIZER (if not pthread_mutex_init) is still wrong.
-- Hannes
From: Stefan Beller <hidden> Date: 2016-10-26 20:43:31
On Wed, Oct 26, 2016 at 1:26 PM, Jeff King [off-list ref] wrote:
On Wed, Oct 26, 2016 at 10:25:38PM +0200, Johannes Sixt wrote:
quoted
Am 26.10.2016 um 21:51 schrieb Stefan Beller:
quoted
it is
very convenient to not have to explicitly initialize mutexes?
Not to initialize a mutex is still wrong for pthreads.
I think Stefan was being loose with his wording. There would still be an
initializer, but it would be a constant (and in the case of pthread
emulation on Windows, would just be NULL).
Exactly, so we would do
/* as per the man page of pthread_mutexes: */
pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
int somefunction()
{
pthread_mutex_lock(&mymutex); /* threadsafely initialised on first use */
...
pthread_unlock(&mymutex);
}
and for the Windows compat we'd do
#define PTHREAD_MUTEX_INITIALIZER NULL
#define pthread_mutex_lock emulate_pthread_mutex_lock
int emulate_pthread_mutex_lock(volatile MUTEX_TYPE *mx)
{
if (*mx == NULL) /* static initializer? */
{ /* this stackoverflow magic to initialize threadsafely if not init'd */}
EnterCriticalSection(mx) /* as it currently is in compat/win32/pthread.h */
return 0;
}
From: Stefan Beller <hidden> Date: 2016-10-26 20:46:23
On Wed, Oct 26, 2016 at 1:40 PM, Johannes Sixt [off-list ref] wrote:
Am 26.10.2016 um 22:26 schrieb Jeff King:
quoted
On Wed, Oct 26, 2016 at 10:25:38PM +0200, Johannes Sixt wrote:
quoted
Am 26.10.2016 um 21:51 schrieb Stefan Beller:
quoted
it is
very convenient to not have to explicitly initialize mutexes?
Not to initialize a mutex is still wrong for pthreads.
I think Stefan was being loose with his wording. There would still be an
initializer, but it would be a constant (and in the case of pthread
emulation on Windows, would just be NULL).
And I was loose, too: Not to initialize a mutex with at least
PTHREAD_MUTEX_INITILIZER (if not pthread_mutex_init) is still wrong.
My words were wrong, I meant statically initialized instead of the need to
call a function to initialize a mutex. (For the attribute subsystem, where would
that function go? We use attrs all over the place. My current thinking would
be in git.c to initialize the Big Single Attr Lock. I feel like that
is not very well
maintainable though).
Sorry for the confusion,
Stefan
From: Stefan Beller <hidden> Date: 2016-10-26 21:21:08
From: Junio C Hamano <redacted>
Export invalid_attr_name_message() function that returns the
message to be given when a given <name, len> pair
is not a good name for an attribute.
We could later update the message to exactly spell out what the
rules for a good attribute name are, etc.
We do not need to export the validity check 'attr_name_valid()' itself
as we will learn about the validity indirectly in a later patch
via calling 'git_attr_counted()'.
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
Ramsay,
I intend to replace the previous
[PATCH 17/36] attr: expose validity check for attribute names
by this one in a reroll.
Thanks,
Stefan
attr.c | 39 +++++++++++++++++++++++++--------------
attr.h | 2 ++
2 files changed, 27 insertions(+), 14 deletions(-)
@@ -59,23 +59,38 @@ static unsigned hash_name(const char *name, int namelen)returnval;}-staticintinvalid_attr_name(constchar*name,intnamelen)+staticintattr_name_valid(constchar*name,size_tnamelen){/**Attributenamecannotbeginwith'-'andmustconsistof*charactersfrom[-A-Za-z0-9_.].*/if(namelen<=0||*name=='-')-return-1;+return0;while(namelen--){charch=*name++;if(!(ch=='-'||ch=='.'||ch=='_'||('0'<=ch&&ch<='9')||('a'<=ch&&ch<='z')||('A'<=ch&&ch<='Z')))-return-1;+return0;}-return0;+return1;+}++voidinvalid_attr_name_message(structstrbuf*err,constchar*name,intlen)+{+strbuf_addf(err,_("%.*s is not a valid attribute name"),+len,name);+}++staticvoidreport_invalid_attr(constchar*name,size_tlen,+constchar*src,intlineno)+{+structstrbuferr=STRBUF_INIT;+invalid_attr_name_message(&err,name,len);+fprintf(stderr,"%s: %s:%d\n",err.buf,src,lineno);+strbuf_release(&err);}structgit_attr*git_attr_counted(constchar*name,size_tlen)
@@ -13,6 +13,8 @@ extern struct git_attr *git_attr(const char *);/* The same, but with counted string */externstructgit_attr*git_attr_counted(constchar*,size_t);+externvoidinvalid_attr_name_message(structstrbuf*,constchar*,int);+/* Internal use */externconstchargit_attr__true[];externconstchargit_attr__false[];
From: Stefan Beller <hidden> Date: 2016-10-26 22:44:25
This revamps the API of the attr subsystem to be thread safe.
Before we had the question and its results in one struct type.
The typical usage of the API was
static struct git_attr_check *check;
if (!check)
check = git_attr_check_initl("text", NULL);
git_check_attr(path, check);
act_on(check->value[0]);
This has a couple of issues when it comes to thread safety:
* the initialization is racy in this implementation. To make it
thread safe, we need to acquire a mutex, such that only one
thread is executing the code in git_attr_check_initl.
As we do not want to introduce a mutex at each call site,
this is best done in the attr code. However to do so, we need
to have access to the `check` variable, i.e. the API has to
look like
git_attr_check_initl(struct git_attr_check*, ...);
Then one of the threads calling git_attr_check_initl will
acquire the mutex and init the `check`, while all other threads
will wait on the mutex just to realize they're late to the
party and they'll return with no work done.
* While the check for attributes to be questioned only need to
be initalized once as that part will be read only after its
initialisation, the answer may be different for each path.
Because of that we need to decouple the check and the answer,
such that each thread can obtain an answer for the path it
is currently processing.
This commit changes the API and adds locking in
git_attr_check_initl that provides the thread safety for constructing
`struct git_attr_check`.
The usage of the new API will be:
/*
* The initl call will thread-safely check whether the
* struct git_attr_check has been initialized. We only
* want to do the initialization work once, hence we do
* that work inside a thread safe environment.
*/
static struct git_attr_check *check;
git_attr_check_initl(&check, "text", NULL);
/* We're just asking for one attribute "text". */
git_attr_result myresult[1];
/* Perform the check and act on it: */
git_check_attr(path, check, myresult);
act_on(myresult[0].value);
/*
* No need to free the check as it is static, hence doesn't leak
* memory. The result is also static, so no need to free there either.
*/
Signed-off-by: Stefan Beller <redacted>
---
In the reroll of this series, I plan to use the patch as-is below:
* Documentation/commit message matches actual implementation
* initialize the attr lock statically. It was uninitialized in the first
version, which works for me on Linux, but not so in Windows; I split off
the static initialized mutexes on Windows as a separate patch, see
https://public-inbox.org/git/CAGZ79kZryb-5jGif04BtK1V9tgFj-tnahqUk+1Lb7XeecU7cMQ@mail.gmail.com
which would be a requirement for this patch.
Documentation/technical/api-gitattributes.txt | 94 ++++++++++-------
archive.c | 11 +-
attr.c | 143 ++++++++++++++++++--------
attr.h | 71 ++++++++-----
builtin/check-attr.c | 35 ++++---
builtin/pack-objects.c | 16 +--
convert.c | 40 +++----
ll-merge.c | 24 +++--
userdiff.c | 16 +--
ws.c | 8 +-
10 files changed, 280 insertions(+), 178 deletions(-)
@@ -16,15 +16,19 @@ Data Structure of no interest to the calling programs. The name of the attribute can be retrieved by calling `git_attr_name()`.-`struct git_attr_check_elem`::-- This structure represents one attribute and its value.- `struct git_attr_check`::- This structure represents a collection of `git_attr_check_elem`.+ This structure represents a collection of `struct git_attrs`. It is passed to `git_check_attr()` function, specifying the- attributes to check, and receives their values.+ attributes to check, and receives their values into a corresponding+ `struct git_attr_result`.++`struct git_attr_result`::++ This structure represents one results for a check, such that an+ array of `struct git_attr_results` corresponds to a+ `struct git_attr_check`. The answers given in that array are in+ the the same order as the check struct. Attribute Values
@@ -32,7 +36,7 @@ Attribute Values 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-git_attr_check` records it. There are three macros to check these:+git_attr_result` records it. There are three macros to check these: `ATTR_TRUE()`::
@@ -53,19 +57,32 @@ value of the attribute for the path. Querying Specific Attributes -----------------------------* Prepare `struct git_attr_check` using git_attr_check_initl()+* Prepare a `struct git_attr_check` using `git_attr_check_initl()` function, enumerating the names of attributes whose values you are interested in, terminated with a NULL pointer. Alternatively, an- empty `struct git_attr_check` can be prepared by calling- `git_attr_check_alloc()` function and then attributes you want to- ask about can be added to it with `git_attr_check_append()`- function.--* Call `git_check_attr()` to check the attributes for the path.--* Inspect `git_attr_check` structure to see how each of the- attribute in the array is defined for the path.-+ empty `struct git_attr_check` as allocated by git_attr_check_alloc()+ can be prepared by calling `git_attr_check_alloc()` function and+ then attributes you want to ask about can be added to it with+ `git_attr_check_append()` function.+ Both ways with `git_attr_check_initl()` as well as the+ alloc and append route are thread safe, i.e. you can call it+ from different threads at the same time; when check determines+ the initialzisation is still needed, the threads will use a+ single global mutex to perform the initialization just once, the+ others will wait on the the thread to actually perform the+ initialization.++* Allocate an array of `struct git_attr_result` either statically on the+ as a variable on the stack or dynamically via `git_attr_result_alloc`+ when the result size is not known at compile time. The call to initialize+ the result is not thread safe, because different threads need their+ own thread local result anyway.++* Call `git_check_attr()` to check the attributes for the path,+ the given `git_attr_result` will be filled with the result.++* Inspect each `git_attr_result` structure to see how+ each of the attribute in the array is defined for the path. Example -------
@@ -76,28 +93,23 @@ To see how attributes "crlf" and "ident" are set for different paths. we are checking two attributes): -------------static struct git_attr_check *check;-static void setup_check(void)-{- if (check)- return; /* already done */- check = git_attr_check_initl("crlf", "ident", NULL);-}+ static struct git_attr_check *check;+ git_attr_check_initl(check, "crlf", "ident", NULL); ------------ . Call `git_check_attr()` with the prepared `struct git_attr_check`: ------------ const char *path;+ struct git_attr_result result[2];- setup_check();- git_check_attr(path, check);+ git_check_attr(path, check, result); -------------. Act on `.value` member of the result, left in `check->check[]`:+. Act on `result.value[]`: ------------- const char *value = check->check[0].value;+ const char *value = result.value[0]; if (ATTR_TRUE(value)) { The attribute is Set, by listing only the name of the
@@ -123,12 +135,15 @@ the first step in the above would be different. static struct git_attr_check *check; static void setup_check(const char **argv) {+ if (check)+ return; /* already done */ check = git_attr_check_alloc(); while (*argv) { struct git_attr *attr = git_attr(*argv); git_attr_check_append(check, attr); argv++; }+ struct git_attr_result *result = git_attr_result_alloc(check); } ------------
@@ -138,17 +153,20 @@ Querying All Attributes To get the values of all attributes associated with a file:-* Prepare an empty `git_attr_check` structure by calling- `git_attr_check_alloc()`.+* Setup a local variables for the question+ `struct git_attr_check` as well as a pointer where the result+ `struct git_attr_result` will be stored.-* Call `git_all_attrs()`, which populates the `git_attr_check`- with the attributes attached to the path.+* Call `git_all_attrs()`.-* Iterate over the `git_attr_check.check[]` array to examine- the attribute names and values. The name of the attribute- described by a `git_attr_check.check[]` object can be retrieved via- `git_attr_name(check->check[i].attr)`. (Please note that no items+* Iterate over the `git_attr_check.attr[]` array to examine the+ attribute names. The name of the attribute described by a+ `git_attr_check.attr[]` object can be retrieved via+ `git_attr_name(check->attr[i])`. (Please note that no items will be returned for unset attributes, so `ATTR_UNSET()` will return false for all returned `git_array_check` objects.)+ The respective value for an attribute can be found in the same+ index position in of `git_attr_result`.-* Free the `git_array_check` by calling `git_attr_check_free()`.+* Clear the variables by calling `git_attr_check_clear()` and+ `git_attr_result_free()`.
@@ -55,6 +69,16 @@ static struct git_attr *(git_attr_hash[HASHSIZE]);*/staticintcannot_trust_maybe_real;+/*+*Sendoneormoregit_attr_checktogit_check_attrs(),and+*each'value'membertellswhatitsvalueis.+*UnsetoneisreturnedasNULL.+*/+structgit_attr_check_elem{+conststructgit_attr*attr;+constchar*value;+};+/* NEEDSWORK: This will become per git_attr_check */staticstructgit_attr_check_elem*check_all_attr;
@@ -781,7 +805,7 @@ static int macroexpand_one(int nr, int rem)staticintattr_check_is_dynamic(conststructgit_attr_check*check){-return(void*)(check->check)!=(void*)(check+1);+return(void*)(check->attr)!=(void*)(check+1);}staticvoidempty_attr_check_elems(structgit_attr_check*check)
@@ -932,29 +974,44 @@ struct git_attr_check *git_attr_check_initl(const char *one, ...)attr=git_attr(param);if(!attr)die("BUG: %s: not a valid attribute name",param);-check->check[cnt].attr=attr;+check->attr[cnt]=attr;}va_end(params);-returncheck;+*check_=check;+attr_unlock();+}++voidgit_attr_check_alloc(structgit_attr_check**check)+{+attr_lock();+if(!*check)+*check=xcalloc(1,sizeof(structgit_attr_check));++attr_unlock();}-structgit_attr_check*git_attr_check_alloc(void)+structgit_attr_result*git_attr_result_alloc(structgit_attr_check*check){-returnxcalloc(1,sizeof(structgit_attr_check));+returnxcalloc(1,sizeof(structgit_attr_result)*check->check_nr);}-structgit_attr_check_elem*git_attr_check_append(structgit_attr_check*check,-conststructgit_attr*attr)+voidgit_attr_check_append(structgit_attr_check*check,+conststructgit_attr*attr){-structgit_attr_check_elem*elem;+inti;if(check->finalized)die("BUG: append after git_attr_check structure is finalized");if(!attr_check_is_dynamic(check))die("BUG: appending to a statically initialized git_attr_check");-ALLOC_GROW(check->check,check->check_nr+1,check->check_alloc);-elem=&check->check[check->check_nr++];-elem->attr=attr;-returnelem;+attr_lock();+for(i=0;i<check->check_nr;i++)+if(check->attr[i]==attr)+break;+if(i==check->check_nr){+ALLOC_GROW(check->attr,check->check_nr+1,check->check_alloc);+check->attr[check->check_nr++]=attr;+}+attr_unlock();}voidgit_attr_check_clear(structgit_attr_check*check)
@@ -962,12 +1019,12 @@ void git_attr_check_clear(struct git_attr_check *check)empty_attr_check_elems(check);if(!attr_check_is_dynamic(check))die("BUG: clearing a statically initialized git_attr_check");-free(check->check);+free(check->attr);check->check_alloc=0;}-voidgit_attr_check_free(structgit_attr_check*check)+voidgit_attr_result_free(structgit_attr_result*result){-git_attr_check_clear(check);-free(check);+/* No need to free values as they are interned. */+free(result);}
@@ -9,10 +9,16 @@ struct git_attr;*correspondstoit.*/externstructgit_attr*git_attr(constchar*);-/* The same, but with counted string */externstructgit_attr*git_attr_counted(constchar*,size_t);+/*+*Returnthenameoftheattributerepresentedbytheargument.The+*returnvalueisapointertoanull-delimitedstringthatispart+*oftheinternaldatastructure;itshouldnotbemodifiedorfreed.+*/+externconstchar*git_attr_name(conststructgit_attr*);+externvoidinvalid_attr_name_message(structstrbuf*,constchar*,int);/* Internal use */
@@ -24,44 +30,53 @@ extern const char git_attr__false[];#define ATTR_FALSE(v) ((v) == git_attr__false)#define ATTR_UNSET(v) ((v) == NULL)-/*-*Sendoneormoregit_attr_checktogit_check_attrs(),and-*each'value'membertellswhatitsvalueis.-*UnsetoneisreturnedasNULL.-*/-structgit_attr_check_elem{-conststructgit_attr*attr;-constchar*value;-};-structgit_attr_check{intfinalized;intcheck_nr;intcheck_alloc;-structgit_attr_check_elem*check;+conststructgit_attr**attr;+};+#define GIT_ATTR_CHECK_INIT {0, 0, 0, NULL}++structgit_attr_result{+constchar*value;};-externstructgit_attr_check*git_attr_check_initl(constchar*,...);-externintgit_check_attr(constchar*path,structgit_attr_check*);+/*+*Initializethe`git_attr_check`viaoneofthefollowingthreefunctions:+*+*git_attr_check_allocallocatesanemptycheck,+*git_attr_check_appendaddanattributetothegivengit_attr_check+*+*git_all_attrsallocatesacheckandfillsinallattributesthat+*aresetforthegivenpath.+*git_attr_check_initltakesapointertowherethecheckwillbeinitialized,+*followedbyallattributesthataretobechecked.+*Thismakesitpotentiallythreadsafeasitcould+*internallyhaveamutexforthatmemorylocation.+*Currentlyitisnotthreadsafe!+*/+externvoidgit_attr_check_alloc(structgit_attr_check**);+externstructgit_attr_result*git_attr_result_alloc(structgit_attr_check*check);-externstructgit_attr_check*git_attr_check_alloc(void);-externstructgit_attr_check_elem*git_attr_check_append(structgit_attr_check*,conststructgit_attr*);+externvoidgit_attr_check_append(structgit_attr_check*,+conststructgit_attr*);+externvoidgit_attr_check_initl(structgit_attr_check**,+constchar*,...);++externvoidgit_all_attrs(constchar*path,+structgit_attr_check*,+structgit_attr_result**);++/* Query a path for its attributes */+externintgit_check_attr(constchar*path,+structgit_attr_check*,+structgit_attr_result*result);externvoidgit_attr_check_clear(structgit_attr_check*);-externvoidgit_attr_check_free(structgit_attr_check*);-/*-*Returnthenameoftheattributerepresentedbytheargument.The-*returnvalueisapointertoanull-delimitedstringthatispart-*oftheinternaldatastructure;itshouldnotbemodifiedorfreed.-*/-externconstchar*git_attr_name(conststructgit_attr*);+externvoidgit_attr_result_free(structgit_attr_result*);-/*-*Retrieveallattributesthatapplytothespecifiedpath.-*checkholdstheattributesandtheirvalues.-*/-voidgit_all_attrs(constchar*path,structgit_attr_check*check);enumgit_attr_direction{GIT_ATTR_CHECKIN,
From: Stefan Beller <hidden> Date: 2016-10-27 20:57:35
On Mon, Oct 24, 2016 at 2:07 PM, Stefan Beller [off-list ref] wrote:
On Sun, Oct 23, 2016 at 8:07 AM, Ramsay Jones
[off-list ref] wrote:
quoted
On 23/10/16 00:32, Stefan Beller wrote:
quoted
From: Junio C Hamano <redacted>
Export attr_name_valid() function, and a helper function that
returns the message to be given when a given <name, len> pair
is not a good name for an attribute.
We could later update the message to exactly spell out what the
rules for a good attribute name are, etc.
Signed-off-by: Junio C Hamano <redacted>
Signed-off-by: Stefan Beller <redacted>
---
The symbol 'attr_name_valid()' is not used outside of attr.c, even
by the end of this series. Do you expect this function to be used
in any future series? (The export is deliberate and it certainly
seems like it should be part of the public interface, but ...)
refactoring this series again, I will make use of attr_name_valid
outside of attr.c, so I'll keep this patch.
From: Stefan Beller <hidden> Date: 2016-10-27 21:32:45
On Wed, Oct 26, 2016 at 6:33 AM, Duy Nguyen [off-list ref] wrote:
(sorry if this should have been answered if I went through the series
patch by patch, I wanted to do a proper review but finally have to
admit to myself I won't, so I just skim through a single giant diff
instead)
On Sun, Oct 23, 2016 at 6:32 AM, Stefan Beller [off-list ref] wrote:
quoted
+attr;;
+After `attr:` comes a space separated list of "attribute
+requirements", all of which must be met in order for the
+path to be considered a match;
What about (attr=abc def,attr=ghi lkj)? Does it mean (abc && def) ||
(ghi && lkj), or abc && def && ghi && lkj? Or is it forbidden to have
multiple 'attr' attribute in the same pathspec?
Good point. I'll add a test for that.
Remembering the original discussion, multiple attrs
are forbidden for now as it is unclear what you want to see
as a user.
To model (abc && def) || (ghi && lkj), you would need to give
multiple pathspec items as these are naturally ORed:
git ls-files :(attr:abc def) :(attr:ghi lkj) .
(compare "git ls-files Makefile README" which gives
2 files to you, that are named respectively.)
To get "abc && def && ghi && lkj" you go with
git ls-files :(attr:abc def ghi lkj) .
as then all things included into this one attr are
ANDed. I hope the documentation is clear for one
attr.
Thanks,
Stefan