首先,完全备份您的Git仓库!
当您使用git add
命令添加文件时,Git将把该文件的内容创建为一个blob对象并将其添加到其对象数据库中(.git/objects/??/*
)。
让我们逐个查看您的命令:
我使用git add . 添加了所有文件。
$ git add .
这将把当前目录及其子目录中包含的所有文件添加到Git的对象数据库中。与.gitignore
文件匹配的未跟踪的文件将不会被添加。树形文件也将被写入。请参阅我的回答末尾。
然后我提交了
$ git commit -m'added all files'
这将向对象数据库写入一个新的提交对象。该提交将引用单个树。该树引用了blob文件和其他树形子目录。
当我检查状态时,仍有未包括在添加提交中的文件,这很奇怪。
$ git status
我能想到两种情况会导致这种情况发生:有东西修改了你的文件或者新文件在你背后被添加了。
我重新添加了这些未跟踪的文件,这次它奏效了。
$ git add .
我假设你像步骤1中一样再次使用了相同的add
命令。
但我希望所有更改都能在一个提交中,所以查找了如何取消刚刚提交的内容。
我会在本答案结尾告诉你一个更好的方法,不需要用户执行可能危险的reset
命令。
我使用了git reset --hard HEAD^ — 显然是个坏主意,所有文件都被删除了。
$ git reset --hard HEAD^
这个命令会将你当前的工作树和索引设置为恰好位于提交 HEAD^
(倒数第二个提交)处。换句话说,它会放弃任何本地未提交的更改,并将分支指针后退一个提交。它不会影响未跟踪的文件。
然后我使用了 git reflog 找到我离开的位置
$ git reflog
这显示了最近检出的最后几个提交(与 git reflog HEAD
相同)。如果指定了分支名称,它将显示该分支最近指向的最后几个提交。
然后我使用 git reflog __ 回退到我的上一个提交。
不太确定。 git reflog
是(大多数时候)只读命令,不能用于 "回到" 提交。您只能使用它来查找分支(或 HEAD
)指向的提交。
然后我使用 git reset HEAD 取消暂存提交(我本来应该这样做),但我添加的文件(见上文)在提交之后仍然消失了。
$ git reset HEAD
这不会取消暂存此提交,但它将取消暂存索引中的所有已暂存但未提交的更改。最初(第一步),您想说 git reset HEAD^
(或 git reset --mixed HEAD^
)- 这将保持您的工作树不变,但将索引设置为与由 HEAD^
命名的提交所指向的树匹配。
现在,要取回您的文件,您必须使用 git fsck --full --unreachable --no-reflog
。它将扫描 Git 对象数据库中的所有对象,并执行可达性分析。您要查找 blob
对象。还应该有一个 tree
对象,描述了第二个 git add .
之后的状态。
git cat-file -p <object hash>
将打印文件内容,以便您可以验证是否正确。对于 blobs,您可以使用 IO 重定向将内容写入正确的文件名。对于树,您必须使用 git 命令(git read-tree
)。如果只有几个文件,最好直接将它们写入文件中。
这里有一些注意事项:
如果您想将文件添加到最后一次提交(或编辑其提交消息),只需使用 git commit --amend
即可。它基本上是 git reset --soft HEAD^ && git commit -c HEAD@{1}
的包装器。
此外,几乎从不使用 git add .
是个好习惯。通常,您只需要在创建新仓库时使用它一次。更好的选择是 git add -u
、git commit -a
,它们将暂存所有已跟踪文件的更改。要跟踪新文件,最好明确指定它们。
git fsck --full
命令,并耐心地筛选出所有它将报告的不可达 blob,这样或许会有所收获。 - knittl