54

I have a work computer, and it's configured globally to use my work email and name when committing. This is good. However, I'd like to make some sort of rule that says, "if the repo origin is github, use user X and email Y"

I realize you can make a config entry per repository, but I'd like it to be more automatic: if the clone is github, it should use github user details. If I clone from work, it should use work details.

Is there any way to configure this globally based on the remote domain? Or another way?

EDIT/UPDATE

I have accepted the answer below, but modified the script just a bit:

#!/usr/bin/env bash

# "Real" git is the second one returned by 'which'
REAL_GIT=$(which -a git | sed -n 2p)

# Does the remote "origin" point to GitHub?
if ("$REAL_GIT" remote -v 2>/dev/null | grep '^origin\b.*github.com.*(push)$' >/dev/null 2>&1); then

    # Yes.  Set username and email that you use on GitHub.
    export GIT_AUTHOR_NAME=$("$REAL_GIT" config --global user.ghname)
    export GIT_AUTHOR_EMAIL=$("$REAL_GIT" config --global user.ghemail)

fi

"$REAL_GIT" "$@"

The primary addition is the requirement for two git config values.

git config --global user.ghname "Your Name"
git config --global user.ghemail "you@yourmail.com"

This avoids hard coding the values in the script, allowing it to be more portable. Maybe?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
Andrew
  • 3,332
  • 4
  • 31
  • 37
  • Consider changing the accepted answer to boywhoroared. It was submitted after Mike Morearty's answer was accepted, but I think it is a better answer. It is easier to add other domains, and it uses git's post-checkout hook instead of replacing the executable entirely. – charmoniumQ Nov 06 '17 at 20:36
  • related: https://stackoverflow.com/q/4220416 – djvg Dec 17 '21 at 11:11

6 Answers6

53

Git 2.13 adds support for conditional config includes. If you organize your checkouts into directories for each domain of work, then you can add custom settings based on where the checkouts are. In your global git config:

[includeIf "gitdir:code/work/"]
    path = /Users/self/code/work/.gitconfig

And then in ~/code/work/.gitconfig:

[user]
    email = self@work.com

And of course you can do that for as many domains of work as you like.

Jason R. Coombs
  • 41,115
  • 10
  • 83
  • 93
  • 2
    This is good solution for me. For Windows `[includeIf "gitdir:D:/repositories/"]`will match all repositories in `D:\repositories\\`. Final `\` is apparently needed. – ceztko Feb 06 '18 at 08:59
  • 1
    You have to include /** glob if you want it to apply to multiple folders inside the workspace folder like: `[includeIf "gitdir:code/work/**"]` – tiagolisalves Aug 15 '20 at 01:20
  • @tiagolisalves - that's not been my experience. I use config almost identical to what I've described above and every repo in `code/work/*` gets the `code/work/.gitconfig` treatment, even nested repos like `code/work/foo/bar`. Perhaps provide a gist demonstrating the condition you describe? – Jason R. Coombs Aug 15 '20 at 19:17
  • I use globs like ** in Ubuntu. Maybe it is related to distinct implementations. – tiagolisalves Aug 15 '20 at 20:49
  • 1
    I am on Macos Big Sur and glob works fine for multiple folders ... global .gitconfig has `[includeIf "gitdir:code/work/**"] \n \t path = /Users/self/code/work/.gitconfig` another .gitconfig in `/Users/self/code/work/ ` level containing my username, email address – ManiVI Dec 26 '20 at 03:50
21

There is nothing built into Git to do this (as far as I know), but the following shell script seems to work pretty reliably. Modify it to have the user name and email address you want when checking into GitHub, and then save it as an executable named "git" on your path somewhere before the "real" git.

#!/usr/bin/env bash

# "Real" git is the second one returned by 'which'
REAL_GIT=$(which -a git | sed -n 2p)

# Does the remote "origin" point to GitHub?
if ("$REAL_GIT" remote -v 2>/dev/null |
    grep '^origin\b.*github.com.*(push)$' >/dev/null 2>&1); then

    # Yes.  Set username and email that you use on GitHub.
    export GIT_AUTHOR_NAME='*** put your name here ***'
    export GIT_AUTHOR_EMAIL='*** put your email address here ***'

fi

"$REAL_GIT" "$@"

I use a similar trick with ssh on my machine -- I want ssh to change my window background color when it runs, and then change it back when it exits -- and it has worked reliably for me.

Also, note that this is hard-coded to only look at the remote that is named origin.

Mike Morearty
  • 9,953
  • 5
  • 31
  • 35
  • 1
    are GIT_AUTHOR_* only exported for the length of the script + git proper, or would that be global relative to the terminal session? I wouldn't want to override those values for non-github repos after touching a single github repo. – Andrew Dec 07 '12 at 18:15
  • 1
    They are only exported for the length of the script + git proper. – Mike Morearty Dec 07 '12 at 20:55
  • 1
    Today I was testing a shim in ~/bin and ran "export PATH=~/bin:$PATH". Later I switched to a different project, ran git status, and ... fork bomb! My bad, but... from now on I'll be hard-coding $REAL_GIT :-) – choover Oct 29 '13 at 15:54
  • "They are only exported for the length of the script + git proper" Taking a guess, but I think the reason why this is True is because of the first line of the script: it executes within its own `/usr/bin/env bash,` instead of `/usr/bin/bash`. Nice trick. +1s all around. – eduncan911 May 26 '15 at 16:19
  • 1
    @eduncan911 thanks but actually, that's not accurate. On all Unix-like operating systems, shell scripts never export their environment variables to the parent shell. (Windows is different.) – Mike Morearty May 26 '15 at 16:31
  • Learn something every day. :) – eduncan911 May 26 '15 at 16:54
7

I wrote post-checkhout hook to set the repo's local author details based on the repository's origin URL.

It doesn't do wildcard domains, though it makes use of git config --urlmatch which allegedly will fall back to the closest matching URL.

Check it out here: https://github.com/boywhoroared/dotfiles/blob/master/git/template/hooks/post-checkout.d/author

boywhoroared
  • 71
  • 1
  • 2
4

~/.gitconfig

[user]
    name = John Doe
    email = jdoe@private.com

[includeIf "hasconfig:remote.*.url:https://git.work.com/**"]
    path = .gitconfig.work

~/.gitconfig.work

[user]
    name = John Doe
    email = jdoe@work.com

This way the user details from ~/.gitconfig.work will be included and override the settings from ~/.gitconfig if the repo contains any remote matching https://git.work.com/**

2

user.name and user.email are used to "sign" your commits. Since commits are repo-independent (say, the same commit will be in lots of different repos when you push them), those properties are not remote-dependent.

What you can do is to set different logging's when connecting to different remotes via https. You would just put the username in the url, in the form https://username@host/path/to/repo.git and you're done.

But the commit author's will be the same user.name, because commits are made locally, and then just shared with others.

As the commiter's identity is part of the commit, if you make two commits that are the same except for the commiter's name and email, those commits would have different hashes, so they'll be two different commits to git at all. That would be a mess :)

If you really want to do this, maybe you can make something with hooks that pushes your commits to another repo on your computer, and that other repo (via hooks, again) will re-write the commits, change the author, and push to the other remote.

But it's so nasty I'll deny having told you about that ;)

mgarciaisaia
  • 14,521
  • 8
  • 57
  • 81
  • Yes, that's nasty. I'm not sure that's necessary though... I'm not trying to commit as two different people to the same repo, but to different repos. Nor am I trying to rewrite existing commits. – Andrew Dec 07 '12 at 18:09
2

Similar to Mike's answer, but thanks to the uniq command at the fourth line, it will not result in a fork bomb, even if you start a shell inside a shell.

#!/usr/bin/env bash

# "Real" git is the second one returned by 'which'
REAL_GIT=$(which -a git | uniq | sed -n 2p)

# Does the remote "origin" point to GitHub?
if ("$REAL_GIT" remote -v 2>/dev/null |
    grep '^origin\b.*github.com.*(push)$' >/dev/null 2>&1); then

    # Yes.  Set username and email that you use on GitHub.
    export GIT_AUTHOR_NAME='*** put your name here ***'
    export GIT_AUTHOR_EMAIL='*** put your email address here ***'

fi

"$REAL_GIT" "$@"
Nicolas Raoul
  • 58,567
  • 58
  • 222
  • 373