假设稍后我忘记了从哪个提交中签出了 a.txt 的工作版本。有没有我可以用来确定该信息的命令?
否(但请参阅jthill's answer 以相对快速的方法来搜索具有与索引版本匹配的同名文件的 some 提交;这可能足够接近)。这有几个原因:
- Git 确实会跟踪您的当前提交。这只是
HEAD。但它不会跟踪每个工作树文件的一次提交。
- 就此而言,工作树文件一开始就很难跟踪。程序、编辑或人员在不断地改变它们。
- 同时,对于从某个提交中提取的每个文件,Git 在您的 index 中都有该文件的“原始副本”。您的索引,简而言之,是您构建下一个提交的位置。如果您修改了工作树中的文件并希望 new 版本进入您的下一次提交,您可以
git add 将该文件复制到索引中。否则,索引仍保留旧版本。
索引中的文件版本确实有一个哈希 ID,类似于提交哈希 ID。 (事实上,提交和文件,或 blob,Git 中的对象的哈希 ID 计算方式相同。Git 的另外两种内部对象类型也是如此。) commit 对于该提交是唯一的,文件版本 的哈希 ID 不是对于一个特定的提交是唯一的。
例如,考虑序列:
mkdir tmp && cd tmp && git init
(现在我们有一个新的空临时目录来保存新的存储库)
echo a README > README
git add README
echo initial version > file.txt
git add file.txt
git commit -m initial
这个新的 Git 存储库有一个提交,其中包含两个文件,README 和 file.txt。您的索引中还有两个文件,README 和 file.txt。现在我们修改file.txt并提交新的:
echo more data >> file.txt
git add file.txt
git commit -m 'added more'
您的存储库现在有 两个 提交。第二次提交与第一次提交有 same README 文件。该文件仅存储一次,在其“blob”哈希下,并且两个不同的提交(和索引)都使用 same 哈希 ID 来表示“该数据的那个版本”。
事实上,如果我们现在重命名README文件:
git mv README README.md
git commit -m 'rename README'
我们现在有 三个 提交,并且所有三个(和索引)共享一个 blob 对象版本。最新的提交调用该 blob 对象 README.md,而较早的两个提交调用它 README,但所有三个具有相同的哈希 ID。
有一个次要的技术原因,Git 不能存储提交的哈希 ID,从该提交的一些 blob-hash 的索引版本中提取:当你这样做时:
git checkout <hash> -- <path>
Git 将 <hash> 部分转换为 tree 散列,而不是 commit 散列(实际上你可以只给它一个树对象的散列 ID )。因此,它不一定首先具有特定的提交哈希。这也是帮助/文档中的语法为:
git checkout [-p|--patch] [<tree-ish>] [--] [<paths>...]
<tree-ish> 表示 Git 根本不需要提交 ID;如果你给它一个,它会找到对应的<tree> 对象(可能在几个或多个提交之间共享)。
综上所述,如果您愿意,可以找到存储在索引中的某个路径名的哈希 ID:
git ls-files --stage
全部显示,您可以使用git rev-parse 查找个别的。然后,如果愿意,您可以搜索部分或全部提交,找到它们的树对象,读取这些树,并在它们中搜索该哈希。如果您在某些树或树中找到哈希,则可以说文件的索引版本出现在这些路径名下的那些提交中。
因此,给定上述存储库索引中 README.md 的哈希值,我们可以搜索所有三个提交,读取它们的树,并发现当前索引中的 README.md 与存储为 @ 的内容相匹配两次提交中的 987654346@ 和当前(HEAD)提交中的 README.md。