Subject: Re: [PATCH 07/16] git-read-tree: take --submodules option

From: "Shawn O. Pearce" <[email-masked]>
Date: Sat, 19 May 2007 00:27:57 -0400

Junio C Hamano <[email-masked]> wrote:
> Now, suppose "git checkout" needs to recurse into one
> subdirectory that is to have a subproject.  There are three
> cases:

So I've implemented my own Git subproject support in a Java based
tool we use internally.  Its actually driving the Git plumbing (as
JGit isn't complete enough to do the job) but applies quite a bit
to this discussion as it is a working system that implements this
"checkout in superproject and recurse into subproject".

First I don't use the subproject support in the core plumbing,
because that came along from Linus about 2 days after I wrote
this implementation.  Our data file looks like:

	use-component: subproject1 >=df8cfac815...
	use-component: subproject2 >=af9b543820...

Or really anything that is a valid commit-ish, and often these are
actually just annotated tag names.

>  (1) There is no git repository yet (the plumbing layer already
>      makes sure there is a directory, but does not do anything
>      else).

During our build process we scan the root project's data file,
and clone by the relative URL anything we cannot find locally:

	$(git config remote.origin.url)/../component-links/subproject1.git

to get the subproject repository.  We don't require that the
subproject1 directory actually be called subproject1 in the
superproject, its just a recommendation.  That data file is also
our build-system driver and the build system driver is pretty darn
smart about guessing what is going on.  ;-)

You'll notice however that we (more or less) have a very flat
structure.  The component-links directory is really just a set of
symlinks pointing back up a level, as sometimes a component is not
stored in a repository named the component name, but the component
name matters to the build system.

>  (2) There already is a git repository there, which is the
>      correct repository (perhaps determined by .gitmodules and
>      .git/config in the superproject, or presense of the commit
>      that is recorded in the superproject's index).
> 
>  (3) There is a git repository but it is not the correct one.
> 
> For case (2), I think what should happen there is an equivalent
> of this:
> 
> 	$ commit=$(git-rev-parse :subproject)
>         $ cd subproject
> 	$ git-rev-parse --verify $commit || git fetch || barf
>         $ git checkout $commit

Yes.  Except we do a few things differently:

 - Only update the subproject if its a strict fast-forward.

 - Abort on a dirty working directory in the subproject or if a merge
 would be required to keep the current commit and the new commit.
 Yes, we don't really support going "backwards".

 - The merge aborting thing is probably wrong for some users,
 but blindly switching to the target commit feels somewhat wrong
 in our own uses.  Sometimes you need the current version of the
 subproject to help you debug an older version of the superproject,
 or sibling subproject.

 - You can't just checkout $commit if you can rev-parse it.
 You need to verify it and its entire reachable object set exists.
 See the local fetch fast-path thing you did recently in e3c6f240fd.

 - We update the user's current branch.  Because we are doing
 a strict fast-forward we're also assuming the user wants the
 current branch to stay correlated to the superproject branch.  Why?
 Most of our users keep the same branch name in all repositories.

-- 
Shawn.