如何阻止git提交名称不同的两个文件以防万一?

时间:2016-12-14 23:29:42

标签: linux git macos case-sensitive case-insensitive

我们在混合环境中开发 - 有些人在Mac上工作,有些人在Linux上工作。事实证明,这有时是一个挑战,因为那些在Linux上工作的人习惯于让他们的文件系统区分大小写,所以提交(意外或其他)多个文件的问题不一样。 (例如FileName.extfilename.ext

然而,当Mac上的人们去检查存储库时,拥有一个不区分大小写的文件系统意味着这两个文件 - 仅在大小写上不同 - 相互覆盖并造成一般性破坏。

我知道有各种git设置可以帮助人们对不区分大小写的文件系统更好地处理案例更改(例如core.ignorecase),但是这些并不能解决问题所在的问题存储库中的不同文件,只是大小写不同。

我意识到解决这个问题的唯一方法就是确保Linux用户不提交这两个文件,这些文件只在第一时间有所不同。 - 如果区分大小写的文件系统上的用户尝试提交在不区分大小写的文件系统上会相互混淆的文件,那么git中是否会出现一个警告或错误?

1 个答案:

答案 0 :(得分:5)

没有任何内置(尽管应该有,毫无疑问)。您可以做的是提供一个预提交挂钩,用于验证所有名称是否正常,如果没有则阻止提交。

这个钩子只需要在Linux机器上运行(虽然它可以在Linux和Mac上运行很容易,它只是Windows,其默认的贫困工具箱是有问题的)。您可能希望将其添加到侧分支,并向Linux人员提供有关设置它的说明。

您也可以像git pre-commit or update hook for stopping commit with branch names having Case Insensitive match一样检查分支名称。 (有趣的是:关于这个问题的答案是我自己的;我忘了它。)

首先,让我们写一个"检查案件冲突"功能。这只是用case-folding进行排序的问题(以便" helloworld"和#34; helloWorld"彼此相邻放置),然后使用uniq -di打印任何副本(在案例折叠后)字符串,但没有非重复字符串:

sort -f | uniq -di

如果这产生任何输出,这些是"坏名称"。让我们在临时文件中捕获输出并检查其大小,以便我们也可以将它们打印到标准输出:

#! /bin/sh

TF=$(mktemp)
trap "rm -f $TF" 0 1 2 3 15
checkstdin() {
    sort -f | uniq -di > $TF
    test -s $TF || return 0   # if $TF is empty, we are good
    echo "non-unique (after case folding) names found!" 1>&2
    cat $TF 1>&2
    return 1
}

现在我们只需要在将要提交的文件上使用它,也可以在分支名称上使用它。前者列为git ls-files,因此:

git ls-files | checkstdin || {
    echo "ERROR - file name collision, stopping commit" 1>&2
    exit 1
}

您可以使用git diff-index --cached -r --name-only --diff-filter=A HEAD来仅检查添加的文件,允许现有的案例冲突继续,和/或尝试检查多个分支和/或提交中的内容,但这很难。

将上述两个片段合并为一个脚本(并测试),然后将其复制到名为.git/hooks/pre-commit的可执行文件中。

检查分支名称有点棘手。这确实应该在您创建分支名称时发生,而不是在您提交分支名称时发生,并且在客户端上做不好的工作是不可能的 - 它必须在具有适当全局的集中式服务器上完成图。

这是一种在服务器上以预接收脚本,shell脚本而不是Python(如链接答案)的方式。我们仍然需要checkstdin功能,您可能希望在更新挂钩而不是预接收挂钩中执行此操作,因为您不需要拒绝整个推,只是一个分支名称。

NULLSHA=0000000000000000000000000000000000000000 # 40 0s

# Verify that the given branch name $1 is unique,
# even IF we fold all existing branch names' cases.
# To be used on any proposed branch creation (we won't
# look at existing branches).
check_new_branch_name() {
    (echo "$1"; git for-each-ref --format='%(refname:short)' refs/heads) |
      checkstdin || {
        echo "ERROR: new branch name $1 is not unique after case-folding" 1>&2
        exit 1  # or set overall failure status
    }
}

while read oldsha newsha refname; do
    ... any other checks ...
    case $oldsha,$refname in
    $NULLSHA,refs/heads/*) check_new_branch_name ${refname#refs/heads/};;
    esac
    ... continue with any other checks ...
done