40

在某些情况下,我无法使用.gitignore文件,否则,在 上git push,关键文件将从远程清除。在这些情况下,我会应用git update-index --assume-unchanged <file>到我想忽略的文件。

在应用假设不变的规则并调用git push后,这些规则是否会附加到远程分支,以便所有后续拉取(来自其他客户端)将继承它们?或者,这些客户端是否也必须git update-index --assume-unchanged <file>在他们的机器上单独运行命令?

如果命令没有被继承——之前有没有人为此编写过服务器挂钩?而不是强制所有当前和未来的客户防范它?

4

3 回答 3

56

该索引在您的工作站本地,您在那里所做的任何更改都不会传播到同一远程的其他克隆。

(更新,问题更新后)

.gitignore 文件

在某些情况下,我不能使用 .gitignore 文件,否则,在 git push 上,关键文件会从远程清除。

这不是真的。git ignore 文件不会影响已在存储库中跟踪的文件。如果您在提交文件.gitignore后添加文件,它将保留在存储库中;它不会被清除。事实上,它的行为就好像它根本没有被忽略一样。

您可以在临时存储库中轻松检查:

$ mkdir -p /tmp/repo
$ cd /tmp/repo
$ git init
Initialized empty Git repository in /tmp/repo/.git/
$ echo red > a.txt
$ git commit -am '1'
 1 file changed, 1 insertion(+)
 create mode 100644 a.txt
$ echo a.txt > .gitignore
$ echo b.txt >> .gitignore
$ git commit -am '2'
 1 file changed, 2 insertions(+)
 create mode 100644 .gitignore

存储库现在包含两个文件:a.txt.gitignore. 两者都表现正常,您可以在克隆它时看到:

$ cd ..
$ git clone file://repo repo2
$ ls -A repo2
.git       .gitignore a.txt
$ cd repo

如果我们同时修改忽略的文件和请求git status,我们会看到它a.txt被视为已修改,尽管已被 gitignored。我们可以正常添加和提交;实际上,如果您将跟踪文件添加到 gitignore,它的行为就像它根本不在 gitignore 中一样。

$ echo green > a.txt
$ echo blue > b.txt
$ git status --short
 M a.txt
$ git add a.txt

b.txt文件不同,因为在 git 开始跟踪它之前它被忽略了。该文件通常不会进入存储库,但如果我们愿意,我们可以强制它。

$ git add b.txt
The following paths are ignored by one of your .gitignore files:
b.txt
Use -f if you really want to add them.
fatal: no files added
$ git add -f b.txt

发布git commit现在提交两个都被 git 忽略的文件:

$ git commit -m '3'
 2 files changed, 1 insertion(+), 1 deletion(-)
 create mode 100644 b.txt

长话短说,将 git 忽略规则视为指南 :-)

传播假设不变

在应用假设不变的规则并调用 git push 后,这些规则是否会附加到远程分支,以便所有后续拉取(来自其他客户端)将继承它们?或者,这些客户端是否也必须在他们的机器上单独运行 git update-index --assume-unchanged 命令?

后者。您可以获得的最接近的方法是将 shell 脚本添加到为您进行更改的存储库中。

服务器挂钩?

如果命令没有被继承——之前有没有人为此编写过服务器挂钩?而不是强制所有当前和未来的客户防范它?

如果您的目标是编写一个服务器挂钩来删除关键文件,就好像它们根本不是推送的一部分,那是不可能的。Git push 主要处理提交 对象(和refs)。它们的依赖对象,如树和 blob,根据需要传输,受提交的可达性影响。它归结为,如果它没有被提交,你就不能将它推送到远程(这是一个过度简化,但它适用于存储库中的文件)。此外,git 提交受到加密保护。如果不更改提交哈希,则无法更改提交,并且如果更改提交哈希,则基本上会有不同的新提交(可能恰好与旧提交具有相同的差异)。

这意味着服务器不能重写提交;至少不会严重混淆进行推送的客户端(它仍然会有旧提交对象的副本)。

可以做的是编写一个 post-receive 钩子,如果它们包含您不想更新的文件,则拒绝提交。这并不能真正解决您的问题,因为如果您在git commit --assume-unchanged向同事解释时遇到困难,那么您可能会在解释他们如何使用交互式 rebase 来重新创建他们的提交而不包含不需要的文件时遇到更多麻烦。

长话短说,我认为如果您正在处理应该提交一次且此后永远不会提交的文件,就像您现在一样,追逐每个人继续使用假设不变(可能与接收后挂钩)是您最不糟糕的选择。

一种可能的解决方法

如果您可以将这些文件完全排除在 git 之外,您的生活会变得轻松得多。我能想到的一件事:

  • 将存储库中的文件移动到 lib 目录,它们不会一直被修改
  • .gitignore文件在它们的最终位置,它们必须在那个位置,但总是得到不需要的更改
  • 在您的存储库中添加一个“init”脚本,人们需要在克隆之后和开始工作之前运行一次。此脚本将文件复制到正确的位置。
于 2013-09-02T10:58:33.273 回答
2

工作时npm

正如之前的答案所解释的,不可能git update-index --assume-unchanged通过git.

话虽如此,在使用 时npm,您可以添加一个postinstall更新索引的钩子:

{
  "scripts": {
    "assume-unchanged": "git update-index --assume-unchanged file",
    "postinstall": "npm run assume-unchanged"
  }
}

这将在每个npm install.

于 2020-11-24T20:38:20.433 回答
1

一种可能的解决方案是编写一个客户端挂钩,以防止人们将本地更改提交到您要忽略的文件。

这些钩子是在 git 中的某些事件上运行的脚本,例如 pre-commit、post-commit、pre-push 等。您可以在<your_repo>/.git/hooks/.

坏消息是它们也不受版本控制,因此您必须编写自己的安装脚本来将挂钩脚本复制到.git/hooks/

这是一个防止文件被提交的预提交钩子的小示例forbidden.txt(它可以适应获取文件列表):

ROOT_DIR="$(pwd)/"
LIST=$(git diff --cached --name-only --diff-filter=ACRM)

for file in $LIST
do
    if [ "$file" == “forbidden.txt” ]; then
        echo You cannot commit file $file. Please reset it and try again.
        exit 1
    fi
done
exit 0
于 2017-07-18T05:40:17.510 回答