将单个文件还原为git中的先前版本

geo*_*uor 585 git revert

有没有办法在文件上进行不同的提交.假设我已经修改了一个文件5次,并且在我已经提交并推送到存储库之后我想回到更改2.

在我的理解中,唯一的方法是保留许多分支,我做对了吗?如果我是对的,我会在几天内拥有数百个分支,所以我可能真的不理解它.

有人可以清楚吗?

Cas*_*bel 829

让我们从对我们想要做的事情的定性描述开始(Ben Straub的回答中说明了这一点).我们已经进行了一些提交,其中五个更改了给定文件,我们希望将文件还原为以前的版本之一.首先,git不保留单个文件的版本号.它只跟踪内容 - 提交本质上是工作树的快照,以及一些元数据(例如提交消息).因此,我们必须知道哪个提交具有我们想要的文件版本.一旦我们知道了,我们就需要进行一次新的提交,将文件恢复到该状态.(我们不能只是沉迷于历史,因为我们已经推动了这些内容,并且编辑历史与其他人混淆.)

所以让我们从找到正确的提交开始吧.您可以很容易地看到对给定文件进行修改的提交:

git log path/to/file
Run Code Online (Sandbox Code Playgroud)

如果您的提交消息不够好,并且您需要查看每次提交对文件执行的操作,请使用以下-p/--patch选项:

git log -p path/to/file
Run Code Online (Sandbox Code Playgroud)

或者,如果您更喜欢gitk的图形视图

gitk path/to/file
Run Code Online (Sandbox Code Playgroud)

一旦你通过视图菜单启动了gitk,你也可以这样做; 视图的一个选项是要包含的路径列表.

无论哪种方式,您都可以使用所需文件的版本找到提交的SHA1(哈希).现在,你所要做的就是:

# get the version of the file from the given commit
git checkout <commit> path/to/file
# and commit this modification
git commit
Run Code Online (Sandbox Code Playgroud)

(checkout命令首先将文件读入索引,然后将其复制到工作树中,因此无需使用git add它将其添加到索引中以准备提交.)

如果您的文件可能没有简单的历史记录(例如重命名和复制),请参阅VonC的优秀评论.git可以指导更仔细地搜索这些东西,但要牺牲速度.如果你对历史的简单有信心,你就不必费心了.

  • 你不应该使用`-C`或`--find-copies-harder`来检测重命名*和副本*,加上`--follow`继续列出文件的历史记录而不是重命名吗?鉴于您对*文件内容*的回答强调,通过提及其自然结果来完成它可能是好的:**推理**方面(请参阅http://stackoverflow.com/questions/612580/how-does- GIT-解决最合并-问题/ 612747#612747) (11认同)
  • 很棒的解释。你不仅完美地回答了这个问题,还加深了我的理解。我以前从未见过`gitk` - 太棒了! (3认同)
  • 到了这一点!谢谢!! (2认同)
  • `git checkout head~3 filename` 会不起作用吗? (2认同)
  • @EdRandall 当然,`HEAD~3` 会起作用,如果你想要的提交是 HEAD 之前的三。一般来说,你可能并不完全自信,所以我介绍了如何找出你真正想要的提交。 (2认同)

Jim*_*rne 74

Git非常灵活.您不应该需要数百个分支来完成您的要求.如果要将状态一直恢复到第二次更改(并且确实是已经提交并推送的更改),请使用git revert.就像是:

git revert a4r9593432 
Run Code Online (Sandbox Code Playgroud)

其中a4r9593432是您要退出的提交哈希的起始字符.

如果提交包含对许多文件的更改,但您只想恢复其中一个文件,则可以使用git reset(第2或第3个表单):

git reset a4r9593432 -- path/to/file.txt
# the reverted state is added to the staging area, ready for commit
git diff --cached path/to/file.txt        # view the changes
git commit
git checkout HEAD path/to/file.txt        # make the working tree match HEAD           
Run Code Online (Sandbox Code Playgroud)

但这非常复杂,git reset很危险.使用git checkout <hash> <file path>替代,如Jefromi建议.

如果您只想在commit x中查看文件的外观,可以使用git show:

git show a4r9593432:path/to/file.txt
Run Code Online (Sandbox Code Playgroud)

对于所有命令,除了通过提交哈希之外,还有很多方法可以引用提交(请参阅Git用户手册中的命名提交).

  • 另外,`git revert`不是针对一个特定文件,而是针对整个存储库. (6认同)
  • "git revert"不会只是退出*一个*改变 - 不是"将状态一直恢复到第二次改变"吗? (5认同)
  • 我们可以把蛋糕吃下来吗?是否可以从单个文件还原单个提交并保留其他将来的更改? (2认同)

Ben*_*aub 7

Git没有考虑文件版本.git中的版本是整个树的快照.

鉴于此,你真正想要的是一个具有大多数文件的最新内容的树,但是一个文件的内容与之前的5个提交相同.这将采用旧提交之上的新提交的形式,并且最新版本的树将具有您想要的.

我不知道是否有一个单行将单个文件还原为5个提交前的内容,但是低保真解决方案应该有效:结账master~5,将文件复制到其他地方,结账master,复制文件,然后承诺.

  • 您要求的单行程是在单个文件而不是整个工作树上结帐; 看到我的回答. (2认同)

Fel*_*ipe 7

从这里提取:http : //git.661346.n2.nabble.com/Revert-a-single-commit-in-a-single-file-td6064050.html

 git revert <commit> 
 git reset 
 git add <path> 
 git commit ... 
 git reset --hard # making sure you didn't have uncommited changes earlier 
Run Code Online (Sandbox Code Playgroud)

它对我来说非常好。


Ale*_*ski 5

您可以采取一个差异来撤消您想要的更改并提交它.

例如,如果要撤消范围中的更改from..to,请执行以下操作

git diff to..from > foo.diff  # get a reverse diff
patch < foo.diff
git commit -a -m "Undid changes from..to".
Run Code Online (Sandbox Code Playgroud)

  • @Jefromi绝对有理由使用补丁,因为你的答案不等于revert:它将删除自犯罪提交以来的所有更改,而不是像`git revert`那样撤消那个提交的更改.特定于文件和提交的补丁是更精确和语义正确的更改. (3认同)
  • 一些问题.如果你想创建一个补丁,它需要只是有问题的文件的补丁(`git diff to..from path/to/file`).要应用补丁,您应该使用`git apply`而不是补丁.在这种情况下,根本不需要使用补丁; 看到我的回答. (2认同)
  • @Jefromi 这个答案与你的答案不一样。此答案恢复一次提交。您的答案会恢复一个文件。这恰好正是我所需要的。 (2认同)