1061

In my ~/.gitconfig, I list my personal email address under [user], since that's what I want to use for Github repos.

But, I've recently started using git for work, too. My company's git repo allows me to commit, but when it sends out announcements of new changesets, it says they are from Anonymous because it doesn't recognize the email address in my .gitconfig - at least, that's my theory.

Is it possible to specify multiple [user] definitions in .gitconfig? Or is there some other way to override the default .gitconfig for a certain directory? In my case, I check out all work code in ~/worksrc/ - is there a way to specify a .gitconfig for only that directory (and its subdirectories)?

codeforester
  • 39,467
  • 16
  • 112
  • 140
Brock Boland
  • 15,870
  • 11
  • 35
  • 36
  • See [git-config#FILES](http://www.kernel.org/pub/software/scm/git/docs/git-config.html#FILES). – Josh Lee Nov 18 '10 at 22:48
  • 1
    I'm surprised that your company server would do that - it would have to CHANGE the sha of your commit for that to work. If you make a commit to a local checkout, what username do you see? – Alex Brown Nov 18 '10 at 22:59
  • @Alex: Presumably the notification hook is trying to match the author information (email, perhaps) with some other list/database, maybe in order to canonicalize it, maybe looking for some other information. Obviously not the best approach for this particular case, though! – Cascabel Nov 18 '10 at 23:48
  • 1
    @Alex: Forgot the important bit there - it could easily just be a name on the email notification, not actually changing anything on the commit. – Cascabel Nov 19 '10 at 00:49
  • 2
    You can use a git-hook to automate this recurring work: https://github.com/DrVanScott/git-clone-init – Henning Apr 22 '17 at 17:40
  • 2
    Please accept this answer: https://stackoverflow.com/a/43654115/482899. It's best solution since git 2.13. – northtree Jul 26 '17 at 04:01
  • could use the pattern `[includeIf "gitdir:D:/workCode/**"]` – 张馆长 Nov 16 '20 at 06:13
  • @northtree - it's a great answer. Unfortunately, it seems like Xcode does not 'play nicely' when that configuration is present, so I had to remove it: https://stackoverflow.com/a/75151821/906192 In any case, both answers here have merit :) – Peter Jan 17 '23 at 20:29

24 Answers24

1305

You can configure an individual repo to use a specific user / email address which overrides the global configuration. From the root of the repo, run

git config user.name "Your Name Here"
git config user.email your@email.com

whereas the default user / email is configured in your ~/.gitconfig

git config --global user.name "Your Name Here"
git config --global user.email your@email.com
mitch
  • 396
  • 1
  • 7
  • 14
discomurray
  • 14,411
  • 1
  • 21
  • 11
  • 111
    you can see the effects of these settings in the `.git/config` file – Abizern Nov 18 '10 at 23:15
  • i tried this at work to specify my work-specific name/email, but i still seem to be getting emails to my personal account that i tied to the --global git command. I've never used this feature before, but should it send an email (if there is one) to the first found user.email and not trickly up into the --global? does it take some time once i set the local user.name/email or should it be instnataneous? – hellatan Jun 15 '11 at 17:07
  • @dtan see here: http://stackoverflow.com/questions/4665337/git-pushing-as-wrong-user/6665231#6665231 – yuttadhammo Jul 12 '11 at 13:51
  • 29
    You can manually edit those config files with `git config --edit` and `git config --global --edit`. And in case you missed Abizern’s [comment](http://stackoverflow.com/questions/4220416/can-i-specify-multiple-users-for-myself-in-gitconfig#comment4566646_4220493), a repository’s config file is at `/.git/config`. – Rory O'Kane Apr 25 '12 at 20:01
  • 18
    You should also unset **GIT_AUTHOR_EMAIL** and **GIT_COMMITTER_EMAIL** (and *_NAME) as they will override the local settings – ACyclic Sep 14 '12 at 09:58
  • 9
    Is there a way to do this for all repos in a given folder, rather than for individual repos? See my question here: http://stackoverflow.com/questions/21307793/set-git-config-values-for-all-child-folders – scubbo Jan 23 '14 at 12:13
  • After changing the specific repo config, git commands are still executed with my global setting. Is that normal? – mgPePe Jan 30 '16 at 17:49
  • People facing problem even after this, if you are using ssh, ensure you have proper ssh keys for your other email account set in your git hub account. – Kumar Apr 20 '17 at 07:52
  • 8
    See [this answer below](http://stackoverflow.com/a/43654115/3507206) for more upto-date solution with `Git 2.13` released today. – tejasbubane May 10 '17 at 11:20
  • Note that Apple's Keychain Access or Windows' Credentials Manager may have cached your previous username/password – cowlinator Oct 23 '17 at 04:47
  • This doesn't work, if you have `.netrc` with different credentials. Git ignores whatever information there is in your local configuration and sends random authentication to the server owned by someone else. Nice job. – wvxvw Feb 25 '18 at 11:31
944

Since git 2.13, it is possible to solve this using newly introduced Conditional includes.

An example:

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

Remember that [includeIf...] should follows default [user] at the top.

Duc Filan
  • 6,769
  • 3
  • 21
  • 26
Tomáš Janoušek
  • 9,877
  • 1
  • 13
  • 13
  • 86
    This is the best answer now that git 2.13 has been released. – tejasbubane May 10 '17 at 11:18
  • 2
    Would this solution work for all subdirectories within the one specified in the include statement, assuming they have no .gitconfig files of their own? I would think so, but so far testing hasn't borne this out. – Gary Jun 30 '17 at 19:28
  • 4
    @Gary Yes, according to my experience and the docs: “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.”, “; include for all repositories inside $HOME/to/group [includeIf "gitdir:~/to/group/"]” – Tomáš Janoušek Jun 30 '17 at 21:38
  • This is exactly what I needed. I did have to go to 2.13.1 docs to find out more about the `includeIf` section: https://git-scm.com/docs/git-config/2.13.1#_conditional_includes – gligoran Aug 15 '17 at 09:51
  • @gligoran There seems to be something wrong with the website. It says there are no changes between 2.13.0 and 2.13.1 version of git-config manpage, yet the 2.13.0 one doesn't mention includeIf. :-( – Tomáš Janoušek Aug 15 '17 at 11:26
  • 1
    It is mentioned briefly here: https://github.com/git/git/blob/v2.13.0/Documentation/RelNotes/2.13.0.txt – SunSparc Aug 28 '17 at 23:00
  • 17
    The ***gitdir*** should include the last '/'. – Chris Yim Sep 15 '17 at 01:42
  • 2
    Do not replace the `~` by `$HOME` as git will already expand it for you as explained here : https://git-scm.com/docs/git-config#git-config-pathname – MCMZL Mar 06 '19 at 09:10
  • This answer I think better addresses the original issue, that is : specify multiple identities at once, in the main ~/.gitconfig, without having to specify them explicitly for each repositories belonging to the same "class" (eg: all corporate work below ~/corporate, and all personal below ~/personal, etc..) For me it should be accepted as the best answer, as of 2019 – a427 Mar 28 '19 at 08:20
  • 2
    For windows users, the .gitconfig file is located in the folder %USERPROFILE%, which for me is C:\users\matt. Also note that the paths specified ARE case sensitive. Mine looks like this: [includeIf "gitdir:C:/code/MyCompany/"] path = C:/code/MyCompany/.gitconfig – Matt Frear Apr 05 '19 at 16:11
  • 25
    You can check that that it works recursively by running `git config --list` in different directories. In subdirectories of `~/work/` that contain a git repository, the `includeIf` takes effect. Note that in subdirectories of `~/work/` that don't contain a git repository, the `includeIf` is not executed. – NZD Apr 25 '19 at 21:05
  • 1
    on windows it should have different path? – Qui-Gon Jinn Dec 15 '19 at 01:07
  • 4
    Run `git config user.email` in a git repository in the work path to see if it works. – nilsi Sep 18 '20 at 13:30
  • Looks like it's also possible to configure the credential helper in the referenced imports: https://git-scm.com/docs/gitcredentials#_avoiding_repetition – steamer25 Jan 05 '21 at 17:59
  • what should I do if I need to clone with a different username and email, when the project folder doesn't yet exist on the machine? How can I switch the username and email prior to clone? – mishap Apr 20 '21 at 18:04
  • @mishap Why would you need to do that? Username and e-mail isn't being used during clone. – Tomáš Janoušek Apr 20 '21 at 19:29
  • @TomášJanoušek, ok, so my problem is somewhere else, I have two github accounts and configured them in `~/.ssh/config`, same as in @Kaleem Ullah answer along with two separate pairs of private and public keys. But I have no clue how to switch from one account to the other before ssh clone. Git seems to be stuck on my home account. Testing both hosts with `ssh -T [host]` responds with authenticating home account username. Cloning work repo with correct host from `~/.ssh/config` results in access rights error. – mishap Apr 20 '21 at 19:47
  • the above does not seem to work when a repository contains submodules. One still needs to explicitly do `git config --local user.email me@my-work.com` for each submodule. Is there a solution for that case ? – nass Oct 21 '21 at 12:20
  • In GitHub Desktop `File > Options > Git` it shows an error and tells me that I am using the global config settings. However, running `git config user.email` within my personal repo shows different. After a test commit as well, it works. You can ignore what GitHub Desktop shows. – Bro3Simon Feb 17 '22 at 01:25
  • On windows I had to replace `"gitdir:~/work/"` with `"gitdir:**/work/"` and specify the full path to the new gitconfig – somethingRandom Jun 15 '22 at 08:14
  • Make sure all the capitalized & small letters in the path are written exactly as they are in your system, else this won't work. use `pwd` command to copy/paste your directory path properly if needed. – M.Ed Jun 20 '22 at 10:09
  • Would it be possible to use `hasconfig:remote.*.url:` to specify an email globally any repo that uses the company git host or domain? – ElJeffe Jun 17 '23 at 03:19
  • Example: `[includeIf "hasconfig:remote.*.url:https://gitlab.mycompany.com/**"]` – ElJeffe Jun 17 '23 at 03:37
123

Or you can add following information in your local .git/config file

[user]  
    name = Your Name
    email = your.email@gmail.com
Rahul Prasad
  • 8,074
  • 8
  • 43
  • 49
117

One command github accounts switch

This solution takes the form of a single git alias. Once executed, the current project user will be attached to another account

Generate ssh keys

ssh-keygen -t rsa -C "rinquin.arnaud@gmail.com" -f '/Users/arnaudrinquin/.ssh/id_rsa'

[...]

ssh-keygen -t rsa -C "arnaud.rinquin@wopata.com" -f '/Users/arnaudrinquin/.ssh/id_rsa_pro'

Link them to your GitHub / Bitbucket accounts

  1. copy default public key pbcopy < ~/.ssh/id_rsa.pub
  2. login to your GitHub acount
  3. paste the key in the add SSH key github page
  4. copy other public key pbcopy < ~/.ssh/id_rsa_pro.pub
  5. repeat and adapt steps 2 to 4 for every other account

Step 1. Automatic ssh key switching.

We can configure ssh to send a use a specific encryption key depending on the host. The nice thing is that you can have several aliases for the same hostname.

See this example ~/.ssh/config file:

# Default GitHub
Host github.com
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_rsa

# Professional github alias
Host github_pro
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_rsa_pro

git remote configuration

You can now use these aliases in the git remotes by changing git@github.com by git@github_pro.

You can either change your existing projects remotes (using something like git remote set-url origin git@github_pro:foo/bar.git) or adapt them directly when cloning them.

git clone git@github.com:ArnaudRinquin/atom-zentabs.git

using alias, it become:

git clone git@github_pro:ArnaudRinquin/atom-zentabs.git

Step 2. Changing git user.email

Git config settings can be global or per project. In our case, we want a per project settings. It is very easy to change it:

git config user.email 'arnaud.rinquin@wopata.com'

While this is easy, it takes way to long for the developers we are. We can write a very simple git alias for that.

We are going to add it to the ~/.gitconfig file.

[user]
    name = Arnaud Rinquin
    email = rinquin.arnaud@gmail.com

...

[alias]
    setpromail = "config user.email 'arnaud.rinquin@wopata.com'"

Then, all we have to do is git setpromail to have our email changed for this project only.

Step 3. One command switch please?!

Wouldn’t it be nice to switch from default account to a specified one with a single parameter-less command? This is definitely possible. This command will have two steps:

  • change current project remotes to the chosen aliases
  • change current project user.email config

We already have a one command solution for the second step, but the first one is way harder. One command remote host change

Here comes the solution in the form of another git alias command to add to your ~/.gitconfig:

[alias]
  changeremotehost = !sh -c \"git remote -v | grep '$1.*fetch' | sed s/..fetch.// | sed s/$1/$2/ | xargs git remote set-url\"

This allows changing all remotes from one host to another (the alias). See the example:

$ > git remote -v
origin  git@github.com:ArnaudRinquin/arnaudrinquin.github.io.git (fetch)
origin  git@github.com:ArnaudRinquin/arnaudrinquin.github.io.git (push)

$ > git changeremotehost github.com github_pro

$ > git remote -v
origin  git@github_pro:ArnaudRinquin/arnaudrinquin.github.io.git (fetch)
origin  git@github_pro:ArnaudRinquin/arnaudrinquin.github.io.git (push)

Combine them all

We now just have to combine the two commands into one, this is quite easy. See how I also integrate bitbucket host switching.

[alias]
  changeremotehost = !sh -c \"git remote -v | grep '$1.*fetch' | sed s/..fetch.// | sed s/$1/$2/ | xargs git remote set-url\"
  setpromail = "config user.email 'arnaud.rinquin@wopata.com'"
  gopro = !sh -c \"git changeremotehost github.com github_pro && git changeremotehost bitbucket.com bitbucket_pro && git setpromail\"

Source Link -Github

Source Link -Tutorial

Kaleem Ullah
  • 6,799
  • 3
  • 42
  • 47
  • 1
    This is brilliant, thanks. I work with a bunch of repos per email so my `setpromail` alias does a `config --global` instead (and I have other set aliases for setting different email addresses). It works! – michel-slm Jun 08 '16 at 17:08
  • 1
    For those 2 different accounts how can I sign with different gpg keys ? I have 2x gpg keys for 2x github accounts and would like to sign differently. "git config --global user.signingkey xxxx" – hakkican Apr 06 '18 at 08:09
  • 2
    This answer is awesome. I nearly didn't read it because I thought I found what from the other answer. Definitely deserves more upvotes. PS. It is even better when combined with `useConfigOnly = true` from the other answer. – steinybot Jun 14 '18 at 21:13
  • Is not IdentifyFile, it is IdenfityFile. – Christian May 09 '20 at 08:24
  • 2
    The link to the tutorial is no longer valid. I found it at https://github.com/ArnaudRinquin/blog/blob/master/2014-03-11-one-command-github-account-switch.md – greymatter Jun 25 '20 at 01:20
  • Ohh dude you just saved my life thank you very much now i can login multiple origin and accounts – Vikas Kandari Dec 27 '21 at 05:16
  • I think the order of this is potentially wrong. You can't clone a repo if your global git user isn't tied to the repo. So does that mean you have to globally switch to clone a different user's repo and then globally switch back to your default and then do the local git username switch? – AlxVallejo Mar 03 '23 at 15:01
51

With conditional includes in Git 2.13, it is now possible to have multiple user/email coexist on one machine with little work.

user.gitconfig has my personal name and email. work-user.gitconfig has my work name and email. Both files are at ~ path.

So my personal name/email applies by default. For c:/work/ dir, my work name/email is applied. For c:/work/github/ dir, my personal name/email is applied. This works as the last setting gets applied.

# ~/.gitconfig
[include]
    path = user.gitconfig
[includeIf "gitdir/i:c:/work/"]
    path = work-user.gitconfig
[includeIf "gitdir/i:c:/work/github/"]
    path = user.gitconfig

gitdir is case-sensitive and gitdir/i is case-insensitive.

"gitdir/i:github/" would apply the conditional include for any directory with github in its path.

hIpPy
  • 4,649
  • 6
  • 51
  • 65
  • While your answer is appreciated and good Tomáš Janoušek gave the same 20 days before you. Please consider deleting this answer. – Hedge Sep 15 '17 at 17:03
  • 10
    @Hedge Yes, I upvoted his answer, but it took me a while to configure on windows and that's where `gitdir/i` helped me out (which his answer does not mention). – hIpPy Sep 15 '17 at 21:44
  • 2
    The particular way @hIpPy implemented this directly applied to my use case and made it that much easier to follow than Tomas' answer. I upvoted both. – Isaac Adams Apr 03 '20 at 21:38
46

After getting some inspiration from Orr Sella's blog post I wrote a pre-commit hook (resides in ~/.git/templates/hooks) which would set specific usernames and e-mail addresses based on the information inside a local repositorie's ./.git/config:

You have to place the path to the template directory into your ~/.gitconfig:

[init]
    templatedir = ~/.git/templates

Then each git init or git clone will pick up that hook and will apply the user data during the next git commit. If you want to apply the hook to already exisiting repos then just run a git init inside the repo in order to reinitialize it.

Here is the hook I came up with (it still needs some polishing - suggestions are welcome). Save it either as

~/.git/templates/hooks/pre_commit

or

~/.git/templates/hooks/post-checkout

and make sure it is executable: chmod +x ./post-checkout || chmod +x ./pre_commit

#!/usr/bin/env bash

# -------- USER CONFIG
# Patterns to match a repo's "remote.origin.url" - beginning portion of the hostname
git_remotes[0]="Github"
git_remotes[1]="Gitlab"

# Adjust names and e-mail addresses
local_id_0[0]="my_name_0"
local_id_0[1]="my_email_0"

local_id_1[0]="my_name_1"
local_id_1[1]="my_email_1"

local_fallback_id[0]="${local_id_0[0]}"
local_fallback_id[1]="${local_id_0[1]}"


# -------- FUNCTIONS
setIdentity()
{
    local current_id local_id

    current_id[0]="$(git config --get --local user.name)"
    current_id[1]="$(git config --get --local user.email)"

    local_id=("$@")

    if [[ "${current_id[0]}" == "${local_id[0]}" &&
          "${current_id[1]}" == "${local_id[1]}" ]]; then
        printf " Local identity is:\n"
        printf "»  User: %s\n»  Mail: %s\n\n" "${current_id[@]}"
    else
        printf "»  User: %s\n»  Mail: %s\n\n" "${local_id[@]}"
        git config --local user.name "${local_id[0]}"
        git config --local user.email "${local_id[1]}"
    fi

    return 0
}

# -------- IMPLEMENTATION
current_remote_url="$(git config --get --local remote.origin.url)"

if [[ "$current_remote_url" ]]; then

    for service in "${git_remotes[@]}"; do

        # Disable case sensitivity for regex matching
        shopt -s nocasematch

        if [[ "$current_remote_url" =~ $service ]]; then
            case "$service" in

                "${git_remotes[0]}" )
                    printf "\n»» An Intermission\n»  %s repository found." "${git_remotes[0]}"
                    setIdentity "${local_id_0[@]}"
                    exit 0
                    ;;

                "${git_remotes[1]}" )
                    printf "\n»» An Intermission\n»  %s repository found." "${git_remotes[1]}"
                    setIdentity "${local_id_1[@]}"
                    exit 0
                    ;;

                * )
                    printf "\n»  pre-commit hook: unknown error\n» Quitting.\n"
                    exit 1
                    ;;

            esac
        fi
    done
else
    printf "\n»» An Intermission\n»  No remote repository set. Using local fallback identity:\n"
    printf "»  User: %s\n»  Mail: %s\n\n" "${local_fallback_id[@]}"

    # Get the user's attention for a second
    sleep 1

    git config --local user.name "${local_fallback_id[0]}"
    git config --local user.email "${local_fallback_id[1]}"
fi

exit 0

EDIT:

So I rewrote the hook as a hook and command in Python. Additionally it's possible to call the script as a Git command (git passport), too. Also it's possible to define an arbitrary number of IDs inside a configfile (~/.gitpassport) which are selectable on a prompt. You can find the project at github.com: git-passport - A Git command and hook written in Python to manage multiple Git accounts / user identities.

Saucier
  • 4,200
  • 1
  • 25
  • 46
  • 1
    This works neatly. However, it worked better for me by making this a post-checkout (instead of post-commit) hook. My suggestions to improve this answer are, mention that: 1. the snippet needs to be saved as ~/.git/templates/hooks/post-checkout and be given permission `chmod +x post-checkout`, 2. `git_remotes` values are beginning portion of the entire hostname, e.g. `git@github.com`, 3. `local_id` values should be edited by user to respective names and email addresses. – Shantanu Kumar Dec 24 '14 at 16:37
  • @ShantanuKumar Thanks for your comment. I adjusted the answer as you proposed. Maybe I will rewrite the script in Python soon. – Saucier Dec 29 '14 at 16:41
  • Added support for regular expressions and for repositories without remotes. For easy download all merged [here](https://github.com/cfi/git-passport/tree/dev_all). Regexps to distinguish identities for different projects on the same service. And supporting remoteless repos with e.g. a default identity makes sense if you `git init` new projects from an IDE such as `eclipse` (which cannot handle interactive pre-commit triggers) – cfi Oct 27 '15 at 12:57
31

If you do not want to have a default email address (email address links to a github user), you can configure that you want to be asked. How you can do that depends on the version of git you use, see below.

The (intended) drawback is that you have to configure your email address (and your name) once for every repository. So, you cannot forget to do it.

Version < 2.7.0

[user]
    name = Your name
    email = "(none)"

in your global configuration ~/.gitconfig as stated in a comment by Dan Aloni in Orr Sella's blog post. When trying to do the first commit in a repository, git fails with the nice message:

*** Please tell me who you are.

Run

  git config --global user.email "you@example.com"
  git config --global user.name "Your Name"

to set your account's default identity.
Omit --global to set the identity only in this repository.

fatal: unable to auto-detect email address (got '(none)')

The name is taken from the global config when the email address is set locally (the message is not perfectly accurate).

2.7.0 ≤ Version < 2.8.0

The behaviour in versions < 2.7.0 was not intended and fixed with 2.7.0. You can still use a pre-commit hook as described in Orr Sella's blog post. This solution works also for other versions, but the other solutions not for this version.

Version ≥ 2.8.0

Dan Aloni added an option to achieve that behaviour (see release notes). Use it with:

[user]
    useConfigOnly = true

To make it work you may not give a name or email address in the global config. Then, at the first commit, you get an error message

fatal: user.useConfigOnly set but no name given

So the message is not very instructive, but since you set the option explicitly, you should know what to do. In contrast to the solution of versions < 2.7.0, you always have to set both name and email manually with

$ git config user.name "Your Name"
$ git config user.email "your@email.com"

which sets your name and email only in the current repository.

John
  • 1,472
  • 1
  • 19
  • 22
  • I use this method for a few months, it worked great. I have multiple email addresses (personal, work) and I do not want to have a "default" one configured in global gitconfig. With special value "(none)" git will prompt me to provide a valid address every time I spin up a new repo, instead of guessing one based on username and hostname (which is annoying and I need to --amend it). However, recently with upgraded version of Git (2.7.0, maybe earlier), I find out that special value "(none)" does not trigger fatal errors any more. Instead it will just use "John Doe <(none)>" as is ... – Zhuoyun Wei Feb 02 '16 at 13:11
  • @wzyboy: Oh, you are right. I used `git bisect` to find that commit [19ce497c...](https://git.kernel.org/cgit/git/git.git/commit/?id=19ce497cf594a3c01af33a65b27b19e7459212d6) introduced this behaviour. However, independent of the version (2.5 - 2.7) I can use `email =` (with no argument) in the config and it shows the same behaviour as `email = "(none)"` in old versions. Can you confirm this? If so, I'll edit my answer. I am just sceptical as it seems so obvious and I did not use it before. – John Feb 03 '16 at 17:17
  • I tried `email =` in 2.7.0, Git still guesses email address based on username and hostname. I now use `pre-commit` approach in Sella's blog. I also notified the Dan Aloni who came up with the `"(none)"` idea in Sella's post and he filed up a patch to formally implement this as a feature: http://permalink.gmane.org/gmane.comp.version-control.git/285301 – Zhuoyun Wei Feb 04 '16 at 03:25
  • 3
    since git-2.8: The "user.useConfigOnly" configuration variable can be used to force the user to always set user.email & user.name https://github.com/git/git/blob/master/Documentation/RelNotes/2.8.0.txt – rofrol Mar 29 '16 at 13:33
  • @rofrol Thanks for the info. I updated my answer accordingly. – John Mar 29 '16 at 16:12
18

Another option to get git to work with multiple names / emails is by aliasing git and using the -c flag to override the global and repository-specific config.

For example, by defining an alias:

alias git='/usr/bin/git -c user.name="Your name" -c user.email="name@example.com"'

To see whether it works, simply type git config user.email:

$ git config user.email
name@example.com

Instead of an alias, you could also put a custom git executable within your $PATH.

#!/bin/sh
/usr/bin/git -c user.name="Your name" -c user.email="name@example.com" "$@"

An advantage of these method over a repository-specific .git/config is that it applies to every git repository when the custom git program is active. In this way, you can easily switch between users/names without modifying any (shared) configuration.

Rob W
  • 341,306
  • 83
  • 791
  • 678
16

Here are the complete steps after reading many answers here

How to set up Multiple SSH Keys settings for different github account

You might want to start checking your currently saved keys

$ ssh-add -l

If you decide to delete all cached keys before (optional, carefull about this)

$ ssh-add -D

Then you can create a ssh pub/priv key linked to each email/account that you wish/need to use

$ cd ~/.ssh
$ ssh-keygen -t rsa -C "work@company.com" <-- save it as "id_rsa_work"
$ ssh-keygen -t rsa -C "pers@email.com" <-- save it as "id_rsa_pers"

After performing this commands you will have the following files created

~/.ssh/id_rsa_work      
~/.ssh/id_rsa_work.pub

~/.ssh/id_rsa_pers
~/.ssh/id_rsa_pers.pub 

Make sure authentication agent is running

$ eval `ssh-agent -s`

Add the generated keys as following (from the ~/.ssh folder)

$ ssh-add id_rsa_work
$ ssh-add id_rsa_pers

Now you can check your saved keys again

$ ssh-add -l

Now you need to add the generated public keys to your github/bickbuket server Acces Keys

Clone each of the repos to different folders

Go to the folder where the user work will work and execute this

$ git config user.name "Working Hard"
$ git config user.email "work@company.com" 

Just to see what this does check the contents of the ".git/config"

Go to the folder where the user pers will work and execute this

$ git config user.name "Personal Account"
$ git config user.email "pers@email.com" 

Just to see what this does check the contents of the ".git/config"

After all this you will be able to commit your personal and work code by just switching between those two folders

In case you are using Git Bash and need to generate ssh keys under Windows follow this steps:

https://support.automaticsync.com/hc/en-us/articles/202357115-Generating-an-SSH-Key-on-Windows

Mauricio Gracia Gutierrez
  • 10,288
  • 6
  • 68
  • 99
15

This answer is partially inspired by the post by @Saucier, but I was looking for an automated way to set user.name and user.email on a per repo basis, based on the remote, that was a little more light weight than the git-passport package that he developed. Also h/t to @John for the useConfigOnly setting. Here is my solution:

.gitconfig changes:

[github]
    name = <github username>
    email = <github email>
[gitlab]
    name = <gitlab username>
    email = <gitlab email>
[init]
    templatedir = ~/.git-templates
[user]
    useConfigOnly = true

post-checkout hook which should be saved to the following path: ~/.git-templates/hooks/post-checkout:

#!/usr/bin/env bash

# make regex matching below case insensitive
shopt -s nocasematch

# values in the services array should have a corresponding section in
# .gitconfig where the 'name' and 'email' for that service are specified
remote_url="$( git config --get --local remote.origin.url )"
services=(
    'github'
    'gitlab'
)

set_local_user_config() {
    local service="${1}"
    local config="${2}"
    local service_config="$( git config --get ${service}.${config} )"
    local local_config="$( git config --get --local user.${config} )"

    if [[ "${local_config}" != "${service_config}" ]]; then
        git config --local "user.${config}" "${service_config}"
        echo "repo 'user.${config}' has been set to '${service_config}'"
    fi
}

# if remote_url doesn't contain the any of the values in the services
# array the user name and email will remain unset and the
# user.useConfigOnly = true setting in .gitconfig will prompt for those
# credentials and prevent commits until they are defined
for s in "${services[@]}"; do
    if [[ "${remote_url}" =~ "${s}" ]]; then
        set_local_user_config "${s}" 'name'
        set_local_user_config "${s}" 'email'
        break
    fi
done

I use different credentials for github and gitlab, but those references in the code above could be replaced or augmented with any service that you use. In order to have the post-checkout hook automatically set the user name and email locally for a repo after a checkout make sure the service name appears in the remote url, add it to the services array in the post-checkout script and create a section for it in your .gitconfig that contains your user name and email for that service.

If none of the service names appear in the remote url or the repo doesn't have a remote the user name and email will not be set locally. In these cases the user.useConfigOnly setting will be in play which will not allow you to make commits until the user name and email are set at the repo level, and will prompt the user to configure that information.

Grant Humphries
  • 2,696
  • 2
  • 23
  • 24
15

git aliases (and sections in git configs) to the rescue!

add an alias (from command line):

git config --global alias.identity '! git config user.name "$(git config user.$1.name)"; git config user.email "$(git config user.$1.email)"; :'

then, set, for example

git config --global user.github.name "your github username"
git config --global user.github.email your@github.email

and in a new or cloned repo you can run this command:

git identity github

This solution isn't automatic, but unsetting user and email in your global ~/.gitconfig and setting user.useConfigOnly to true would force git to remind you to set them manually in each new or cloned repo.

git config --global --unset user.name
git config --global --unset user.email
git config --global user.useConfigOnly true
Michael Moussa
  • 4,207
  • 5
  • 35
  • 53
codesnik
  • 299
  • 2
  • 7
  • 1
    I like your solution; however, when I unset my global it just makes the commit with my computer's host name rather than having git remind me to set them in any given repo :( – ENG618 Jun 20 '17 at 14:59
14

After reading all the answers, that's the solution I came with:

Scenario:

  • Local OS: Linux (bash)
  • two accounts on GitHub (work/personal)
  • ssh access with different keys
  • my work account will be the "main" one (so it doesn't need custom configuration)

Configuration:

  1. Set a "fake" host, which allows me to select the proper SSH keypair.
    At ~/.ssh/config:
    # Personal GitHub account
    Host github-personal
      HostName github.com
      User git
      IdentityFile ~/.ssh/id_rsa_personal
      IdentitiesOnly yes
    
  2. "Tell" git it should use a custom configuration within a given directory subtree:
    At ~/.gitconfig:
    # My personal projects
    [includeIf "gitdir/i:~/REPOS/PERSONAL/"]
       path = ~/REPOS/PERSONAL/personal.gitconfig
    
    
  3. Finally, my config at ~/REPOS/PERSONAL/personal.gitconfig:
    # gitconfig for personal projects
    [user]
      email = personalemail@example.com
    
    [url "git@github-personal"]
      insteadOf = git@github.com
    

Once the three above are in place, I can clone a personal repo like this:

user@host:~/REPOS/PERSONAL$ git clone git@github.com:jmnavarrol/python-multigit.git
Cloning in 'python-multigit'...
(...)
user@host:~/REPOS/PERSONAL$ cd python-multigit && git remote -v
origin  git@github-personal:jmnavarrol/python-multigit.git (fetch)
origin  git@github-personal:jmnavarrol/python-multigit.git (push)
user@host:~/REPOS/PERSONAL/python-multigit$

The addition of the url rewriting at ~/REPOS/PERSONAL/personal.gitconfig...

[url "git@github-personal"]
  insteadOf = git@github.com

...is what allows me referencing the repo just by its "standard" URL in scripts, subrepos (i.e. the case of python-multigit itself -yes, lame attempt at a bit of self-promotion) without risking using the "wrong" URLs, therefore, the wrong authentication.

My two cents.

  • Note that just doing `Host github.com` instead of a unique identifier will allow you to use `git@github.com` just fine – Arcsector Feb 09 '22 at 23:42
9

There is a simple solution that seems to work well for avoiding mistakes.

Simply remove the [user] section from your ~/.gitconfig, which will prevent you from making any commits without setting user.name for each repository.

In your ~/.bashrc, add some simple aliases for the user and email:

alias ggmail='git config user.name "My Name";git config user.email me@gmail.com'
alias gwork='git config user.name "My Name";git config user.email me@work.job'
Zantier
  • 833
  • 1
  • 8
  • 18
5

Windows Environment

Additional this can be modified from Git Extensions --> Settings --> Global Settings, if you have it installed in your systems.

gitextensions-latest-release

Right Click on a folder/directory in Windows Environment to access these settings. enter image description here

Update : How to switch/maintain multiple settings in Version 2.49 How to switch/maintain multiple settings in Version 2.49

Abhijeet
  • 8,561
  • 5
  • 70
  • 76
  • How does this address multiple users, instead of just editing them? –  Feb 14 '18 at 21:44
  • @D_N Updated with new screenshot with options to switch settings. – Abhijeet Feb 15 '18 at 10:15
  • Open local repo folder and select git-config from navigation menu then click the local repo tab, the contents will be applied to $local_repo_path/.git/config as above answer. – maxwu May 12 '18 at 12:37
4

GIT_AUTHOR_EMAIL + local .bashrc

.bashrc_local: don't track this file, put it only on your work computer:

export GIT_AUTHOR_EMAIL='me@work.com'
export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"

.bashrc: track this file, make it the same on both work and home computers:

F="$HOME/.bashrc_local"
if [ -r "$F" ]; then
    . "$F"
fi

I'm using https://github.com/technicalpickles/homesick to sync my dotfiles.

If only gitconfig would accept environment variables: Shell variable expansion in git config

Community
  • 1
  • 1
Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
3

Prior to following the below steps, you must have multiple ssh keys. Those can be generated using here command

Steps

  1. Go to c:\users\Your User\.ssh
  2. touch config
  3. Configure ssh keys per domain

config file

  1. Go to git repo using gitbash.
  2. Configure username and name per repo "git config user.email="abc@gmail.com"
  3. Configure name per repo. git config user.name="Mo Sh"
  4. Execute git commands. it would pick SSH keys automatically.
Mohan
  • 907
  • 5
  • 22
  • 45
2

Maybe it is a simple hack, but it is useful. Just generate 2 ssh keys like below.

Generating public/private rsa key pair.
Enter file in which to save the key (/Users/GowthamSai/.ssh/id_rsa): work
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in damsn.
Your public key has been saved in damsn.pub.
The key fingerprint is:
SHA256:CrsKDJWVVek5GTCqmq8/8RnwvAo1G6UOmQFbzddcoAY GowthamSai@Gowtham-MacBook-Air.local
The key's randomart image is:
+---[RSA 4096]----+
|. .oEo+=o+.      |
|.o o+o.o=        |
|o o o.o. +       |
| =.+ .  =        |
|= *+.   S.       |
|o*.++o .         |
|=.oo.+.          |
| +. +.           |
|.o=+.            |
+----[SHA256]-----+

Same way create one more for personal. So, you have 2 ssh keys, work and company. Copy work.pub, work, personal.pub, personal to ~/.ssh/ Directory.

Then create a shell script with the following lines and name it as crev.sh (Company Reverse) with the following content.

cp ~/.ssh/work ~/.ssh/id_rsa
cp ~/.ssh/work.pub ~/.ssh/id_rsa.pub

Same way, create one more called prev.sh (Personal Reverse) with the following content.

cp ~/.ssh/personal ~/.ssh/id_rsa
cp ~/.ssh/personal.pub ~/.ssh/id_rsa.pub

in ~/.bashrc add aliases for those scripts like below

alias crev="sh ~/.ssh/crev.sh"
alias prev="sh ~/.ssh/prev.sh"
source ~/.bashrc

Whenever you wanna use company, just do crev, and if you wanna use personal do prev :-p.

Add those ssh keys to your GitHub accounts. Make sure, you don't have id_rsa generated previously, because those scripts will overwrite id_rsa. If you have already generated id_rsa, use that for one of the accounts. Copy them as personal and skip the generation of personal keys.

spences10
  • 570
  • 1
  • 13
  • 32
gwthm.in
  • 638
  • 9
  • 24
  • Configure git by running `export GIT_SSH_COMMAND="ssh -i ~/.ssh/personal"` rather than switching id_rsa files all the time. If you forget to set it, you won't use the wrong credentials by accicent. – Dave Sep 05 '22 at 05:06
2

Although most questions sort of answered the OP, I just had to go through this myself and without even googling I was able to find the quickest and simplest solution. Here's simple steps:

  • copy existing .gitconfg from your other repo
  • paste into your newly added repo
  • change values in .gitconfig file, such as name, email and username [user] name = John email = john@email.net username = john133
  • add filename to .gitignore list, to make sure you don't commit .gitconfig file to your work repo
Tatarin
  • 1,238
  • 11
  • 28
2

You can also use git commit --author "Your Name <your@email.com>" at the moment of doing a commit in a repo where you want to commit as a different user.

szx
  • 6,433
  • 6
  • 46
  • 67
0

I made a bash function that handle that. Here is the Github repo.

For record:

# 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'
Arount
  • 9,853
  • 1
  • 30
  • 43
0

Just add this to your ~/.bash_profile to switch between default keys for github.com

# Git SSH keys swap
alias work_git="ssh-add -D  && ssh-add -K ~/.ssh/id_rsa_work"
alias personal_git="ssh-add -D && ssh-add -K ~/.ssh/id_rsa"
-1

Something like Rob W's answer, but allowing different a different ssh key, and works with older git versions (which don't have e.g. a core.sshCommand config).

I created the file ~/bin/git_poweruser, with executable permission, and in the PATH:

#!/bin/bash

TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT

cat > $TMPDIR/ssh << 'EOF'
#!/bin/bash
ssh -i $HOME/.ssh/poweruserprivatekey $@
EOF

chmod +x $TMPDIR/ssh
export GIT_SSH=$TMPDIR/ssh

git -c user.name="Power User name" -c user.email="power@user.email" $@

Whenever I want to commit or push something as "Power User", I use git_poweruser instead of git. It should work on any directory, and does not require changes in .gitconfig or .ssh/config, at least not in mine.

Jellby
  • 2,360
  • 3
  • 27
  • 56
-2

There can be multiple ways to do this, but I'm achieving this via the below shell function.

function gitprofile() {
    if [[ $1 == "private" ]]; then
        if grep -q "tom@workmail.com" "/Users/tomtaylor/.gitconfig"; then
            echo "Found in gitconfig. No action required."
        else 
            echo "Found in gitconfig1"
            cp /Users/tomtaylor/.gitconfig /Users/tomtaylor/.gitconfig2
            mv /Users/tomtaylor/.gitconfig1 /Users/tomtaylor/.gitconfig
            mv /Users/tomtaylor/.gitconfig2 /Users/tomtaylor/.gitconfig1
        fi
    elif [[ $1 == "public" ]]; then
        if grep -q "tom@gmail.com" "/Users/tomtaylor/.gitconfig"; then
            echo "Found in gitconfig. No action required."
        else 
            echo "Found in gitconfig1"
            cp /Users/tomtaylor/.gitconfig /Users/tomtaylor/.gitconfig2
            mv /Users/tomtaylor/.gitconfig1 /Users/tomtaylor/.gitconfig
            mv /Users/tomtaylor/.gitconfig2 /Users/tomtaylor/.gitconfig1
        fi
}

Invoking it like,

gitprofile "public" -> This would switch to profile with tom@gmail.com and

gitprofile "private" -> This would switch to tom@workmail.com.

Add this function to your ~/.bash_profile or ~/.zshrc based on your current terminal preference. Restart the terminal or just compile the script like

. ~/.bash_profile

to make the function effective. Hope it helps !

Tom Taylor
  • 3,344
  • 2
  • 38
  • 63
-2

Let's redefine the specs - you just want to be able to use multiple identities with git against different organisations - all you need is 2 oneliners:

 # generate a new private public key pair for org1  
 email=me@org1.eu;ssh-keygen -t rsa -b 4096 -C $email -f $HOME/.ssh/id_rsa.$email

 # use org1 auth in THIS shell session - Ctrl + R, type org1 in new one 
 email=me@org1.eu;export GIT_SSH_COMMAND="ssh -p 22 -i ~/.ssh/id_rsa.$email"

Why I know that this is by far the most simple solution:

  # me using 9 different orgs with separate git auths dynamically
  find $HOME/.ssh/id_rsa.*@* | wc -l
  18
Yordan Georgiev
  • 5,114
  • 1
  • 56
  • 53