19

The situation is as follows:

  • I have multiple domains in which I write code, e.g. professional and free time. Those are represented by different directories on my computer.
  • Each of those domains contains multiple Git repositories, in a hierarchical directory structure inside one of the domain directories.

Per domain, I want to use a different email address as part of author/committer information. That is, I want my private address to be listed in my free-time projects and my company address in my professional ones.

git config knows 3 scopes: repository, global and system-wide. What I basically need is a 4th scope between repository and global, representing a group of repositories (or simply a directory in the file system).

It seems like git config doesn't allow that. Of course I could set the email address per repository, but I want to avoid this manual step every time I set up or clone a repository. One option would be to write a script that wraps git init/clone and git config, are there other ideas?

TheOperator
  • 5,936
  • 29
  • 42
  • 1
    Possible duplicate of [I use several computers with different gitconfigs. How do I stop leaking my e-mail addresses to GitHub?](http://stackoverflow.com/questions/39756170/i-use-several-computers-with-different-gitconfigs-how-do-i-stop-leaking-my-e-ma) – Scott Weldon Oct 26 '16 at 22:55

3 Answers3

25

Based on https://stackoverflow.com/a/44036640/2377961 I think I found a way how it could work.

First, you create different config files for your "custom-scopes" (e.g. professional, freetime, ect.) and add your desired user-config for each scope

# ~/all-projects.gitconfig
[user]
    name = TheOperator
# ~/professional.gitconfig
[user]
    email = yourname@yourwork.com
# ~/freetime.gitconfig
[user]
    email = yourname@private-email.com

than you add lines like this in your standard gitconfig:

# ~/.gitconfig
[include]
    path = all-projects.gitconfig
[includeIf "gitdir/i:c:/work/"]
    path = professional.gitconfig
[includeIf "gitdir/i:c:/freetime/"]
    path = freetime.gitconfig 

The directories after gitdir/i should match the parents directory of your project groups. In my example you should store your git-repos for freetime-domains e.g. c:/freetime/my-freetime.domain/.git

Julia Meshcheryakova
  • 3,162
  • 3
  • 22
  • 42
Radon8472
  • 4,285
  • 1
  • 33
  • 41
  • 1
    A note that you can set default values in "~/.gitconfig": later values will override earlier values in the file (at least in the current Git implementation), so "all-proects.gitconfig" is redundant – Epic Wink Nov 16 '19 at 09:13
  • @Epic you are right, for my example it is possible to add the values from the all-projects config into the users .gitconfig. But that would be mad if more than one user like to use this project config (e.g. if all-project contains not the "user.email" but other values e.g. textconv-Rules ect. – Radon8472 Nov 18 '19 at 17:55
5

The solution I came up with is inspired from Scott Weldon's answer. Since it was not directly applicable for my case, I adapted the hook's bash script and improved several parts*.

Assume the following directory structure from the home directory:

~
   .gitconfig           // [init] templatedir
   .git-template
      hooks
         post-checkout  // our bash script
   MyDomain
      .gitconfig        // [user] name, email

Initially I let Git know where my template directory is. On Windows, you may need to specify the absolute path instead (C:/Users/MyUser/.git-template).

git config --global init.templatedir '~/.git-template'

In ~/MyDomain/.gitconfig I store the configuration for that directory (domain), which should be applied to all repositories inside it and its subdirectories.

cd ~/MyDomain
git config --file .gitconfig user.name "My Name"
git config --file .gitconfig user.email "my@email.com"

The interesting part is the post-checkout bash script, which defines the post-checkout hook. I used a custom user.inferredConfig flag to execute it only once (on git clone), not repeatedly (on git checkout). It would of course also be possible to create a separate file to represent that state.

#!/bin/bash

# Git post-checkout hook for automated use of directory-local git config
# https://stackoverflow.com/a/40450106

# Check for custom git-config flag, to execute hook only once on clone, not repeatedly on every checkout
if grep -q "inferredConfig" .git/config
then
    exit
fi

# Automatically set Git config values from parent folders.
echo "Infer Git configuration from directory..."

# Go upwards in directory hierarchy, examine all .gitconfig files we find
# Allows to have multiple nested .gitconfig files with different scopes
dir=$(pwd)
configFiles=()
while [ "$dir" != "/" ]
do
    # Skip first directory (the newly created Git repo)
    dir=$(dirname "$dir")
    if [ -f "$dir/.gitconfig" ]
    then
        configFiles+=("$dir/.gitconfig")
    fi
done

# Iterate through configFiles array in reverse order, so that more local configurations override parent ones
for (( index=${#configFiles[@]}-1 ; index>=0 ; index-- )) ; do
    gitconfig="${configFiles[index]}"

    echo "* From $gitconfig:"
    # Iterate over each line in found .gitconfig file
    output=$(git config --file "$gitconfig" --list)
    while IFS= read -r line
    do
        # Split line into two parts, separated by '='
        IFS='=' read key localValue <<< "$line"

        # For values that differ from the parent Git configuration, adjust the local one
        parentValue=$(git config $key)
        if [ "$parentValue" != "$localValue" ]
        then
            echo "  * $key: $localValue"
            git config "$key" "$localValue"
        fi
    done <<< "$output"

    # Set custom flag that we have inferred the configuration, so that future checkouts don't need to do it
    git config user.inferredConfig 1
done

*: The changes from the original code include:

  1. Works with spaces in paths (especially interesting on Windows)
  2. Parses key-value pairs from .gitconfig correctly (don't read lines with for, iterate with while read instead)
  3. Checks .gitconfig files from root to local directory, not vice-versa
  4. Invokes hook only at initial clone, not at every checkout
  5. Output the config settings that are applied on git clone
Community
  • 1
  • 1
TheOperator
  • 5,936
  • 29
  • 42
0

As a simple solution, use something like autoenv (sorry for the self-advertising) to set your mail address with environment variables:

echo 'export GIT_AUTHOR_NAME="Your name"' > professional/.env
echo 'export GIT_AUTHOR_EMAIL="you@example.com"' >> professional/.env
cd professional
das_j
  • 4,444
  • 5
  • 31
  • 47
  • Thanks for this hint. Here I'd like to go with a solution that doesn't require external tools, but autoenv looks like it could be useful elsewhere :) – TheOperator Nov 06 '16 at 13:58
  • autoenv and direnv look very interesting! But don't they only work in the shell? What if I use a GUI git client? – Iliyan Georgiev Sep 24 '17 at 16:52