Git 排除分支文件

Git excludesfile for a branch

我想忽略分支中的某些文件,而不必依赖跟踪的 .gitignore 文件,该文件将在与其他分支合并时被覆盖。

我密切关注 a Stack Overflow answer along with the linked blog post, but my repo doesn’t seem to be recognizing the specified excludesfile in my .git/config. The Git documentation (git-config, gitignore) 似乎没有说明如何指定和处理特定分支的排除文件。

我的 .git/config 看起来像这样:

[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
        ignorecase = true
        precomposeunicode = true
        excludesfile = +/info/exclude

[branch "myspecialbranch"]
        excludesfile = +/info/exclude_specialbranch

我的 .git/info/exclude 文件看起来像这样(默认情况下,我没有碰它):

# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~
.DS_Store

我的“特殊分支”中没有 .gitignore 文件。

如果我尝试忽略像 info.php 这样的文件,我的 .git/info/exclude_specialbranch 文件看起来像这样:

info.php

... 但它不会忽略该文件。

如果我 运行 git status --ignored,它只列出默认 exclude 文件中的 .DS_Store 文件。

但是,如果我将 info.php 添加到我的 .git/info/exclude 文件中:

# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~
.DS_Store
info.php

它完全忽略了该文件。

请注意,在 .git/config[core] 下没有 excludesfile = +/info/exclude 行它仍然有效。 Git 似乎不需要我告诉它就知道系统范围的 exclude 在哪里。

我感觉 .git/config 无法识别我的自定义排除文件的地址:

excludesfile = +/info/exclude_specialbranch

…很可能是因为使用 + 来描述 .git 目录的位置。

在 Git(更新的)版本中处理自定义排除文件的正确方法是什么?

我正在 运行宁 OSX 10.9.5 和 Git 版本 2.2.1。

Git 不支持每个分支排除文件

您正在尝试实现 Git 不支持的目标。 blog post is the original source of this hoax that the Stack Overflow answer only parroted. As noted in comments under that answer, even the original blog post contains discussion that brings out that the solution does not work and it links to a newer blog post 提到即使是作者也无法重现该行为。

为什么不起作用?如果您阅读 man gitignore and man git-config,您会发现仅引用了 core.excludesfile。那里没有 branch.<name>.excludesfilecore.excludesfile 旨在使您能够排除例如Vim .swp 文件或您的软件使用的其他临时文件。

core.excludesfile

In addition to .gitignore (per-directory) and .git/info/exclude, Git looks into this file for patterns of files which are not meant to be tracked. "~/" is expanded to the value of $HOME and "~user/" to the specified user’s home directory. Its default value is $XDG_CONFIG_HOME/git/ignore. If $XDG_CONFIG_HOME is either not set or empty, $HOME/.config/git/ignore is used instead. See gitignore(5).

解决方法

我相信每个分支排除文件的最佳近似是使用 post-checkout hook 和通过符号链接实现 .gitignore 来实现的。

每个分支都会有例如一个 .gitignores 目录,其中包含以相应分支命名的文件。然后会有一个默认使用的 .gitignores/__default 文件。 .gitignore 将被所有排除文件排除,并由 post-checkout 挂钩创建为指向 .gitignores.

中相应文件的符号链接

如果您不想跟踪排除的文件,您可以对 .git/info/exclude 文件执行相同的操作,作为指向 .git/info/excludes/__default 等的符号链接。

这是我为此编写的脚本:

#!/bin/bash                                                                      

# This is designed to allow per-branch un-ignoring of certain files.
# Use case: Compiled CSS files on master to push to server.
# A special .gitignore file will be present on master at
# {$gitignorePath}/{$disabledMasterGitignoreName} and that file should 
# enable the css files using the ! flag. @https://git-scm.com/docs/gitignore

# When the branch specified by script parameters
# is checked out, {$gitignorePath}/{$disabledMasterGitignoreName} is 
# copied to .gitignore. 
# On other branches this gitignore file is named $disabledMasterGitignoreName, versioned,
# and {$gitignorePath}.gitignore will be deleted. Note, you must ignore 
# {$gitignorePath}.gitignore from your main .gitignore file
#
# To enable put this file in your path and call this script
# in .git/hooks/post-checkout with pass a list of single-space-separated branches that
# will enable the branch-specific .gitignore.

# One caveat is that you can only merge into the branch containing the .gitignore
# file. Otherwise you'll end up re-committing the files. This is fine if you are
# using gitflow and `master` contains your special .gitigore using the ! syntax
# that is un-ignoring files.
#
# Background: @

set -e                                                                           

gitignorePath='docroot/sites/all/themes'
disabledMasterGitignoreName='.gitignore_master--disabled'
#branchWithGitignoreEnabled='master'

branch=$(git rev-parse --abbrev-ref HEAD)

gitignoreRoot="$(git rev-parse --show-toplevel)/${gitignorePath}"

if [ -f "${gitignorePath}/.gitignore" ]
then
    masterGitignoreExists=true
fi

if [ -f "${gitignorePath}/${disabledMasterGitignoreName}" ]
then
    disabledMasterGitignoreExists=true
fi

IFS=' ' read -a params <<< "$@"

if [[ " ${params[@]} " =~ " ${branch} " ]]
then
  if [ $disabledMasterGitignoreExists ]
  then
    cp -f "${gitignoreRoot}/${disabledMasterGitignoreName}" "${gitignoreRoot}/.gitignore"
    echo "Enabled ${gitignorePath}/.gitignore"
  fi
elif [ $masterGitignoreExists ]
then
    rm -f "${gitignorePath}/.gitignore"
    if [ masterGitignoreExists ]
    then
      echo "Disabled ${gitignorePath}/.gitignore"
    fi
fi