Git 配置目录范围,包含多个存储库

Git config with directory scope, containing multiple repositories

情况如下:

对于每个域,我想使用不同的电子邮件地址作为 author/committer 信息的一部分。也就是说,我希望在我的业余项目中列出我的私人地址,在我的专业项目中列出我的公司地址。

git config 知道 3 个范围:存储库、全局和系统范围。 我基本上需要的是存储库和全局之间的第 4 个作用域,代表一组存储库(或只是文件系统中的一个目录)。

似乎 git config 不允许这样做。当然,我可以为每个存储库设置电子邮件地址,但我想在每次设置或克隆存储库时避免这个手动步骤。一种选择是编写一个包装 git init/clonegit config 的脚本,还有其他想法吗?

作为一个简单的解决方案,使用类似 autoenv 的东西(对不起自我广告)来设置环境变量的邮件地址:

echo 'export GIT_AUTHOR_NAME="Your name"' > professional/.env
echo 'export GIT_AUTHOR_EMAIL="you@example.com"' >> professional/.env
cd professional

我想出的解决方案的灵感来自 。由于它不能直接适用于我的情况,我修改了 hook 的 bash 脚本并改进了几个部分*。

假定主目录的目录结构如下:

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

最初我让 Git 知道我的模板目录在哪里。在 Windows 上,您可能需要改为指定绝对路径 (C:/Users/MyUser/.git-template)。

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

~/MyDomain/.gitconfig 中,我存储了该目录 (domain) 的配置,它应该应用于它及其子目录中的所有存储库。

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

有趣的部分是 post-checkout bash 脚本,它定义了 post-checkout 钩子。我使用自定义 user.inferredConfig 标志仅执行一次(在 git clone 上),而不是重复执行(在 git checkout 上)。当然也可以创建一个单独的文件来表示该状态。

#!/bin/bash

# Git post-checkout hook for automated use of directory-local git config
# 

# 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

*:对原始代码的更改包括:

  1. 在路径中使用空格(在 Windows 上特别有趣)
  2. 正确解析 .gitconfig 中的键值对 (don't read lines with for, iterate with while read instead)
  3. 从根目录到本地目录检查 .gitconfig 个文件,反之亦然
  4. 仅在初始克隆时调用挂钩,而不是在每次结帐时调用挂钩
  5. 输出应用于 git clone
  6. 的配置设置

基于,我想我找到了一种工作方式。

首先为您的 "custom-scopes" 创建不同的配置文件(例如专业、空闲时间等)并为每个范围添加您想要的 user-config

# ~/all-projects.gitconfig
[user]
   name = TheOperator

# ~/professional.gitconfig
[user]
   email = yourname@yourwork.com

.

# ~/freetime.gitconfig
[user]
   email = yourname@private-email.com

比你在标准 gitconfig 中添加这样的行:

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

"gitdir/i" 之后的目录应该与您的项目组的父目录相匹配。 在我的示例中,您应该将 git-repos 存储为 freetime-domains,例如"c:/freetime/my-freetime.domain/.git"