Submodule branch is lost

I have a single submodule I'm using in a project that points to a specific branch on the submodule's repo. This seems to work fine, but sometimes SourceTree does not commit to the correct submodule branch. When this happens I have to open the submodule repo by clicking on the repo in the submodules list, and then switch to the correct branch.

When I do this it appears to be creating a new detached head, and if I do commit to it then the head goes away and my changes are lost.

Do you guys have any idea what is going on? Like I said it doesn't happen all of the time, but it is fequent enough that I may have to stop using SourceTree for my submodule management.

Thanks

2 answers

Yeah, the reason for this is that every commit point in your parent repo is actually tracking a specific commit on each submodule, it's not tracking a branch. This is important because if for example something breaking changes in the submodule branch, you don't want to get that when you check out your parent until you've altered your own code to match. So at any point in time every submodule record registered on your parent repo is pointing at a specific commit. This naturally means that the local checkout is on a detached HEAD a lot of the time, if the submodule branch moves ahead. It just means you have to be careful when you use the submodule contents as a working copy too - you have to make sure you checkout a branch before you make your own changes to the subrepo (and once you've made those changes, committed, you'll commit to the parent repo to change which submodule commit it's tracking).

It can sometimes be easier to maintain separate checkouts of submodule projects that you edit a lot, and just use the submodules as read-only versions that you pull the changes into, then you don't have to worry about this. However I know that can be imprtactical if you need to make changes in both parent and submodule at the same time, e.g. because of an interface change or something.

Ok, that is what I figured. In my project it makes sense to use a branch because I'm building a library that is used for a specific project, but it will eventually be merged into the master branch so it can be used on other projects as well.

The branch is so the current state of the library doesn't change once it's in production and we move on developing out other stuff on the library.

So now I have two questions. Giving my description how would you suggest I setup my repos? Also, when this does happen is there a way to merge the changes from the detached head into the correct branch without having to switch to the branch and loose the changes in the detached head?

Since your development of the submodule is so tightly coupled with the parent for the moment, developing them in the same tree is probably sensible, if you need to test things with the parent before committing changes to the submodule for example.

There are various ways you can avoid losing track of your detached head when you go to merge, the simplest to do inside SourceTree is just to create a temporary tag on the submodule where you are. This will keep the detached head visible in the graph when you switch away from it so you can merge it in, and then you can delete the tag afterwards (or just leave it, and move it next time using the Advanced options in the tag dialog). There are other ways without using a tag but you'd have to make a note of the SHA and either do it on the command line or via a Custom Action.

The alternative if you don't need to test with uncommitted submodule changes is to work on your submodule in a separate checkout (as its own repo, not a submodule), on a branch, and then just Fetch in your submodule and checkout the new commits each time you want to change the submodule state. This is more long-winded in the parent but simpler for the submodule, so which one you use depends on the balance of changes between the two.

Ok, that's some very good information, but I have one last question. Is it possible to just checkout a branch of a GIT repo and have it inside the root of my parent working copy? Kindof like a submodule but it'll be it's own project in SourceTree?

The project I'm working on is an Xcode project, and I'm trying to avoid having to keep my library files in a fully seperate directory as we have a few other developers working on the same project, and do not want to have to relink header files every time the project file changes.

We also have other projecdts that rely on the same library repo, but are dependant on different branches. Therefore, I need a way to be able to checkout a branch of the library repo and store it inside of the Xcode root directory. That way I can work on projects that rely on different branches of the library repo without having to switch my library project's branch.

Any thoughts on how I can acheive this?

Thanks for all the help btw!

Yeah, you can definitely do that - git will just ignore any subfolder which has a .git folder rooted in it, there just won't be any explicit link between the parent and library repo, so anyone checking out the parent would have to manually install the library repo themselves in the right place. The submodule mechanism just automates all this and makes sure you're getting the version that was intended to work with the parent. If everything's in flux right now and you're happy to manually manage the relationship between the repos for the moment then a simple subfolder, not set up as a submodule, could work well. You could then add the library as a submodule when things have stabilised more and you're making fewer changes to the library.

We also have other projecdts that rely on the same library repo, but are dependant on different branches.

Submodules are designed for that very case, but they don't track branches, they track individual commits (git branches are actually just pointers to commits anyway that happen to move when you commit when you have the branch checked out). Therefore you can have many projects referring to the same library subrepo but each of them tracking a different commit. Changing the commit that a parent repo tracks is a deliberate act in the parentt, it never happens automatically.

When using command-line git, it's not true that the submodule is necessarily in detached head state.  You can go into a submodule and commit changes, and it will still be on the HEAD of its current branch – then if you do `git status` in the parent project, it will show that the submodule has new commits.  If you then add and commit the submodule, it updates which commit the parent points to.

Detached HEAD state only occurs if you commit changes to the submodule and then do `git submodule update` in the parent project, because, as the documentation says, that command is equivalent to `get checkout` of the commit the parent projects points to, which at that point is no longer the HEAD.

Shen developing on submodules, you typically never need to run `git submodule update` except to clone and checkout submodules the very first time, or intentionally restore to the parent project's commit.

What this means is SourceTree must be doing `git submodule update` when we don't expect/intend for it to.  It would be more helpful if SourceTree instead asks the user "Do you want to check out the associated commit(s) of the project's submodules?".  That way we can avoid accidentally getting into detached HEAD state.

We are suffering from this same behavior.  

We have 5 people working on projects with a few libraries that need to be updated from time to time.  People forget and commit to never never land.  It is to the point that it has become a significant problem, loosing a couple of hours a work every week. 

We would love to see some warning or something to let the coder know he is about to shoot himself in the foot.

Suggest an answer

Log in or Sign up to answer
How to earn badges on the Atlassian Community

How to earn badges on the Atlassian Community

Badges are a great way to show off community activity, whether you’re a newbie or a Champion.

Learn more
Community showcase
Published May 30, 2018 in Sourcetree

Tip from the team: configuring Git or Mercurial in Sourcetree

Supported Platforms macOS Windows To make using Sourcetree as simple yet powerful as possible we embed (bundle) dependencies such as Git, Git LFS, and Mercurial. We strive to keep these...

571 views 1 2
Read article

Atlassian User Groups

Connect with like-minded Atlassian users at free events near you!

Find a group

Connect with like-minded Atlassian users at free events near you!

Find my local user group

Unfortunately there are no AUG chapters near you at the moment.

Start an AUG

You're one step closer to meeting fellow Atlassian users at your local meet up. Learn more about AUGs

Groups near you