如何从Git中的特定版本中检索单个文件?

我有一个Git存储库,我想看看几个月前一些文件的样子。我在那个日期找到了修订;它是27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8。我需要看看一个文件的样子,并将其保存为(“新”)文件。

我设法使用gitk查看文件,但它没有保存它的选项。我尝试使用命令行工具,最接近的是:

git-show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8 my_file.txt

但是,此命令显示的是差异,而不是文件内容。我知道我以后可以使用PAGER=cat之类的东西将输出重定向到文件,但我不知道如何获取实际的文件内容。

基本上,我正在寻找类似于svncat的东西。

472669 次浏览

您需要提供文件的完整路径:

git show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8:full/repo/path/to/my_file.txt

使用git show

为了完成你自己的答案,语法确实是

git show objectgit show $REV:$FILEgit show somebranch:from/the/root/myfile.txtgit show HEAD^^^:test/test.py

该命令采用通常的修订样式,这意味着您可以使用以下任何一种:

  1. 分支名称(建议 by灰烬
  2. HEAD+x^个字符
  3. 给定版本的SHA1哈希
  4. 给定SHA1哈希的前几个(可能是5个)字符

提示重要的是要记住,当使用“git show”时,始终指定从存储库根目录开始的路径,而不是您当前的目录位置。

(尽管迈克·莫拉蒂提到了,至少在git1.7.5.4中,您可以通过在路径的开头放置“./”来指定相对路径。例如:

git show HEAD^^:./test.py

使用git restore

使用Git 2.23+(2019年8月),您还可以使用#0替换令人困惑的#1命令

git restore -s <SHA1>     -- afilegit restore -s somebranch -- afile

这将在工作树上仅恢复来源(#0)提交SHA1或分支somebranch中存在的文件。
要恢复索引:

git restore -s <SHA1> -SW -- afile

-SW--staged --worktree的缩写)


的评论 byStarwarswi所示

它允许您将内容通过管道传输到文件中,如果您只想快速比较提交的文件,这很棒。

例如,你可以这样做:

git show 1234:path/to/file.txt > new.txtgit show 1234~:path/to/file.txt > old.txt

然后比较它们。


使用低级git管道命令

在git1.5. x之前,这是通过一些管道完成的:

git ls-tree <rev>
在提交中显示一个或多个“blob”对象的列表

git cat-file blob <file-SHA1>
cat文件,因为它已在特定版本中提交(类似于svn)猫)。使用git ls-tree检索给定file-sha1

的值
git cat-file -p $(git-ls-tree $REV $file | cut -d " " -f 3 | cut -f 1)::

git-ls-tree列出了修订版$REV$file的对象ID,这是从输出中剪切出来的,用作git-cat-file的参数,实际上应该称为git-cat-object,并简单地将该对象转储到stdout


注意:从Git 2.11(2016年第四季度)开始,您可以将内容过滤器应用于git cat-file输出。

提交3214594提交7bcf341(09 Sep 2016),提交7bcf341(09 Sep 2016)提交b9e62f6提交16dcc29(2016年8月24日)byJohannes Schindelin(#0).
(由Junio C Hamano----#0----合并于提交7889ed2,21 Sep 2016)

git config diff.txt.textconv "tr A-Za-z N-ZA-Mn-za-m <"git cat-file --textconv --batch

注意:“git cat-file --textconv”最近(2017年)开始出现分段故障,已在Git 2.15(2017年第四季度)中更正

提交cc0ea7c(2017年9月21日)by杰夫·金(#0)
(合并于提交bfbc2fc中的Junio C Hamano----#0----,2017年9月28日)

如果您希望将当前分支中的文件内容与来自先前提交或不同分支的文件内容替换/覆盖,您可以使用以下命令执行此操作:

git checkout 08618129e66127921fbfcbc205a06153c92622fe path/to/file.txt

git checkout mybranchname path/to/file.txt

然后,您必须提交这些更改,以便它们在当前分支中有效。

通过签出以前的提交和复制文件从以前的提交中获取文件。

  • 注意你在哪个分支:git分支
  • 检查你想要的上一个提交:git checkout 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8
  • 将您想要的文件复制到临时位置
  • 检查您从哪里开始的分支:git checkout theBranchYouNoted
  • 复制您放置在临时位置的文件
  • 将您的更改提交到git:git commit -m "added file ?? from previous commit"

并将其很好地转储到文件中(至少在Windows上)-Git Bash:

$ echo "`git show 60d8bdfc:src/services/LocationMonitor.java`" >> LM_60d8bdfc.java

需要"引号,因此它保留换行符。

这将帮助您在提交之间获取所有已删除的文件,而无需指定路径,如果有很多文件被删除,这很有用。

git diff --name-only --diff-filter=D $commit~1 $commit | xargs git checkout $commit~1

在Windows中,使用Git Bash:

  • 在您的工作区中,将dir更改为文件所在的文件夹
  • git show cab485c83b53d56846eb883babaaf4dff2f2cc46:./your_file.ext > old.ext

最简单的方法是写:

git show HASH:file/path/name.ext > some_new_name.ext

在哪里:

  • HASH是Git版本SHA-1哈希数
  • 文件/路径/name.ext是您要查找的文件的名称
  • some_new_name.ext是保存旧文件的路径和名称

示例

git show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8:my_file.txt > my_file.txt.OLD

这将从版本27cf8e中保存my_file.txt作为名称为my_file.txt.OLD的新文件

它使用Git 2.4.5进行了测试。

如果您想检索已删除文件,您可以使用HASH~1(在指定HASH之前提交一次)。

示例:

git show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8~1:deleted_file.txt > deleted_file.txt
git checkout {SHA1} -- filename

此命令从特定提交中获取复制的文件。

除了其他答案列出的所有选项外,您还可以将git reset与感兴趣的Git对象(哈希、分支、HEAD~x、标签……)和文件的路径一起使用:

git reset <hash> /path/to/file

在您的示例中:

git reset 27cf8e8 my_file.txt

这样做是为了将my_file.txt恢复到索引中提交27cf8e8处的版本,同时在工作目录中保持不变(因此在当前版本中)。

从那里,事情很容易:

  • 您可以将文件的两个版本与git diff --cached my_file.txt进行比较
  • 如果您决定不喜欢它,您可以使用git restore --staged file.txt(或者,在Git v2.23之前,git reset file.txt)删除旧版本的文件
  • 您可以使用git commit -m "Restore version of file.txt from 27cf8e8"git restore file.txt恢复旧版本(或者,在Git v2.23之前,git checkout -- file.txt
  • 您可以通过运行git add -p file.txt(然后是git commitgit restore file.txt)将旧版本的更新添加到新版本。

最后,如果您运行,您甚至可以交互式地选择在第一步中重置哪个大块:

git reset -p 27cf8e8 my_file.txt

因此,带有路径的git reset为您提供了很大的灵活性来检索文件的特定版本以与其当前签出的版本进行比较,如果您选择这样做,则可以完全或仅将某些块恢复到该版本。


编辑:我刚刚意识到我没有回答你的问题,因为你想要的不是一个差异或一个简单的方法来检索部分或全部旧版本,而是简单地cat那个版本。

当然,您仍然可以在使用以下命令重置文件后执行此操作:

git show :file.txt

输出到标准输出或

git show :file.txt > file_at_27cf8e8.txt

但是如果这就是你想要的,那么像其他人建议的那样,直接用git show 27cf8e8:file.txt运行git show当然要直接得多。

我将留下这个答案,因为直接运行git show可以让你立即获得旧版本,但是如果你想用它做些什么,从那里这样做并不像你在索引中重置那个版本那样方便。

使用git show $REV:$FILE,因为其他人已经指出了它可能是正确的答案。我发布另一个答案是因为当我尝试这种方法时,我有时会从git得到以下错误:

fatal: path 'link/foo' exists on disk, but not in 'HEAD'

当文件路径的一部分是符号链接时,就会出现问题。在这种情况下,git show $REV:$FILE方法将没有工作。重现步骤:

$ git init .$ mkdir test$ echo hello > test/foo$ ln -s test link$ git add .$ git commit -m "initial commit"$ cat link/foohello$ git show HEAD:link/foofatal: path 'link/foo' exists on disk, but not in 'HEAD'

问题是,像realpath这样的实用程序在这里没有帮助,因为符号链接可能不再存在于当前提交中。我不知道一个好的通用解决方案。在我的情况下,我知道符号链接只能存在于路径的第一个组件中,所以我使用git show $REV:$FILE方法两次解决了这个问题。这有效,因为当git show $REV:$FILE在符号链接上使用时,它的目标会被打印出来:

$ git show HEAD:linktest

而对于目录,该命令将输出一个标头,后跟目录内容:

$ git show HEAD:testtree HEAD:test
foo

所以在我的例子中,我只是检查了第一次调用git show $REV:$FILE的输出,如果它只是一行,那么我用结果替换了路径的第一个组件,以通过git解析符号链接。

如果您更喜欢使用GUI来执行此操作,如果您知道文件大约在何时更改,则以下操作将起作用。

在安装了Git扩展的Visual Studio上:

  1. Git->查看分支历史
  2. 右键单击文件更改的提交,然后选择查看提交详细信息
  3. 在右侧的更改列表中,单击感兴趣的文件。您将看到更改前后完整文件内容的并排比较。只需选择全部并复制文件内容并粘贴到您想要的任何地方。