【问题标题】:How to check the commit id of the checkout file in Git?如何在 Git 中检查签出文件的提交 ID?
【发布时间】:2017-04-24 20:00:21
【问题描述】:

假设我的存储库中有三个文件 a.txt 提交:

>git log -- a.txt
commit 63cfed7cebe84009aa4fba6e4a62ea5b0bec7660
commit 874028c921f22e0c3e44d0faf13eef2b7638d3ff
commit 0a89b0751afdb111ba775922e85bbac6302f727c

然后我发出命令

>git checkout 874028c921f22e0c3e44d0faf13eef2b7638d3ff  a.txt 

将文件 a.txt 检出回 v1。

假设稍后我忘记了从哪个提交中签出了 a.txt 的工作版本。有没有我可以用来确定该信息的命令?

我尝试了以下方法:

>git log --a.txt

它显示了这个文件的所有提交日志。

>git status 
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
      modified:   a.txt

但所有这些都不是我想要的。我需要查看当前版本的a.txt。那么我可以使用什么cmd来确定当前a.txt的commit_id?

【问题讨论】:

  • git log --stat?

标签: git version commit


【解决方案1】:

使用 git hash-object,您可以找到工作树中的 a.txt 的 SHA1。

git hash-object a.txt

然后搜索所有 git 提交以找到特定对象 SHA1

for rev in $(git rev-list --all); do git ls-tree -r $rev | grep -q <SHA1> && echo $rev ; done

这将为您提供当前版本的 a.txt 的提交列表。此结果列表中的最后一行将是您当前版本的 a.txt 的第一个提交

【讨论】:

    【解决方案2】:

    考虑到你的条件,是的,不难:

    find=$(git rev-parse :a.txt)   # as staged i.e. last checked out or added
    for commit in $(git rev-list @ -- a.txt); do 
            [[ $(git rev-parse $commit:a.txt) = $find ]] && { echo $commit; break; }
    done
    

    将打印引入您正在寻找的版本的提交。

    正如@torek 指出的那样,(a) 索引记录了您上次在该路径上签出或添加(或使用某些核心命令设置)的任何内容的 id,因此执行其他任何操作都意味着您无法搜索以前有什么;并且 (b) 在您的示例中,至少 874028..63cfed~ 范围内的每个提交也具有相同的 a.txt 内容,因此您也可以从其中的任何一个中检查出来。但是,如果您确实做了所示的操作并且在一些不能只是 ^Rcheckout 或以其他方式轻松调用/编辑命令的愚蠢环境中操作,您可以使用上面的 sn-p。

    【讨论】:

      【解决方案3】:

      假设稍后我忘记了从哪个提交中签出了 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 存储库有一个提交,其中包含两个文件,READMEfile.txt。您的索引中还有两个文件,READMEfile.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 将 &lt;hash&gt; 部分转换为 tree 散列,而不是 commit 散列(实际上你可以只给它一个树对象的散列 ID )。因此,它不一定首先具有特定的提交哈希。这也是帮助/文档中的语法为:

      git checkout [-p|--patch] [&lt;tree-ish&gt;] [--] [&lt;paths&gt;...]

      &lt;tree-ish&gt; 表示 Git 根本不需要提交 ID;如果你给它一个,它会找到对应的&lt;tree&gt; 对象(可能在几个或多个提交之间共享)。

      综上所述,如果您愿意,可以找到存储在索引中的某个路径名的哈希 ID:

      git ls-files --stage
      

      全部显示,您可以使用git rev-parse 查找个别的。然后,如果愿意,您可以搜索部分或全部提交,找到它们的树对象,读取这些树,并在它们中搜索该哈希。如果您在某些树或树中找到哈希,则可以说文件的索引版本出现在这些路径名下的那些提交中。

      因此,给定上述存储库索引中 README.md 的哈希值,我们可以搜索所有三个提交,读取它们的树,并发现当前索引中的 README.md 与存储为 @ 的内容相匹配两次提交中的 987654346@ 和当前(HEAD)提交中的 README.md

      【讨论】:

      • 感谢您的详细解释。
      • @buddha:我添加了一个指向 jthill 答案的链接,这可能符合您的目的(很难说,因为不清楚您的目的可能是什么)。
      猜你喜欢
      • 1970-01-01
      • 2016-07-30
      • 1970-01-01
      • 2017-12-19
      • 2018-09-20
      • 1970-01-01
      • 1970-01-01
      • 2017-05-09
      • 1970-01-01
      相关资源
      最近更新 更多