分支的Git排除文件

30

我希望在一个分支中忽略某些文件,而不必依赖于被其他分支合并时会被覆盖的已跟踪的.gitignore 文件。

我已经仔细遵循了Stack Overflow回答以及链接的博客文章,但是我的仓库似乎没有识别到我在.git/config中指定的excludesfile。Git文档(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

在我的“specialbranch”分支下,我没有一个.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。

2个回答

40

Git不支持分支级别的排除文件

你正在尝试实现Git不支持的功能。这个博客帖子是最初宣传这个“伪命题”的来源,而Stack Overflow回答只是照搬了它。正如那个回答下面的评论所指出的,即使原始博客帖子也包含一篇讨论表明该解决方案不起作用,并链接到一篇更新的博客帖子,提到即使作者也无法重现该行为。

为什么它不起作用呢?如果你阅读man gitignoreman git-config,你会发现只有core.excludesfile被提及,没有branch.<name>.excludesfilecore.excludesfile的作用是让你能够排除例如Vim .swp文件或其他你的软件使用的临时文件。

core.excludesfile

除了.gitignore(针对每个目录)和.git/info/exclude之外,Git还会查阅这个文件中不希望被跟踪的文件模式。"~/"会扩展为$HOME的值,“~user/”会扩展为指定用户的主目录。其默认值是$XDG_CONFIG_HOME/git/ignore。如果$XDG_CONFIG_HOME未设置或为空,则使用$HOME/.config/git/ignore。请参见gitignore(5)

解决方法

我认为最佳的分支排除文件的近似值是使用post-checkout hook并重写工作树中.gitignore的内容。*

每个分支将具有例如.gitignores目录,其中的文件命名为相应的分支名称。然后,将使用.gitignores/__default文件作为默认文件。所有排除文件都会排除.gitignore,并且post-checkout hook会将其作为与.gitignores中相应文件的副本创建。

如果您不想跟踪排除文件,则可以将.git/info/exclude文件作为.git/info/excludes/__default的副本等进行操作。

*: 特别说明:我曾经建议使用符号链接更新钩子,使.gitignore指向正确的内容,但自Git v2.32.0起无法再将.gitignore作为符号链接。现在,在编辑.gitignore时,更改会在切换分支时丢失,而不是被添加到分支的自定义排除文件中。


哈,谢谢 - 我刚刚完成了阅读整个 http://git-scm.com/docs/git-config (!) ,并且想知道为什么我没有看到 branch.excludesfile - 我会尝试你的解决方法。 - goredwards
我查看了git-config和gitignore手册的历史记录,它们从未提到过branch.<name>.excludesfile - Palec
3
其实,我还不确定它从未起作用。我只是确定它从未被记录下来。让我感到困惑的是你试图跟随的回答获得了很高的赞数。我给它投了反对票,并点赞了说它不起作用的评论,我建议任何读到这篇文章的人也这样做。 - Palec
2
Hooks是单独的可执行文件或脚本,Git不会创建它们。我可能永远不会编写这样的脚本,因为我不需要那个功能,而且它也不是简单的。如果您在实现它时遇到任何问题并且找不到答案,可以发布一个新问题来寻求帮助。@tukusejssirs - Palec
1
谢谢提醒,@Jasancos!我更新了我的答案,这样即使更改后,解决方法仍然有效。通过使用副本而不是符号链接,它会失去一些便利性,但应该可以工作。 - Palec
显示剩余7条评论

3
这是我写的一个脚本,用于实现此功能:
#!/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: @https://dev59.com/lV0b5IYBdhLWcg3wIOF-

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

或者查看https://gist.github.com/wizioo/c89847c7894ede628071,作为另一个答案推荐的评论。 - Dan Nissenbaum

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接