'Git submodule keeps resetting to (no branch)

I've removed and re-added the submodule over and over and over, but everytime we clone the parent repository, the submodule is at "(no branch)" Can someone provide the steps to get a submodule and properly, definitively, without any doubt, lock it on a commit?

Steps I took:

  • git submodule add https://bitbucket.org/bla/bla.git my/submodule/path
  • Update the submodule to point to a latest commit in a branch. Commit message says From <hash> X months ago Initial commit To: <hash> 1 day ago <somemessage>
  • Pushed the changes
  • git clone on a colleague's computer git clone https://bitbucket.org/bla/bla.git

The submodule is on (no branch) on my colleague's computer. It should not be. It should be at the commit I put it at. What have I missed?

Edit So maybe "(no branch)" is just misleading. On my colleague's machine, it checks out at Initial commit. That's the core problem.



Solution 1:[1]

Well, this is embarrassing, it took us way too long to figure this out because we weren't reading properly, as you can already tell from the question.

The reason it worked fine on my machine and not on my colleague's machine is because I was using https to check out the repository and he was using SSH. The submodule was included with a https url and that failed. I used the relative path as suggested here https://stackoverflow.com/a/48067316/1980516

Solution 2:[2]

You'll need to make a new commit in the superproject, after you make a new commit in the submodule.

As multiple comments say, this is how submodules are supposed to work (though people find it quite frustrating). It may help to understand why they work this way, though.

Remember that a submodule is a (separate) Git repository. That is, if you have repository X as a superproject with submodule Y as a submodule, you have two repositories, X and Y. A Git repository is, at its, heart, a database of commits. To make the database of commits more usable, the repository also includes a database of names that map to hash IDs. However, for submodule purposes, the names database in Y becomes irrelevant! So we'll pretend it doesn't exist.

To use this thing, you (or someone else) will clone repository X to a copy X2 and then select some commit within X2 to check out. You'll use the new names database in X2 (which is independent of the names database in X—your branch names aren't their branch names—but is at least primed from that one) to pick the commit hash ID. Then you'll run git submodule update --init within the X2 clone, which will read the .gitmodules file extracted by the checkout so that it knows where to go to clone Y to make Y2 within X2.

Having cloned Y into Y2, the Git operating in X2 needs to know which commit within Y2 to check out. This is where the Y2 names database becomes irrelevant. Your Git software, operating in your X2, gets the hash ID to use in Y2 from a commit in X2. Which commit? Why, the current commit, the one that supplied the .gitmodules file and the fact that there's a Y in the first place.

Each commit you make in X or X2 carries with it the correct commit to check out in a Y or Y2. Hence, if you ever add a new commit to a Y repository, you must then also go back to the corresponding X superproject, run git add there to pick up the new hash ID from the updated Y, and run git commit in X to record that new hash ID in a new X commit. That way, any clone of this X will know to get the new Y commit. Clones of X are not using any Y names-database to get commit hash IDs for a Y submodule. They're using commits within the clone of the superproject X.

In other words, it's all a question of where the hash ID comes from. Branch names feed into branch-and-other-names databases within a Git clone, but they're easily changed. Superprojects that are controlling submodules want a rigid, unchangeable identifier for the commit to check out in the submodule—and that's a hash ID, not a name.

(Humans sometimes want more flexibility than this, and the first answer submodules give to that is "tough s**t". However, Git does now have some mechanisms for recording branch names and trying to use them. But backwards compatibility means that these mechanisms are klunky and extremely user-unfriendly, and I don't recommend them as they are not only klunky, they're also fragile. Know what the original design here is, and hence why submodules behave the way they do, and use that.)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Diana
Solution 2 torek