Set Up a Submodule Where Only You Have Write Access.
Say you want to add a submodule to your repository, and this submodule is a one to which you have push access. It is frequently useful to be able to publish a repository with the submodule URL as pull-only, while keeping the push-pull URL in your own repository. This way, you can make changes to the submodule repository from within the enclosing repository. This is a simple thing to do in Git once you know how submodules are arranged.
For convenience, let's use a a couple of GitHub repositories as an example. Let's start with the Hello repository, and say I want to add the Just Testing repository as a submodule. That's simple enough; I check out the Hello repository to my local drive:
(You may notice the unfamiliar -o github in the clone command. This names the remote for this clone "github" instead of the default "origin". Git is distributed, and I often have more than one remote for repositories, so I like to name my remotes semantically. Another name I use is "dropbox" for obvious reasons).
Now I'm in the repository I add the remote. This is where I have to make a choice. GitHub gives you a choice of clone URLs:
If I use the Read+Write URLs, for the submodules, and someone else clones my repository, they will be using URLs to which they do not have access. So, cloning the repository will work, but when they go to initialise the submodule, the command will fail because they can't access the repository through that URL. But, If I use the Read-Only URL, I won't be able to push to my submodule repository. That's okay, because I can add the repository with Read+Write access for me but change it for the shared repository. Let's start by adding the submodule to the repository using the Read+Write URL:
This will add a directory called "JustTesting" to my repository and check out the files. You can check the submodule with git submodule status which, in my case, shows this:
a73686133fc66884faac1f8c1d9603b9ffcf09b1 JustTesting (heads/master)
Which shows the sha of the checked out commit of the submodule (there are no - or + adornments so there are no issues) the directory which contains it (JustTesting) and the current description (in this case it just shows the head of the master branch. If there were any tags in the repository it would show the output of git describe).
Checking the status of the repository with git status shows that we have two new files:
JustTesting isn't a new file, it's just the way that Git sees a submodule. What we are interested in is the ".gitmodules" file. You can see that this shows where the the submodule is kept and what it's URL is. Now let's look at the way the submodule is configured:
First I find out what the name of the remote is (yes, I know it's origin) and then I get information about the remote. This shows that it is configured to the Read+Write URL
Let's take a look at the ".git/config" file. This is where the configuration for the local repository is kept:
The interesting section is at the bottom, which has information about the submodule. This looks a lot like the entry in the ".gitmodules" file, except that it doesn't reference the path to the submodule.
Now let's change the URL in the ".gitmodules" file so that it points to the Read-only URL.
So there are two URLs for this submodule. The repository itself contains the Read-Only URL in ".gitmodules" and this is the configuration that is shared among all repositories that clone it. And the private Read+Write module is configured in the ".git/config" file which applies only to this particular repository. Now I just need to commit the changes and I can push this back up to GitHub.
Go and look at the Hello repository for yourself. You can see that JustTesting is a submodule. And if you clone this and update the submodule, it will be configured to point to the Read-Only version of JustTesting:
cd tmp git clone -o github [email protected]:Abizern/Hello.git Hello2 cd Hello2 git submodule update --init
And you can see for yourself that ".gitmodules", ".git/config" are configured for the Read-only URL. I'm not going to show you the output of those because I want to show you something more interesting.
Imagine I am at my other machine and I want to clone this repository but still have Read+Write Access to the submodule. This is how I go about that. I clone the repository as normal but I don't update the submodule in the same way as I just did above
Then – I init the submodule:
And you can see that all this does is register the submodule, but does not actually check out the repository:
Now - I can change my local configuration in ".git/config" so that the submodule URL is the Read+Write one:
And now I update the submodule repository:
And I have Read+Write access from my local submodule:
That seems like a lot of steps - but only because I went through it in detail. This is why it works:
URLs for submodules are kept in the ".gitmodules" file. As this is in the top level of the repository, it is shared amongst all repositories that clone from it. When you initially add a submodule to your repository, an entry is added to the ".gitmodules" file and to the local ".git/config" file and the repository is checked out. All in one step (although the changes to the repo have to be committed manually, this allows you to change the public facing configuration).
When you clone a repository that has submodules, all you are getting is the ".gitmodules" file. It also gets an entry that refers to the sha of the submodule to check out, I've written about this previously. It takes two steps to actually set up the repository. First, git submodule init copies the submodule settings from ".gitmodules" to ".git/config" and then git submodule update pulls down the repository at the sha that is referenced. Normally, you can do this with one step: git submodule update --init as in the Hello2 example. But If you do it in two steps, you have a chance to edit the ".git/config" file before the repository is pulled down, which means you can customise the way that the submodule is set up in your own repository.