我有一个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
不会被删除-当我在提交之间移动时,它将保持不变。我是否误解了.gitignore
或git rm --cached
?
为什么会这样?
答案 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)
.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 status
说nothing 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}}中列出文件。这有可能在某个时候意外地重新添加文件。另一个技巧是完全停止将文件保留在工作树中。那不是很令人满意,但是可以。