Git正在结账时放弃我的.gitignore文件

时间:2016-11-18 11:11:10

标签: git gitignore git-checkout

当我将分支A结帐到分支B时,我的.gitignore A中的某些文件如果不在.gitignore分支B中则被删除。 我试图编辑我的.git / info / exclude文件,但我没有看到差异。 我尝试使用--assume-unchanged和--skip-worktree(将文件保存在.gitignore中+ make add -f)但我无法检查我的文件是否被修改。我真的需要忽略我的档案...... 你有一些分享的技巧吗?

由于

1 个答案:

答案 0 :(得分:4)

不可思议

问题归结为:Git does not think .gitignore means what you think it means

.gitignore中列出的文件实际上并未被忽略。如果文件名与.gitignoreexcludefiles控件的内容匹配,则:

  • git status和其他命令会把它从"文件中抛出来抱怨未跟踪"名单。也就是说,如果 实际上 没有跟踪,而git status会抱怨它,它就不会抱怨。这个动作是公平的"忽略" -y,但是如果 未跟踪"事情:路径名与ignore条目匹配的事实不会使文件变为未跟踪。
  • git add各种"添加所有文件"如果标记当前未跟踪,则会跳过它。 (这是三个行动中唯一真正完全"忽略" -y。)
  • 从某些角度来看,最糟糕的是:如果Git遵循一些会导致它覆盖文件的指令,那么 >现在没有跟踪,事实上有一个匹配的忽略条目告诉Git:"随意覆盖文件。"换句话说,这不是忽略的文件列表,它是(安全地) clobbered 的文件列表。 (这不是这个特殊情况下的问题,但它是.gitignore的一个重要方面,并不意味着人们认为它意味着什么。)

你在这里被击中的原因是因为有问题的文件实际上被跟踪了,尽管它在.gitignore

除此之外:索引,以及它意味着什么"真的没有跟踪"

单词"未跟踪"或短语"真正未跟踪",出现在上面几次。在Git中,未经跟踪的文件有点神秘:什么,正是,使文件"未跟踪"?答案非常简单 - "非常简单"因为这个 Git。 当且仅当文件不在索引中时,文件才会被跟踪。

(有git add -N个特殊索引条目,但它们在许多Git版本中都被严重破坏,所以最好再避免使用它们几年。另一个可能的复杂因素是"索引"本身是一个复杂的野兽。它的大多数复杂性至少意味着是隐形和自动的,但在实践中它们大多数。如果你把索引想象成"这就是我要在下次提交时放入的内容"你会好的。还要记住索引是在合并期间存储额外信息的地方:如果你正在处理冲突合并后,冲突状态保存在索引中。)

从提交切换到提交会使Git写入或删除文件

当您运行git checkout B从分支A切换到分支B时,Git做了两件事:

  • 更改"当前分支" (存储在特殊的HEAD文件中)从A到B.这实际上发生在 last ,当且仅当结账的其余部分成功时。
  • 但首先,如果两个不同的分支命名不同的提交git checkout必须更改索引和工作树内容

在第一步中出现问题。分支A命名一些提交(通过其原始哈希ID,7fe3291...或其他)。分支B还命名一些提交(通过原始哈希ID)。如果分支 B 命名相同的提交,Git的工作非常简单,因为您不是要求Git从一个提交移动到另一个,只是为了改变它的当前分支名称"的想法。但如果两个分支命名两个不同的提交,Git需要调整索引和工作树。

请记住,索引是进入 next 提交的内容。如果您现在要在分支B上,那么索引与现有提交或多或少相匹配。同样,工作树需要或多或少地匹配。

Git 尝试保持已更改但未暂存的文件,处于已更改但未暂存的状态。 (实际上,它还会尝试将更改和暂存的文件保持在更改和暂存状态。有关详细信息,请参阅Git - checkout another branch when there are uncommitted changes on the current branch。)但是如果文件当前已跟踪 -is在索引中 - 并且不存在在提交中我们将切换到没有未提交的更改, Git 必须从索引中删除文件,在此过程中,还会将其从工作树中删除

显然,此处数据丢失没有问题。这些文件位于索引中,没有未提交的更改。这意味着这些文件的索引条目匹配这些文件的已提交(HEAD)版本。 (具体来说,存储在索引中的哈希与存储在与HEAD提交相关联的树中的哈希匹配。)这反过来意味着该文件的内容被安全地保存在HEAD提交中。如果你想要回复内容,你可以在切换到其他提交后,简单地从现有提交中提取内容,即现在HEAD@{1}提交的内容:

git show HEAD@{1}:path/to/file > path/to/file  # skips smudge filter

或:

git checkout HEAD@{1} -- path/to/file          # applies smudge filter

应用涂抹过滤器并执行任何行尾CRLF操作的git checkout变体也会将文件写入索引,以便文件显然 在你刚刚切换到的提交中没有跟踪,这就是为什么Git删除它 - 现在在索引中,因此被跟踪。

关于潜在错误的附注

我不知道任何实际Git版本中的任何实际错误,但我使用Git 2.10.1对此帖子进行了抽查。当Git检查是否存在对文件的未分级更改时,可能会发生明显的潜在错误,而在将文件作为切换到另一个提交的一部分进行删除之前。请记住,此处安全删除或覆盖文件的测试是:

  1. 此文件是否已被跟踪和修改,如果是, safe
  2. 是否跟踪并修改了此文件?如果是这样,安全。
  3. 文件是否未跟踪并且能够破解?如果是这样,安全
  4. 否则(未跟踪但不能破坏):安全。
  5. 案例1是潜在的错误。我们如何测试"跟踪但未经修改的"? 如果您为文件设置了--assume-unchanged--skip-worktree标志怎么办?这些索引标志通常会使Git将文件视为"未修改"。如果Git在git checkout期间服从这些标记,则可能会覆盖或删除该文件。

    我用2.10.1做的(轻度)测试表明,Git在测试是否可以删除文件时将这些标记放在一边。也就是说,我修改了一个跟踪文件,但在索引中设置了该文件的位。然后我试图切换到缺少该文件的提交。 Git给了我一个错误信息。

    如果Git的某些其他版本意外服从这里的索引标志,即使,他们也可能删除该文件匹配HEAD提交。在这种情况下,没有in-Git方法来恢复内容。