Managing multiple Git users through Git hooks
I upgraded my work laptop recently and the very first Git commit I made outputted this:
Yes, I had not set my Git username and email. However, was Git suggesting the best approach to configure the Git user? Setting the username and email globally is definitely the easiest way to suppress the message but, like any other global configuration, you need to think twice before going for it. In my case, most of the Git projects on my laptop are hosted on a company-hosted platform, and I will definitely want to use my work email for them. However, I’m quite active on Github as well. On Github projects, however, I do not want to use my work email when committing and that’s how everything started.
I didn’t mind using the same Git user name on work projects and Github projects, so I set that globally by running:
git config --global user.name “Name”
However, setting a global Git user email was not going to help me. What I wanted was to globally configure Git to use a different user email based on the domain of the fetch url¹. This seemed easy to solve at the beginning, but to be honest, it took me much longer than I thought it would. Since I couldn’t find a conclusive solution online, I decided to share mine to help other developers looking to achieve the same goal.
How to set a domain based user in Git?
First, I was hoping to be able to set a domain-based user globally in the .gitconfig file, but unfortunately that feature is not currently available in Git.
Therefore, I had to switch my focus and find a way to set the user config at the project level. The manual way of doing it is to navigate to the project repo and run the following command:
git config user.email "your email@example.com"
However, if you work on multiple Git projects and want to set the Git user for each one separately, I don’t recommend the manual solution. Imagine you have cloned a repo in the middle of a busy day. If you forget to set the repo-level user config, then you will be reminded to set it after you have made the first commit in the repo, which is what happened to me. You will get the message I posted above. By then, not only will you need to run the command to set the user, you will also need to modify the author of the first commit. If you are like me and work on multiple projects at the time, then this would just be tedious. I needed to find a way to automatically set the user info at the project level as soon as a project was cloned. Keep reading to learn how I did it.
Anytime I think of some form of customization in a Git repo, my mind goes directly to Git hooks. Git hooks are scripts that help you automate tasks before or after certain Git commands are executed. They are very powerful tools that enable you to design very elegant workflows.
In my case, I decided to use a Git hook to set the Git user based on the project’s hosting provider. I had already set Git hooks on individual projects, but this time I wanted a global hook that is automatically added to every repo I clone on my machine. As it turns out there are no global Git hooks in the sense of having some hooks in your home directory under .git/hooks. When I think about it though, it makes sense, since Git hooks generally have project specific use cases. Obviously, this is not the end of the story. You can achieve the same goal by using global Git templates.
Git templates
When you clone a Git repo or initiate one using git init, a directory called .git is added to your repo. This is also called the template directory and it includes some directory structure, including one called hooks. The hooks directory comes with some sample hooks, which are disabled by default. Git allows you to replace these default hooks with your pre-defined ones so that when you initialize a Git repo, your hooks will be added to the project instead of the sample ones. That’s exactly how we are going to achieve global hooks.
Below are the steps to override the default hooks using Git templates:
- Create a directory to hold the global hooks:
mkdir -p ~/.git_templates/hooks
Note that it is very important to have a directory called “hooks” in your Git template directory. The reason is that, by default, Git looks for the hooks directory to run the hooks.
2. Configure Git to use the templates by running the following command:
git config --global init.templatedir '~/.git_templates'
This will add an entry to the .gitconfig file like this:
[init]templatedir = ~/.git_templates
Now, every time you run git init, Git will add all the templates inside the .git_templates directory to the project’s .git directory.
This is all perfect. The only thing left to figure out is the hook itself.
What hook should be used?
As I mentioned earlier, I wanted a hook that sets the user email based on the repo origin before even the commit author is set. Before giving you the answer and talking about the content of the hook, I would like to share with you my fruitful journey of finding the hook hoping my ‘aha!’ moments will be useful to you as well.
There is no post-clone hook in Git. My best bet was a pre-commit hook. I tried that and very surprisingly, after making a commit, I got the same warning as before: my computer’s username and hostname was used as the Git user since it was not set. This wasn’t what I expected, but I learned that the commit author is set even before the pre-commit hook runs!! Unfortunately, that meant I needed a hook that ran earlier in the chain, even before the commit author is set. Searching through the available Git hooks, the only possibility was post-checkout. I hadn’t picked that one earlier because I thought that after cloning a repo, the first Git hook that is very likely to be run is the pre-commit. What I had forgotten was that the git clone command is indeed a combination of a few commands including git init and git checkout. There you go. Post-checkout hook was exactly what I was looking for!
Now that we know what hook to use, let’s navigate to our template directory and add the hook there.
- Create a file named post-checkout and add it to the template directory at ~/.git_templates/hooks
- Make sure the hook is executable by running
chmod +x ~/.git_templates/hooks/post-checkout
3. Paste the following code inside the post-checkout file:
The script itself is simple. In the get_domain function, I’m fetching the domain by parsing the url associated with the default remote, which is origin. Then, in the set_user function, I’m using an exhaustive case statement to set the user email based on the domain. If you are cloning a project from a new domain that is not included in the hook, you will get a classic yellow warning saying that no Git user has been set for this domain in the post-checkout hook. This warning is, indeed, more for future me than for anything else.
The beauty of the script is that you can set a lot of domain-based configurations right after you clone a new repo on your machine. For example, if you also want to use a different name for different domains, add the following command to the case statements:
git config user.name "Your Name"
The only caveat to this approach is that it only works on freshly cloned repos and not the existing ones. For existing repos, you will have to navigate to the repo and set the email manually by running git config user.email “email”
Using this setup, I can now push code to Github and any other Git hosting services without having to worry about what email is attached to my commits. I hope you try it as well and find it helpful!!
¹ In Git projects, push url and fetch url are the same by default. If you have added extra push urls to your Git repo, be aware that this article is setting the Git user based only on the fetch url.