Git Submodules in Visual Studio

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.

Git Submodules in Visual Studio - my project
My simple little plugin. Click image to enlarge.

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.

Video explaining the code and the helper library to include.

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>
Git Submodules in Visual Studio - adding a git submodule
Adding the helper repository to my respository. Click image to enlarge.

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.

Video showing how to add a Git Submodule, and the effect it has on the repository.

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.

Git Submodules in Visual Studio - submodule reference on GitHub
Submodule is shown with a link to the referenced repository. Click image to enlarge.

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.

Video showing how to include the shared helper project and pushing the changes.

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.

Git Submodules in Visual Studio - my simplified code
Greatly simplified code when inheriting the JonasPluginBase class. Click to enlarge.

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.

Video showing how to simplify the code of my plugin by consuming the submodule helper code.

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.

Git Submodules in Visual Studio - the Canary Tracer for Microsoft Dynamics 365
CanaryTracer Gist added to improve the helper library. Click to enlarge.

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.

Adding and implementing the CanaryTracer extension to the helper library.

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.

Git Submodules in Visual Studio - Don't commit submodule changes from your repository
Submodule changes shall be pushed from the submodule repository. Click to enlarge.

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.

Switching solution and committing to the helper library repository.

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.

Git Submodules in Visual Studio - updating submodule reverts local changes
How a submodule should not be updated. Click to enlarge.

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.

This is how you should not do – but what I have done too many times.

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.

Git Submodules in Visual Studio - update to use latest version of submodule repository

After you have the latest files locally, you can update your repository with the new referenced commit in the submodule.

Making sure my solution is referencing the latest submodule version and committing the update.

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.

Git Submodules in Visual Studio - tracking submodule changes on GitHub

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.

Investigating changes in the remote repository.

References and links

Sample project: https://github.com/rappen/CoolRappSolution
Helper library: https://github.com/rappen/JonasPluginBase
CanaryTracer: https://jonasr.app/canary
Full video describing this article: https://jonasr.app/git-vs-subm-video


3 thoughts on “Git Submodules in Visual Studio

  1. 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?

    1. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.