Git add 命令:添加被忽略的文件

58
我正在尝试从git中删除之前跟踪的目录,这是有效的,但是每次后续的git add ., git add -A等操作都会将其重新添加。这是我所做的:
在项目根目录中的.gitignore文件中添加:
node_modules

运行以下内容:
git rm -r --cached node_modules
git commit -a -m "removed node_modules"
git push origin master

到目前为止,这将从远程仓库中删除目录。问题是,当我稍后运行git status时,它告诉我node_modules目录未被跟踪,并在未来的提交中不断添加它回来。
我错过了什么,或者如何找到我的问题的根源?
这里得到以下信息:
git add命令默认不会添加被忽略的文件。 ... git add命令可以使用-f(强制)选项添加被忽略的文件。
评论中的额外信息:
我正在跟踪.gitignore文件。 git check-ignore node_modules/按预期返回node_modules/。
没有使用子模块。
更新:
我创建了一个样本,似乎可以复制上述步骤中的问题。

https://github.com/awhitehouse104/SampleRepo

解决方案:

总结下面的答案和评论,问题出在我的.gitignore文件编码上。我在Windows 8上使用echo 'node_modules' > .gitignore创建文件,结果它以UTF-16带BOM的形式呈现(根据下面的答案)。经过几次谷歌搜索,似乎这是powershell的默认编码方式,我可以确认保存为UTF-8似乎已解决了这个问题。

tldr; 可能不要使用此方法创建.gitignore文件,或者准备更改编码方式

echo 'node_modules' > .gitignore


@Blue 是的,那个问题的最佳答案基本上就是我列出来尝试过的。我会深入研究一些较次的答案,但起初没有什么特别引人注目的地方。 - aw04
你的gitignore文件中还有其他内容吗?gitignore文件的编码是什么?文件中是否存在尾随或前导空格? - Tim
不确定,但我正在尝试排除边缘情况。 - Tim
@aw04 你能确认一下在项目的新克隆中是否也存在这种情况吗?我认为你可能已经以某种方式破坏了当前的克隆。 - Anshul Goyal
显示剩余6条评论
10个回答

18

你可能在.gitignore文件中的node_modules行之后某处有一个负面规则(包含再次规则,以!开头)。

git check-ignore文档中存在错误/不明确的地方。你期望如果git check-ignore node_modules/打印出node_modules/,那么node_modules/会被忽略。但实际上,它会打印出一个路径名,如果该路径名与任何忽略模式(正面或负面)匹配。唯一确定的方法是使用-v--verbose)选项,这将使git check-ignore打印匹配的模式。
此外,如果git check-ignore -v说一个目录被忽略了,并不一定意味着该目录中的所有文件都被忽略。示例仓库:

/
    .git/
    .gitignore
    node_modules/
        bar
        foo
$ cat .gitignore 
/node_modules/*
!/node_modules/foo

$ git check-ignore -v node_modules/
.gitignore:1:/node_modules/*    node_modules/
             ^ positive pattern => ignored

$ git check-ignore -v node_modules/foo
.gitignore:2:!/node_modules/foo node_modules/foo
             ^ negative pattern => not ignored

$ git add -A

$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   node_modules/foo
#

如果 git check-ignore -v node_modules/ 显示 node_modules/ 被忽略了,那么请执行 git add -A node_modules/, 然后针对被添加的单个文件运行 git check-ignore -v --no-index 命令,查明为什么它们被添加。


更新:我没想到:你的 .gitignore 文件采用了“UTF-16 带字节顺序标记”编码:

$ cat .gitignore | hexdump -vC
00000000  ff fe 6e 00 6f 00 64 00  65 00 5f 00 6d 00 6f 00  |..n.o.d.e._.m.o.|
00000010  64 00 75 00 6c 00 65 00  73 00 0d 00 0a 00        |d.u.l.e.s.....|

这就是为什么 git 可能无法处理它。将文件保存为 UTF-8 无 BOM,那应该可以解决问题。但我建议针对 git check-ignore 提交错误报告 - 在这种情况下,它的输出显然与 git 实际忽略的内容不一致。


没有负面规则。最后一部分对我理解非常有帮助(+1),尽管在检查单个文件时没有输出任何内容。因此,如果我正确理解了预期的输出,它基本上告诉我我已经知道的东西。 - aw04
2
嗯,这很奇怪。也许是git里有个bug...你能展示一下这些文件吗:.gitignore.git/info/exclude$XDG_CONFIG_HOME/git/ignore(如果存在)、$HOME/.config/git/ignore(如果存在),以及由git config --get-all core.excludesfile报告的任何文件。也许你可以使用tar打包整个repo并在这里分享(或者告诉从哪里克隆)? - Roman
1
也许你可以从那个代码库中创建一个 [mcve]。 - Roman
经过一个小时的在线研究,编码技巧解决了我的问题。谢谢! - dumduke
我自己也遇到了类似的问题。我使用VSCode内置的PowerShell终端,通过echo命令创建了我的.gitignore文件。 - oerdal
显示剩余3条评论

10

将文件添加到Git忽略列表,然后:

git update-index --assume-unchanged <file>

我需要对node_modules目录中的每个文件都这样做吗?总体上是个好主意,但在这种情况下似乎相当不切实际。 - aw04
你也可以将它与文件夹一起使用。 - hspandher
很酷,这可能是一个有趣的解决方法。我会试一试。 - aw04

7

您可以这样做

git check-ignore -v --no-index path/with/unexpected/result

要查看为什么git add会或不会添加该路径,请查看git check-ignore文档

特别要注意的是,您想检查实际添加了什么,而不是目录。

此外,请运行find . -name .git命令。子模块是嵌套的存储库,.gitmodules和子模块命令对其有所帮助,但它们只是为了帮助处理子模块。


1
这些信息很有帮助,谢谢。然而,在特定文件上使用 "git check-ignore -v --no-index <路径>" 并没有返回任何结果。它可以用于文件夹,但无法用于单个文件。我猜这说明了某种原因导致它们没有被忽略,但并没有告诉我为什么。 - aw04

4
以下是关于如何忽略已经被跟踪的node_modules文件夹的方法:
  1. 创建 .gitignore 文件并将 node_modules 添加到其中。

  2. 提交 .gitignore 文件。从此时开始,您在 node_modules 文件夹中更新的任何内容都不会出现在 git status 中。

  3. 但是这并不会删除我们之前在 node_modules 文件夹下提交的内容。所以现在我们需要删除之前提交的内容。

    为此,请使用 git rm --cached node_modules -r

  4. 现在,git status 将显示这些文件已被删除。

  5. 使用带有任何消息的 git commit -m "node_modules removed" 命令。

现在,所有内容都应该已从代码库中删除,并且将来的更改也不会被跟踪。

1
这与我问题中的步骤具有相同的结果。它可以工作,但会在下一次 git add 时将文件添加回去。 - aw04

0

我创建了一个存储库来尝试复制您的问题,并从http://www.stackoverflow.com/questions/11451535/gitignore-not-working获得了第一个答案。

如果您感兴趣,这是我的存储库:https://github.com/IAMZERG/so_project_gitignore

请尝试将以下内容添加到.gitignore中:

**/directory_to_remove

之后,运行git rm --cached directory_to_remove -r

git status应该显示您已删除要删除的目录中的许多文件。然后,提交并推送到远程,一切都应该很好...也许?


在尝试解决问题之前,你能否重现这个问题? - aw04
我已经解决了你的问题。最初,我尝试通过在vim中使用 :w ++enc=utf-8 .gitignore 写入utf-8格式的.gitignore文件来修复它,但看起来这并不能解决问题。我使用git rm命令移除了node_modules和.gitignore文件,然后创建了一个新的.gitignore文件。我将这些更改一起提交,并添加了我的新.gitignore文件,然后事情似乎就自然而然地解决了...不知道为什么我认为前面的**/会起作用...这实际上并不重要。这是我分叉的库,其中包含修复:SampleRepo - IAMZERG

0

我找到了一种方法,即使文件已经被提交过,也可以将它们添加到.gitignore中。这有点粗暴,但效果很好。

  1. 将不同的文件添加到您的.gitignore中

  2. 将这些文件移出您的git项目

  3. 重新扫描和StageChanged,这些操作会告诉您这些文件已被删除

  4. 提交

  5. 将文件移回您的项目,它们将不再被跟踪!


不需要移动文件:git rm --cached + git commit 做的是同样的事情。 - VonC

0

gitignore - 指定要忽略的故意未跟踪的文件。

$HOME/.config/git/ignore, $GIT_DIR/info/exclude, .gitignore

gitignore 文件中的每一行都指定了一个模式。在决定是否忽略路径时,Git 通常会从多个来源检查 gitignore 模式,按照以下优先顺序从高到低(在一级优先顺序内,最后匹配的模式确定结果):

若想要忽略整个目录,您可以使用 /**

末尾的 /** 匹配目录内的所有内容。例如,abc/** 匹配相对于 .gitignore 文件位置的 "abc" 目录内所有文件,深度无限。

或者

您可以通过将以下行添加到根 .gitignore 文件来忽略整个目录:

/Dir_Name

或者,您可以添加 /logs/.gitignore 文件并包含以下内容:

[^.]*

目录将保留在您的repo中,但是/directory内的所有文件都将被忽略。很容易!

您需要遵循的步骤是:

  1. 从项目目录中删除它(而不实际删除它):

    git rm --cached folder/*

  2. 如果您还没有.gitignore文件,可以在项目文件夹中创建一个:
  3. project/.gitignore。将folder/*(或上面列表中您认为更好的任何模式)放入.gitignore中并提交:
  4. git commit -m“message”。
  5. 将更改推送到github。

例如,排除除特定目录foo/bar之外的所有内容(请注意/*-没有斜杠,通配符也将排除foo/bar内的所有内容):

$ cat .gitignore
# exclude everything except directory foo/bar
/*
!/foo
/foo/*
!/foo/bar

0

如果您不想使用 git rm --cached,则可以采用另一种方法。

rm -Rf node_modules
git add -u
git commit -m "stop tracking node_modules"

npm install
# done

请注意 node_modules 和 node_modules/ 之间的区别,你似乎已经正确理解了。(感谢 umläute 对此的指正)

1
不添加 node_modules/(带有尾随斜杠)的原因是什么?如果您想确保只排除与模式匹配的目录(而不是同名文件),则添加尾随斜杠是正确的方法... - umläute
我忘记了我当时使用的系统,但我相信过去如果我的.gitignore文件中有结尾斜杠,它就无法正常工作。感谢@umläute,我已经编辑了我的帖子。 - Jonathan.Brink
不错的想法,但它并不能防止文件再次被添加。 - aw04

-1

我曾经遇到类似的问题。确保我的编码是ANSI,行结束符是Unix(LF)解决了我的问题。


-1

起点必须是裸的。这里有一个小的bash示例来演示它。如果它不能代表您的用例,请随意编辑它。

#!/bin/bash

rm -rf /tmp/git_test
mkdir /tmp/git_test/
git init --bare /tmp/git_test/primary_repo

# populate repo
cd /tmp/git_test/
git clone ./primary_repo ./secondary_repo
cd secondary_repo
echo hi_bob > some_content
mkdir node_modules
echo hi_dave > node_modules/moduleA
git add .
git commit -m "populated primary"
git push

# do the removal in tertiary 
cd /tmp/git_test/
git clone ./primary_repo ./tertiary_repo
cd tertiary_repo
echo node_modules >> .gitignore
git add .gitignore
git rm -r --cached node_modules
git commit -a -m "removed node_modules"
git push origin master
rm -r node_modules
echo --------------------------------
echo git status:
git status

你能详细解释一下“原点必须是裸的”是什么意思吗? - aw04
一个裸仓库没有工作副本。当我最初使用非裸仓库设置实验时,推送被拒绝了,主仓库仍然挂着该目录。你的源在哪里? - Fabian
@aw04:你能否提供一个脚本来复制你的问题?这比从口头描述中工作要容易得多。 - Fabian

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