We’ve made a subdirectory S into a separate repo and re-added it as a submodule in a branch B. Now when I want to switch back to the original branch A, where the subdirectory was checked into the original repo, git complains that the stuff from the submodule is untracked and has to be moved away first. Can we switch back to A at all without manually dragging the S away and they moving it back in for B?
Update: the comments below indicate it may be a deficiency of the old branch and git not willing to do a smart thing here. Is this so, and what kind of rebasing, if any, of the original branch with S in the main repo can let it coexist and be checkout-able at any time vs. the submodularized version?
Your history looks something like this:
git ls-tree Ashows (e.g.):git ls-tree A:Sshows (e.g.):git ls-tree Bshows (e.g.):(cd S; git ls-tree HEAD)shows (e.g.):You want to move from commit Y (or later) to commit X (or earlier) or vice versa.
If your active branch is B, then
git checkout Asays (e.g.):Git tries very hard to never lose data unless you tell it to do so (e.g. with “force” options). The problem Git finds and reports here is that branch A has different content for
S/somefilethen the working tree. BecauseS/somefileis not tracked (from the perspective of the superproject), Git refuses to replace the file and thus refuses to switch branches/commits.Git could arguably be smarter about this (by noticing that the file is tracked in the submodule, so it should not really be considered untracked when switching branches in the superproject), but it is a limitation of the current implementation. There is a Google Summer of Code 2011 project for Git that aims to address some areas of submodule support, but it is not clear to me whether this exact problem will be covered.
You could, as you suggest, rewrite your history so that
Salways appeared to have been a submodule. This would certainly present the smoothest surface for future commit switches, but it is complicated by the fact that you would need to make sure you have commits in the submodule’s origin repository that reflect each historical state of theSdirectory in the original commits. If you have many differentStrees (i.e. you had made some local changes underSbefore converting it to a submodule), then this may be a complicated process/script.A simpler workaround might be to temporarily checkout an “empty branch” in the submodule before switching to a commit that has it as a directory.
Create the “empty branch” in the submodule.
You could publish this “branch” in the submodule’s origin repository so that no one else would need to recreate it form themselves.
When you need to switch from a commit where
Sis a submodule to a commit whereSis a directory, checkout the “empty branch” in the submodule first:You will see this warning because Git will leave behind
S/.git:Because
S/.gitis still present, you should be careful to issue Git commands only outsideSwhen working on a commit that hasSas a directory; Git commands issued underSwould operate on theS/.git(in this state, it is just a “subrepository”, not a full submodule) instead of the top level.gitrepository.When you need to switch from a commit where
Sis a directory to a commit whereSis a submodule, you will need to check out the appropriate branch/commit in the submodule after switching the superproject’s commit.You can use
git submodule updateto restore the commit that is recorded in the superproject. Or, if you were working on a branch in the submodule, just check it back out.