@@ -285,23 +285,26 @@ static void strip_trailing_slashes(char *dir)staticintadd_one_reference(structstring_list_item*item,void*cb_data){-structstrbufsb=STRBUF_INIT;+structstrbuferr=STRBUF_INIT;int*required=cb_data;-char*ref_git=compute_alternate_path(item->string,&sb);+char*ref_git=compute_alternate_path(item->string,&err);if(!ref_git){if(*required)-die("%s",sb.buf);+die("%s",err.buf);elsefprintf(stderr,_("info: Could not add alternate for '%s': %s\n"),-item->string,sb.buf);+item->string,err.buf);}else{+structstrbufsb=STRBUF_INIT;strbuf_addf(&sb,"%s/objects",ref_git);add_to_alternates_file(sb.buf);+strbuf_release(&sb);}-strbuf_release(&sb);+strbuf_release(&err);+free(ref_git);return0;}
@@ -435,11 +435,12 @@ char *compute_alternate_path(const char *path, struct strbuf *err){char*ref_git=NULL;constchar*repo,*ref_git_s;-structstrbuferr_buf=STRBUF_INIT;+intseen_error=0;ref_git_s=real_path_if_valid(path);if(!ref_git_s){-strbuf_addf(&err_buf,_("path '%s' does not exist"),path);+seen_error=1;+strbuf_addf(err,_("path '%s' does not exist"),path);gotoout;}else/*
@@ -462,40 +463,41 @@ char *compute_alternate_path(const char *path, struct strbuf *err)ref_git=ref_git_git;}elseif(!is_directory(mkpath("%s/objects",ref_git))){structstrbufsb=STRBUF_INIT;+seen_error=1;if(get_common_dir(&sb,ref_git)){-strbuf_addf(&err_buf,+strbuf_addf(err,_("reference repository '%s' as a linked ""checkout is not supported yet."),path);gotoout;}-strbuf_addf(&err_buf,_("reference repository '%s' is not a "+strbuf_addf(err,_("reference repository '%s' is not a ""local repository."),path);gotoout;}if(!access(mkpath("%s/shallow",ref_git),F_OK)){-strbuf_addf(&err_buf,_("reference repository '%s' is shallow"),+strbuf_addf(err,_("reference repository '%s' is shallow"),path);+seen_error=1;gotoout;}if(!access(mkpath("%s/info/grafts",ref_git),F_OK)){-strbuf_addf(&err_buf,+strbuf_addf(err,_("reference repository '%s' is grafted"),path);+seen_error=1;gotoout;}out:-if(err_buf.len){-strbuf_addbuf(err,&err_buf);+if(seen_error){free(ref_git);ref_git=NULL;}-strbuf_release(&err_buf);returnref_git;}
Thanks,
Stefan
v4:
Thanks to Junios critial questions regarding the design, I took a step back
to look at the bigger picture, again.
new patches:
clone: factor out checking for an alternate path
clone: recursive and reference option triggers submodule alternates
The last patch redesigns completely how we approach the problem.
Now there are no new command line options (that relate to the problem
of marrying --recursive and --reference), but instead we communicate
everything via configuration options to have a lasting effect (i.e.
submodule update remembers the decision of the initial setup)
Thanks,
Stefan
v3:
Thanks to Junios critial questions regarding the design, I took a step back
to look at the bigger picture.
--super-reference sounds confusing. (what is the super referring to?)
So drop that approach.
Instead we'll compute where the reference might be in the superproject scope
and ask the submodule clone operation to consider an optional reference.
If the referenced alternate is not there, we'll just warn about it and
carry on.
* fixed the style in patch 2.
* fixed another bug in the last patch, that is unrelated, but would have helped
me a lot.
Thanks,
Stefan
Documentation/git-clone.txt | 9 ++++++++-
builtin/clone.c | 36 ++++++++++++++++++++++++++++--------
builtin/submodule--helper.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
git-submodule.sh | 2 +-
t/t7408-submodule-reference.sh | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------------------------------------------------
5 files changed, 217 insertions(+), 97 deletions(-)
v2:
* fixed the p1,2 cleanups
* added documentation to patches 5,6
* improved commit message in v4
Thanks,
Stefan
v1:
Currently when cloning a superproject with --recursive and --reference
only the superproject learns about its alternates. The submodules are
cloned independently, which may incur lots of network costs.
Assume that the reference repository has the submodules at the same
paths as the to-be-cloned submodule and try to setup alternates from
there.
Some submodules in the referenced superproject may not be there,
(they are just not initialized/cloned/checked out), which yields
an error for now. In future work we may want to soften the alternate
check and not die in the clone when one of the given alternates doesn't
exist.
patch 1,2 are modernizing style of t7408,
patches 3,4 are not strictly necessary, but I think it is a good thing
to not leave the submodule related C code in a crippled state (i.e.
allowing only one reference). The shell code would also need this update,
but it looked ugly to me, so I postpone it until more of the submodule code
is written in C.
Thanks,
Stefan
Stefan Beller (6):
t7408: modernize style
t7408: merge short tests, factor out testing method
submodule--helper module-clone: allow multiple references
submodule--helper update-clone: allow multiple references
submodule update: add super-reference flag
clone: reference flag is used for submodules as well
builtin/clone.c | 22 ++++--
builtin/submodule--helper.c | 45 ++++++++----
git-submodule.sh | 12 +++-
t/t7408-submodule-reference.sh | 153 +++++++++++++++++++++++------------------
4 files changed, 147 insertions(+), 85 deletions(-)
--
2.9.2.572.g9d9644e.dirty
From: Stefan Beller <hidden> Date: 2016-08-15 21:53:48
No functional change intended. This commit only changes formatting
to the style we recently use, e.g. starting the body of a test with a
single quote on the same line as the header, and then having the test
indented in the following lines.
Whenever we change directories, we do that in subshells.
Signed-off-by: Stefan Beller <redacted>
---
t/t7408-submodule-reference.sh | 140 +++++++++++++++++++++--------------------
1 file changed, 71 insertions(+), 69 deletions(-)
@@ -8,74 +8,76 @@ test_description='test clone --reference'base_dir=$(pwd)-U=$base_dir/UPLOAD_LOG--test_expect_success'preparing first repository'\-'test_create_repoA&&cdA&&-echofirst>file1&&-gitaddfile1&&-gitcommit-mA-initial'--cd"$base_dir"--test_expect_success'preparing second repository'\-'gitcloneAB&&cdB&&-echosecond>file2&&-gitaddfile2&&-gitcommit-mB-addition&&-gitrepack-a-d&&-gitprune'--cd"$base_dir"--test_expect_success'preparing superproject'\-'test_create_reposuper&&cdsuper&&-echofile>file&&-gitaddfile&&-gitcommit-mB-super-initial'--cd"$base_dir"--test_expect_success'submodule add --reference'\-'cdsuper&&gitsubmoduleadd--reference../B"file://$base_dir/A"sub&&-gitcommit-mB-super-added'--cd"$base_dir"--test_expect_success'after add: existence of info/alternates'\-'test_line_count = 1 super/.git/modules/sub/objects/info/alternates'--cd"$base_dir"--test_expect_success'that reference gets used with add'\-'cdsuper/sub&&-echo"0 objects, 0 kilobytes">expected&&-gitcount-objects>current&&-diffexpectedcurrent'--cd"$base_dir"--test_expect_success'cloning superproject'\-'git clone super super-clone'--cd"$base_dir"--test_expect_success'update with reference'\-'cd super-clone && git submodule update --init --reference ../B'--cd"$base_dir"--test_expect_success'after update: existence of info/alternates'\-'test_line_count = 1 super-clone/.git/modules/sub/objects/info/alternates'--cd"$base_dir"--test_expect_success'that reference gets used with update'\-'cdsuper-clone/sub&&-echo"0 objects, 0 kilobytes">expected&&-gitcount-objects>current&&-diffexpectedcurrent'--cd"$base_dir"+test_expect_success'preparing first repository''+test_create_repoA&&+(+cdA&&+echofirst>file1&&+gitaddfile1&&+gitcommit-mA-initial+)+'++test_expect_success'preparing second repository''+gitcloneAB&&+(+cdB&&+echosecond>file2&&+gitaddfile2&&+gitcommit-mB-addition&&+gitrepack-a-d&&+gitprune+)+'++test_expect_success'preparing superproject''+test_create_reposuper&&+(+cdsuper&&+echofile>file&&+gitaddfile&&+gitcommit-mB-super-initial+)+'++test_expect_success'submodule add --reference''+(+cdsuper&&+gitsubmoduleadd--reference../B"file://$base_dir/A"sub&&+gitcommit-mB-super-added+)+'++test_expect_success'after add: existence of info/alternates''+test_line_count=1super/.git/modules/sub/objects/info/alternates+'++test_expect_success'that reference gets used with add''+(+cdsuper/sub&&+echo"0 objects, 0 kilobytes">expected&&+gitcount-objects>current&&+diffexpectedcurrent+)+'++test_expect_success'cloning superproject''+gitclonesupersuper-clone+'++test_expect_success'update with reference''+cdsuper-clone&&gitsubmoduleupdate--init--reference../B+'++test_expect_success'after update: existence of info/alternates''+test_line_count=1super-clone/.git/modules/sub/objects/info/alternates+'++test_expect_success'that reference gets used with update''+cdsuper-clone/sub&&+echo"0 objects, 0 kilobytes">expected&&+gitcount-objects>current&&+diffexpectedcurrent+' test_done
From: Stefan Beller <hidden> Date: 2016-08-15 21:53:50
Tests consisting of one line each can be consolidated to have fewer tests
to run as well as fewer lines of code.
When having just a few git commands, do not create a new shell but
use the -C flag in Git to execute in the correct directory.
Signed-off-by: Stefan Beller <redacted>
---
t/t7408-submodule-reference.sh | 48 ++++++++++++++++++++++--------------------
1 file changed, 25 insertions(+), 23 deletions(-)
@@ -40,16 +49,14 @@ test_expect_success 'preparing superproject' ')'-test_expect_success'submodule add --reference''+test_expect_success'submodule add --reference uses alternates''(cdsuper&&gitsubmoduleadd--reference../B"file://$base_dir/A"sub&&-gitcommit-mB-super-added-)-'--test_expect_success'after add: existence of info/alternates''-test_line_count=1super/.git/modules/sub/objects/info/alternates+gitcommit-mB-super-added&&+gitrepack-ad+)&&+test_alternate_is_usedsuper/.git/modules/sub/objects/info/alternatessuper/sub' test_expect_success'that reference gets used with add''
@@ -61,23 +68,18 @@ test_expect_success 'that reference gets used with add' ')'-test_expect_success'cloning superproject''-gitclonesupersuper-clone-'--test_expect_success'update with reference''-cdsuper-clone&&gitsubmoduleupdate--init--reference../B-'--test_expect_success'after update: existence of info/alternates''-test_line_count=1super-clone/.git/modules/sub/objects/info/alternates-'+# The tests up to this point, and repositories created by them+# (A, B, super and super/sub), are about setting up the stage+# for subsequent tests and meant to be kept throughout the+# remainder of the test.+# Tests from here on, if they create their own test repository,+# are expected to clean after themselves.-test_expect_success'that reference gets used with update''-cdsuper-clone/sub&&-echo"0 objects, 0 kilobytes">expected&&-gitcount-objects>current&&-diffexpectedcurrent+test_expect_success'updating superproject keeps alternates''+test_when_finished"rm -rf super-clone"&&+gitclonesupersuper-clone&&+git-Csuper-clonesubmoduleupdate--init--reference../B&&+test_alternate_is_usedsuper-clone/.git/modules/sub/objects/info/alternatessuper-clone/sub' test_done
From: Stefan Beller <hidden> Date: 2016-08-15 21:53:55
Allow the user to pass in multiple references to update_clone.
Currently this is only internal API, but once the shell script is
replaced by a C version, this is needed.
This fixes an API bug between the shell script and the helper.
Currently the helper accepts "--reference" "--reference=foo"
as a OPT_STRING whose value happens to be "--reference=foo", and
then uses
if (suc->reference)
argv_array_push(&child->args, suc->reference)
where suc->reference _is_ "--reference=foo" when invoking the
underlying "git clone", it cancels out.
With this change we omit one of the "--reference" arguments when
passing references from the shell script to the helper.
Signed-off-by: Stefan Beller <redacted>
---
builtin/submodule--helper.c | 14 +++++++++-----
git-submodule.sh | 2 +-
2 files changed, 10 insertions(+), 6 deletions(-)
@@ -584,7 +584,7 @@ struct submodule_update_clone {/* configuration parameters which are passed on to the children */intquiet;intrecommend_shallow;-constchar*reference;+structstring_listreferences;constchar*depth;constchar*recursive_prefix;constchar*prefix;
From: Stefan Beller <hidden> Date: 2016-08-15 21:53:58
In a later patch we want to determine if a path is suitable as an
alternate from other commands than builtin/clone. Move the checking
functionality of `add_one_reference` to `compute_alternate_path` that is
defined in cache.h.
Signed-off-by: Stefan Beller <redacted>
---
builtin/clone.c | 43 +++++++-------------------------
cache.h | 1 +
sha1_file.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 86 insertions(+), 34 deletions(-)
@@ -282,44 +282,19 @@ static void strip_trailing_slashes(char *dir)staticintadd_one_reference(structstring_list_item*item,void*cb_data){-char*ref_git;-constchar*repo;-structstrbufalternate=STRBUF_INIT;--/* Beware: read_gitfile(), real_path() and mkpath() return static buffer */-ref_git=xstrdup(real_path(item->string));--repo=read_gitfile(ref_git);-if(!repo)-repo=read_gitfile(mkpath("%s/.git",ref_git));-if(repo){-free(ref_git);-ref_git=xstrdup(repo);-}--if(!repo&&is_directory(mkpath("%s/.git/objects",ref_git))){-char*ref_git_git=mkpathdup("%s/.git",ref_git);-free(ref_git);-ref_git=ref_git_git;-}elseif(!is_directory(mkpath("%s/objects",ref_git))){-structstrbufsb=STRBUF_INIT;-if(get_common_dir(&sb,ref_git))-die(_("reference repository '%s' as a linked checkout is not supported yet."),-item->string);-die(_("reference repository '%s' is not a local repository."),-item->string);-}+structstrbuferr=STRBUF_INIT;+structstrbufsb=STRBUF_INIT;+char*ref_git=compute_alternate_path(item->string,&err);-if(!access(mkpath("%s/shallow",ref_git),F_OK))-die(_("reference repository '%s' is shallow"),item->string);+if(!ref_git)+die("%s",err.buf);-if(!access(mkpath("%s/info/grafts",ref_git),F_OK))-die(_("reference repository '%s' is grafted"),item->string);+strbuf_addf(&sb,"%s/objects",ref_git);+add_to_alternates_file(sb.buf);-strbuf_addf(&alternate,"%s/objects",ref_git);-add_to_alternates_file(alternate.buf);-strbuf_release(&alternate);free(ref_git);+strbuf_release(&err);+strbuf_release(&sb);return0;}
@@ -425,6 +425,82 @@ void add_to_alternates_file(const char *reference)free(alts);}+/*+*Computetheexactpathanalternateisatandreturnsit.Incaseof+*errorNULLisreturnedandthehumanreadableerrorisaddedto`err`+*`path`mayberelativeandshouldpointto$GITDIR.+*`err`mustnotbenull.+*/+char*compute_alternate_path(constchar*path,structstrbuf*err)+{+char*ref_git=NULL;+constchar*repo,*ref_git_s;+intseen_error=0;++ref_git_s=real_path_if_valid(path);+if(!ref_git_s){+seen_error=1;+strbuf_addf(err,_("path '%s' does not exist"),path);+gotoout;+}else+/*+*Beware:read_gitfile(),real_path()andmkpath()+*returnstaticbuffer+*/+ref_git=xstrdup(ref_git_s);++repo=read_gitfile(ref_git);+if(!repo)+repo=read_gitfile(mkpath("%s/.git",ref_git));+if(repo){+free(ref_git);+ref_git=xstrdup(repo);+}++if(!repo&&is_directory(mkpath("%s/.git/objects",ref_git))){+char*ref_git_git=mkpathdup("%s/.git",ref_git);+free(ref_git);+ref_git=ref_git_git;+}elseif(!is_directory(mkpath("%s/objects",ref_git))){+structstrbufsb=STRBUF_INIT;+seen_error=1;+if(get_common_dir(&sb,ref_git)){+strbuf_addf(err,+_("reference repository '%s' as a linked "+"checkout is not supported yet."),+path);+gotoout;+}++strbuf_addf(err,_("reference repository '%s' is not a "+"local repository."),path);+gotoout;+}++if(!access(mkpath("%s/shallow",ref_git),F_OK)){+strbuf_addf(err,_("reference repository '%s' is shallow"),+path);+seen_error=1;+gotoout;+}++if(!access(mkpath("%s/info/grafts",ref_git),F_OK)){+strbuf_addf(err,+_("reference repository '%s' is grafted"),+path);+seen_error=1;+gotoout;+}++out:+if(seen_error){+free(ref_git);+ref_git=NULL;+}++returnref_git;+}+intforeach_alt_odb(alt_odb_fnfn,void*cb){structalternate_object_database*ent;
From: Stefan Beller <hidden> Date: 2016-08-15 21:54:03
In the next patch we introduce optional references; To better distinguish
between optional and required references we rename the variable.
Signed-off-by: Stefan Beller <redacted>
---
builtin/clone.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
@@ -50,7 +50,7 @@ static int option_verbosity;staticintoption_progress=-1;staticenumtransport_familyfamily;staticstructstring_listoption_config=STRING_LIST_INIT_NODUP;-staticstructstring_listoption_reference=STRING_LIST_INIT_NODUP;+staticstructstring_listoption_required_reference=STRING_LIST_INIT_NODUP;staticintoption_dissociate;staticintmax_jobs=-1;
@@ -79,7 +79,7 @@ static struct option builtin_clone_options[] = {N_("number of submodules cloned in parallel")),OPT_STRING(0,"template",&option_template,N_("template-directory"),N_("directory from which templates will be used")),-OPT_STRING_LIST(0,"reference",&option_reference,N_("repo"),+OPT_STRING_LIST(0,"reference",&option_required_reference,N_("repo"),N_("reference repository")),OPT_BOOL(0,"dissociate",&option_dissociate,N_("use --reference only while cloning")),
From: Stefan Beller <hidden> Date: 2016-08-15 21:54:08
In a later patch we want to try to create alternates for submodules,
but they might not exist in the referenced superproject. So add a way
to skip the non existing references and report them.
Signed-off-by: Stefan Beller <redacted>
---
Documentation/git-clone.txt | 5 ++++-
builtin/clone.c | 35 +++++++++++++++++++++++++----------
2 files changed, 29 insertions(+), 11 deletions(-)
@@ -90,13 +90,16 @@ If you want to break the dependency of a repository cloned with `-s` on its source repository, you can simply run `git repack -a` to copy all objects from the source repository into a pack in the cloned repository.---reference <repository>::+--reference[-if-able] <repository>:: If the reference repository is on the local machine, automatically setup `.git/objects/info/alternates` to obtain objects from the reference repository. Using an already existing repository as an alternate will require fewer objects to be copied from the repository being cloned, reducing network and local storage costs.+ When using the `--reference-if-able`, a non existing+ directory is skipped with a warning instead of aborting+ the clone. + *NOTE*: see the NOTE for the `--shared` option, and also the `--dissociate` option.
@@ -51,6 +51,7 @@ static int option_progress = -1;staticenumtransport_familyfamily;staticstructstring_listoption_config=STRING_LIST_INIT_NODUP;staticstructstring_listoption_required_reference=STRING_LIST_INIT_NODUP;+staticstructstring_listoption_optional_reference=STRING_LIST_INIT_NODUP;staticintoption_dissociate;staticintmax_jobs=-1;
@@ -81,6 +82,8 @@ static struct option builtin_clone_options[] = {N_("directory from which templates will be used")),OPT_STRING_LIST(0,"reference",&option_required_reference,N_("repo"),N_("reference repository")),+OPT_STRING_LIST(0,"reference-if-able",&option_optional_reference,+N_("repo"),N_("reference repository")),OPT_BOOL(0,"dissociate",&option_dissociate,N_("use --reference only while cloning")),OPT_STRING('o',"origin",&option_origin,N_("name"),
@@ -283,24 +286,36 @@ static void strip_trailing_slashes(char *dir)staticintadd_one_reference(structstring_list_item*item,void*cb_data){structstrbuferr=STRBUF_INIT;-structstrbufsb=STRBUF_INIT;+int*required=cb_data;char*ref_git=compute_alternate_path(item->string,&err);-if(!ref_git)-die("%s",err.buf);--strbuf_addf(&sb,"%s/objects",ref_git);-add_to_alternates_file(sb.buf);+if(!ref_git){+if(*required)+die("%s",err.buf);+else+fprintf(stderr,+_("info: Could not add alternate for '%s': %s\n"),+item->string,err.buf);+}else{+structstrbufsb=STRBUF_INIT;+strbuf_addf(&sb,"%s/objects",ref_git);+add_to_alternates_file(sb.buf);+strbuf_release(&sb);+}-free(ref_git);strbuf_release(&err);-strbuf_release(&sb);+free(ref_git);return0;}staticvoidsetup_reference(void){-for_each_string_list(&option_required_reference,add_one_reference,NULL);+intrequired=1;+for_each_string_list(&option_required_reference,+add_one_reference,&required);+required=0;+for_each_string_list(&option_optional_reference,+add_one_reference,&required);}staticvoidcopy_alternates(structstrbuf*src,structstrbuf*dst,
From: Stefan Beller <hidden> Date: 2016-08-15 21:54:11
When `--recursive` and `--reference` is given, it is reasonable to
expect that the submodules are created with references to the submodules
of the given alternate for the superproject.
An initial attempt to do this was presented to the mailing list, which
used flags that are passed around ("--super-reference") that instructed
the submodule clone to look for a reference in the submodules of the
referenced superproject. This is not well thought out, as any further
`submodule update` should also respect the initial setup.
When a new submodule is added to the superproject and the alternate
of the superproject does not know about that submodule yet, we rather
error out informing the user instead of being unclear if we did or did
not use a submodules alternate.
To solve this problem introduce new options that store the configuration
for what the user wanted originally.
Signed-off-by: Stefan Beller <redacted>
---
Documentation/config.txt | 12 ++++++
builtin/clone.c | 19 +++++++++
builtin/submodule--helper.c | 87 ++++++++++++++++++++++++++++++++++++++++++
t/t7408-submodule-reference.sh | 43 +++++++++++++++++++++
4 files changed, 161 insertions(+)
@@ -2837,6 +2837,18 @@ submodule.fetchJobs:: in parallel. A value of 0 will give some reasonable default. If unset, it defaults to 1.+submodule.alternateLocation::+ Specifies how the submodules obtain alternates when submodules are+ cloned. Possible values are `no`, `superproject`.+ By default `no` is assumed, which doesn't add references. When the+ value is set to `superproject` the submodule to be cloned computes+ its alternates location relative to the superprojects alternate.++submodule.alternateErrorStrategy+ Specifies how to treat errors with the alternates for a submodule+ as computed via `submodule.alternateLocation`. Possible values are+ `ignore`, `info`, `die`.+ tag.forceSignAnnotated:: A boolean to specify whether annotated tags created should be GPG signed. If `--annotate` is specified on the command line, it takes
@@ -947,6 +947,25 @@ int cmd_clone(int argc, const char **argv, const char *prefix)elsefprintf(stderr,_("Cloning into '%s'...\n"),dir);}++if(option_recursive){+if(option_required_reference.nr&&+option_optional_reference.nr)+die(_("clone --recursive is not compatible with "+"both --reference and --reference-if-able"));+elseif(option_required_reference.nr){+string_list_append(&option_config,+"submodule.alternateLocation=superproject");+string_list_append(&option_config,+"submodule.alternateErrorStrategy=die");+}elseif(option_optional_reference.nr){+string_list_append(&option_config,+"submodule.alternateLocation=superproject");+string_list_append(&option_config,+"submodule.alternateErrorStrategy=info");+}+}+init_db(option_template,INIT_DB_QUIET);write_config(&option_config);
@@ -82,4 +82,47 @@ test_expect_success 'updating superproject keeps alternates' 'test_alternate_is_usedsuper-clone/.git/modules/sub/objects/info/alternatessuper-clone/sub'+test_expect_success'submodules use alternates when cloning a superproject''+test_when_finished"rm -rf super-clone"&&+gitclone--referencesuper--recursivesupersuper-clone&&+(+cdsuper-clone&&+# test superproject has alternates setup correctly+test_alternate_is_used.git/objects/info/alternates.&&+# test submodule has correct setup+test_alternate_is_used.git/modules/sub/objects/info/alternatessub+)+'++test_expect_success'missing submodule alternate fails clone and submodule update''+test_when_finished"rm -rf super-clone"&&+gitclonesupersuper2&&+test_must_failgitclone--recursive--referencesuper2super2super-clone&&+(+cdsuper-clone&&+# test superproject has alternates setup correctly+test_alternate_is_used.git/objects/info/alternates.&&+# update of the submodule succeeds+test_must_failgitsubmoduleupdate--init&&+# and we have no alternates:+test_must_failtest_alternate_is_used.git/modules/sub/objects/info/alternatessub&&+test_must_failtest_path_is_filesub/file1+)+'++test_expect_success'ignoring missing submodule alternates passes clone and submodule update''+test_when_finished"rm -rf super-clone"&&+gitclone--reference-if-ablesuper2--recursivesuper2super-clone&&+(+cdsuper-clone&&+# test superproject has alternates setup correctly+test_alternate_is_used.git/objects/info/alternates.&&+# update of the submodule succeeds+gitsubmoduleupdate--init&&+# and we have no alternates:+test_must_failtest_alternate_is_used.git/modules/sub/objects/info/alternatessub&&+test_path_is_filesub/file1+)+'+ test_done
From: Jacob Keller <hidden> Date: 2016-08-23 22:05:07
On Mon, Aug 15, 2016 at 2:53 PM, Stefan Beller [off-list ref] wrote:
No functional change intended. This commit only changes formatting
to the style we recently use, e.g. starting the body of a test with a
single quote on the same line as the header, and then having the test
indented in the following lines.
Whenever we change directories, we do that in subshells.
I looked this over using -w to ignore whitespace changes, and it
appears to have no functional changes. Much cleaner style overall, and
easier to read the new file as it is now. The tests pass fine both
before and after this commit, and I don't see anything that should
functionally change the results.
Thanks,
Jake
From: Jacob Keller <hidden> Date: 2016-08-23 22:27:23
On Mon, Aug 15, 2016 at 2:53 PM, Stefan Beller [off-list ref] wrote:
Tests consisting of one line each can be consolidated to have fewer tests
to run as well as fewer lines of code.
When having just a few git commands, do not create a new shell but
use the -C flag in Git to execute in the correct directory.
Signed-off-by: Stefan Beller <redacted>
---
Looks good. The resulting test file is easier to read, and you created
a common function to perform checking if we used alternates instead of
duplicating that part multiple times.
This change wasn't mentioned in the description. You updated the tests
to use a stronger check for when alternates is in use. This is good. I
might have mentioned it in the description but it's not worth a
re-roll.
-test_expect_success 'that reference gets used with update' '
- cd super-clone/sub &&
- echo "0 objects, 0 kilobytes" >expected &&
- git count-objects >current &&
- diff expected current
The stronger variant was used once here, but you use it in several
locations. It's good to have extracted this as the new tests are more
readable.
Thanks,
Jake
I realize this was directly copied but the use of !repo to determine
what file path here was definitely not easy to process, because it
made me think we were adding .git twice to the path. Not sure if there
is an easier way to expand this and avoid that confusion?
Also, what happens if repo is false, so we run the first block above,
but is_directory fails? We just continue along even though it appears
like we should fail? I'm not 100% sure I follow the logic here.
Regardless this appears to be a pretty direct copy of what was there
before so I don't think it's worse.
Thanks,
Jake
From: Jacob Keller <hidden> Date: 2016-08-23 22:34:38
On Mon, Aug 15, 2016 at 2:53 PM, Stefan Beller [off-list ref] wrote:
Allow the user to pass in multiple references to update_clone.
Currently this is only internal API, but once the shell script is
replaced by a C version, this is needed.
This fixes an API bug between the shell script and the helper.
Currently the helper accepts "--reference" "--reference=foo"
as a OPT_STRING whose value happens to be "--reference=foo", and
then uses
if (suc->reference)
argv_array_push(&child->args, suc->reference)
where suc->reference _is_ "--reference=foo" when invoking the
underlying "git clone", it cancels out.
With this change we omit one of the "--reference" arguments when
passing references from the shell script to the helper.
Yep, I see the API fix, and it looks correct. Makes use of the helper
easier and more likely to be done correctly.
@@ -584,7 +584,7 @@ struct submodule_update_clone {/* configuration parameters which are passed on to the children */intquiet;intrecommend_shallow;-constchar*reference;+structstring_listreferences;constchar*depth;constchar*recursive_prefix;constchar*prefix;
@@ -710,8 +711,11 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,argv_array_pushl(&child->args,"--path",sub->path,NULL);argv_array_pushl(&child->args,"--name",sub->name,NULL);argv_array_pushl(&child->args,"--url",url,NULL);-if(suc->reference)-argv_array_push(&child->args,suc->reference);
Here, you now no longer pass in the reference as a whole, but assume
that it is actually just the value to pass to --reference. And below
you correctly replace the extra --reference. Ok so I see how you fixed
the API bug.
From above, I see how you fixed this to assume (as above) that
$reference is "--reference <value>" and pass it directly. It worked
before but was definitely a bit "brittle" in that direct calling of
the helper function would not behave as expected. Nice!
Regards,
Jake
From: Jacob Keller <hidden> Date: 2016-08-23 22:36:43
On Mon, Aug 15, 2016 at 2:53 PM, Stefan Beller [off-list ref] wrote:
In a later patch we want to try to create alternates for submodules,
but they might not exist in the referenced superproject. So add a way
to skip the non existing references and report them.
@@ -90,13 +90,16 @@ If you want to break the dependency of a repository cloned with `-s` on its source repository, you can simply run `git repack -a` to copy all objects from the source repository into a pack in the cloned repository.---reference <repository>::+--reference[-if-able] <repository>:: If the reference repository is on the local machine, automatically setup `.git/objects/info/alternates` to obtain objects from the reference repository. Using an already existing repository as an alternate will require fewer objects to be copied from the repository being cloned, reducing network and local storage costs.+ When using the `--reference-if-able`, a non existing+ directory is skipped with a warning instead of aborting+ the clone. + *NOTE*: see the NOTE for the `--shared` option, and also the `--dissociate` option.
@@ -51,6 +51,7 @@ static int option_progress = -1;staticenumtransport_familyfamily;staticstructstring_listoption_config=STRING_LIST_INIT_NODUP;staticstructstring_listoption_required_reference=STRING_LIST_INIT_NODUP;+staticstructstring_listoption_optional_reference=STRING_LIST_INIT_NODUP;staticintoption_dissociate;staticintmax_jobs=-1;
@@ -81,6 +82,8 @@ static struct option builtin_clone_options[] = {N_("directory from which templates will be used")),OPT_STRING_LIST(0,"reference",&option_required_reference,N_("repo"),N_("reference repository")),+OPT_STRING_LIST(0,"reference-if-able",&option_optional_reference,+N_("repo"),N_("reference repository")),OPT_BOOL(0,"dissociate",&option_dissociate,N_("use --reference only while cloning")),OPT_STRING('o',"origin",&option_origin,N_("name"),
@@ -283,24 +286,36 @@ static void strip_trailing_slashes(char *dir)staticintadd_one_reference(structstring_list_item*item,void*cb_data){structstrbuferr=STRBUF_INIT;-structstrbufsb=STRBUF_INIT;+int*required=cb_data;char*ref_git=compute_alternate_path(item->string,&err);-if(!ref_git)-die("%s",err.buf);--strbuf_addf(&sb,"%s/objects",ref_git);-add_to_alternates_file(sb.buf);+if(!ref_git){+if(*required)+die("%s",err.buf);+else+fprintf(stderr,+_("info: Could not add alternate for '%s': %s\n"),+item->string,err.buf);+}else{+structstrbufsb=STRBUF_INIT;+strbuf_addf(&sb,"%s/objects",ref_git);+add_to_alternates_file(sb.buf);+strbuf_release(&sb);+}
I might have done this with a "goto out" instead of the else block,
but this is reasonable as well.
Regards,
Jake
From: Jacob Keller <hidden> Date: 2016-08-23 22:59:29
On Mon, Aug 15, 2016 at 2:53 PM, Stefan Beller [off-list ref] wrote:
quoted hunk
When `--recursive` and `--reference` is given, it is reasonable to
expect that the submodules are created with references to the submodules
of the given alternate for the superproject.
An initial attempt to do this was presented to the mailing list, which
used flags that are passed around ("--super-reference") that instructed
the submodule clone to look for a reference in the submodules of the
referenced superproject. This is not well thought out, as any further
`submodule update` should also respect the initial setup.
When a new submodule is added to the superproject and the alternate
of the superproject does not know about that submodule yet, we rather
error out informing the user instead of being unclear if we did or did
not use a submodules alternate.
To solve this problem introduce new options that store the configuration
for what the user wanted originally.
Signed-off-by: Stefan Beller <redacted>
---
Documentation/config.txt | 12 ++++++
builtin/clone.c | 19 +++++++++
builtin/submodule--helper.c | 87 ++++++++++++++++++++++++++++++++++++++++++
t/t7408-submodule-reference.sh | 43 +++++++++++++++++++++
4 files changed, 161 insertions(+)
@@ -2837,6 +2837,18 @@ submodule.fetchJobs:: in parallel. A value of 0 will give some reasonable default. If unset, it defaults to 1.+submodule.alternateLocation::+ Specifies how the submodules obtain alternates when submodules are+ cloned. Possible values are `no`, `superproject`.+ By default `no` is assumed, which doesn't add references. When the+ value is set to `superproject` the submodule to be cloned computes+ its alternates location relative to the superprojects alternate.++submodule.alternateErrorStrategy+ Specifies how to treat errors with the alternates for a submodule+ as computed via `submodule.alternateLocation`. Possible values are+ `ignore`, `info`, `die`.+ tag.forceSignAnnotated:: A boolean to specify whether annotated tags created should be GPG signed. If `--annotate` is specified on the command line, it takes
@@ -947,6 +947,25 @@ int cmd_clone(int argc, const char **argv, const char *prefix)elsefprintf(stderr,_("Cloning into '%s'...\n"),dir);}++if(option_recursive){+if(option_required_reference.nr&&+option_optional_reference.nr)+die(_("clone --recursive is not compatible with "+"both --reference and --reference-if-able"));
So if you have multiple references that don't all match we basically
just refuse to allow recursive?
Would it be better to simply assume that we want to die on missing
references instead of failing the clone here? That is, treat it so
that multiple reference and reference-if-able will die, and only info
if we got only reference-if-able?
Probably what's here is fine, and mixing reference and
reference-if-able doesn't make much sense.
Thanks,
Jake
From: Stefan Beller <hidden> Date: 2016-08-23 23:03:28
quoted
+
+ if (option_recursive) {
+ if (option_required_reference.nr &&
+ option_optional_reference.nr)
+ die(_("clone --recursive is not compatible with "
+ "both --reference and --reference-if-able"));
So if you have multiple references that don't all match we basically
just refuse to allow recursive?
Would it be better to simply assume that we want to die on missing
references instead of failing the clone here?
The new config options are per repo (or even set globally), and not
per alternate. And as we communicate the [if-able] part via the config
options to the submodules it is not feasible to transport both
kinds of (reference-or-die and reference-but-ignore-misses).
That is why I introduced this check in the first place. If we'd go back
to the drawing board and come up with a solution that is on a
"per alternate" basis we could allow such things.
That is, treat it so
that multiple reference and reference-if-able will die, and only info
if we got only reference-if-able?
Probably what's here is fine, and mixing reference and
reference-if-able doesn't make much sense.
I think the reference-if-able doesn't make sense for one project alone
as you can easily script around that, but is only useful if you have
submodules in a partially checked out superproject that you want
to reference to.
Thanks,
Stefan
From: Jacob Keller <hidden> Date: 2016-08-24 06:30:23
On Tue, Aug 23, 2016 at 4:03 PM, Stefan Beller [off-list ref] wrote:
quoted
quoted
+
+ if (option_recursive) {
+ if (option_required_reference.nr &&
+ option_optional_reference.nr)
+ die(_("clone --recursive is not compatible with "
+ "both --reference and --reference-if-able"));
So if you have multiple references that don't all match we basically
just refuse to allow recursive?
Would it be better to simply assume that we want to die on missing
references instead of failing the clone here?
The new config options are per repo (or even set globally), and not
per alternate. And as we communicate the [if-able] part via the config
options to the submodules it is not feasible to transport both
kinds of (reference-or-die and reference-but-ignore-misses).
That is why I introduced this check in the first place. If we'd go back
to the drawing board and come up with a solution that is on a
"per alternate" basis we could allow such things.
quoted
That is, treat it so
that multiple reference and reference-if-able will die, and only info
if we got only reference-if-able?
Probably what's here is fine, and mixing reference and
reference-if-able doesn't make much sense.
I think the reference-if-able doesn't make sense for one project alone
as you can easily script around that, but is only useful if you have
submodules in a partially checked out superproject that you want
to reference to.
Thanks,
Stefan
I'm not sure there is a better design. How are alternates stored? In
a config section? Or is there some way we can store the is-able per
alternate and look it up when adding them to submodule?
Thanks,
Jake
From: Stefan Beller <hidden> Date: 2016-08-24 22:52:59
On Tue, Aug 23, 2016 at 11:29 PM, Jacob Keller [off-list ref] wrote:
On Tue, Aug 23, 2016 at 4:03 PM, Stefan Beller [off-list ref] wrote:
quoted
quoted
quoted
+
+ if (option_recursive) {
+ if (option_required_reference.nr &&
+ option_optional_reference.nr)
+ die(_("clone --recursive is not compatible with "
+ "both --reference and --reference-if-able"));
So if you have multiple references that don't all match we basically
just refuse to allow recursive?
Would it be better to simply assume that we want to die on missing
references instead of failing the clone here?
The new config options are per repo (or even set globally), and not
per alternate. And as we communicate the [if-able] part via the config
options to the submodules it is not feasible to transport both
kinds of (reference-or-die and reference-but-ignore-misses).
That is why I introduced this check in the first place. If we'd go back
to the drawing board and come up with a solution that is on a
"per alternate" basis we could allow such things.
quoted
That is, treat it so
that multiple reference and reference-if-able will die, and only info
if we got only reference-if-able?
Probably what's here is fine, and mixing reference and
reference-if-able doesn't make much sense.
I think the reference-if-able doesn't make sense for one project alone
as you can easily script around that, but is only useful if you have
submodules in a partially checked out superproject that you want
to reference to.
Thanks,
Stefan
I'm not sure there is a better design. How are alternates stored? In
a config section?
Alternates are stored in .git/objects/info/alternates
with each alternate in a new line. On that file (from
(man gitrepository-layout):
objects/info/alternates
This file records paths to alternate object stores that this object store
borrows objects from, one pathname per line. Note that not only native
Git tools use it locally, but the HTTP fetcher also tries to use it remotely;
this will usually work if you have relative paths (relative to the object
database, not to the repository!) in your alternates file, but it will not work
if you use absolute paths unless the absolute path in filesystem and web
URL is the same. See also objects/info/http-alternates.
So changing that file is out of question.
Ideally we would have a flag for each path here, though.
Or is there some way we can store the is-able per
alternate and look it up when adding them to submodule?
I guess we could invent a file as alternate-flags that is matches
line by line to the alternates file.
I don't think we'd want to go that way for now as it would really only
help in an edge case?
If we later find out we need the flag on a per-alternate basis we can
still come up with a solution and just not set these config variables,
so I think we'll be fine for now with this approach.
Thanks,
Stefan
From: Jacob Keller <hidden> Date: 2016-08-25 04:55:27
On Wed, Aug 24, 2016 at 3:52 PM, Stefan Beller [off-list ref] wrote:
On Tue, Aug 23, 2016 at 11:29 PM, Jacob Keller [off-list ref] wrote:
quoted
On Tue, Aug 23, 2016 at 4:03 PM, Stefan Beller [off-list ref] wrote:
quoted
quoted
quoted
+
+ if (option_recursive) {
+ if (option_required_reference.nr &&
+ option_optional_reference.nr)
+ die(_("clone --recursive is not compatible with "
+ "both --reference and --reference-if-able"));
So if you have multiple references that don't all match we basically
just refuse to allow recursive?
Would it be better to simply assume that we want to die on missing
references instead of failing the clone here?
The new config options are per repo (or even set globally), and not
per alternate. And as we communicate the [if-able] part via the config
options to the submodules it is not feasible to transport both
kinds of (reference-or-die and reference-but-ignore-misses).
That is why I introduced this check in the first place. If we'd go back
to the drawing board and come up with a solution that is on a
"per alternate" basis we could allow such things.
quoted
That is, treat it so
that multiple reference and reference-if-able will die, and only info
if we got only reference-if-able?
Probably what's here is fine, and mixing reference and
reference-if-able doesn't make much sense.
I think the reference-if-able doesn't make sense for one project alone
as you can easily script around that, but is only useful if you have
submodules in a partially checked out superproject that you want
to reference to.
Thanks,
Stefan
I'm not sure there is a better design. How are alternates stored? In
a config section?
Alternates are stored in .git/objects/info/alternates
with each alternate in a new line. On that file (from
(man gitrepository-layout):
objects/info/alternates
This file records paths to alternate object stores that this object store
borrows objects from, one pathname per line. Note that not only native
Git tools use it locally, but the HTTP fetcher also tries to use it remotely;
this will usually work if you have relative paths (relative to the object
database, not to the repository!) in your alternates file, but it will not work
if you use absolute paths unless the absolute path in filesystem and web
URL is the same. See also objects/info/http-alternates.
So changing that file is out of question.
Ideally we would have a flag for each path here, though.
quoted
Or is there some way we can store the is-able per
alternate and look it up when adding them to submodule?
I guess we could invent a file as alternate-flags that is matches
line by line to the alternates file.
I don't think we'd want to go that way for now as it would really only
help in an edge case?
If we later find out we need the flag on a per-alternate basis we can
still come up with a solution and just not set these config variables,
so I think we'll be fine for now with this approach.
From: Jacob Keller <hidden> Date: 2016-08-31 06:22:07
On Tue, Aug 30, 2016 at 10:04 PM, Stefan Beller [off-list ref] wrote:
On Wed, Aug 24, 2016 at 4:37 PM, Jacob Keller [off-list ref] wrote:
quoted
Yes that seems reasonable.
Thanks,
Jake
I reviewed all your comments and you seem to be ok with including this
series as it is queued currently?
Thanks,
Stefan
Yea based on what's in Junio's tree, I think we've squashed in all the
suggested changes, unless there have been more suggestions I missed.
Regards,
Jake