Ever since I started working with development, I have both created and consumed libraries, frameworks, utilities and snippets to simplify and streamline my code. The methods to include this code have varied a lot; from simply copy-pasting code to linked files to private NuGet servers with build automation, full CI/CD etc.
Lately I have started working with Git Submodules, and I struggled a lot with changes being reverted, project dependencies locked in Catch 22 scenarios, and Git command line parameters that I never really understood…
So I finally took the time to really understand how Visual Studio can help me and which processes to follow to stay away from the pitfalls and get the most out of this feature.
1. Scenario 🔗
The scenario I have is not really relevant to how you can work with Git Submodules, the methods are likely the same for most other types of platforms, projects and languages. Anyway, I have a simple (stupid) little plugin for Microsoft Dynamics 365 / Common Data Service. It just reads the name of the user that initiated the call to the plugin, and writes the name to the Plugin Trace Log.
To make this code simpler and not have to do the always repeated instantiation of TracingService, PluginContext and OrganizationService, I have a little helper library with the
JonasPluginBase class. The code in this library is also available through a shared project, so I don’t have to add a dependency to a separate assembly from my plugin.
2. Adding a submodule 🔗
Adding a submodule is still not available in the Visual Studio UI, so to add the helper repository to my solution I have to execute Git command:
git submodule add <path to helper repository>
This adds the referenced repository to a subfolder in my plugin repository. It will create a
.gitmodules file defining name, path and source of the added module.
3. Including helper code and committing 🔗
Now that the helper repository has been added to a subfolder under my local repository, I can easily add the shared project from the submodule to my existing solution.
When the changes are committed and pushed, the remote repository shows a representation of the submodule. It looks like a folderwith a somewhat different icon, and it clearly states which version (commit id) of the referenced repository that is included.
Opening that folder does not take you to a subfolder in the current repository, but navigates into the referenced repository. It is a “fake” folder, pointing you somewhere else.
4. Consuming the base class 🔗
Now that the
JonasPluginBase helper code is available in my solution I can consume this library to simplify my code.
One of the advantages of including the helper code as a submodule compared to consuming a NuGet package or similar is that we have the code in our solution to help debugging and seeing what is actually going on inside that library.
5. Updating the base class 🔗
Another great advantage of including submodules is that you can update the code of the referenced project from within “your own” solution. This speeds up development and encourages continuous improvement of the helper libraries.
So I will improve the JonasPluginBase by adding my good old Canary Tracer.
In the image above I have added the
CanaryTracer that contains an extension to
ITracingService to dump everything in the
PluginContext to the trace log – essentially being your canary in the coal mine. The Gist is available here and you may also want to read my article A Canary in CRM.
6. Committing the submodule 🔗
After making changes to the submodule code, it is important not to try to push the changes while still in “your” repository. This is one of the mistakes I made and confusions I had about submodules.
Instead you need to switch to the repository of the submodule, and find the changes to push there. The video below goes through how to easily go through that process.
7. Updating reference – wrong way 🔗
When the submodule code is updated, the consuming repository detects this as a pending change to the submodule.
This is one of the core things I had a hard time wrapping my head around. I can see that the submodule is updated, bu tmy instinct says that I do not want to “push the submodule” from my own repository, I just want to make sure I’m using the latest version available of it. So that gut feeling told me to right click the pending change to see what I could do with it. And there is an option for “Submodule Update” – sounds perfect, please do update my submodule.
But what this option does is to update the files in my local submodule folder with the files from the remote repository – and from the commit that my repository currently points at. End result being that any local changes I had in my submodule folder will now be reverted to the commit to which my repo points to.
8. Referencing latest version of submodule repo 🔗
When you are in a situation like the one I put myself in the above section, or just want to make sure you are now referencing the latest version or the remote submodule repository, you can always do a
git checkout master (or whatever branch you want) from within the submodule repository folder.
After you have the latest files locally, you can update your repository with the new referenced commit in the submodule.
9. Tracking changes on GitHub 🔗
When you look at the submodule on GitHub you can see the specific commit of the submodule that is referenced. This is as far as I know still not possible to see from within Visual Studio.
Looking at the commit to your repository you can even see the included changes from the submodule repository, not only that the reference was updated in your repository.
References and links 🔗
Sample project: https://github.com/rappen/CoolRappSolution
Helper library: https://github.com/rappen/JonasPluginBase
Full video describing this article: http://jonasr.app/git-vs-subm-video
11 thoughts on “Git Submodules in Visual Studio”
Hej Jonas, really good explained!
I do have one question. Since we only can deploy one DLL to Cds/Dynamics 365 MDA we normally have to use some merging toll (like ILMerge), which is not supported or put all our code directly in our Plugin project (either copying everything or maybe linking cs-files).
Would this be fixed with the shown solution (using git submodules) or will your solution still generate two DLL-files?
Thanks Benedikt 🙂
This still depends on the type of librarry project you are including. In my case I had a Shared Project, which means the code is accessible to the consuming project and will be compiled into it. If the library you consume is a classic library and you reference the compiled dll, you would still need to ILMerge that.
For this reason I would recommend you always include the “code”, whenever possible, instead of the resulting assembly.
Thx for this post. I’m struggling with integration of multiple VS-Projects that comes with Git-Submodules. You may have a lock to this issue posted on stackoverflow:
Any help would be very appreciated!
Very nice guide for working with submodules that is still usable in 2022 with the new Git experience. I wish I would have found this guide earlier as I had to find out how to work with submodules in VS2022 myself. Your very detailed guide helped me along with feeling more secure about this strategy.
submodule support has become a little bit better with VS2022 but there are still some quirks.
Thank you, Johan!
I agree, I still use this blog from myself, now and then… 😂
Because I just can’t really remember it all, when I use it too rarely.
VS2022 is better, but some things are still hard to do… like using a submodule, making changes, and then trying to create a pull request for the source… 🙄
Thanks for the concise guide on this, appreciate that the vids are all short and to the point. Helped a lot with organising the VS and git repositories I’m setting up.
Anyone able to get more than 10 or 11 submodules showing in Visual Studio? Git in Visual Studio is not detecting changes in all of the submodules when a solution contains more than 10 or 11 submodules.
I think we’re stucked with max 10 repos for now…
See here: https://devblogs.microsoft.com/visualstudio/multi-repository-support-released/#10-repository-limit