From: Stefan Beller <hidden> Date: 2017-05-01 18:01:12
The first three patches fix bugs in existing submodule code,
sort of independent from the last 2 patches, which are RFCs.
In submodules as of today you always end up with a detached HEAD,
which may be scary to people used to working on branches. (I myself
enjoy working with a detached HEAD).
The detached HEAD in a submodule is the correct in the submodule-as-a-lib
case, but in case you actively want to work inside submodules, you
may want to have proper branch support, e.g. when typing:
git checkout --recuse-submodules master
you may want to have the submodules on the master branch as well.
There are a couple of challenges though:
* We'd probably want to pay attention to the branch configuration
(.gitmodules submodule.NAME.branch to be "." or "foo")
* What if the submodule does not have a master branch?
Jonathan proposed off list that we may just want to create the
branch in the submodule. This is not implemented in this series.
* In case the branch exists in the submodule and doesn't point at the
the object as recorded in the superproject, we may either want to
update the branch to point at the superproject record or we want to
reject the "reattaching HEAD". Patch 4 provides the later.
Any other ideas on why this could be a bad idea or corner cases?
Thanks,
Stefan
Stefan Beller (5):
submodule_move_head: fix leak of struct child_process
submodule_move_head: prepare env for child correctly
submodule: avoid auto-discovery in new working tree manipulator code
submodule--helper: reattach-HEAD
submodule_move_head: reattach submodule HEAD to branch if possible.
builtin/submodule--helper.c | 12 ++++++
submodule.c | 93 ++++++++++++++++++++++++++++++++++++++++-----
submodule.h | 10 +++++
3 files changed, 106 insertions(+), 9 deletions(-)
--
2.13.0.rc1.1.gbc33f0f778
From: Stefan Beller <hidden> Date: 2017-05-01 18:01:14
While fixing the leak of `cp`, reuse it instead of having to declare
another struct child_process.
Signed-off-by: Stefan Beller <redacted>
---
submodule.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
@@ -1466,17 +1466,19 @@ int submodule_move_head(const char *path,gotoout;}+child_process_clear(&cp);+if(!(flags&SUBMODULE_MOVE_HEAD_DRY_RUN)){if(new){-structchild_processcp1=CHILD_PROCESS_INIT;+child_process_init(&cp);/* also set the HEAD accordingly */-cp1.git_cmd=1;-cp1.no_stdin=1;-cp1.dir=path;+cp.git_cmd=1;+cp.no_stdin=1;+cp.dir=path;-argv_array_pushl(&cp1.args,"update-ref","HEAD",new,NULL);+argv_array_pushl(&cp.args,"update-ref","HEAD",new,NULL);-if(run_command(&cp1)){+if(run_command(&cp)){ret=-1;gotoout;}
@@ -1492,6 +1494,7 @@ int submodule_move_head(const char *path,}}out:+child_process_clear(&cp);returnret;}
From: Stefan Beller <hidden> Date: 2017-05-01 18:01:17
All commands that are run in a submodule, are run in a correct setup,
there is no need to prepare the environment without setting the GIT_DIR
variable. By setting the GIT_DIR variable we fix issues as discussed in
10f5c52656 (submodule: avoid auto-discovery in
prepare_submodule_repo_env(), 2016-09-01)
Signed-off-by: Stefan Beller <redacted>
---
submodule.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
From: Stefan Beller <hidden> Date: 2017-05-01 18:01:19
We forgot to prepare the submodule env, which is only a problem for
nested submodules. See 2e5d6503bd (ls-files: fix recurse-submodules
with nested submodules, 2017-04-13) for further explanation.
Signed-off-by: Stefan Beller <redacted>
---
submodule.c | 1 +
1 file changed, 1 insertion(+)
From: Stefan Beller <hidden> Date: 2017-05-01 18:01:21
Add a new command, that reattaches a detached HEAD to its configured
branch in a submodule.
In a later patch we will teach git-submodule as well as other submodule
worktree manipulators (git reset/checkout --recurse-submodules) to not
end up in a detached HEAD state in the submodules.
Signed-off-by: Stefan Beller <redacted>
---
builtin/submodule--helper.c | 12 ++++++++
submodule.c | 69 +++++++++++++++++++++++++++++++++++++++++++++
submodule.h | 10 +++++++
3 files changed, 91 insertions(+)
@@ -1499,6 +1499,75 @@ int submodule_move_head(const char *path,returnret;}+intreattach_HEAD(constchar*submodule_path,intflags)+{+constchar*branch;+structobject_idsub_head_object;+structobject_idsub_branch_object;++conststructsubmodule*sub=submodule_from_path(null_sha1,submodule_path);++if(!sub){+if(!(flags&REATTACH_HEAD_DIE_ON_ERROR))+return-1;+die(_("no submodule mapping found in .gitmodules for path '%s'"),+submodule_path);+}++if(!sub->branch){+if(!(flags&REATTACH_HEAD_DIE_ON_ERROR))+return-1;+die(_("no branch configured to follow for submodule '%s'"),+sub->path);+}++/* lookup branch value in .gitmodules */+if(strcmp(".",sub->branch)){+branch=sub->branch;+}else{+/* special care for '.': Is the superproject on a branch? */+structobject_idoid;+branch=resolve_refdup("HEAD",0,oid.hash,NULL);+if(!branch){+if(!(flags&REATTACH_HEAD_DIE_ON_ERROR))+return-1;+die(_("Not on any branch, but submodule configured to follow superprojects branch"));+}+}++if(!strcmp("HEAD",branch))+return0;++resolve_gitlink_ref(sub->path,"HEAD",sub_head_object.hash);+resolve_gitlink_ref(sub->path,branch,sub_branch_object.hash);++if(!oidcmp(&sub_head_object,&sub_branch_object)){+structchild_processcp=CHILD_PROCESS_INIT;+structstrbufreason=STRBUF_INIT;++cp.dir=sub->path;+prepare_submodule_repo_env(&cp.env_array);++strbuf_addf(&reason,"reattach HEAD to %s",branch);+argv_array_pushl(&cp.args,"git","symbolic-ref",+"-m",reason.buf,"HEAD",branch,NULL);+if(run_command(&cp)){+if(!(flags&REATTACH_HEAD_DIE_ON_ERROR))+return-1;+die(_("could not run symbolic-ref in submodule '%s'"),+sub->path);+}+strbuf_release(&reason);+return0;+}else{+fprintf(stderr,"not reattaching HEAD, object ids differ:\n"+"HEAD is %s\n branch is %s",+oid_to_hex(&sub_head_object),+oid_to_hex(&sub_branch_object));+return1;+}+}+staticintfind_first_merges(structobject_array*result,constchar*path,structcommit*a,structcommit*b){
From: Stefan Beller <hidden> Date: 2017-05-01 18:01:22
Side note: We also want to call this from git submodule update
Signed-off-by: Stefan Beller <redacted>
---
submodule.c | 2 ++
1 file changed, 2 insertions(+)
@@ -1466,17 +1466,19 @@ int submodule_move_head(const char *path,gotoout;}+child_process_clear(&cp);+if(!(flags&SUBMODULE_MOVE_HEAD_DRY_RUN)){if(new){-structchild_processcp1=CHILD_PROCESS_INIT;+child_process_init(&cp);/* also set the HEAD accordingly */-cp1.git_cmd=1;-cp1.no_stdin=1;-cp1.dir=path;+cp.git_cmd=1;+cp.no_stdin=1;+cp.dir=path;-argv_array_pushl(&cp1.args,"update-ref","HEAD",new,NULL);+argv_array_pushl(&cp.args,"update-ref","HEAD",new,NULL);-if(run_command(&cp1)){+if(run_command(&cp)){ret=-1;gotoout;}
@@ -1492,6 +1494,7 @@ int submodule_move_head(const char *path,}}out:+child_process_clear(&cp);returnret;}
From: Brandon Williams <hidden> Date: 2017-05-01 18:24:13
On 05/01, Stefan Beller wrote:
The first three patches fix bugs in existing submodule code,
sort of independent from the last 2 patches, which are RFCs.
In submodules as of today you always end up with a detached HEAD,
which may be scary to people used to working on branches. (I myself
enjoy working with a detached HEAD).
The detached HEAD in a submodule is the correct in the submodule-as-a-lib
case, but in case you actively want to work inside submodules, you
may want to have proper branch support, e.g. when typing:
git checkout --recuse-submodules master
you may want to have the submodules on the master branch as well.
I don't know why submodules were originally designed to be in a
detached HEAD state but I much prefer working on branches (as I'm sure
many other developers do) so the prospect of this becoming the norm is
exciting! :D
There are a couple of challenges though:
* We'd probably want to pay attention to the branch configuration
(.gitmodules submodule.NAME.branch to be "." or "foo")
Yes, I agree. If we have a particular name of a branch configured to be
tracked, then we should respect that and try to attach HEAD onto that
branch name.
* What if the submodule does not have a master branch?
Jonathan proposed off list that we may just want to create the
branch in the submodule. This is not implemented in this series.
I think it would be fine to create the branch, just as long as it
doesn't already exist as I can't think of a negative impact of this
(aside from maybe having more branches in the submodules after a
prolonged time period?)
* In case the branch exists in the submodule and doesn't point at the
the object as recorded in the superproject, we may either want to
update the branch to point at the superproject record or we want to
reject the "reattaching HEAD". Patch 4 provides the later.
The later seems like the most correct thing to do. It would be
unfortunate if I had done some work on top of the master branch in a
submodule (which wasn't reflected in the superproject), then done a
'checkout master' in the super project only to go back to the submodule
and find that all my work is gone! (Well not gone, but you'd have to go
into the scary reflog!) It just makes sense to leave HEAD in a detached
state here as it prevents loss of work.
Any other ideas on why this could be a bad idea or corner cases?
Thanks,
Stefan
Stefan Beller (5):
submodule_move_head: fix leak of struct child_process
submodule_move_head: prepare env for child correctly
submodule: avoid auto-discovery in new working tree manipulator code
submodule--helper: reattach-HEAD
submodule_move_head: reattach submodule HEAD to branch if possible.
builtin/submodule--helper.c | 12 ++++++
submodule.c | 93 ++++++++++++++++++++++++++++++++++++++++-----
submodule.h | 10 +++++
3 files changed, 106 insertions(+), 9 deletions(-)
--
2.13.0.rc1.1.gbc33f0f778
From: Brandon Williams <hidden> Date: 2017-05-01 18:37:08
On 05/01, Stefan Beller wrote:
Add a new command, that reattaches a detached HEAD to its configured
branch in a submodule.
In a later patch we will teach git-submodule as well as other submodule
worktree manipulators (git reset/checkout --recurse-submodules) to not
end up in a detached HEAD state in the submodules.
Signed-off-by: Stefan Beller <redacted>
+int reattach_HEAD(const char *submodule_path, int flags)
+{
+ const char *branch;
+ struct object_id sub_head_object;
+ struct object_id sub_branch_object;
+
+ const struct submodule *sub = submodule_from_path(null_sha1, submodule_path);
+
+ if (!sub) {
+ if (!(flags & REATTACH_HEAD_DIE_ON_ERROR))
+ return -1;
+ die(_("no submodule mapping found in .gitmodules for path '%s'"),
+ submodule_path);
It may be a bit clearer to do the following:
if (flags & REATTACH_HEAD_DIE_ON_ERROR)
die(...);
return -1;
It just feels weird to me to have the inverted logic, that's my opinion
though.
quoted hunk
+ }++ if (!sub->branch) {+ if (!(flags & REATTACH_HEAD_DIE_ON_ERROR))+ return -1;+ die(_("no branch configured to follow for submodule '%s'"),+ sub->path);+ }++ /* lookup branch value in .gitmodules */+ if (strcmp(".", sub->branch)) {+ branch = sub->branch;+ } else {+ /* special care for '.': Is the superproject on a branch? */+ struct object_id oid;+ branch = resolve_refdup("HEAD", 0, oid.hash, NULL);+ if (!branch) {+ if (!(flags & REATTACH_HEAD_DIE_ON_ERROR))+ return -1;+ die(_("Not on any branch, but submodule configured to follow superprojects branch"));+ }+ }++ if (!strcmp("HEAD", branch))+ return 0;
So this is the case where the superproject is in a detached-HEAD state?
In that case then we don't need to continue because if the superproject
isn't on a branch, then there isn't a reason the submodule should be on
a branch.
quoted hunk
++ resolve_gitlink_ref(sub->path, "HEAD", sub_head_object.hash);+ resolve_gitlink_ref(sub->path, branch, sub_branch_object.hash);++ if (!oidcmp(&sub_head_object, &sub_branch_object)) {+ struct child_process cp = CHILD_PROCESS_INIT;+ struct strbuf reason = STRBUF_INIT;++ cp.dir = sub->path;+ prepare_submodule_repo_env(&cp.env_array);++ strbuf_addf(&reason, "reattach HEAD to %s", branch);+ argv_array_pushl(&cp.args, "git", "symbolic-ref",+ "-m", reason.buf, "HEAD", branch, NULL);+ if (run_command(&cp)) {+ if (!(flags & REATTACH_HEAD_DIE_ON_ERROR))+ return -1;+ die(_("could not run symbolic-ref in submodule '%s'"),+ sub->path);+ }+ strbuf_release(&reason);+ return 0;+ } else {+ fprintf(stderr, "not reattaching HEAD, object ids differ:\n"+ "HEAD is %s\n branch is %s",+ oid_to_hex(&sub_head_object),+ oid_to_hex(&sub_branch_object));+ return 1;+ }+}+ static int find_first_merges(struct object_array *result, const char *path, struct commit *a, struct commit *b) {
From: Stefan Beller <hidden> Date: 2017-05-01 19:00:55
On Mon, May 1, 2017 at 11:36 AM, Brandon Williams [off-list ref] wrote:
if (flags & REATTACH_HEAD_DIE_ON_ERROR)
die(...);
return -1;
It just feels weird to me to have the inverted logic, that's my opinion
though.
Yeah, me too. But my feelings were not as important as staying
under 80 characters a line. And as the error message is longer than
the "return -1", I wanted to have it not indented yet another level.
quoted
+ }++ if (!sub->branch) {+ if (!(flags & REATTACH_HEAD_DIE_ON_ERROR))+ return -1;+ die(_("no branch configured to follow for submodule '%s'"),+ sub->path);+ }++ /* lookup branch value in .gitmodules */+ if (strcmp(".", sub->branch)) {+ branch = sub->branch;+ } else {+ /* special care for '.': Is the superproject on a branch? */+ struct object_id oid;+ branch = resolve_refdup("HEAD", 0, oid.hash, NULL);+ if (!branch) {+ if (!(flags & REATTACH_HEAD_DIE_ON_ERROR))+ return -1;+ die(_("Not on any branch, but submodule configured to follow superprojects branch"));+ }+ }++ if (!strcmp("HEAD", branch))+ return 0;
So this is the case where the superproject is in a detached-HEAD state?
and the submodule config is branch='.'
In that case then we don't need to continue because if the superproject
isn't on a branch, then there isn't a reason the submodule should be on
a branch.
From: Stefan Beller <hidden> Date: 2017-05-02 19:32:29
v2:
* I dropped the RFC patches for reattaching HEAD as that is not the main
concern of this series.
* patch1 is just about dropping cp1 to reusing cp, instead of additionally
"fixing" mem leaks as finish_command does that for us
* reordered the patches, former patch 3 now as patch 2 (avoid auto-discovery
in new working tree manipulator code)
* Regarding former patch2, I wrote:
Junio wrote:
quoted
Sounds good (if only because this makes it similar to other
codepaths).
Is this something whose breakage before the patch is easily
demonstrated with a test?
I'll try to come up with a test in a reroll.
done
Thanks,
Stefan
Stefan Beller (3):
submodule_move_head: reuse child_process structure for futher commands
submodule: avoid auto-discovery in new working tree manipulator code
submodule: properly recurse for read-tree and checkout
submodule.c | 21 +++++++++++----------
t/lib-submodule-update.sh | 7 +------
t/t1013-read-tree-submodule.sh | 1 -
t/t2013-checkout-submodule.sh | 1 -
4 files changed, 12 insertions(+), 18 deletions(-)
--
2.13.0.rc1.19.g083305f9b1
From: Stefan Beller <hidden> Date: 2017-05-02 19:32:31
We do not need to declare another struct child_process, but we can just
reuse the existing `cp` struct.
Signed-off-by: Stefan Beller <redacted>
---
submodule.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
@@ -1468,15 +1468,15 @@ int submodule_move_head(const char *path,if(!(flags&SUBMODULE_MOVE_HEAD_DRY_RUN)){if(new){-structchild_processcp1=CHILD_PROCESS_INIT;+child_process_init(&cp);/* also set the HEAD accordingly */-cp1.git_cmd=1;-cp1.no_stdin=1;-cp1.dir=path;+cp.git_cmd=1;+cp.no_stdin=1;+cp.dir=path;-argv_array_pushl(&cp1.args,"update-ref","HEAD",new,NULL);+argv_array_pushl(&cp.args,"update-ref","HEAD",new,NULL);-if(run_command(&cp1)){+if(run_command(&cp)){ret=-1;gotoout;}
From: Stefan Beller <hidden> Date: 2017-05-02 19:32:34
All commands that are run in a submodule, are run in a correct setup,
there is no need to prepare the environment without setting the GIT_DIR
variable. By setting the GIT_DIR variable we fix issues as discussed in
10f5c52656 (submodule: avoid auto-discovery in
prepare_submodule_repo_env(), 2016-09-01)
Signed-off-by: Stefan Beller <redacted>
---
submodule.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
From: Stefan Beller <hidden> Date: 2017-05-02 19:32:38
We forgot to prepare the submodule env, which is only a problem for
nested submodules. See 2e5d6503bd (ls-files: fix recurse-submodules
with nested submodules, 2017-04-13) for further explanation.
To come up with a proper test for this, we'd need to look at nested
submodules just as in that given commit. It turns out we're lucky
and these tests already exist, but are marked as failing. We need
to pass `--recurse-submodules` to read-tree additionally to make
these tests pass. Passing that flag alone would not make the tests
pass, such that this covers testing for the bug fix of the submodule
env as well.
Signed-off-by: Stefan Beller <redacted>
---
submodule.c | 3 ++-
t/lib-submodule-update.sh | 7 +------
t/t1013-read-tree-submodule.sh | 1 -
t/t2013-checkout-submodule.sh | 1 -
4 files changed, 3 insertions(+), 9 deletions(-)
From: Brandon Williams <hidden> Date: 2017-05-02 19:42:43
On 05/02, Stefan Beller wrote:
v2:
* I dropped the RFC patches for reattaching HEAD as that is not the main
concern of this series.
* patch1 is just about dropping cp1 to reusing cp, instead of additionally
"fixing" mem leaks as finish_command does that for us
* reordered the patches, former patch 3 now as patch 2 (avoid auto-discovery
in new working tree manipulator code)
* Regarding former patch2, I wrote:
quoted
Junio wrote:
quoted
Sounds good (if only because this makes it similar to other
codepaths).
Is this something whose breakage before the patch is easily
demonstrated with a test?
quoted
I'll try to come up with a test in a reroll.
done
Thanks,
Stefan
Changes look good.
Stefan Beller (3):
submodule_move_head: reuse child_process structure for futher commands
submodule: avoid auto-discovery in new working tree manipulator code
submodule: properly recurse for read-tree and checkout
submodule.c | 21 +++++++++++----------
t/lib-submodule-update.sh | 7 +------
t/t1013-read-tree-submodule.sh | 1 -
t/t2013-checkout-submodule.sh | 1 -
4 files changed, 12 insertions(+), 18 deletions(-)
--
2.13.0.rc1.19.g083305f9b1