20

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?

1

3 Answers 3

31

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 = [email protected]
# ~/freetime.gitconfig
[user]
    email = [email protected]

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

Sign up to request clarification or add additional context in comments.

2 Comments

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 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.
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 "[email protected]"

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

Comments

1

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="[email protected]"' >> professional/.env
cd professional

2 Comments

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 :)
autoenv and direnv look very interesting! But don't they only work in the shell? What if I use a GUI git client?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.