Johan Herland [off-list ref] writes:
I still don't like this, as IMHO it's too subtle, and possibly conflicts
with explicitly tracking submodule branches (which, to me, is a more
important feature).
If you mean, by "explicitly tracking", to say "I don't care which commit
from the submodule appears at this path, as long as it is at the tip of
this branch", I still don't think it makes much sense, but what I outlined
is not _incompatible_ with such a scheme. In fact I think it would rather
fit naturally as a sanity/safety measure.
I presume that in your "explicitly tracked" world, if the user tries to
commit at the superproject level with a submodule commit that is
inconsistent with that "explicitly tracked" branch (e.g. the commit is not
reachable from the tip of that branch), you would issue a warning of some
sort, using that knowledge. What I outlined uses the exact same knowledge
of which branch in the submodule the superproject branch is tied to to
reject irrelevant existing merges as resolution candidates.
Of course, this ".gitmodule in superproject can tell you which branch of
submodule it follows" is optional; the user needs to take responsibility
of picking the right one among I, E and G, of course, if the information
does not exist or is not available.
On Monday 21 June 2010, Junio C Hamano wrote:
Johan Herland [off-list ref] writes:
quoted
I still don't like this, as IMHO it's too subtle, and possibly
conflicts with explicitly tracking submodule branches (which, to me,
is a more important feature).
If you mean, by "explicitly tracking", to say "I don't care which commit
from the submodule appears at this path, as long as it is at the tip of
this branch", I still don't think it makes much sense, but what I
outlined is not _incompatible_ with such a scheme. In fact I think it
would rather fit naturally as a sanity/safety measure.
I'll first try to explain where I'm coming from, to hopefully eliminate any
confusion about my position:
IMHO, there should be 2 primary modes for submodules in Git:
A. Explicitly tracking submodule commits. This is the existing submodule
behaviour. The superproject refers directly (in its tree) to a submodule
commit. The .gitmodules file contains associated information
(submodule.<name>.path, .url and .update).
B. Explicitly tracking submodule branches. An extra setting
(submodule.<name>.branch) is added to the .gitmodules file, to determine
which submodule branch to checkout. This setting overrides whatever
submodule commit, if any, is stored in the superproject tree. There are two
sub-modes for this mode:
B.1. There is no submodule entry at all in the superproject tree. This
indicates that you are not at all interested in tracking the history of the
submodule relative to the superproject. You are always interested in
checking out the tip of submodule.<name>.branch in the submodule, even when
digging into the superproject's history.
B.2. There is a submodule entry in the superproject tree. This "hybrid"
approach indicates that although you primarily want to track a branch in the
submodule (i.e. you mostly want the latest version of the submodule's
branch), you still want a record of where your submodule has been pointing,
in your superproject's history. Exactly when (or how often) the
superproject's submodule entry should be updated is yet TBD. So is when to
check out according to .branch, and when to use the recorded submodule
commit. In the end, it'll probably be a policy decision for the projects
that choose this approach.
Ok, that hopefully explains the basic idea of tracking submodule commits (A)
vs. tracking submodule branches (B). Now, how would this apply to merging
submodules?
In case of B, I'd argue that the submodule merging should _only_ look at the
value of submodule.<name>.branch from the superproject's .gitmodules. If
this setting is ambiguous (because of merge conflicts in .gitmodules), Git
should not touch the submodule at all (until .gitmodules is resolved).
Otherwise, Git's only task is to checkout whatever branch is specified by
.gitmodules. If the commit that is checked out does not descend from all of
the merge alternatives, a warning should be printed.
With that in mind, I enter this discussion because it might provide insight
on how to solve the problem of merging submodules in scenario A.
In mode A there is no submodule.<name>.branch setting, and I would not like
to add an additional setting (let's call it submodule.<name>.merge_branch
for now) that is "weaker" than submodule.<name>.branch (meaning that it does
not trigger the transition from mode A to mode B). There are two major
reasons for this:
1. submodule.<name>.merge_branch would add semantics to the case of merging
submodules that would be similar in spirit to what submodule.<name>.branch
does (the "spirit" here is the special relationship to a submodule branch
that we're establishing), but still the .merge_branch setting would be
different in practice, by (a) only applying to the case of merging
submodules (while .branch changes the semantics of almost all submodule
operations), and (b) not even in the case of merging submodules would the
options do the same thing. I fear the semantics of the .merge_branch option
would be too complicated for an average user, and that its similarity to
.branch would cause confusion.
2. What would happen if you enabled _both_ .merge_branch and .branch? In the
case of merging submodules which setting will "win"? Even worse, if you set
.merge_branch to "foo", and .branch to "bar", what will then happen?
Ok, so if I oppose adding .merge_branch, what do I propose instead?
Currently, not much, I'm afraid. But I have a gut feeling that the use case
presented by Heiko and Jens is best solved EITHER by having no special
branch relationships between the superproject and submodule (which AFAICS is
what we currently agree on in this thread, and Heiko has already submitted a
patch to this effect), OR by employing a conservative version of mode B.2 in
which we use .branch to track a submodule branch, but still keep a close
look at the recorded submodule commit, and, if necessary, maybe introduce
some other options to tell Git when to use the recorded commit, and when to
use the branch tip.
In other words, I think we should explore the .branch direction before we
add complexity and potential confusion by prematurely adding another option
that it somewhat similar to .branch in some contexts.
I presume that in your "explicitly tracked" world, if the user tries to
commit at the superproject level with a submodule commit that is
inconsistent with that "explicitly tracked" branch (e.g. the commit is
not reachable from the tip of that branch), you would issue a warning of
some sort, using that knowledge.
Yes.
What I outlined uses the exact same
knowledge of which branch in the submodule the superproject branch is
tied to to reject irrelevant existing merges as resolution candidates.
True, but as I've argued above, I'm not sure that adding another setting
(aka. .merge_branch) for this special/limited kind of branch tracking is
worth it.
Of course, this ".gitmodule in superproject can tell you which branch of
submodule it follows" is optional; the user needs to take responsibility
of picking the right one among I, E and G, of course, if the information
does not exist or is not available.
Yes, of course. And this corresponds to what I've proposed for scenario A,
when there is no branch-related setting specified for the submodule.
Hope this helps,
...Johan
--
Johan Herland, [off-list ref]
www.herland.net
Johan Herland [off-list ref] writes:
True, but as I've argued above, I'm not sure that adding another setting
(aka. .merge_branch) for this special/limited kind of branch tracking is
worth it.
I don't think .merge_branch is necessary nor even desired. In fact, I
think your use of .branch, especially in the variant that does not have
any submodule entry in the superproject tree, of your version (B) does not
have conceptual advantage. You checkout the superproject first (which
would be the natural thing to do, as you may get update to its .gitmodules
there), and checkout the then-tip of the named branch of the submodule,
you would immediately get a stale checkout when you then go fetch the
updates to the submodule.
And the worst part is that you wouldn't even _notice_ that your checkout
is stale, as there is no record in the superproject which commit you were
supposed to be using to be consistent with the version the committer of
the superproject commit used to record it.
I on the other hand think what you called "hybrid" makes sense (and I
don't even think it is hybrid but rather is a natural way to do this).
With the submodule.*.branch entry, you can:
- make sure that your checkout is consistent; if your submodule checks
out a different commit or branch from what the superproject records in
its tree or in its .gitmodules (e.g. you forgot to update the submodule
when you switched superproject branch), git can notice the situation
and can help you implement policy decisions;
- record a commit that is different from the tip of the submodule branch
when making a superproject commit; git can notice the situation and can
help you implement policy decisions (e.g. you could choose to reject
and tell the user to advance the submodule branch first before making
the commit in the superproject);
- use it as an advisory "existing merge commit selector", as discussed in
this thread.
Thinking about what would happen in your (B) that doesn't record the exact
commit, I think that it doesn't have any advantage over the "hybrid" one.
The "hybrid" one can help you to make sure that what you commit in the
superproject's .gitmodules and submodule's branch tip are kept consistent.
When they are kept consistent, then switching branches in the superproject
should always flip between the tips of branches, no?
On Tuesday 22 June 2010, Junio C Hamano wrote:
Johan Herland [off-list ref] writes:
quoted
True, but as I've argued above, I'm not sure that adding another
setting (aka. .merge_branch) for this special/limited kind of
branch tracking is worth it.
I don't think .merge_branch is necessary nor even desired.
Good.
In fact,
I think your use of .branch, especially in the variant that does not
have any submodule entry in the superproject tree, of your version
(B) does not have conceptual advantage. You checkout the
superproject first (which would be the natural thing to do, as you
may get update to its .gitmodules there), and checkout the then-tip
of the named branch of the submodule, you would immediately get a
stale checkout when you then go fetch the updates to the submodule.
Not if the initial checkout of the submodule included an implicit
fetch/pull within the submodule (exact behaviour probably to be
controlled with some config).
And the worst part is that you wouldn't even _notice_ that your
checkout is stale, as there is no record in the superproject which
commit you were supposed to be using to be consistent with the
version the committer of the superproject commit used to record it.
The idea (in B.1.) is that if you _truly_ don't care which version of
the submodule you're checking out, then there should be no submodule
entry in the superproject that would "pollute" your status/diffs.
Note that I'm not in this camp myself, as I would very much like to keep
track of where my submodule is (and has been) checked out. So I'll stop
trying to argue for a use case that I'm not going to use. At the time,
it seemed like a logical conclusion of the tracking-submodule-branches
debate, but if it's not going to be used in practice, there's no point
in keeping it alive.
I on the other hand think what you called "hybrid" makes sense (and I
don't even think it is hybrid but rather is a natural way to do
this).
Agreed. I too believe that the "hybrid" is much more useful in practice
than the extreme version (without a gitlink entry in the superproject).
But I also believe that Git shouldn't enforce specific workflows, so
that if people actually want to track submodule branches in the
non-"hybrid" (haphazard) manner, then Git should not stand in their
way.
On the other hand, if nobody's gonna do this, there's no point in
implementing support for it.
With the submodule.*.branch entry, you can:
- make sure that your checkout is consistent; if your submodule
checks out a different commit or branch from what the superproject
records in its tree or in its .gitmodules (e.g. you forgot to update
the submodule when you switched superproject branch), git can notice
the situation and can help you implement policy decisions;
- record a commit that is different from the tip of the submodule
branch when making a superproject commit; git can notice the
situation and can help you implement policy decisions (e.g. you could
choose to reject and tell the user to advance the submodule branch
first before making the commit in the superproject);
- use it as an advisory "existing merge commit selector", as
discussed in this thread.
Ah, I see. IINM you indeed prefer to use the same setting (aka.
submodule.<name>.branch) for controlling both the merging of submodules
(as discussed in this thread), and the other aspects of the submodule
branch tracking feature. We agree here.
However, it seems you would like the .branch setting to be more advisory
in nature: Instead of blindly checking out whatever .branch specifies,
you'd rather have a more careful interplay where the .branch option
should only check whether it is consistent with what happens to be
checked out in the submodule, and warn when this is not the case.
This may be better than what I suggested, but it's hard to say anything
for sure until the various alternatives are tested in practice.
Thinking about what would happen in your (B) that doesn't record the
exact commit, I think that it doesn't have any advantage over the
"hybrid" one. The "hybrid" one can help you to make sure that what
you commit in the superproject's .gitmodules and submodule's branch
tip are kept consistent. When they are kept consistent, then
switching branches in the superproject should always flip between the
tips of branches, no?
Yes, if branch "foo" in the superproject records "branch = subfoo"
in .gitmodules, and the current tip of "subfoo" as a gitlink, and
branch "bar" in the superproject records "branch = subbar"
in .gitmodules and the current tip of "subbar" as a gitlink, then,
indeed, switching between these two branches should auto-flip between
the branch tips.
One of the remaining questions is what happens when the superproject
does not change, but commits are added to the submodule branches. Now,
when switching between branches in the superproject, should Git:
1. Do nothing (this is the current behaviour, the user is forced to 'git
submodule update' after 'git checkout' in the superproject)
2. Only checkout the recorded gitlink (this defeats the purpose of
branch-tracking, IMO)
3. Checkout the recorded gitlink, but warn about the updated branch tip
(a valid behaviour IMO)
4. Checkout the updated branch tip of the submodule, and warn about the
out-of-date gitlink entry (also a valid behaviour IMO)
5. Checkout the updated branch tip AND stage a new gitlink in the
superproject (this is a bit to "magic", IMO)
...Johan
--
Johan Herland, [off-list ref]
www.herland.net