git update-index --assume-unchanged规则是否会在pull上传播给客户端?

时间:2013-09-02 10:25:53

标签: git gitignore

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

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

如果命令没有被继承 - 有没有人为此编写过服务器挂钩?而不是强制要求所有当前和未来的客户免受它的侵害?

3 个答案:

答案 0 :(得分:38)

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

(更新后,问题更新后)

.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后,这些规则是否会附加到远程分支,以便所有后续的pull(来自其他客户端)将继承它们?或者,这些客户端是否还必须在其计算机上单独运行git update-index --assume-unchanged命令?

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

服务器挂钩?

  

如果命令没有被继承 - 有没有人为此编写过服务器挂钩?而不是强制要求所有当前和未来的客户免受它的侵害?

如果您的目标是编写一个删除关键文件的服务器挂钩,就好像它们根本不是推送的一部分那样,那是不可能的。 Git推送优惠主要是提交 objects(以及refs)。它们的依赖对象(如树和blob)根据需要进行传输,具体取决于提交的可达性。它归结为,如果它没有提交,你不能把它推到一个遥控器(这是一个过于简单化,但它适用于存储库中的文件)。另外,git提交是加密保护的。您无法在不更改提交哈希的情况下更改提交,如果更改提交哈希,您基本上会有一个不同的新提交(可能恰好具有与旧提交相同的差异)。

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

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

长话短说,我认为追逐每个人继续使用假设不变(可能与后接收挂钩相结合)是你最不好的选择,如果你正在处理应该提交一次的文件,而不是之后,就像你一样现在有了。

可能的解决方法

如果您可以将这些文件全部保存在git中,那么您的生活将变得更加轻松。我能想到的其中一件事:

  • 将存储库中的文件移动到lib目录,在那里它们不会一直被修改
  • .gitignore位于其最终位置的文件,它们必须存在的文件,但始终会进行不必要的更改
  • 向您的存储库添加一个“init”脚本,人们需要在克隆之后和开始工作之前运行一次。此脚本将文件复制到正确的位置。

答案 1 :(得分:1)

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

这些钩子是在git中的某些事件上运行的脚本,如预提交,提交后,推送前等。您可以在<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

答案 2 :(得分:1)

使用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上运行。