From: Jonathan Tan <hidden> Date: 2021-08-10 18:29:12
This patch series removes the need to add submodule ODBs as alternates
in all codepaths covered by t7814. I believe that that is also all the
codepaths covered by "git grep", but if it isn't, the uncovered
codepaths will still work - they will just not benefit from the
performance improvements.
In doing this work of migrating away from adding submodule ODBs as
alternates, I'm mainly motivated by the possibility of adding partial
clone submodule support, but this has benefits even for those who do not
use partial clones, as described in the documentation in patch 1.
To reviewers: you can cherry-pick the last patch onto one of the earlier
ones to observe what happens when the code still accesses a submodule
object as if it were in the_repository.
Jonathan Tan (7):
submodule: lazily add submodule ODBs as alternates
grep: use submodule-ODB-as-alternate lazy-addition
grep: typesafe versions of grep_source_init
grep: read submodule entry with explicit repo
grep: allocate subrepos on heap
grep: add repository to OID grep sources
t7814: show lack of alternate ODB-adding
builtin/grep.c | 49 +++++++++++++++++++++---------
grep.c | 48 ++++++++++++++++++-----------
grep.h | 10 ++++--
object-file.c | 5 +++
submodule.c | 25 ++++++++++++++-
submodule.h | 8 +++++
t/README | 10 ++++++
t/t7814-grep-recurse-submodules.sh | 3 ++
8 files changed, 122 insertions(+), 36 deletions(-)
--
2.33.0.rc1.237.g0d66db33f3-goog
From: Jonathan Tan <hidden> Date: 2021-08-10 18:29:07
Replace an existing parse_object_or_die() call (which implicitly works
on the_repository) with a function call that allows a repository to be
passed in. There is no such direct equivalent to parse_object_or_die(),
but we only need the type of the object, so replace with
oid_object_info().
Signed-off-by: Jonathan Tan <redacted>
---
builtin/grep.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
@@ -457,27 +457,27 @@ static int grep_submodule(struct grep_opt *opt,subopt.repo=&subrepo;if(oid){-structobject*object;+enumobject_typeobject_type;structtree_desctree;void*data;unsignedlongsize;structstrbufbase=STRBUF_INIT;obj_read_lock();-object=parse_object_or_die(oid,NULL);+object_type=oid_object_info(&subrepo,oid,NULL);obj_read_unlock();data=read_object_with_reference(&subrepo,-&object->oid,tree_type,+oid,tree_type,&size,NULL);if(!data)-die(_("unable to read tree (%s)"),oid_to_hex(&object->oid));+die(_("unable to read tree (%s)"),oid_to_hex(oid));strbuf_addstr(&base,filename);strbuf_addch(&base,'/');init_tree_desc(&tree,data,size);hit=grep_tree(&subopt,pathspec,&tree,&base,base.len,-object->type==OBJ_COMMIT);+object_type==OBJ_COMMIT);strbuf_release(&base);free(data);}else{
From: Jonathan Tan <hidden> Date: 2021-08-10 18:29:08
grep_source_init() can create "struct grep_source" objects and,
depending on the value of the type passed, some void-pointer parameters have
different meanings. Because one of these types (GREP_SOURCE_OID) will
require an additional parameter in a subsequent patch, take the
opportunity to increase clarity and type safety by replacing this
function with individual functions for each type.
Signed-off-by: Jonathan Tan <redacted>
---
builtin/grep.c | 4 ++--
grep.c | 43 +++++++++++++++++++++++++++----------------
grep.h | 8 +++++---
3 files changed, 34 insertions(+), 21 deletions(-)
From: Jonathan Tan <hidden> Date: 2021-08-10 18:29:09
In the parent commit, Git was taught to add submodule ODBs as alternates
lazily, but grep does not use this because it computes the path to add
directly, not going through add_submodule_odb(). Add an equivalent to
add_submodule_odb() that takes the exact ODB path and teach grep to use
it.
Signed-off-by: Jonathan Tan <redacted>
---
builtin/grep.c | 2 +-
submodule.c | 5 +++++
submodule.h | 1 +
3 files changed, 7 insertions(+), 1 deletion(-)
From: Jonathan Tan <hidden> Date: 2021-08-10 18:29:09
Teach Git to add submodule ODBs as alternates to the object store of
the_repository only upon the first access of an object not in
the_repository, and not when add_submodule_odb() is called.
This provides a means of gradually migrating from accessing a
submodule's object through alternates to accessing a submodule's object
by explicitly passing its repository object. Any Git command can declare
that it might access submodule objects by calling add_submodule_odb()
(as they do now), but the submodule ODBs themselves will not be added
until needed, so individual commands and/or combinations of arguments
can be migrated one by one.
[The advantage of explicit repository-object passing is code clarity (it
is clear which repository an object read is from), performance (there is
no need to linearly search through all submodule ODBs whenever an object
is accessed from any repository, whether superproject or submodule), and
the possibility of future features like partial clone submodules (which
right now is not possible because if an object is missing, we do not
know which repository to lazy-fetch into).]
This commit also introduces an environment variable that a test may set
to make the actual registration of alternates fatal, in order to
demonstrate that its codepaths do not need this registration.
Signed-off-by: Jonathan Tan <redacted>
---
object-file.c | 5 +++++
submodule.c | 20 +++++++++++++++++++-
submodule.h | 7 +++++++
t/README | 10 ++++++++++
4 files changed, 41 insertions(+), 1 deletion(-)
@@ -32,6 +32,7 @@#include"packfile.h"#include"object-store.h"#include"promisor-remote.h"+#include"submodule.h"/* The maximum size for an object header. */#define MAX_HEADER_LEN 32
@@ -1592,6 +1593,10 @@ static int do_oid_object_info_extended(struct repository *r,break;}+if(register_all_submodule_odb_as_alternates())+/* We added some alternates; retry */+continue;+/* Check if it is a missing object */if(fetch_if_missing&&repo_has_promisor_remote(r)&&!already_retried&&
@@ -448,6 +448,16 @@ GIT_TEST_CHECKOUT_WORKERS=<n> overrides the 'checkout.workers' setting to <n> and 'checkout.thresholdForParallelism' to 0, forcing the execution of the parallel-checkout code.+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=<boolean>, when true, makes+registering submodule ODBs as alternates a fatal action. Support for+this environment variable can be removed once the migration to+explicitly providing repositories when accessing submodule objects is+complete (in which case we might want to replace this with a trace2+call so that users can make it visible if accessing submodule objects+without an explicit repository still happens) or needs to be abandoned+for whatever reason (in which case the migrated codepaths still retain+their performance benefits).+ Naming Tests ------------
From: Jonathan Tan <hidden> Date: 2021-08-10 18:29:17
Currently, struct repository objects corresponding to submodules are
allocated on the stack in grep_submodule(). This currently works because
they will not be used once grep_submodule() exits, but a subsequent
patch will require these structs to be accessible for longer (perhaps
even in another thread). Allocate them on the heap and clear them only
at the very end.
Signed-off-by: Jonathan Tan <redacted>
---
builtin/grep.c | 37 ++++++++++++++++++++++++++++---------
1 file changed, 28 insertions(+), 9 deletions(-)
@@ -65,6 +65,9 @@ static int todo_done;/* Has all work items been added? */staticintall_work_added;+staticstructrepository**repos_to_free;+staticsize_trepos_to_free_nr,repos_to_free_alloc;+/* This lock protects all the variables above. */staticpthread_mutex_tgrep_mutex;
From: Jonathan Tan <hidden> Date: 2021-08-10 18:29:19
Record the repository whenever an OID grep source is created, and teach
the worker threads to explicitly provide the repository when accessing
objects.
Signed-off-by: Jonathan Tan <redacted>
---
builtin/grep.c | 2 +-
grep.c | 7 +++++--
grep.h | 4 +++-
3 files changed, 9 insertions(+), 4 deletions(-)
From: Jonathan Tan <hidden> Date: 2021-08-10 18:29:20
The previous patches have made "git grep" no longer need to add
submodule ODBs as alternates, at least for the code paths tested in
t7814. Demonstrate this by making adding a submodule ODB as an alternate
fatal in this test.
Signed-off-by: Jonathan Tan <redacted>
---
t/t7814-grep-recurse-submodules.sh | 3 +++
1 file changed, 3 insertions(+)
On Tue, Aug 10, 2021 at 11:28:38AM -0700, Jonathan Tan wrote:
This patch series removes the need to add submodule ODBs as alternates
in all codepaths covered by t7814. I believe that that is also all the
codepaths covered by "git grep", but if it isn't, the uncovered
codepaths will still work - they will just not benefit from the
performance improvements.
In doing this work of migrating away from adding submodule ODBs as
alternates, I'm mainly motivated by the possibility of adding partial
clone submodule support, but this has benefits even for those who do not
use partial clones, as described in the documentation in patch 1.
To reviewers: you can cherry-pick the last patch onto one of the earlier
ones to observe what happens when the code still accesses a submodule
object as if it were in the_repository.
One note - I liked this tip, I think that is a useful thing for
reviewers to see. Thanks.
- Emily
@@ -32,6 +32,7 @@#include"packfile.h"#include"object-store.h"#include"promisor-remote.h"+#include"submodule.h"/* The maximum size for an object header. */#define MAX_HEADER_LEN 32
@@ -1592,6 +1593,10 @@ static int do_oid_object_info_extended(struct repository *r,break;}+if(register_all_submodule_odb_as_alternates())+/* We added some alternates; retry */+continue;+
Ok, this is where we finally get around to loading the alternate much
later. Fine.
quoted hunk
/* Check if it is a missing object */
if (fetch_if_missing && repo_has_promisor_remote(r) &&
!already_retried &&
@@ -165,6 +165,8 @@ void stage_updated_gitmodules(struct index_state *istate)die(_("staging updated .gitmodules failed"));}+staticstructstring_listadded_submodule_odb_paths=STRING_LIST_INIT_NODUP;+/* TODO: remove this function, use repo_submodule_init instead. */intadd_submodule_odb(constchar*path){
@@ -178,12 +180,28 @@ int add_submodule_odb(const char *path)ret=-1;gotodone;}-add_to_alternates_memory(objects_directory.buf);+string_list_insert(&added_submodule_odb_paths,+strbuf_detach(&objects_directory,NULL));
And here is where we hijack the usual alternate load and put the path
into the lazy load list instead. Ok.
done:
strbuf_release(&objects_directory);
return ret;
}
+int register_all_submodule_odb_as_alternates(void)
+{
+ int i;
+ int ret = added_submodule_odb_paths.nr;
+
+ for (i = 0; i < added_submodule_odb_paths.nr; i++)
+ add_to_alternates_memory(added_submodule_odb_paths.items[i].string);
+ if (ret) {
+ string_list_clear(&added_submodule_odb_paths, 0);
+ if (git_env_bool("GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB", 0))
+ BUG("register_all_submodule_odb_as_alternates() called");
Nice, and this is the flag you mentioned in the cover letter to complain
if we're trying to use alternates to access submodule objects. This will
be useful for finding out other random places where we are using that
weird hack.
@@ -448,6 +448,16 @@ GIT_TEST_CHECKOUT_WORKERS=<n> overrides the 'checkout.workers' setting to <n> and 'checkout.thresholdForParallelism' to 0, forcing the execution of the parallel-checkout code.+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=<boolean>, when true, makes+registering submodule ODBs as alternates a fatal action. Support for+this environment variable can be removed once the migration to+explicitly providing repositories when accessing submodule objects is+complete (in which case we might want to replace this with a trace2+call so that users can make it visible if accessing submodule objects+without an explicit repository still happens) or needs to be abandoned+for whatever reason (in which case the migrated codepaths still retain+their performance benefits).+ Naming Tests ------------
On Tue, Aug 10, 2021 at 11:28:40AM -0700, Jonathan Tan wrote:
quoted hunk
In the parent commit, Git was taught to add submodule ODBs as alternates
lazily, but grep does not use this because it computes the path to add
directly, not going through add_submodule_odb(). Add an equivalent to
add_submodule_odb() that takes the exact ODB path and teach grep to use
it.
Signed-off-by: Jonathan Tan <redacted>
---
builtin/grep.c | 2 +-
submodule.c | 5 +++++
submodule.h | 1 +
3 files changed, 7 insertions(+), 1 deletion(-)
@@ -450,7 +450,7 @@ static int grep_submodule(struct grep_opt *opt,*storeisnolongerglobalandinsteadisamemberoftherepository*object.*/-add_to_alternates_memory(subrepo.objects->odb->path);+add_submodule_odb_by_path(subrepo.objects->odb->path);
I had wondered whether we can entirely drop add_to_alternates_memory()
but I see on second reading that that's still used by
register_all_submodule_odb...(). I wonder if it can become static in
submodule.c to prevent users from dodging the envvar+BUG()?
On Tue, Aug 10, 2021 at 11:28:41AM -0700, Jonathan Tan wrote:
grep_source_init() can create "struct grep_source" objects and,
depending on the value of the type passed, some void-pointer parameters have
different meanings. Because one of these types (GREP_SOURCE_OID) will
require an additional parameter in a subsequent patch, take the
opportunity to increase clarity and type safety by replacing this
function with individual functions for each type.
Signed-off-by: Jonathan Tan <redacted>
Like Junio said, it is very neat.
Reviewed-by: Emily Shaffer <redacted>
On Tue, Aug 10, 2021 at 11:28:42AM -0700, Jonathan Tan wrote:
Replace an existing parse_object_or_die() call (which implicitly works
on the_repository) with a function call that allows a repository to be
passed in. There is no such direct equivalent to parse_object_or_die(),
but we only need the type of the object, so replace with
oid_object_info().
Always exciting to see less implicit use of the_repository ;)
quoted hunk
Signed-off-by: Jonathan Tan <redacted>
---
builtin/grep.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
@@ -457,27 +457,27 @@ static int grep_submodule(struct grep_opt *opt,subopt.repo=&subrepo;if(oid){-structobject*object;+enumobject_typeobject_type;structtree_desctree;void*data;unsignedlongsize;structstrbufbase=STRBUF_INIT;obj_read_lock();-object=parse_object_or_die(oid,NULL);+object_type=oid_object_info(&subrepo,oid,NULL);
One thing I wonder is whether we are missing out on some error
conditions we used to get with parse_object_or_die() by using
oid_object_info() instead. Do we need to be more defensive in
investigating 'oid' before calling that helper, now?
obj_read_unlock();
data = read_object_with_reference(&subrepo,
- &object->oid, tree_type,
+ oid, tree_type,
And a handful of instances where we were using object->oid instead of
the oid we were already passed. Ok.
&size, NULL);
if (!data)
- die(_("unable to read tree (%s)"), oid_to_hex(&object->oid));
+ die(_("unable to read tree (%s)"), oid_to_hex(oid));
strbuf_addstr(&base, filename);
strbuf_addch(&base, '/');
init_tree_desc(&tree, data, size);
hit = grep_tree(&subopt, pathspec, &tree, &base, base.len,
- object->type == OBJ_COMMIT);
+ object_type == OBJ_COMMIT);
And finally, using the type we got from oid_object_info instead. Ok.
@@ -65,6 +65,9 @@ static int todo_done;/* Has all work items been added? */staticintall_work_added;+staticstructrepository**repos_to_free;+staticsize_trepos_to_free_nr,repos_to_free_alloc;
One thing I was curious about was whether it would make more sense to
use an existing utility library in Git to handle this. But I think we
kind of are, since ALLOC_GROW is used, so this is idiomatic for Git
project. Ok, fine, I guess this is life at C ;)
quoted hunk
+
/* This lock protects all the variables above. */
static pthread_mutex_t grep_mutex;
@@ -168,6 +171,17 @@ static void work_done(struct work_item *w) grep_unlock(); }+static void free_repos(void)+{+ int i;++ for (i = 0; i < repos_to_free_nr; i++) {+ repo_clear(repos_to_free[i]);+ free(repos_to_free[i]);+ }+ free(repos_to_free);+}
Should repos_to_free_nr be reset here? I guess it doesn't make sense to,
since we'd be trying to use-after-free the initial repos_to_free head
pointer too if we wanted to reuse this array.
quoted hunk
+
static void *run(void *arg)
{
int hit = 0;
@@ -415,19 +429,24 @@ static int grep_submodule(struct grep_opt *opt, const struct object_id *oid, const char *filename, const char *path, int cached) {- struct repository subrepo;+ struct repository *subrepo; struct repository *superproject = opt->repo; const struct submodule *sub; struct grep_opt subopt;- int hit;+ int hit = 0; sub = submodule_from_path(superproject, null_oid(), path); if (!is_submodule_active(superproject, path)) return 0;- if (repo_submodule_init(&subrepo, superproject, sub))+ subrepo = xmalloc(sizeof(*subrepo));+ if (repo_submodule_init(subrepo, superproject, sub)) {+ free(subrepo); return 0;+ }+ ALLOC_GROW(repos_to_free, repos_to_free_nr + 1, repos_to_free_alloc);+ repos_to_free[repos_to_free_nr++] = subrepo;
Is this the only place we add to the repos_to_free array? It looks like
yes, so I guess this doesn't need a helper.
quoted hunk
/*
* NEEDSWORK: repo_read_gitmodules() might call
@@ -438,7 +457,7 @@ static int grep_submodule(struct grep_opt *opt, * subrepo's odbs to the in-memory alternates list. */ obj_read_lock();- repo_read_gitmodules(&subrepo, 0);+ repo_read_gitmodules(subrepo, 0); /* * NEEDSWORK: This adds the submodule's object directory to the list of
@@ -450,11 +469,11 @@ static int grep_submodule(struct grep_opt *opt, * store is no longer global and instead is a member of the repository * object. */- add_submodule_odb_by_path(subrepo.objects->odb->path);+ add_submodule_odb_by_path(subrepo->objects->odb->path); obj_read_unlock(); memcpy(&subopt, opt, sizeof(subopt));- subopt.repo = &subrepo;+ subopt.repo = subrepo; if (oid) { enum object_type object_type;
@@ -464,9 +483,9 @@ static int grep_submodule(struct grep_opt *opt, struct strbuf base = STRBUF_INIT; obj_read_lock();- object_type = oid_object_info(&subrepo, oid, NULL);+ object_type = oid_object_info(subrepo, oid, NULL); obj_read_unlock();- data = read_object_with_reference(&subrepo,+ data = read_object_with_reference(subrepo, oid, tree_type, &size, NULL); if (!data)
@@ -484,7 +503,6 @@ static int grep_submodule(struct grep_opt *opt, hit = grep_cache(&subopt, pathspec, cached); }- repo_clear(&subrepo); return hit; }
On Tue, Aug 10, 2021 at 11:28:44AM -0700, Jonathan Tan wrote:
Record the repository whenever an OID grep source is created, and teach
the worker threads to explicitly provide the repository when accessing
objects.
Since this extra information is not used by this series at all, does it
make sense to only include this patch with the series covering the rest
of your partial-clone-with-submodules work?
- Emily
On Tue, Aug 10, 2021 at 11:28:45AM -0700, Jonathan Tan wrote:
quoted hunk
The previous patches have made "git grep" no longer need to add
submodule ODBs as alternates, at least for the code paths tested in
t7814. Demonstrate this by making adding a submodule ODB as an alternate
fatal in this test.
Signed-off-by: Jonathan Tan <redacted>
---
t/t7814-grep-recurse-submodules.sh | 3 +++
1 file changed, 3 insertions(+)
This proof seems pretty handy, assuming nobody else is directly calling
add_to_alternates_memory() (and therefore skipping the envvar check). I
would feel slightly more convinced if that function was file-static
somewhere, but I am not familiar with the problem space enough to say
whether that's possible.
This patch by itself, though:
Reviewed-by: Emily Shaffer <redacted>
- Emily
On Wed, Aug 11, 2021 at 6:55 PM Emily Shaffer [off-list ref] wrote:
On Tue, Aug 10, 2021 at 11:28:45AM -0700, Jonathan Tan wrote:
quoted
The previous patches have made "git grep" no longer need to add
submodule ODBs as alternates, at least for the code paths tested in
t7814. Demonstrate this by making adding a submodule ODB as an alternate
fatal in this test.
Signed-off-by: Jonathan Tan <redacted>
---
t/t7814-grep-recurse-submodules.sh | 3 +++
1 file changed, 3 insertions(+)
This proof seems pretty handy, assuming nobody else is directly calling
add_to_alternates_memory() (and therefore skipping the envvar check).
Hmm, there is at least one call chain in grep which might end up
calling add_to_alternates_memory() directly (although it only seems to
happen on a very specific case):
grep_submodule > repo_read_gitmodules >
config_from_gitmodules > add_to_alternates_memory
We can check that with the following:
git init A
git init A/B
git init A/B/C
echo f >A/B/C/f
git -C A/B/C add f
git -C A/B/C commit -m f
git -C A/B submodule add ./C
git -C A/B commit -m C
git -C A submodule add ./B
git -C A commit -m B
rm B/.gitmodules
gdb -ex 'break add_to_alternates_memory' -ex 'run' --args \
git grep --recurse-submodules .
This patch series removes the need to add submodule ODBs as alternates
in all codepaths covered by t7814. I believe that that is also all the
codepaths covered by "git grep", but if it isn't, the uncovered
codepaths will still work - they will just not benefit from the
performance improvements.
In doing this work of migrating away from adding submodule ODBs as
alternates, I'm mainly motivated by the possibility of adding partial
clone submodule support, but this has benefits even for those who do not
use partial clones, as described in the documentation in patch 1.
To reviewers: you can cherry-pick the last patch onto one of the earlier
ones to observe what happens when the code still accesses a submodule
object as if it were in the_repository.
Thanks for this series. I am very excited to see steps toward enabling
partial clones for submodules! And having fewer implicit references to
the_repository is great as well.
I don't have any comments that other reviewers haven't already
mentioned, so I'll just add:
Reviewed-by: Josh Steadmon <redacted>
From: Ramsay Jones <hidden> Date: 2021-08-11 23:07:44
On Wed, Aug 11, 2021 at 02:42:20PM -0700, Emily Shaffer wrote:
On Tue, Aug 10, 2021 at 11:28:41AM -0700, Jonathan Tan wrote:
quoted
grep_source_init() can create "struct grep_source" objects and,
depending on the value of the type passed, some void-pointer parameters have
different meanings. Because one of these types (GREP_SOURCE_OID) will
require an additional parameter in a subsequent patch, take the
opportunity to increase clarity and type safety by replacing this
function with individual functions for each type.
Signed-off-by: Jonathan Tan <redacted>
Like Junio said, it is very neat.
[Sorry for piggy-backing, I have already deleted the original mail :( ]
Just a quick note: grep_source_init_buf() is only called from
grep.c:1833, before its definition at grep.c:1869, so it could be marked
as static (as things stand). Do you anticipate any future callers from
outside of grep.c? (after removing the declaration from grep.h, you
would need to add a forward declaration or, better, move the definition
to before the call (or the call (and grep_buffer()) after the definition)).
ATB,
Ramsay Jones
On Tue, Aug 10, 2021 at 3:29 PM Jonathan Tan [off-list ref] wrote:
Record the repository whenever an OID grep source is created, and teach
the worker threads to explicitly provide the repository when accessing
objects.
This patch fixes the second NEEDSWORK comment at grep_submodule(),
right? Maybe this comment could be replaced with a mention that
add_submodule_odb_by_path() is called for testing purposes, and it
should no longer produce a real addition to the alternates list?
@@ -187,6 +187,7 @@ struct grep_source {GREP_SOURCE_BUF,}type;void*identifier;+structrepository*repo;/* if GREP_SOURCE_OID */
Hmm, the grep threads now have two `struct repository` references, one
in `struct grep_source` and one in `struct grep_opt` (see
builtin/grep.c:run()). The one from `struct grep_opt` will always be
`the_repository` (in the worker threads). I wonder if, in the long
run, we could instruct the worker threads to use the new reference
from `struct grep_source` throughout grep.c, and perhaps even remove
the one from `struct grep_opt`.
This would solve the issue I mentioned in [1], about git grep
currently ignoring the textconv attributes from submodules, and
instead applying the ones from the superproject in the submodules'
files. (Here there is an example of this bug: [2] .)
It would also allow grep to use the textconv cache from each
submodule, instead of saving/reading everything from the
superproject's textconv cache.
While this doesn't happen, though, could we perhaps add a comment
somewhere to avoid any confusion regarding the two different
repository pointers that the worker threads hold?
[1]: https://lore.kernel.org/git/CAHd-oW5iEQarYVxEXoTG-ua2zdoybTrSjCBKtO0YT292fm0NQQ@mail.gmail.com/
[2]: https://gitlab.com/-/snippets/1896951
Does it need to be public? Could this be a static in submodule.c
instead?
Thanks for taking a look at this patch series.
To answer this question: no - in this patch, I need to use it from
object-file.c to actually register the submodule ODBs as alternates when
we try to read an object that is not in the superproject.
From: Jonathan Tan <hidden> Date: 2021-08-13 16:31:22
I had wondered whether we can entirely drop add_to_alternates_memory()
but I see on second reading that that's still used by
register_all_submodule_odb...(). I wonder if it can become static in
submodule.c to prevent users from dodging the envvar+BUG()?
You mean make add_to_alternates_memory() static? I'm not sure how we can
do that - alternates is a concern that extends beyond submodules.
Curious whether add_submodule_odb() can be reduced by calling
add_submodule_odb_by_path() internally.
That's a reasonable idea, but I don't think it works in this case - in
particular, add_submodule_odb_by_path() dups its argument whereas
add_submodule_odb() already has an allocated string that it is prepared
to give up ownership of. Also, add_submodule_odb_by_path() is only one
line, so it won't save us much.
From: Jonathan Tan <hidden> Date: 2021-08-13 16:32:46
Just a quick note: grep_source_init_buf() is only called from
grep.c:1833, before its definition at grep.c:1869, so it could be marked
as static (as things stand). Do you anticipate any future callers from
outside of grep.c? (after removing the declaration from grep.h, you
would need to add a forward declaration or, better, move the definition
to before the call (or the call (and grep_buffer()) after the definition)).
No, I do not - thanks for the note, and I'll make it static in the next
reroll.
One thing I wonder is whether we are missing out on some error
conditions we used to get with parse_object_or_die() by using
oid_object_info() instead. Do we need to be more defensive in
investigating 'oid' before calling that helper, now?
For the purposes of grep, I don't think that this matters - we just want
to find the ultimate tree that this object represents.
One thing I was curious about was whether it would make more sense to
use an existing utility library in Git to handle this. But I think we
kind of are, since ALLOC_GROW is used, so this is idiomatic for Git
project. Ok, fine, I guess this is life at C ;)
Yeah, this *is* the utility library :-)
quoted
+static void free_repos(void)
+{
+ int i;
+
+ for (i = 0; i < repos_to_free_nr; i++) {
+ repo_clear(repos_to_free[i]);
+ free(repos_to_free[i]);
+ }
+ free(repos_to_free);
+}
Should repos_to_free_nr be reset here? I guess it doesn't make sense to,
since we'd be trying to use-after-free the initial repos_to_free head
pointer too if we wanted to reuse this array.
Hmm...I guess that if I'm going through the trouble of clearing the
repos instead of just letting all the memory be collected at the end of
a process, I should also reset _nr and _alloc. I'll make that change.
From: Jonathan Tan <hidden> Date: 2021-08-13 16:44:15
On Tue, Aug 10, 2021 at 11:28:44AM -0700, Jonathan Tan wrote:
quoted
Record the repository whenever an OID grep source is created, and teach
the worker threads to explicitly provide the repository when accessing
objects.
Since this extra information is not used by this series at all, does it
make sense to only include this patch with the series covering the rest
of your partial-clone-with-submodules work?
It is used by this series - it prevents an instance in which the
submodule ODBs would be lazily registered as alternates. I'll add a note
in the commit message to this effect.
From: Jonathan Tan <hidden> Date: 2021-08-13 16:47:40
On Tue, Aug 10, 2021 at 3:29 PM Jonathan Tan [off-list ref] wrote:
quoted
Record the repository whenever an OID grep source is created, and teach
the worker threads to explicitly provide the repository when accessing
objects.
This patch fixes the second NEEDSWORK comment at grep_submodule(),
right? Maybe this comment could be replaced with a mention that
add_submodule_odb_by_path() is called for testing purposes, and it
should no longer produce a real addition to the alternates list?
@@ -187,6 +187,7 @@ struct grep_source {GREP_SOURCE_BUF,}type;void*identifier;+structrepository*repo;/* if GREP_SOURCE_OID */
Hmm, the grep threads now have two `struct repository` references, one
in `struct grep_source` and one in `struct grep_opt` (see
builtin/grep.c:run()). The one from `struct grep_opt` will always be
`the_repository` (in the worker threads). I wonder if, in the long
run, we could instruct the worker threads to use the new reference
from `struct grep_source` throughout grep.c, and perhaps even remove
the one from `struct grep_opt`.
This would solve the issue I mentioned in [1], about git grep
currently ignoring the textconv attributes from submodules, and
instead applying the ones from the superproject in the submodules'
files. (Here there is an example of this bug: [2] .)
It would also allow grep to use the textconv cache from each
submodule, instead of saving/reading everything from the
superproject's textconv cache.
While this doesn't happen, though, could we perhaps add a comment
somewhere to avoid any confusion regarding the two different
repository pointers that the worker threads hold?
[1]: https://lore.kernel.org/git/CAHd-oW5iEQarYVxEXoTG-ua2zdoybTrSjCBKtO0YT292fm0NQQ@mail.gmail.com/
[2]: https://gitlab.com/-/snippets/1896951
Ah...another good catch. Thanks also for a link to your email - I'll
mention it when I add the comment you suggested.
From: Jonathan Tan <hidden> Date: 2021-08-13 16:50:52
Hmm, there is at least one call chain in grep which might end up
calling add_to_alternates_memory() directly (although it only seems to
happen on a very specific case):
grep_submodule > repo_read_gitmodules >
config_from_gitmodules > add_to_alternates_memory
We can check that with the following:
[snip reproduction recipe]
Thanks - it looks like my patch set is incomplete then. I'll make
config_from_gitmodules() use my new function and if the existing grep
test cases don't cover your reproduction recipe, I'll add yours in.
From: Jonathan Tan <hidden> Date: 2021-08-13 21:05:34
In the parent commit, Git was taught to add submodule ODBs as alternates
lazily, but grep does not use this because it computes the path to add
directly, not going through add_submodule_odb(). Add an equivalent to
add_submodule_odb() that takes the exact ODB path and teach grep to use
it.
Signed-off-by: Jonathan Tan <redacted>
---
builtin/grep.c | 2 +-
submodule.c | 5 +++++
submodule.h | 1 +
3 files changed, 7 insertions(+), 1 deletion(-)
From: Jonathan Tan <hidden> Date: 2021-08-13 21:05:34
Teach Git to add submodule ODBs as alternates to the object store of
the_repository only upon the first access of an object not in
the_repository, and not when add_submodule_odb() is called.
This provides a means of gradually migrating from accessing a
submodule's object through alternates to accessing a submodule's object
by explicitly passing its repository object. Any Git command can declare
that it might access submodule objects by calling add_submodule_odb()
(as they do now), but the submodule ODBs themselves will not be added
until needed, so individual commands and/or combinations of arguments
can be migrated one by one.
[The advantage of explicit repository-object passing is code clarity (it
is clear which repository an object read is from), performance (there is
no need to linearly search through all submodule ODBs whenever an object
is accessed from any repository, whether superproject or submodule), and
the possibility of future features like partial clone submodules (which
right now is not possible because if an object is missing, we do not
know which repository to lazy-fetch into).]
This commit also introduces an environment variable that a test may set
to make the actual registration of alternates fatal, in order to
demonstrate that its codepaths do not need this registration.
Signed-off-by: Jonathan Tan <redacted>
---
object-file.c | 5 +++++
submodule.c | 20 +++++++++++++++++++-
submodule.h | 7 +++++++
t/README | 10 ++++++++++
4 files changed, 41 insertions(+), 1 deletion(-)
@@ -32,6 +32,7 @@#include"packfile.h"#include"object-store.h"#include"promisor-remote.h"+#include"submodule.h"/* The maximum size for an object header. */#define MAX_HEADER_LEN 32
@@ -1592,6 +1593,10 @@ static int do_oid_object_info_extended(struct repository *r,break;}+if(register_all_submodule_odb_as_alternates())+/* We added some alternates; retry */+continue;+/* Check if it is a missing object */if(fetch_if_missing&&repo_has_promisor_remote(r)&&!already_retried&&
@@ -448,6 +448,16 @@ GIT_TEST_CHECKOUT_WORKERS=<n> overrides the 'checkout.workers' setting to <n> and 'checkout.thresholdForParallelism' to 0, forcing the execution of the parallel-checkout code.+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=<boolean>, when true, makes+registering submodule ODBs as alternates a fatal action. Support for+this environment variable can be removed once the migration to+explicitly providing repositories when accessing submodule objects is+complete (in which case we might want to replace this with a trace2+call so that users can make it visible if accessing submodule objects+without an explicit repository still happens) or needs to be abandoned+for whatever reason (in which case the migrated codepaths still retain+their performance benefits).+ Naming Tests ------------
From: Jonathan Tan <hidden> Date: 2021-08-13 21:05:40
grep_source_init() can create "struct grep_source" objects and,
depending on the value of the type passed, some void-pointer parameters have
different meanings. Because one of these types (GREP_SOURCE_OID) will
require an additional parameter in a subsequent patch, take the
opportunity to increase clarity and type safety by replacing this
function with individual functions for each type.
Signed-off-by: Jonathan Tan <redacted>
---
builtin/grep.c | 4 ++--
grep.c | 46 ++++++++++++++++++++++++++++------------------
grep.h | 7 ++++---
3 files changed, 34 insertions(+), 23 deletions(-)
From: Jonathan Tan <hidden> Date: 2021-08-13 21:05:41
Replace an existing parse_object_or_die() call (which implicitly works
on the_repository) with a function call that allows a repository to be
passed in. There is no such direct equivalent to parse_object_or_die(),
but we only need the type of the object, so replace with
oid_object_info().
Signed-off-by: Jonathan Tan <redacted>
---
builtin/grep.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
@@ -457,27 +457,27 @@ static int grep_submodule(struct grep_opt *opt,subopt.repo=&subrepo;if(oid){-structobject*object;+enumobject_typeobject_type;structtree_desctree;void*data;unsignedlongsize;structstrbufbase=STRBUF_INIT;obj_read_lock();-object=parse_object_or_die(oid,NULL);+object_type=oid_object_info(&subrepo,oid,NULL);obj_read_unlock();data=read_object_with_reference(&subrepo,-&object->oid,tree_type,+oid,tree_type,&size,NULL);if(!data)-die(_("unable to read tree (%s)"),oid_to_hex(&object->oid));+die(_("unable to read tree (%s)"),oid_to_hex(oid));strbuf_addstr(&base,filename);strbuf_addch(&base,'/');init_tree_desc(&tree,data,size);hit=grep_tree(&subopt,pathspec,&tree,&base,base.len,-object->type==OBJ_COMMIT);+object_type==OBJ_COMMIT);strbuf_release(&base);free(data);}else{
From: Jonathan Tan <hidden> Date: 2021-08-13 21:05:43
Currently, struct repository objects corresponding to submodules are
allocated on the stack in grep_submodule(). This currently works because
they will not be used once grep_submodule() exits, but a subsequent
patch will require these structs to be accessible for longer (perhaps
even in another thread). Allocate them on the heap and clear them only
at the very end.
Signed-off-by: Jonathan Tan <redacted>
---
builtin/grep.c | 39 ++++++++++++++++++++++++++++++---------
1 file changed, 30 insertions(+), 9 deletions(-)
@@ -65,6 +65,9 @@ static int todo_done;/* Has all work items been added? */staticintall_work_added;+staticstructrepository**repos_to_free;+staticsize_trepos_to_free_nr,repos_to_free_alloc;+/* This lock protects all the variables above. */staticpthread_mutex_tgrep_mutex;
From: Jonathan Tan <hidden> Date: 2021-08-13 21:05:45
Record the repository whenever an OID grep source is created, and teach
the worker threads to explicitly provide the repository when accessing
objects.
Signed-off-by: Jonathan Tan <redacted>
---
builtin/grep.c | 15 ++++++---------
grep.c | 7 +++++--
grep.h | 13 ++++++++++++-
3 files changed, 23 insertions(+), 12 deletions(-)
From: Jonathan Tan <hidden> Date: 2021-08-13 21:05:47
When reading the config of a submodule, if reading from a blob, read
using an explicitly specified repository instead of by adding the
submodule's ODB as an alternate and then reading an object from
the_repository.
Signed-off-by: Jonathan Tan <redacted>
---
config.c | 18 ++++++++++++------
config.h | 3 +++
submodule-config.c | 5 +++--
3 files changed, 18 insertions(+), 8 deletions(-)
@@ -49,6 +49,8 @@ const char *config_scope_name(enum config_scope scope);structgit_config_source{unsignedintuse_stdin:1;constchar*file;+/* The repository if blob is not NULL; leave blank for the_repository */+structrepository*repo;constchar*blob;enumconfig_scopescope;};
@@ -136,6 +138,7 @@ int git_config_from_mem(config_fn_t fn,constchar*buf,size_tlen,void*data,conststructconfig_options*opts);intgit_config_from_blob_oid(config_fn_tfn,constchar*name,+structrepository*repo,conststructobject_id*oid,void*data);voidgit_config_push_parameter(constchar*text);voidgit_config_push_env(constchar*spec);
From: Jonathan Tan <hidden> Date: 2021-08-13 21:05:48
The previous patches have made "git grep" no longer need to add
submodule ODBs as alternates, at least for the code paths tested in
t7814. Demonstrate this by making adding a submodule ODB as an alternate
fatal in this test.
Signed-off-by: Jonathan Tan <redacted>
---
t/t7814-grep-recurse-submodules.sh | 3 +++
1 file changed, 3 insertions(+)
On Fri, Aug 13, 2021 at 6:05 PM Jonathan Tan [off-list ref] wrote:
When reading the config of a submodule, if reading from a blob, read
using an explicitly specified repository instead of by adding the
submodule's ODB as an alternate and then reading an object from
the_repository.
Great!
At first, I thought this would also allow us to remove another
NEEDSWORK comment in grep_submodule(), together with a lock
protection:
/*
* NEEDSWORK: repo_read_gitmodules() might call
* add_to_alternates_memory() via config_from_gitmodules(). This
* operation causes a race condition with concurrent object readings
* performed by the worker threads. That's why we need obj_read_lock()
* here. It should be removed once it's no longer necessary to add the
* subrepo's odbs to the in-memory alternates list.
*/
obj_read_lock();
repo_read_gitmodules(subrepo, 0);
Back when I wrote this comment, my conclusion was that the alternates
mechanics were the only thread-unsafe object-reading operations in
repo_read_gitmodules()'s call chains. So once the add-to-alternates
mechanics were gone, we could also remove the lock.
But with further inspection now, I see that this is not really the
case. For example, we have a few global variables in packfile.c
collecting some statistics (pack_mmap_calls, pack_open_windows, etc.)
which are updated on obj readings from both the_repository *and*
submodules. So I no longer think its safe to remove the
obj_read_lock() protection here, as the NEEDSWORK comment suggests,
even if we are not using the alternates list anymore.
Do you want to remove this comment in your patchset? I can also send a
follow-up patch explaining this situation and removing the comment
(but not the locking), if you prefer.
Ok. Like in grep_submodule(), this should no longer add the submodule
ODB to the alternates list, so this call is now mostly used as a
fallback and also for testing.
To see if we are indeed testing this add-to-alternates case, I
reverted the change that made the code read from the submodule instead
of the_repository:
@@ -1805,7 +1805,7 @@ int git_config_from_blob_oid(config_fn_t fn,unsignedlongsize;intret;-buf=repo_read_object_file(repo,oid,&type,&size);+buf=read_object_file(oid,&type,&size);
Then, I ran t7814-grep-recurse-submodules.sh , where you've added the
GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1 envvar. This correctly
produced the following error:
BUG: submodule.c:205: register_all_submodule_odb_as_alternates() called
[...]
not ok 23 - grep --recurse-submodules with submodules without
.gitmodules in the working tree
Nice! So the change made by this patch is covered by test 23. I think
it would be nice to mention that in this patch's message.
On Fri, Aug 13, 2021 at 6:05 PM Jonathan Tan [off-list ref] wrote:
quoted hunk
Record the repository whenever an OID grep source is created, and teach
the worker threads to explicitly provide the repository when accessing
objects.
Signed-off-by: Jonathan Tan <redacted>
---
builtin/grep.c | 15 ++++++---------
grep.c | 7 +++++--
grep.h | 13 ++++++++++++-
3 files changed, 23 insertions(+), 12 deletions(-)
Hmm, I think the "not per-repo" part is a bit confusing, as it refers
to "the repository" ("the repository should not be per-repo"?) Could
we remove that part?
Maybe we could also be a bit more specific regarding the suggested
conversion: "See if we can remove this field, because the repository
should probably be per-source. That is, grep.c functions using
`grep_opt.repo` should probably start using `grep_source.repo`
instead." (But that's nitpicking from my part, feel free to ignore
it.)
@@ -1840,28 +1850,28 @@ int grep_buffer(struct grep_opt *opt, char *buf, unsigned long size)returnr;}-voidgrep_source_init(structgrep_source*gs,enumgrep_source_typetype,-constchar*name,constchar*path,-constvoid*identifier)+voidgrep_source_init_file(structgrep_source*gs,constchar*name,+constchar*path){-gs->type=type;+gs->type=GREP_SOURCE_FILE;gs->name=xstrdup_or_null(name);gs->path=xstrdup_or_null(path);gs->buf=NULL;gs->size=0;gs->driver=NULL;+gs->identifier=xstrdup(path);
At first, it seemed a bit odd to me that we use
`xstrdup_or_null(path)` first but `xtsrdup(path)` later. But, I saw
that we already did this before this patch, and that the second call
is fine as `path` is required to be non-NULL in this case (i.e.
GREP_SOURCE_FILE), because we need it for grep_source_load_file()
later. (Also, the only caller is correctly passing a non-NULL buffer,
so all is good.)
On Fri, Aug 13, 2021 at 6:05 PM Jonathan Tan [off-list ref] wrote:
Thanks everyone for your reviews. I believe I've addressed all of them.
The main change is that I've also made a change in submodule-config to
avoid registering submodule ODBs as alternates (thanks, Matheus, for
noticing this).
Thanks for working on this! I reviewed the entire series and left a
few comments on some small things, but overall it looks good to me.
Reviewed-by: Matheus Tavares <redacted>
From: Jonathan Tan <hidden> Date: 2021-08-16 19:44:39
quoted
@@ -120,7 +120,16 @@ struct grep_opt { struct grep_pat *header_list; struct grep_pat **header_tail; struct grep_expr *pattern_expression;++ /*+ * NEEDSWORK: See if we can remove this field, because the repository+ * should probably be per-source, not per-repo.
Hmm, I think the "not per-repo" part is a bit confusing, as it refers
to "the repository" ("the repository should not be per-repo"?) Could
we remove that part?
Maybe we could also be a bit more specific regarding the suggested
conversion: "See if we can remove this field, because the repository
should probably be per-source. That is, grep.c functions using
`grep_opt.repo` should probably start using `grep_source.repo`
instead." (But that's nitpicking from my part, feel free to ignore
it.)
Thanks - your suggestion is much clearer, so I'll use it.
On Mon, Aug 16, 2021 at 11:32 AM Matheus Tavares Bernardino
[off-list ref] wrote:
On Fri, Aug 13, 2021 at 6:05 PM Jonathan Tan [off-list ref] wrote:
quoted
When reading the config of a submodule, if reading from a blob, read
using an explicitly specified repository instead of by adding the
submodule's ODB as an alternate and then reading an object from
the_repository.
Great!
At first, I thought this would also allow us to remove another
NEEDSWORK comment in grep_submodule(), together with a lock
protection:
/*
* NEEDSWORK: repo_read_gitmodules() might call
* add_to_alternates_memory() via config_from_gitmodules(). This
* operation causes a race condition with concurrent object readings
* performed by the worker threads. That's why we need obj_read_lock()
* here. It should be removed once it's no longer necessary to add the
* subrepo's odbs to the in-memory alternates list.
*/
obj_read_lock();
repo_read_gitmodules(subrepo, 0);
Back when I wrote this comment, my conclusion was that the alternates
mechanics were the only thread-unsafe object-reading operations in
repo_read_gitmodules()'s call chains. So once the add-to-alternates
mechanics were gone, we could also remove the lock.
But with further inspection now, I see that this is not really the
case. For example, we have a few global variables in packfile.c
collecting some statistics (pack_mmap_calls, pack_open_windows, etc.)
which are updated on obj readings from both the_repository *and*
submodules.
Sorry, this is incorrect. I forgot that repo_read_object_file() (which
is part of repo_read_gitmodules()'s call chain) also acquires the
obj_read_mutex before accessing those global variables. So the
NEEDSWORK might be right.
Nevertheless, I think it might be better to look into
repo_read_gitmodules() more carefully before removing this lock. And
this is something for another series. Sorry about the noise.
From: Jonathan Tan <hidden> Date: 2021-08-16 20:02:29
/*
* NEEDSWORK: repo_read_gitmodules() might call
* add_to_alternates_memory() via config_from_gitmodules(). This
* operation causes a race condition with concurrent object readings
* performed by the worker threads. That's why we need obj_read_lock()
* here. It should be removed once it's no longer necessary to add the
* subrepo's odbs to the in-memory alternates list.
*/
obj_read_lock();
repo_read_gitmodules(subrepo, 0);
Back when I wrote this comment, my conclusion was that the alternates
mechanics were the only thread-unsafe object-reading operations in
repo_read_gitmodules()'s call chains. So once the add-to-alternates
mechanics were gone, we could also remove the lock.
But with further inspection now, I see that this is not really the
case. For example, we have a few global variables in packfile.c
collecting some statistics (pack_mmap_calls, pack_open_windows, etc.)
which are updated on obj readings from both the_repository *and*
submodules. So I no longer think its safe to remove the
obj_read_lock() protection here, as the NEEDSWORK comment suggests,
even if we are not using the alternates list anymore.
Do you want to remove this comment in your patchset? I can also send a
follow-up patch explaining this situation and removing the comment
(but not the locking), if you prefer.
I think you can make the patch yourself - the comment change you
describe seems unrelated to this patch set.
quoted hunk
Ok. Like in grep_submodule(), this should no longer add the submodule
ODB to the alternates list, so this call is now mostly used as a
fallback and also for testing.
To see if we are indeed testing this add-to-alternates case, I
reverted the change that made the code read from the submodule instead
of the_repository:
@@ -1805,7 +1805,7 @@ int git_config_from_blob_oid(config_fn_t fn,unsignedlongsize;intret;-buf=repo_read_object_file(repo,oid,&type,&size);+buf=read_object_file(oid,&type,&size);
Then, I ran t7814-grep-recurse-submodules.sh , where you've added the
GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1 envvar. This correctly
produced the following error:
BUG: submodule.c:205: register_all_submodule_odb_as_alternates() called
[...]
not ok 23 - grep --recurse-submodules with submodules without
.gitmodules in the working tree
Nice! So the change made by this patch is covered by test 23. I think
it would be nice to mention that in this patch's message.
Thanks for checking this. I'll mention this in the commit message.
From: Jonathan Tan <hidden> Date: 2021-08-16 20:09:43
quoted
@@ -1827,7 +1829,7 @@ static int git_config_from_blob_ref(config_fn_t fn, if (get_oid(name, &oid) < 0)
This should be `repo_get_oid(repo, ...)` now.
Ah, good catch! This wasn't caught by the tests because the submodule
config mechanism always passes a full-length hexadecimal string hash as
"name" - and probably would never be caught because
git_config_from_blob_ref() is only called from config_with_options(),
which is called with non-NULL source from only 2 files:
submodule-config.c (this one) and builtin/config.c (which most likely
will never operate on any repo other than the_repository). I'll refactor
the API to avoid this situation in the first place.
From: Jonathan Tan <hidden> Date: 2021-08-16 20:57:18
quoted
quoted
@@ -1827,7 +1829,7 @@ static int git_config_from_blob_ref(config_fn_t fn, if (get_oid(name, &oid) < 0)
This should be `repo_get_oid(repo, ...)` now.
Ah, good catch! This wasn't caught by the tests because the submodule
config mechanism always passes a full-length hexadecimal string hash as
"name" - and probably would never be caught because
git_config_from_blob_ref() is only called from config_with_options(),
which is called with non-NULL source from only 2 files:
submodule-config.c (this one) and builtin/config.c (which most likely
will never operate on any repo other than the_repository). I'll refactor
the API to avoid this situation in the first place.
The refactoring I was thinking of was parsing the OID in
builtin/config.c and then passing only the OID (instead of a
user-provided string that could contain anything), but that doesn't
really work because the user-provided string is used in certain outputs.
I'll just change it to repo_get_oid() in this patch.
From: Jonathan Tan <hidden> Date: 2021-08-16 21:10:04
Thanks for reviewing, everyone. Here are the requested changes.
Jonathan Tan (8):
submodule: lazily add submodule ODBs as alternates
grep: use submodule-ODB-as-alternate lazy-addition
grep: typesafe versions of grep_source_init
grep: read submodule entry with explicit repo
grep: allocate subrepos on heap
grep: add repository to OID grep sources
submodule-config: pass repo upon blob config read
t7814: show lack of alternate ODB-adding
builtin/grep.c | 64 +++++++++++++++++++-----------
config.c | 20 ++++++----
config.h | 3 ++
grep.c | 51 +++++++++++++++---------
grep.h | 22 ++++++++--
object-file.c | 5 +++
submodule-config.c | 5 ++-
submodule.c | 25 +++++++++++-
submodule.h | 8 ++++
t/README | 10 +++++
t/t7814-grep-recurse-submodules.sh | 3 ++
11 files changed, 161 insertions(+), 55 deletions(-)
Range-diff against v2:
1: 5994a517e8 = 1: 5994a517e8 submodule: lazily add submodule ODBs as alternates
2: 31e9b914c4 = 2: 31e9b914c4 grep: use submodule-ODB-as-alternate lazy-addition
3: aa3f1f3c89 = 3: aa3f1f3c89 grep: typesafe versions of grep_source_init
4: 050deacfb7 = 4: 050deacfb7 grep: read submodule entry with explicit repo
5: 3f24815224 ! 5: 7d1eeac4b5 grep: allocate subrepos on heap
@@ builtin/grep.c: static void work_done(struct work_item *w)
+ repo_clear(repos_to_free[i]);
+ free(repos_to_free[i]);
+ }
-+ free(repos_to_free);
++ FREE_AND_NULL(repos_to_free);
+ repos_to_free_nr = 0;
+ repos_to_free_alloc = 0;
+}
6: 50c69a988b ! 6: f362fc278c grep: add repository to OID grep sources
@@ grep.h: struct grep_opt {
+
+ /*
+ * NEEDSWORK: See if we can remove this field, because the repository
-+ * should probably be per-source, not per-repo. This is potentially the
-+ * cause of at least one bug - "git grep" ignoring the textconv
-+ * attributes from submodules. See [1] for more information.
++ * should probably be per-source. That is, grep.c functions using this
++ * field should probably start using "repo" in "struct grep_source"
++ * instead.
++ *
++ * This is potentially the cause of at least one bug - "git grep"
++ * ignoring the textconv attributes from submodules. See [1] for more
++ * information.
+ * [1] https://lore.kernel.org/git/CAHd-oW5iEQarYVxEXoTG-ua2zdoybTrSjCBKtO0YT292fm0NQQ@mail.gmail.com/
+ */
struct repository *repo;
7: 94db10a4e5 ! 7: 8b86618531 submodule-config: pass repo upon blob config read
@@ Commit message
submodule's ODB as an alternate and then reading an object from
the_repository.
+ This makes the "grep --recurse-submodules with submodules without
+ .gitmodules in the working tree" test in t7814 work when
+ GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB is true.
+
Signed-off-by: Jonathan Tan [off-list ref]
## config.c ##
@@ config.c: int git_config_from_blob_oid(config_fn_t fn,
const char *name,
void *data)
{
-@@ config.c: static int git_config_from_blob_ref(config_fn_t fn,
+ struct object_id oid;
- if (get_oid(name, &oid) < 0)
+- if (get_oid(name, &oid) < 0)
++ if (repo_get_oid(repo, name, &oid) < 0)
return error(_("unable to resolve config blob '%s'"), name);
- return git_config_from_blob_oid(fn, name, &oid, data);
+ return git_config_from_blob_oid(fn, name, repo, &oid, data);
8: 4a51fcfb77 = 8: 4b3176f99e t7814: show lack of alternate ODB-adding
--
2.33.0.rc1.237.g0d66db33f3-goog
From: Jonathan Tan <hidden> Date: 2021-08-16 21:10:05
Teach Git to add submodule ODBs as alternates to the object store of
the_repository only upon the first access of an object not in
the_repository, and not when add_submodule_odb() is called.
This provides a means of gradually migrating from accessing a
submodule's object through alternates to accessing a submodule's object
by explicitly passing its repository object. Any Git command can declare
that it might access submodule objects by calling add_submodule_odb()
(as they do now), but the submodule ODBs themselves will not be added
until needed, so individual commands and/or combinations of arguments
can be migrated one by one.
[The advantage of explicit repository-object passing is code clarity (it
is clear which repository an object read is from), performance (there is
no need to linearly search through all submodule ODBs whenever an object
is accessed from any repository, whether superproject or submodule), and
the possibility of future features like partial clone submodules (which
right now is not possible because if an object is missing, we do not
know which repository to lazy-fetch into).]
This commit also introduces an environment variable that a test may set
to make the actual registration of alternates fatal, in order to
demonstrate that its codepaths do not need this registration.
Signed-off-by: Jonathan Tan <redacted>
---
object-file.c | 5 +++++
submodule.c | 20 +++++++++++++++++++-
submodule.h | 7 +++++++
t/README | 10 ++++++++++
4 files changed, 41 insertions(+), 1 deletion(-)
@@ -32,6 +32,7 @@#include"packfile.h"#include"object-store.h"#include"promisor-remote.h"+#include"submodule.h"/* The maximum size for an object header. */#define MAX_HEADER_LEN 32
@@ -1592,6 +1593,10 @@ static int do_oid_object_info_extended(struct repository *r,break;}+if(register_all_submodule_odb_as_alternates())+/* We added some alternates; retry */+continue;+/* Check if it is a missing object */if(fetch_if_missing&&repo_has_promisor_remote(r)&&!already_retried&&
@@ -448,6 +448,16 @@ GIT_TEST_CHECKOUT_WORKERS=<n> overrides the 'checkout.workers' setting to <n> and 'checkout.thresholdForParallelism' to 0, forcing the execution of the parallel-checkout code.+GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=<boolean>, when true, makes+registering submodule ODBs as alternates a fatal action. Support for+this environment variable can be removed once the migration to+explicitly providing repositories when accessing submodule objects is+complete (in which case we might want to replace this with a trace2+call so that users can make it visible if accessing submodule objects+without an explicit repository still happens) or needs to be abandoned+for whatever reason (in which case the migrated codepaths still retain+their performance benefits).+ Naming Tests ------------
From: Jonathan Tan <hidden> Date: 2021-08-16 21:10:06
In the parent commit, Git was taught to add submodule ODBs as alternates
lazily, but grep does not use this because it computes the path to add
directly, not going through add_submodule_odb(). Add an equivalent to
add_submodule_odb() that takes the exact ODB path and teach grep to use
it.
Signed-off-by: Jonathan Tan <redacted>
---
builtin/grep.c | 2 +-
submodule.c | 5 +++++
submodule.h | 1 +
3 files changed, 7 insertions(+), 1 deletion(-)
From: Jonathan Tan <hidden> Date: 2021-08-16 21:10:09
grep_source_init() can create "struct grep_source" objects and,
depending on the value of the type passed, some void-pointer parameters have
different meanings. Because one of these types (GREP_SOURCE_OID) will
require an additional parameter in a subsequent patch, take the
opportunity to increase clarity and type safety by replacing this
function with individual functions for each type.
Signed-off-by: Jonathan Tan <redacted>
---
builtin/grep.c | 4 ++--
grep.c | 46 ++++++++++++++++++++++++++++------------------
grep.h | 7 ++++---
3 files changed, 34 insertions(+), 23 deletions(-)
From: Jonathan Tan <hidden> Date: 2021-08-16 21:10:11
Replace an existing parse_object_or_die() call (which implicitly works
on the_repository) with a function call that allows a repository to be
passed in. There is no such direct equivalent to parse_object_or_die(),
but we only need the type of the object, so replace with
oid_object_info().
Signed-off-by: Jonathan Tan <redacted>
---
builtin/grep.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
@@ -457,27 +457,27 @@ static int grep_submodule(struct grep_opt *opt,subopt.repo=&subrepo;if(oid){-structobject*object;+enumobject_typeobject_type;structtree_desctree;void*data;unsignedlongsize;structstrbufbase=STRBUF_INIT;obj_read_lock();-object=parse_object_or_die(oid,NULL);+object_type=oid_object_info(&subrepo,oid,NULL);obj_read_unlock();data=read_object_with_reference(&subrepo,-&object->oid,tree_type,+oid,tree_type,&size,NULL);if(!data)-die(_("unable to read tree (%s)"),oid_to_hex(&object->oid));+die(_("unable to read tree (%s)"),oid_to_hex(oid));strbuf_addstr(&base,filename);strbuf_addch(&base,'/');init_tree_desc(&tree,data,size);hit=grep_tree(&subopt,pathspec,&tree,&base,base.len,-object->type==OBJ_COMMIT);+object_type==OBJ_COMMIT);strbuf_release(&base);free(data);}else{
From: Jonathan Tan <hidden> Date: 2021-08-16 21:10:12
Currently, struct repository objects corresponding to submodules are
allocated on the stack in grep_submodule(). This currently works because
they will not be used once grep_submodule() exits, but a subsequent
patch will require these structs to be accessible for longer (perhaps
even in another thread). Allocate them on the heap and clear them only
at the very end.
Signed-off-by: Jonathan Tan <redacted>
---
builtin/grep.c | 39 ++++++++++++++++++++++++++++++---------
1 file changed, 30 insertions(+), 9 deletions(-)
@@ -65,6 +65,9 @@ static int todo_done;/* Has all work items been added? */staticintall_work_added;+staticstructrepository**repos_to_free;+staticsize_trepos_to_free_nr,repos_to_free_alloc;+/* This lock protects all the variables above. */staticpthread_mutex_tgrep_mutex;
From: Jonathan Tan <hidden> Date: 2021-08-16 21:10:15
Record the repository whenever an OID grep source is created, and teach
the worker threads to explicitly provide the repository when accessing
objects.
Signed-off-by: Jonathan Tan <redacted>
---
builtin/grep.c | 15 ++++++---------
grep.c | 7 +++++--
grep.h | 17 ++++++++++++++++-
3 files changed, 27 insertions(+), 12 deletions(-)
From: Jonathan Tan <hidden> Date: 2021-08-16 21:10:16
When reading the config of a submodule, if reading from a blob, read
using an explicitly specified repository instead of by adding the
submodule's ODB as an alternate and then reading an object from
the_repository.
This makes the "grep --recurse-submodules with submodules without
.gitmodules in the working tree" test in t7814 work when
GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB is true.
Signed-off-by: Jonathan Tan <redacted>
---
config.c | 20 +++++++++++++-------
config.h | 3 +++
submodule-config.c | 5 +++--
3 files changed, 19 insertions(+), 9 deletions(-)
@@ -49,6 +49,8 @@ const char *config_scope_name(enum config_scope scope);structgit_config_source{unsignedintuse_stdin:1;constchar*file;+/* The repository if blob is not NULL; leave blank for the_repository */+structrepository*repo;constchar*blob;enumconfig_scopescope;};
@@ -136,6 +138,7 @@ int git_config_from_mem(config_fn_t fn,constchar*buf,size_tlen,void*data,conststructconfig_options*opts);intgit_config_from_blob_oid(config_fn_tfn,constchar*name,+structrepository*repo,conststructobject_id*oid,void*data);voidgit_config_push_parameter(constchar*text);voidgit_config_push_env(constchar*spec);
From: Jonathan Tan <hidden> Date: 2021-08-16 21:10:17
The previous patches have made "git grep" no longer need to add
submodule ODBs as alternates, at least for the code paths tested in
t7814. Demonstrate this by making adding a submodule ODB as an alternate
fatal in this test.
Signed-off-by: Jonathan Tan <redacted>
---
t/t7814-grep-recurse-submodules.sh | 3 +++
1 file changed, 3 insertions(+)
On Mon, Aug 16, 2021 at 6:10 PM Jonathan Tan [off-list ref] wrote:
Thanks for reviewing, everyone. Here are the requested changes.
Thanks. You've addressed all my comments from the previous rounds.
One minor suggestion which is not worth a re-roll on its own:
Range-diff against v2:
[...]
7: 94db10a4e5 ! 7: 8b86618531 submodule-config: pass repo upon blob config read
@@ Commit message
When reading the config of a submodule, if reading from a blob, read
using an explicitly specified repository instead of by adding the
submodule's ODB as an alternate and then reading an object from
the_repository.
+ This makes the "grep --recurse-submodules with submodules without
+ .gitmodules in the working tree" test in t7814 work when
+ GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB is true.
This sounds to me as if the test was previously failing when
GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB is true, which is not the case.
I think an alternative could be:
This code path is exercised by the "grep --recurse-submodules with
submodules without .gitmodules in the working tree" test in t7814. The
test passes with GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1,
showing that we indeed are no longer accessing the submodule's ODB
through the alternates list.
But it's really a minor thing. With or without this change:
Reviewed-by: Matheus Tavares <redacted>
Record the repository whenever an OID grep source is created, and teach
the worker threads to explicitly provide the repository when accessing
objects.
[...]
I ran into this comment and read the linked E-Mail, and then the
downthread
https://lore.kernel.org/git/CAHd-oW6uG1fap-T4UF17bJmjoHAqWCDq9KbY+_8a3cEnnfATxg@mail.gmail.com/;
Given Matheus's "I've somehow missed this guard and the..." there I'm
not quite sure what/if we should be doing here & what this comment is
recommending? I.e. do we still need to adjust the call chains as noted
in the E-Mail the comment links to, or not?
On Mon, Sep 27, 2021 at 9:09 AM Ævar Arnfjörð Bjarmason [off-list ref] wrote:
On Mon, Aug 16 2021, Jonathan Tan wrote:
quoted
Record the repository whenever an OID grep source is created, and teach
the worker threads to explicitly provide the repository when accessing
objects.
[...]
struct grep_pat *header_list;
struct grep_pat **header_tail;
struct grep_expr *pattern_expression;
+
+ /*
+ * NEEDSWORK: See if we can remove this field, because the repository
+ * should probably be per-source. That is, grep.c functions using this
+ * field should probably start using "repo" in "struct grep_source"
+ * instead.
+ *
+ * This is potentially the cause of at least one bug - "git grep"
+ * ignoring the textconv attributes from submodules. See [1] for more
+ * information.
+ * [1] https://lore.kernel.org/git/CAHd-oW5iEQarYVxEXoTG-ua2zdoybTrSjCBKtO0YT292fm0NQQ@mail.gmail.com/
+ */
struct repository *repo;
+
I ran into this comment and read the linked E-Mail, and then the
downthread
https://lore.kernel.org/git/CAHd-oW6uG1fap-T4UF17bJmjoHAqWCDq9KbY+_8a3cEnnfATxg@mail.gmail.com/;
Given Matheus's "I've somehow missed this guard and the..." there I'm
not quite sure what/if we should be doing here & what this comment is
recommending? I.e. do we still need to adjust the call chains as noted
in the E-Mail the comment links to, or not?
I think we should still adjust the call chains, yes. The downthread
message you mentioned is kind of a tangent about performance, where
Junio helped me understand something I had previously missed in the
code, regarding the persistence of the attributes stack.
But the issue that started the thread was about a correctness problem:
the superproject textconv attributes are being used on submodules'
files when running `git grep` with `--recurse-submodules --textconv`.
The three cases to consider are:
- .gitattributes from the working tree
- .gitattributes from the index
- .git/info/attributes
On all these cases, the superproject attributes are being used on the
submodule. Additionally, if the superproject does not define any
attribute, the submodule attributes are being ignored in all cases
except by the first one (but that is only because the code sees the
.gitattributes file on the submodule as if it were a "regular"
subdirectory of the surperproject. So the submodule's .gitattribures
takes higher precedence when evaluating the attributes for files in
that directory).
Another issue is that the textconv cache is always saved to (and read
from) the superproject gitdir, even for submodules' files.
Here are some test cases that demonstrate these issues:
-- snipsnap --
@@ -441,4 +441,104 @@ test_expect_success 'grep --recurse-submodules with --cached ignores worktree motest_must_failgitgrep--recurse-submodules--cached"A modified line in submodule">actual2>&1&&test_must_be_emptyactual'++test_expect_failure'grep --textconv: superproject .gitattributes does not affect submodules''+reset_and_clean&&+test_config_globaldiff.d2x.textconv"sed -e \"s/d/x/\""&&+echo"a diff=d2x">.gitattributes&&++cat>expect<<-\EOF&&+a:(1|2)x(3|4)+EOF+gitgrep--textconv--recurse-submodulesx>actual&&+test_cmpexpectactual+'++test_expect_failure'grep --textconv: superproject .gitattributes (from index) does not affect submodules''+reset_and_clean&&+test_config_globaldiff.d2x.textconv"sed -e \"s/d/x/\""&&+echo"a diff=d2x">.gitattributes&&+gitadd.gitattributes&&+rm.gitattributes&&++cat>expect<<-\EOF&&+a:(1|2)x(3|4)+EOF+gitgrep--textconv--recurse-submodulesx>actual&&+test_cmpexpectactual+'++test_expect_failure'grep --textconv: superproject .git/info/attributes does not affect submodules''+reset_and_clean&&+test_config_globaldiff.d2x.textconv"sed -e \"s/d/x/\""&&+super_attr="$(gitrev-parse--path-format=relative--git-pathinfo/attributes)"&&+test_when_finishedrm-f"$super_attr"&&+echo"a diff=d2x">"$super_attr"&&++cat>expect<<-\EOF&&+a:(1|2)x(3|4)+EOF+gitgrep--textconv--recurse-submodulesx>actual&&+test_cmpexpectactual+'++test_expect_success'grep --textconv corectly reads submodule .gitattributes''+reset_and_clean&&+test_config_globaldiff.d2x.textconv"sed -e \"s/d/x/\""&&+echo"a diff=d2x">submodule/.gitattributes&&++cat>expect<<-\EOF&&+submodule/a:(1|2)x(3|4)+EOF+gitgrep--textconv--recurse-submodulesx>actual&&+test_cmpexpectactual+'++test_expect_failure'grep --textconv corectly reads submodule .gitattributes (from index)''+reset_and_clean&&+test_config_globaldiff.d2x.textconv"sed -e \"s/d/x/\""&&+echo"a diff=d2x">submodule/.gitattributes&&+git-Csubmoduleadd.gitattributes&&+rmsubmodule/.gitattributes&&++cat>expect<<-\EOF&&+submodule/a:(1|2)x(3|4)+EOF+gitgrep--textconv--recurse-submodulesx>actual&&+test_cmpexpectactual+'++test_expect_failure'grep --textconv corectly reads submodule .git/info/attributes''+reset_and_clean&&+test_config_globaldiff.d2x.textconv"sed -e \"s/d/x/\""&&++# Workaround: we use --path-format=relative because the absolute path+# contains whitespaces and that seems to confuse test_when_finished+#+submodule_attr="submodule/$(git-Csubmodulerev-parse--path-format=relative--git-pathinfo/attributes)"&&+test_when_finishedrm-f"$submodule_attr"&&+echo"a diff=d2x">"$submodule_attr"&&++cat>expect<<-\EOF&&+submodule/a:(1|2)x(3|4)+EOF+gitgrep--textconv--recurse-submodulesx>actual&&+test_cmpexpectactual+'++test_expect_failure'grep saves textconv cache in the appropriated repository''+reset_and_clean&&+test_config_globaldiff.d2x_cached.textconv"sed -e \"s/d/x/\""&&+test_config_globaldiff.d2x_cached.cachetextconvtrue&&+echo"a diff=d2x_cached">submodule/.gitattributes&&++# Note: we only read/write to the textconv cache when grepping from an+# OID as the working tree file might have modifications. That is why+# we use --cached here.+#+gitgrep--textconv--cached--recurse-submodulesx&&+test_path_is_missing"$(gitrev-parse--git-pathrefs/notes/textconv/d2x_cached)"&&+test_path_is_file"$(git-Csubmodulerev-parse--git-pathrefs/notes/textconv/d2x_cached)"+'+ test_done--
Junio seemed to agree that the behavior I described above is not the
correct one:
"None of the attributes defined in the superproject
should affect the paths in the submodule, as it is a totally
separate project, oblivious to the existence of enclosing the
superproject."
But he raised an important concern regarding how to fix this without
affecting [too much] performance:
"As there is only one attribute cache IIUC, invalidating
the whole cache for the top-level and replacing it with the one for
a submodule, every time we cross the module boundary, would probably
have a negative effect on the performance, and I am not sure what
would happen if you run more than one threads working in different
repositories (i.e. top-level and submodules)."
Maybe we would need a different attributes stack for each repository?
On Mon, Sep 27, 2021 at 9:09 AM Ævar Arnfjörð Bjarmason [off-list ref] wrote:
quoted
On Mon, Aug 16 2021, Jonathan Tan wrote:
quoted
Record the repository whenever an OID grep source is created, and teach
the worker threads to explicitly provide the repository when accessing
objects.
[...]
struct grep_pat *header_list;
struct grep_pat **header_tail;
struct grep_expr *pattern_expression;
+
+ /*
+ * NEEDSWORK: See if we can remove this field, because the repository
+ * should probably be per-source. That is, grep.c functions using this
+ * field should probably start using "repo" in "struct grep_source"
+ * instead.
+ *
+ * This is potentially the cause of at least one bug - "git grep"
+ * ignoring the textconv attributes from submodules. See [1] for more
+ * information.
+ * [1] https://lore.kernel.org/git/CAHd-oW5iEQarYVxEXoTG-ua2zdoybTrSjCBKtO0YT292fm0NQQ@mail.gmail.com/
+ */
struct repository *repo;
+
I ran into this comment and read the linked E-Mail, and then the
downthread
https://lore.kernel.org/git/CAHd-oW6uG1fap-T4UF17bJmjoHAqWCDq9KbY+_8a3cEnnfATxg@mail.gmail.com/;
Given Matheus's "I've somehow missed this guard and the..." there I'm
not quite sure what/if we should be doing here & what this comment is
recommending? I.e. do we still need to adjust the call chains as noted
in the E-Mail the comment links to, or not?
I think we should still adjust the call chains, yes. The downthread
message you mentioned is kind of a tangent about performance, where
Junio helped me understand something I had previously missed in the
code, regarding the persistence of the attributes stack.
But the issue that started the thread was about a correctness problem:
the superproject textconv attributes are being used on submodules'
files when running `git grep` with `--recurse-submodules --textconv`.
The three cases to consider are:
- .gitattributes from the working tree
- .gitattributes from the index
- .git/info/attributes
On all these cases, the superproject attributes are being used on the
submodule. Additionally, if the superproject does not define any
attribute, the submodule attributes are being ignored in all cases
except by the first one (but that is only because the code sees the
.gitattributes file on the submodule as if it were a "regular"
subdirectory of the surperproject. So the submodule's .gitattribures
takes higher precedence when evaluating the attributes for files in
that directory).
Another issue is that the textconv cache is always saved to (and read
from) the superproject gitdir, even for submodules' files.
Here are some test cases that demonstrate these issues:
-- snipsnap --
@@ -441,4 +441,104 @@ test_expect_success 'grep --recurse-submodules with --cached ignores worktree motest_must_failgitgrep--recurse-submodules--cached"A modified line in submodule">actual2>&1&&test_must_be_emptyactual'++test_expect_failure'grep --textconv: superproject .gitattributes does not affect submodules''+reset_and_clean&&+test_config_globaldiff.d2x.textconv"sed -e \"s/d/x/\""&&+echo"a diff=d2x">.gitattributes&&++cat>expect<<-\EOF&&+a:(1|2)x(3|4)+EOF+gitgrep--textconv--recurse-submodulesx>actual&&+test_cmpexpectactual+'++test_expect_failure'grep --textconv: superproject .gitattributes (from index) does not affect submodules''+reset_and_clean&&+test_config_globaldiff.d2x.textconv"sed -e \"s/d/x/\""&&+echo"a diff=d2x">.gitattributes&&+gitadd.gitattributes&&+rm.gitattributes&&++cat>expect<<-\EOF&&+a:(1|2)x(3|4)+EOF+gitgrep--textconv--recurse-submodulesx>actual&&+test_cmpexpectactual+'++test_expect_failure'grep --textconv: superproject .git/info/attributes does not affect submodules''+reset_and_clean&&+test_config_globaldiff.d2x.textconv"sed -e \"s/d/x/\""&&+super_attr="$(gitrev-parse--path-format=relative--git-pathinfo/attributes)"&&+test_when_finishedrm-f"$super_attr"&&+echo"a diff=d2x">"$super_attr"&&++cat>expect<<-\EOF&&+a:(1|2)x(3|4)+EOF+gitgrep--textconv--recurse-submodulesx>actual&&+test_cmpexpectactual+'++test_expect_success'grep --textconv corectly reads submodule .gitattributes''+reset_and_clean&&+test_config_globaldiff.d2x.textconv"sed -e \"s/d/x/\""&&+echo"a diff=d2x">submodule/.gitattributes&&++cat>expect<<-\EOF&&+submodule/a:(1|2)x(3|4)+EOF+gitgrep--textconv--recurse-submodulesx>actual&&+test_cmpexpectactual+'++test_expect_failure'grep --textconv corectly reads submodule .gitattributes (from index)''+reset_and_clean&&+test_config_globaldiff.d2x.textconv"sed -e \"s/d/x/\""&&+echo"a diff=d2x">submodule/.gitattributes&&+git-Csubmoduleadd.gitattributes&&+rmsubmodule/.gitattributes&&++cat>expect<<-\EOF&&+submodule/a:(1|2)x(3|4)+EOF+gitgrep--textconv--recurse-submodulesx>actual&&+test_cmpexpectactual+'++test_expect_failure'grep --textconv corectly reads submodule .git/info/attributes''+reset_and_clean&&+test_config_globaldiff.d2x.textconv"sed -e \"s/d/x/\""&&++# Workaround: we use --path-format=relative because the absolute path+# contains whitespaces and that seems to confuse test_when_finished+#+submodule_attr="submodule/$(git-Csubmodulerev-parse--path-format=relative--git-pathinfo/attributes)"&&+test_when_finishedrm-f"$submodule_attr"&&+echo"a diff=d2x">"$submodule_attr"&&++cat>expect<<-\EOF&&+submodule/a:(1|2)x(3|4)+EOF+gitgrep--textconv--recurse-submodulesx>actual&&+test_cmpexpectactual+'++test_expect_failure'grep saves textconv cache in the appropriated repository''+reset_and_clean&&+test_config_globaldiff.d2x_cached.textconv"sed -e \"s/d/x/\""&&+test_config_globaldiff.d2x_cached.cachetextconvtrue&&+echo"a diff=d2x_cached">submodule/.gitattributes&&++# Note: we only read/write to the textconv cache when grepping from an+# OID as the working tree file might have modifications. That is why+# we use --cached here.+#+gitgrep--textconv--cached--recurse-submodulesx&&+test_path_is_missing"$(gitrev-parse--git-pathrefs/notes/textconv/d2x_cached)"&&+test_path_is_file"$(git-Csubmodulerev-parse--git-pathrefs/notes/textconv/d2x_cached)"+'+ test_done
Thanks! I think it would be very good to have these tests in-tree along
with an updated comment pointing to them.