我遇到了Git的奇怪行为:
我有一个存储库,其中包含.gitignore
文件中指定的许多未跟踪文件和文件夹。
我所做的确切步骤:
git stash
git checkout <hash of first commit>
git checkout <my working branch>
git stash apply
然后我注意到一些(不是全部)我未跟踪的文件和文件夹已经消失。怎么会这样?
其他信息:
隐藏文件与消失的文件无关,我注意到存储操作只是为了完整性
我没有执行其中一个命令git stash --include-untracked
或git stash save -u
,因为@Ashish Mathew猜到了
似乎只有第一次提交时尚未加入.gitignore
的文件和文件已消失,但后来又被添加到文件中
答案 0 :(得分:5)
藏匿的文件与消失的文件无关......
确实
似乎只有第一次提交时尚未出现在
.gitignore
中的文件和文件已消失,但后来又被添加到文件中
这再加上一件事(几乎可以肯定)是问题的根源。幸运的是,您应该能够获取这些文件,或者至少某些版本这些文件。不幸的是,你必须把它们全部拼出来并和Git一起大惊小怪,你可能会得到错误的版本。请参阅底部的示例会话。
即使.gitignore
文件要忽略它,也不会忽略未跟踪(被跟踪)的文件。仅忽略未跟踪的文件:跟踪文件,未跟踪但未被忽略,或未跟踪和忽略文件。
但是等一下:确切地说,未跟踪的文件是什么?
这个定义是Git中为数不多的简单明了的定义之一。或者,更确切地说,如果很清楚索引是什么。不幸的是,索引很难看。
我对索引的最佳一行描述是:*索引是您构建 next 提交的地方。*
此索引(也称为临时区域和缓存)会跟踪 - 即索引 - 您的工作树。您的工作树是您工作的地方:它使您的文件采用正常的非Git格式。在Git存储库内的提交中永久存储且只读的文件具有特殊的,压缩的,仅Git格式。索引&#34;位于&#34;之间。这两个地方:它包含所有可提交的文件,来自您的工作树,都设置为提交。但索引中的文件是可更改(与提交内部不同),即使它们已经转换为特殊的Git格式。
这意味着您的索引实际上为空非常罕见。大多数情况下,它只匹配您当前的提交。那是因为你刚刚检查了那个提交,它将这些文件放入你的索引(只有Git形式,准备下次提交)和你的工作树(以常规普通文件形式,准备使用或编辑)。
如果修改文件F
并运行git add F
,git add
将替换索引中的文件副本(格式为Git)之前。该索引并非为空 - 其中包含F
,以及其他所有内容 - 它只是匹配当前提交,因此大多数Git命令都在在您在工作树中更改F
之前,请提及F
。
所以,我们考虑一下:
几个月前检查了我的第一次提交:
git checkout <hash of first commit>
这告诉Git:从第一次提交中填充索引和工作树。假设我们还没有实际运行此命令,只考虑:这将做什么?该提交中的内容是什么?
好吧,当你创建它时,该提交包含索引中的任何内容 - 无论你使用git add
复制到索引中。这包括,例如,文件abc.txt
,您后来决定未跟踪。
要进行无人跟踪,您必须在某个时刻从索引中删除 abc.txt
,可能是:
git rm --cached abc.txt
(将工作树副本留在原位,同时删除索引副本)。在git rm --cached
之后,您执行了git commit
。从您运行git rm --cached
开始,到目前为止,索引中的文件不是。它在工作树中。所以未跟踪。
现在你已经告诉Git检查你的第一次提交了,但是......好吧,第一次提交中有abc.txt
。 Git需要将已提交的abc.txt
版本复制到索引和到工作树中。
此时,如果工作树中已经存在abc.txt
,Git将检查您是否要使用其他abc.txt
来破坏它。大多数情况下,Git会拒绝这样做,告诉你先把它移开。但是,如果工作树中的abc.txt
与提交中的abc.txt
匹配,那么使用提交中的abc.txt
填充索引是安全的。毕竟,它与工作树中的那个匹配。
所以在这一点上,Git将该提交中的所有文件提取到索引和工作树中。 (对于这个一般性的想法有一些复杂的但是试图安全的例外:见Checkout another branch when there are uncommitted changes on the current branch。)而且,嘿嘿,现在git checkout <my working branch>
是指数。现在它被追踪了!
所以现在你环顾四周,看看你的旧提交,并决定:
abc.txt
现在Git必须将索引和工作树内容从第一次提交(其中包含<my working branch>
)切换到abc.txt
的提示提交。该提交不其中包含git checkout <hash>
。 Git将从索引中删除文件...并将其从工作树中删除,因为它已跟踪。
结帐完成后,现在索引中的
答案是盯着我们:它在第一次提交中。当您运行git checkout <my working branch>
时,Git将文件复制到索引和工作中 - 树(除了它毕竟不必触及工作树版本)。当你运行<hash>
以取回时,Git 删除了文件,但是提交是只读的,并且(大部分)是永久性的,所以文件仍然存在,只有Git形式,提交<hash>
。
诀窍是让它 out 提交git show hash:path > path
而不用将它放回索引中,这样它就会在正常的非Git中保持不变格式。这些天最简单的方法是使用git show hash:abc.txt > abc.txt
,例如:
git show
(请注意,默认情况下--textconv
不会应用行尾转换和污迹过滤器 - 在现代Git中,您应该能够使用.gitgnore
)来实现此目的。
你必须为Git删除的每个文件执行此操作,这可能会非常痛苦。
README
使Git OK使用clobbering数据我为测试目的制作了一个小存储库。在此存储库中,我使用abc.txt
和文件original
进行了初始提交,其中包含一行$ mkdir tt
$ cd tt
$ git init
Initialized empty Git repository in ...
$ echo original > abc.txt
$ echo for testing overwrite > README
$ git add README abc.txt
$ git commit -m initial
[master (root-commit) a721a23] initial
2 files changed, 2 insertions(+)
create mode 100644 README
create mode 100644 abc.txt
$ git tag initial
$ git rm abc.txt
rm 'abc.txt'
$ git commit -m 'remove abc'
[master 20ba026] remove abc
1 file changed, 1 deletion(-)
delete mode 100644 abc.txt
$ touch unrelated.txt
$ echo abc.txt > .gitignore
$ git add .gitignore unrelated.txt
$ git commit -m 'add unrelated file and ignore rule'
[master 067ea61] add unrelated file and ignore rule
2 files changed, 1 insertion(+)
create mode 100644 .gitignore
create mode 100644 unrelated.txt
:
$ git log --oneline --decorate
067ea61 add unrelated file and ignore rule
20ba026 remove abc
a721a23 (tag: initial) initial
我们现在有一个包含三个提交的存储库:
abc.txt
让我们将一些宝贵的数据放入(忽略)$ echo precious > abc.txt
$ git status
On branch master
nothing to commit, working tree clean
$ cat abc.txt
precious
:
initial
现在让我们查看提交$ git checkout initial
Note: checking out 'initial'.
You are in 'detached HEAD' state. [mass snip]
HEAD is now at a721a23... initial
$ cat abc.txt
original
:
.gitignore
哎呀,我们宝贵的数据已被破坏了!
abc.txt
指令赋予Git删除文件的权限。为了证明这一点,让我们$ cp /dev/null .gitignore
$ git add .gitignore
$ git commit -m 'do not ignore precious abc.txt'
[master 564c4fd] do not ignore precious abc.txt
Date: Thu Feb 8 14:16:08 2018 -0800
1 file changed, 1 deletion(-)
$ git log --oneline --decorate
564c4fd (HEAD -> master) do not ignore precious abc.txt
067ea61 add unrelated file and ignore rule
20ba026 remove abc
a721a23 (tag: initial) initial
$ echo precious > abc.txt
$ git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
abc.txt
nothing added to commit but untracked files present (use "git add" to track)
不被忽视(但也不被跟踪):
initial
现在,如果我们要求切换到$ git checkout initial
error: The following untracked working tree files would be overwritten by checkout:
abc.txt
Please move or remove them before you switch branches.
Aborting
:
{{1}}
因此忽略文件会产生令人讨厌的副作用:它们变得更加惹人喜爱。我(我认为,与过去的其他人一起)已经研究过Git的教学区别之间的区别是否被忽视,并且可能会破坏&#34;并且&#34;忽略但是珍贵,不要破坏&#34;并且无法简单地解决它并放弃了努力。
(我认为Git在某一方面对此有了更好的表现,但是这个例子表明它至少在Git 2.14.1中仍然很糟糕,这是我在这组测试中使用的版本。)