如果我git rm --cached一个文件,然后将其添加到.gitignore,然后在创建文件之前签出提交,是否将从磁盘上删除该文件?

时间:2018-09-26 03:55:45

标签: git

我有一个git存储库,其中有一个名为todo.txt的文件,该文件列出了您需要做/要学习的个人事情。
我决定不想在我的 Github 存储库或本地git存储库中使用它,因为它是个人资料,与项目无关。我应该从一开始就将其放在.gitignore中。但是,我没有这样做,而且我知道,如果您将一个当前正在跟踪的文件放在.gitignore中,则git不会取消跟踪它。所以我需要一个命令从版本控制中删除文件而不删除它。我以为git rm --cached todo.txt会做到这一点。所以我做到了。然后,我添加了todo.txt to .gitignore。然后,我committed .gitignore到我的本地存储库。然后,后来,我从创建todo.txt之前开始进行git checkout到更早的提交。

这样做的时候,todo.txt从磁盘上删除了,当我回到之前的提交时,它没有恢复。我的印象是todo.txt不会被删除-当我在提交之间移动时,它将保持不变。我是否误解了.gitignoregit rm --cached

为什么会这样?

2 个答案:

答案 0 :(得分:0)

更新后,您正在描述另一个问题。为了避免git删除文件,我认为最好从拥有该文件的一个修订版本中获取一个副本,将其保存在其他位置,然后重写分支的历史记录以使其永远不在历史记录中(甚至并将其包含在.gitignore上),然后您就可以放心地将其放到项目中,因为git不再会将它弄乱了。

假设您将文件添加到master〜10上。您可以这样做:

git checkout master~10 cp my-file.txt ../ # save it outside of the project, just in case git rm --cached my-file.txt # remove it from the commit, keep it in the project echo my-file.txt >> .gitignore # add the file to .gitignore git add .gitignore git commit --amend --no-edit # now the file is outside of this new brach history git cherry-pick master~10..master # replay history of the master branch git branch -f master # put master pointer on new branch git checkout master

这时文件应该在FS上,如果您沿着master的历史记录前进,git不必关心它。

答案 1 :(得分:0)

TL; DR:这是索引的错误,主要是/,有点破折号.gitignore

只要todo.txt在当前提交和索引中为,并且您切换到todo.txt不是中的提交提交后,Git将从索引中删除todo.txt,因此也将从工作树中删除它。如果这样会破坏宝贵的文件数据,Git有时会抱怨,但是如果todo.txt.gitignore版,那么Git仍然可以随时删除文件。

这里的一个问题是在一个点上将“磁盘”视为一种单独的名词,而在另一点上将其视为集合名词。我们需要用特殊的词来描述情况和动作,因为Git是一个版本控制系统,因此具有某些文件的多个副本-在我们谈论的那一点上文件的名称仍然是todo.txt 1

由于Git即将存储文件内容,但是文件内容必须通过 名称进行访问,因此我们可以使用Git提供的冒号来描述某些文件版本:git show a123456:todo.txt显示todo.txt的版本,该版本随其缩写为a123456的提交保存。这些内容作为 blob对象存储在Git中。这些版本是永久性的-无论如何,它和包含它们的提交一样永久性-并且完全不可更改。

但是当然,当我们运行less todo.txt时,我们也可以在Git称为工作树工作树中访问该文件的一个版本。或vim todo.txt或我们用来访问和更改它的任何内容。这个工作树版本根本没有存储在Git中。这不是永久的;它是 可变的,当然也可以移动。

最后,todo.txt还有一个版本-有时甚至还有一个版本。这个版本就是Git所说的版本,分别是 index 临时区域或有时是 cache 。该索引具有多种功能,但是最简单的描述是:索引是您(和Git)构建将要进行的下一个提交的地方。所有这些都很重要,因为它会影响Git处理文件的方式。在工作树中。

我们可以通过提交的哈希和名称todo.txt来调用提交的a123456:todo.txt。我们可以使用名称todo.txt来调用:todo.txt的索引版本。 2 我们可以将这些名称与git show一起使用以查看存储的内容这些文件的版本。提交的内容被冻结(不可更改),而索引的内容可以更改,直到我们将其冻结为新的提交为止。 Git无法git show工作树版本,但是没有必要:它只是普通的todo.txt,我们可以使用拥有的非Gitty计算机程序来查看它并进行更改。< / p>


1 文件名的问题很棘手。 Git与其他版本控制系统的答案不同:许多其他系统使用某种隐藏的文件标识符,而Git仅具有文件名。 Git会在不重要的情况下进行动态重命名检测,因此我们可以忽略有关名称的棘手问题,而只担心名称todo.txt。例如,在ClearCase中,名为todo.txt的文件在其他版本中可能具有其他名称。

2 这也是0:todo.txt,因为索引实际上有四个编号的插槽。插槽1-3用于合并,因此我们在这里忽略它们。


未跟踪的文件和被忽略的文件

我们刚刚注意到,Git从索引/暂存区中的任何内容进行提交。这意味着当前不在Git索引中的文件不在您将要进行的下一次提交中。这就是git rm --cached起作用的原因:它删除了 index 中的文件,而无需触摸工作树中的文件。您提交的 next 提交没有 not 具有todo.txt作为提交文件,因此,如果下一次提交具有哈希ID fedcba9,则不会有{ {1}}。

现在,假设您正在执行此新提交fedcba9:todo.txt,而该提交没有fedcba9。文件todo.txt也不在索引中(由于您刚刚将todo.txt制成索引,因此文件的格式与提交时相同,即只是提交的版本被永久冻结,而索引的版本可以更改或删除)。 fedcba9 文件是,但是在工作树中。

如果在工作树中有一个名称类似todo.txt的文件,但该文件不在索引中,则该文件被未跟踪。 (如果该文件不在工作树中,也不在索引中,那么它根本就不是文件:就像the man upon the stair who isn't there。)

Git对于未跟踪的文件通常非常小心。如果我们要求Git检查其中包含todo.txt的旧提交,例如a123456,则Git在创建索引时将不得不破坏我们的工作树todo.txt todo.txt中的:todo.txt,因此工作树a123456:todo.txt匹配与todo.txt匹配的索引:todo.txt。因此,Git会对此抱怨:通过用a123456:todo.txt中的一个覆盖它,可能会破坏我们精心创建的todo.txt

但是,G,Git 抱怨关于未跟踪的文件,不断地将您吸引到a123456,或者在您使用任何 add时git add-给它们现在所有文件操作。请记住,git add只是意味着*将此文件复制到索引中,进行创建或覆盖之前的任何版本。如果我们不希望这样做,则可以创建一个名为git add的文件,并将文件名放入.gitignore文件中。这使Git关闭了该文件。

A,在各种情况下,使Git可以随意窃听文件的内容。因此,如果我们现在要求Git签出其他包含.gitignore的提交,例如a123456,则Git会查询我们当前的todo.txt并发现.gitignore是被忽略并因此变得容易崩溃。因此Git将todo.txt提取到a123456:todo.txt(索引)和:todo.txt(工作树)中,同时使提交todo.txt为当前提交。

请注意,这样做的副作用是也将a123456中的.gitignore替换为a123456,如果不在.gitignore中,则删除a123456。没什么大不了的,不是真的。重要的是,当我们选择检出a123456时,我们从:todo.txt的索引中获得了a123456:todo.txt,并且该版本也进入了工作树。

请注意,在签出提交后,通常情况下,任何文件的所有三个活动版本都匹配。HEAD提交(无论其哈希ID如何) —保持冻结的todo.txt,索引:todo.txt与提交的版本匹配,工作树版本与索引版本匹配。有时候这很重要,因为这意味着git statusnothing to commit, working tree clean:没有工作树在进行中的编辑需要小心。

现在,我们要求Git重新提交fedcba9,例如通过运行git checkout master(假设master当前名称为fedcba9)。 Git查看此提交,并将其与当前a123456提交进行比较。有a123456:todo.txt,但没有fedcba9:todo.txt。因此,Git从索引中删除 :todo.txt,也从工作树中删除 todo.txt

由于它很干净,所以这不是问题。如果您希望返回,即使它是.gitignore中的fedcba9 -d,也只需git show a123456:todo.txt > todo.txt,现在您有了工作树文件。

真正的问题来自todo.txt干净的。也就是说,由于todo.txt是一个工作树文件,因此我们可以启动编辑器并对其进行更改,以便todo.txt与不存在的:todo.txt不匹配匹配不存在的fedcba9::todo.txt。如果现在我们要求Git切换到a123456,Git通常会在这里抱怨,工作树版本不干净,切换提交也不安全。但是,如果todo.txt忽略的确如此-Git对自己说:哦,所以可以用a123456中的那个来掩盖它! > Git执行了此操作,现在我们丢失了已编辑的todo.txt,取而代之的是a123456:todo.txt,当我们切换回todo.txt时,该.gitignore被删除了。

这是一个现实的问题,没有真正的解决办法

除了重写历史记录外,最接近工作中树解决方案的是避免在{{1}}中列出文件。这有可能在某个时候意外地重新添加文件。另一个技巧是完全停止将文件保留在工作树中。那不是很令人满意,但是可以。