我有一个场景,我认为我意外地从本地存储库中删除了所有历史记录和文件,并想看看事实是否如此(以及是否可以恢复任何文件)。我最初从远程存储库克隆了一个存储库,但从未在本地创建新分支。我在一个本地目录中创建了几个文件。然后,我意外地将远程repo的master分支合并回本地,导致这些文件被删除。我没有办法回到另一个分支(就像我看到的其他SO问题一样)。没有办法恢复到"合并前"本地版本,是吗?
edit:我还应该注意到,我的本地repo中的文件从未在任何时候提交过。它们只是保存在运行git init
的文件夹中。
edit:我还应该注意到,我的本地repo中的文件从未在任何时候提交过。它们只是保存在运行
git init
的文件夹中。
这是关键信息,这意味着一旦文件丢失,Git就无法帮助您取回文件。如果你有某种操作系统级别的文件备份(如macOS Time Machine),那就是恢复它们的方法。
使用Git之前要知道什么
在使用Git时,了解Git工作的底层模型是很重要的。细节变得非常复杂,但总体描述非常简单:Git存储提交,基本上就是这样。如果提交了,它就在Git中。如果未提交,则不在Git中1
存储库本身主要由两个数据库组成。一个数据库包含Git的对象——提交和其他支持对象。另一个保存名称,例如分支和标记名称。这两个数据库都是简单的键值存储,名称数据库存储使用名称作为键的散列ID值,对象数据库存储使用散列ID作为键的对象。.git
目录中也有一堆辅助文件,但git clone
或多或少是批量复制的对象数据库。名称数据库用于在新克隆中播种一个新的独立名称数据库,克隆包含不同的名称有相同的哈希ID。哈希ID是通用的——通过哈希算法在所有Git存储库之间共享——但名称对每个Git存储库都是唯一的2
因此,存储库由这两个数据库组成,存储在隐藏的.git
目录中。Git还在.git
目录中存储了许多额外的数据,当你使用Git时,这些数据对Git本身的重要性各不相同,但对你作为Git的用户来说,重要性相对较小。这意味着你可以说;是";.git
目录及其内容;是";这两个数据库:任何一个声明都可以,特别是如果您根据需要对其进行限定。
但是你的工作树文件呢?好吧,在我们到达那里之前,让我们注意对象数据库的另一个特性。Git使用的哈希ID系统要求对象永远不会更改。某个对象的哈希ID只是对象内容的密码校验和:这就是Git在算法上管理处处具有相同ID的技巧的方式。但这取决于";永不改变";所有物
这意味着文件的提交副本实际上不能更改。因此,Git在每次提交中都以压缩、Git化、重复数据消除和只读的方式存储每个文件的完整快照。重复数据消除处理了这样一个事实,即大多数提交与以前的一些提交大多具有相同的文件。由于它们是相同的,因此它们被消除了重复,并且根本不使用空间。对象的只读性质使这成为可能,而每次提交的完整快照性质使其成为必要,就像Ouroboros一样。
但是,这意味着您根本无法使用提交的文件。只有Git可以读取它们,实际上没有任何东西,甚至Git本身,可以写入它们。那么它们有什么好处呢?好吧,就像任何存档一样,任何给定提交中的快照都可以提取这就是您的工作树所在的位置。当您选择某个特定的提交时——例如git checkout
或git switch
:您选择了某个分支名称,而该分支名称则选择了当前"的最新提交;关于";该分支--Git将提取提交的文件。这些文件来自档案,进入您的工作树。
标准Git存储库中的工作树是运行git init
的目录(如果您以这种方式创建存储库),或者是git clone
放置新克隆的目录(如您以这种方法创建存储库的话)。隐藏的.git
文件夹存储在顶层的工作树中。存储库位于.git
目录中,但您使用的文件在工作树中这意味着你使用的文件不在Git中提交中保存之前,这些文件不会在Git中。其中一些来自Git,如果您有现有的提交并正在使用其中一个;你可以把它们拿回来,因为它们正在提交中,并且来自Git。但是,您在工作树中修改的任何文件都只是您计算机上的一个文件。它不是Git中的。如果你销毁了它,Git就无法为你取回它。
底线是,未提交的文件不在Git中。这就是为什么你应该尽早并经常做出承诺。Git可以让你找回那些文件;它无法挽回那些你从未犯下的错误。
(请注意,不要通过行动或不行动破坏.git
目录也很重要。不要将.git
文件夹放在云共享/云管理的位置,因为云共享软件往往会损坏Git的内部文件。云同步器假设人类正在处理每个文件,并且人类知道如何处理名为README.txt.cloudified-version-2
的文件等等。Git不知道该怎么办如果云软件重新命名其宝贵的数据库文件,并且在这一点上正确地认为存储库已损坏。)
1这里有一个技术问题。如果在文件上使用了git add
,则会导致文件的内容存储在Git对象数据库中,而不是文件的名称。所以有时候这种内容是可以恢复的。名称存储在Git的索引中,在重要意义上,该索引不如对象数据库条目坚固或更短暂。默认情况下,对象数据库中的实体将至少保留14天,而不考虑其他操作。同时,每次更新建议的下一次提交、运行git merge
、运行git checkout
或git switch
或其他任何操作时,索引都会不断更新。对象数据库中的实体是只读的;但是索引会被定期覆盖,以这种方式丢失的索引条目将永远丢失。
无论如何,除了偶尔可以使用git fsck --lost-found
来恢复git add
生成但从未提交的文件的内容之外,Git对未提交的文件无能为力。
2有一种标准映射,如果我克隆你的Git存储库,我的克隆的名称数据库会将你的分支名称存储为我的远程跟踪姓名。我的分支名称独立于您的分支名称,当第三人克隆我的克隆时,它们将成为另一个第三人的远程跟踪名称标记名称在默认情况下按原样复制,因此标记名称在所有这些克隆中都是通用的。作为运行git clone
和其他Git命令的人员,您负责这种映射,因此这只是一个默认标准。散列ID是通用的这一事实不是由您控制的,而是名称映射的方式。