157

I know that it's possible to set per-repo configs which override the user-level config (i.e. /path/to/my/repo/.gitconfig overrides ~/.gitconfig). Is it possible to set git configs which override the user-level settings for all child folders of a given folder? I.e., I have

|--topLevelFolder1
|--\
|   ---.gitconfig_override
|--\
|   ---childFolder1
|       \---[...]
|--\
|   ---childFolder2
|       \---[...]

And I want the settings defined in .gitconfig_override to apply in childFolder1 and childFolder2.

My motivation for this is as follows: I have a work laptop which I also use in my spare time for personal projects. All my work code is nested within a single folder. When I push to work git repos, I need to do so with my work persona - work login instead of name, and work email. When I push to my own personal (github) repos, I want to do so with my real name and personal email.

Other possible solutions I've thought of (and problems):

  • Create separate users for "work" and "play", set their user-level settings appropriately, and log in as the appropriate user when I switch context (hassle, plus I could easily forget to switch)
  • Create a script that searches for git repos inside "workFolder", and adds/updates their .gitconfig files to hold the appropriate details (if I create a repo and forget to run the script before pushing, I will push as the wrong person)
  • "hack" git such that every time it creates a repo, it checks the filepath and, if appropriate, updates the .gitconfig file (complicated, messy, and almost certainly The Wrong Way To Do It - plus, I wouldn't have the first clue how to go about it!)

I checked this question, which only seems to contain solutions for single repos, not multiple. Hopefully someone will see this question who missed that one!

4
  • Will you be working on the same repository as both "work you" and "personal you"? If not, the question you linked to contains the answer I'd give. Your --global user settings should contain whichever identity you use more. Each repository that should use the other identity should have user.name and user.email set accordingly.
    – Chris
    Commented Jan 23, 2014 at 13:15
  • 1
    I will never be working in the same repo with two different users - each repo will be either "work" or "home" - but the issue comes from the fact that (for work, at least) I have something like 35 repos, and am adding more regularly. I could add the user.name and user.email settings in each of the existing repos, and set up a cron script to add them to any newly added repos, but it would be much easier if I could set them in a single place which "filters down" to any repos in child folders.
    – scubbo
    Commented Jan 23, 2014 at 17:27
  • 1
    @scubbo, please review the answers, as Git has changed, likely making a different answer the correct answer for your question.
    – ndemarco
    Commented Aug 6, 2021 at 18:08
  • 1
    Thanks for the heads-up! I just changed the accepted answer.
    – scubbo
    Commented Aug 7, 2021 at 19:22

5 Answers 5

238

As mentioned by NateEag's edit, git's Conditional Includes are perfect for this. Since that answer's the one for people on git < 2.13, here's one for those who have newer versions.

First, create a new config file somewhere with the settings you want to take effect in the sub-folders - using the original question's folders, let's say it's at ~/topLevelFolder1/.gitconfig_include

In ~/.gitconfig, add:

[includeIf "gitdir:~/toplevelFolder1/"]
    path = ~/topLevelFolder1/.gitconfig_include

Any subfolder of ~/topLevelFolder1 will now include the config in ~/toplevelFolder1/.gitconfig_include - there isn't a need to manually change the .git/config in each subfolder's repo. (This doesn't override whatever's in the subfolder config - it just adds to it, as "include" implies.)

Notes:

  • Within ~/.gitconfig this setting should be located after the config you want to override because includeIf will be overridden again by any config that comes after it.
  • This setting includes the file only if you are in a repository under the given path. It's ignored if you're in any non-repository sub-path.
  • The trailing forward slash (/) in the gitdir condition is important.
  • git config --list is good for testing this. You'll see any overrides below includeIf lines in the output. You can also check specific entries with, e.g., git config --get user.email
  • On Git for Windows, specify paths relative to your user directory with ~/ and absolute paths with the Windows-style drive, like C:/ using forward slashes only. Backslashes and Unix-style mount points like /c/ don't work. Furthermore, in the includeIf part, you must specify the path with the correct case as the comparisons are case sensitive.
9
  • 28
    The trailing slash (/) in the gitdir condition is important.
    – stefanct
    Commented Mar 23, 2019 at 11:26
  • 3
    This should be marked as the correct answer for this question. Thanks to the mention of includeIf (and a little bit of trial and error), I've now configured one specific folder tree on my work PC in which every Git action identifies me as my personal account and uses my personal SSH key, along with a similar folder for work stuff on my personal PC. Meanwhile the rest of each system uses the appropriate email, name and SSH key on both machines. I only wish I upvote this answer more than once.. (:
    – user909694
    Commented Dec 20, 2019 at 7:41
  • Is the if part really necessary? If the referenced path does not exist the entry seems to be ignored. Commented Jan 10, 2020 at 9:18
  • 3
    If only git just did the logical thing and walked the tree looking for .git directories in each parent directory...
    – Ian Kemp
    Commented Sep 2, 2020 at 15:11
  • 2
    @OliverPearmain The goal isn't to include the file if it exists, but to include the file if you are in a repo under the given path.
    – phemmer
    Commented Sep 21, 2020 at 18:47
41

EDIT: Git 2.13 introduced conditional includes, which are designed to solve this exact problem.

My original answer is preserved below, for history's sake (and users stuck on older versions of git).

====================================

The exact behavior you desire is not supported, based on reading the gitconfig manpage.

However, as of git 1.7.12, Git reads config data from four different sources, two of which are user-specific:

$XDG_CONFIG_HOME/git/config and ~/.gitconfig. Entries in ~/.gitconfig override entries in $XDG_CONFIG_HOME/git/config.

That means you can store your personal gitconfig at $XDG_CONFIG_HOME/git/config and put machine-specific overrides in ~/.gitconfig. Something like

[user]
    email = [email protected]

in ~/.gitconfig should cover your email case.

Note that if $XDG_CONFIG_HOME isn't set git will look for ~/.config/git/config.

This works well for me, since I only ever have two personal repos on work machines (my emacs config and my dotfiles). If you add personal repos to your work machines frequently, this may not be good enough for you.

In that case, custom wrappers around git init and git clone would be your best bet.

Any binary on your $PATH whose name matches 'git-*' can be called as a git command, so you'd just need a pair of shell scripts that call the original command with all passed args, then copy the correct config file into .git/config.

1
  • Some git settings can be controlled by setting environment variables. They override values from config file. A very good utility for setting/un-setting env variables per directory hierarchy is, direnv. See my answer below for all the details.
    – Ajay M
    Commented May 11, 2016 at 15:33
17

You can use direnv command to set environment variables that apply to all child folders. You can be anywhere in that directory hierarchy. If the git setting you are trying to set can be controlled by an environment variable, then you are in luck.

Read the direnv basic page http://direnv.net/ to set it up for your shell. For zsh, it was as simple as to stick this line at the bottom of my .zshrc and restart the shell.

eval "$(direnv hook zsh)"

Check if the git setting you want, can be controlled by an environment variable. It seems you want to control author.email for a particular directory-tree, which is controlled by environment variable, GIT_AUTHOR_EMAIL. Note, environment variables take precedence over config. A complete list of environment variables git supports are here: https://git-scm.com/book/en/v2/Git-Internals-Environment-Variables

As, direnv's page specifies, create a file named, .envrc at the root of the hierarchy; in your case, topLevelFolder1

For example:

echo export [email protected] > .envrc

"Allow" the envrc: direnv allow . That's it!

Every time you jump into the hierarchy, direnv will find the said .envrc file and load it.

$ cd ~/topLevelFolder1/childFolder1/project_name
direnv: loading ../../../.envrc
direnv: export +GIT_AUTHOR_EMAIL

$ echo ${GIT_AUTHOR_EMAIL}
[email protected]

Jump out of the directory structure and direnv will unload those variables

cd ~
direnv: unloading
0
9

The [include] section in git config (.git/config, ~/.gitconfig ...) is what you are looking for.

[include]
    path = /path/to/foo.inc ; include by absolute path
    path = foo ; expand "foo" relative to the current file
    path = ~/foo ; expand "foo" in your $HOME directory

See detailed answered question: Is it possible to include a file in your .gitconfig

See git-config Documentation: http://git-scm.com/docs/git-config#_includes

EDIT

Add in childFolder1/.git/config and childFolder2/.git/config:

[include]
    path = ../.gitconfig_override
3
  • 2
    OK - so this solves the issue of only defining the user.name and user.email in one place (in ../.gitconfig_override), but I don't think it actually answers the original question? If childFolder1 and childFolder2 are children of parentFolder, I was looking for a way to set config values in parentFolder that would filter down to any repos rooted in child folders.
    – scubbo
    Commented Sep 18, 2017 at 21:08
  • 1
    As NateEag pointed out, this was only introduced in Git 2.13. (Git 2.13.0 dates from 9 May 2017.) So, if anyone reading this answer wonders why it doesn't work for them, it might be that they are using an older version of Git.
    – user82216
    Commented Mar 30, 2018 at 19:12
  • NOTE that I recently ran into an issue where these conditional includes didn't appear to be respected. The issue was that git (at least, my install - 2.17.0 on MacOSX) appears to require a trailing slash on directory paths.
    – scubbo
    Commented Jul 2, 2019 at 22:57
0

for each sub directory with a .git directory, run git config for each repo individually....


EMAIL="[email protected]";

find . -type d -name ".git" -exec sh -c "cd {} && git config user.email \"$EMAIL\"" \;

1
  • Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
    – Community Bot
    Commented Jun 28 at 12:21

Not the answer you're looking for? Browse other questions tagged or ask your own question.