69

I use two different git emails, one for work and one for public projects. Initially I thought that I could create a separate .gitconfig with a different email in a folder where all my public repos are in, and that git would respect that, but alas it seems that doesn't work. What's the best way to easily setup something similar? I want to avoid having to specifically change the email in each public repo.

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
Suan
  • 34,563
  • 13
  • 47
  • 61
  • 3
    Git 2.13 introduced conditional includes. See [this answer](https://stackoverflow.com/questions/21307793/set-git-config-values-for-all-child-folders/24463387#24463387). – John Lindal Dec 12 '17 at 18:25

7 Answers7

105

The best way to do this since git 2.13 is to use Conditional includes.

An example (copied from an answer here):

Global config ~/.gitconfig

[user]
    name = John Doe
    email = john@doe.tld

[includeIf "gitdir:~/work/**"]
    path = ~/work/.gitconfig

Work specific config ~/work/.gitconfig

[user]
    email = john.doe@company.tld
Tim Harper
  • 2,561
  • 20
  • 23
Chris Crewdson
  • 1,270
  • 1
  • 10
  • 7
  • 4
    This is much neater than the shell customisations in other answers, thanks. – Greg Apr 15 '20 at 08:23
  • 6
    This might be obvious but still interesting: When grouping multiple git repositories beyond a directory which is not a git repository, the effect will only be visible within the git repositories but not in the parent directory. – chronicc Dec 12 '20 at 09:34
  • Can I also use a wildcard in the includeIf to use the config for any subdirectory? I want to organize repos in folders and not check them directly out in `~/work/`, but maybe in `~/work/projects/` or `~/work/papers` – blkpingu Apr 08 '21 at 03:39
  • 4
    [Conditional includes](https://git-scm.com/docs/git-config#_conditional_includes) are pretty powerful. The example above should work in this case because gitdir ends with `/`: `If the pattern ends with /, ** will be automatically added. For example, the pattern foo/ becomes foo/**. In other words, it matches "foo" and everything inside, recursively.` – Chris Crewdson Apr 08 '21 at 21:53
  • 1
    @chronicc Thank you! This was not obvious to me and your comment saved me some frustration. – m00am Aug 17 '21 at 11:42
  • I'd kind of prefer to just include `**`. Less chance for an error that way and I'd prefer less magic. – Tim Harper Feb 26 '23 at 19:10
8

As mentioned in other answers you can't really set your credentials per directory. But git allows you to do so on a per repository basis.

# Require setting user.name and email per-repo
$ git config --global user.useConfigOnly true

This way, on your first commit you will get an error asking you to provide name and email. In your repo folder add your credentials once to your repo and from then on you'll commit with this identity:

$ cd path/to/repo
$ git config user.name My Name
$ git config user.email mail@example.com
Torsten Walter
  • 5,614
  • 23
  • 26
  • This should be the answer. However, it [requires Git 2.8](https://github.com/blog/2131-git-2-8-has-been-released) in order to work. – dsclose Feb 16 '18 at 12:05
  • Unfortunately this duplicates commands. If I have a folder ~/job and I am planning to create 100 new projects under it in next week or so, then I have to keep typing my name etc over and over again 100 times. It would be better if I could do that once for ~/job and it applies to all subfolders that exist or that I may create in future. – theprogrammer Apr 20 '20 at 20:46
7

I have the exact same problem. As a temporary solution, I have this in my .bashrc:

alias git='GIT_AUTHOR_EMAIL=$(
      p=$(pwd)
      while [[ $p != "$HOME" ]]; do
        [ -e $p/.gitemail ] && cat $p/.gitemail && break
        p=$(dirname $p)
      done) GIT_COMMITTER_EMAIL=$(
      p=$(pwd)
      while [[ $p != "$HOME" ]]; do
        [ -e $p/.gitemail ] && cat $p/.gitemail && break
        p=$(dirname $p)
      done) /usr/bin/git'
alias g=git

This way I've got two different .gitemail files in the parent directories:

  • ~/work/.gitemail
  • ~/github/.gitemail

Note that I'm only switching user.email this way, everything else is centralized in ~/.gitconfig. It's a solution, but it's not great.

Hoping someone on StackOverflow has a better idea...

pithyless
  • 1,659
  • 2
  • 16
  • 31
  • This works well, even when using [`defunkt/hub`](http://defunkt.io/hub/). I just changed the path `/usr/bin/git` to `hub` and it worked ^_^ – TrinitronX Jun 25 '13 at 23:02
  • There is an infinite loop in this code. To make matters worse, if you are using a prompt that calls git, this currently results in an **infinite loop** in **all interactive login shells** if you're outside your home directory! I've created a fixed version [in this gist](https://gist.github.com/5979265) that works as a function and simplifies the alias definition quite a bit. – TrinitronX Jul 11 '13 at 21:08
  • 1
    Yes, looking back, my posted bash code is not the best solution. :) While @TrinitronX bash script may be cleaner, I really like how zsh's profiles allow for a cleaner solution: http://stackoverflow.com/a/8645101/145754 – pithyless Jul 25 '13 at 09:01
  • I haven't tried zsh, but it seems to be all the rage. I still like pure bash, as it forces me to write scripts that usually work well out of the box on other *nix platforms. The update on this post reminded me to take a look at this again and realize that there were a couple other edge cases in the script that could cause an infinite loop (dirs with spaces, empty or '.' in dirname result). I've updated [the gist script](https://gist.github.com/trinitronx/5979265) to fix them. – TrinitronX Jul 26 '13 at 16:39
7

Having switched over to ZSH, this is my revised solution to the problem using "profiles" triggered on directory changes. The nice thing about this solution is that it can be used for other settings.

Pop this into your zsh config:

#
# Thanks to: Michael Prokop. 
# More documentation: 
# http://git.grml.org/?p=grml-etc-core.git;f=etc/zsh/zshrc;hb=HEAD#l1120
#
CHPWD_PROFILE='default'
function chpwd_profiles() {
    local -x profile

    zstyle -s ":chpwd:profiles:${PWD}" profile profile || profile='default'
    if (( ${+functions[chpwd_profile_$profile]} )) ; then
        chpwd_profile_${profile}
    fi

    CHPWD_PROFILE="${profile}"
    return 0
}
chpwd_functions=( ${chpwd_functions} chpwd_profiles )

chpwd_profile_default # run DEFAULT profile automatically

And then elsewhere in your zsh git customizations:

zstyle ':chpwd:profiles:/home/user/work(|/|/*)'  profile work
zstyle ':chpwd:profiles:/home/user/fun(|/|/*)'   profile fun

# configuration for profile 'default':
chpwd_profile_default()
{
  [[ ${profile} == ${CHPWD_PROFILE} ]] && return 1
  print "chpwd(): Switching to profile: default"

  export GIT_AUTHOR_EMAIL="default@example.com"
  export GIT_COMMITTER_EMAIL="default@example.com"
}

# configuration for profile 'fun':
chpwd_profile_fun()
{
  [[ ${profile} == ${CHPWD_PROFILE} ]] && return 1
  print "chpwd(): Switching to profile: $profile"

  export GIT_AUTHOR_EMAIL="fun@example.com"
  export GIT_COMMITTER_EMAIL="fun@example.com"
}

# configuration for profile 'work':
chpwd_profile_work()
{
  [[ ${profile} == ${CHPWD_PROFILE} ]] && return 1
  print "chpwd(): Switching to profile: $profile"

  export GIT_AUTHOR_EMAIL="work@example.com"
  export GIT_COMMITTER_EMAIL="work@example.com"
}
pithyless
  • 1,659
  • 2
  • 16
  • 31
4

Have a centralized mechanism for creating repos. Like some script in your path etc. whereby you do:

repo create -t public -n name

or something like that.

The repo command ( just an example, nothing to do with the one from Android ) will create the repo for you in the necessary location and read a config file and set the credentials for that repo (in the .git/config for that repo ) based on the type being public or private etc.

manojlds
  • 290,304
  • 63
  • 469
  • 417
3

The real answer is that its impossible.

However, thanks to @pithyless , and because I was already using a custom 'c' function to switch directories followed by an auto ls, this is what I'm doing now:

# cd + ls, and change git email when in personal projects folder 
function c {
  if [[ "`abspath $1`" == *"$HOME/Projects"* ]]; then
    export GIT_AUTHOR_EMAIL="personal@gmail.com"
  else
    export GIT_AUTHOR_EMAIL="me@work.com"
  fi
  cd "${@:-$HOME}" && ls;
}
Suan
  • 34,563
  • 13
  • 47
  • 61
  • 4
    For future people (since this is marked as the answer), you no longer need to do this since git 2.13. Use conditional includes: https://stackoverflow.com/a/60344116/793055 – Chris Crewdson Apr 16 '20 at 16:28
0

I had same needs, but I wanted all .gitconfig sections can be overrided and not only user.email and user.name.

Because I not found anything I done this: https://github.com/arount/recursive-gitconfig

Here is the current code, but please refer to the github source to get last updates:

# Look for closest .gitconfig file in parent directories
# This file will be used as main .gitconfig file.
function __recursive_gitconfig_git {
    gitconfig_file=$(__recursive_gitconfig_closest)
    if [ "$gitconfig_file" != '' ]; then
        home="$(dirname $gitconfig_file)/"
        HOME=$home /usr/bin/git "$@"
    else
        /usr/bin/git "$@"
    fi
}

# Look for closest .gitconfig file in parents directories
function __recursive_gitconfig_closest {
    slashes=${PWD//[^\/]/}
    directory="$PWD"
    for (( n=${#slashes}; n>0; --n ))
    do
        test -e "$directory/.gitconfig" && echo "$directory/.gitconfig" && return 
    directory="$directory/.."
    done
}

alias git='__recursive_gitconfig_git'

This allow me to use specific .gitconfig depending what repository I'm playing with.

Hope this can help some of you

Arount
  • 9,853
  • 1
  • 30
  • 43