简体   繁体   English

Git 恢复到对特定文件的多次提交

[英]Git revert back to multiple commits on specific files

I have a few files that I have already pushed up publically but only afterwards did I realize that I had some files that say they were changed because of an extra space or some minor change.我有一些文件已经公开推送,但直到后来我才意识到我有一些文件说它们因为额外的空间或一些小改动而被更改。 I want to revert those files back to a few commits ago, but only those specific files.我想将这些文件恢复到几次提交之前,但仅限于那些特定文件。

I've tried git revert <commithash>~4 -- path/to/filename1 path/to/filename2 , where the 4 is for 4 commits before this current one but it didn't seem to work: the changes I was hoping to see go away didn't go away on my files.我试过git revert <commithash>~4 -- path/to/filename1 path/to/filename2 ,其中 4 是在当前提交之前的 4 次提交,但它似乎不起作用:我希望进行的更改see go away 并没有在我的文件中消失。

The commits are currently only on my branch and haven't been merged with any others.提交目前仅在我的分支上,尚未与任何其他分支合并。 I didn't want to revert the entire commit, but only some files from this bad commit which is where I am running into trouble.我不想恢复整个提交,但只想恢复这个错误提交中的一些文件,这就是我遇到麻烦的地方。

I think you will have to undo the commit(s) where you pushed erroneously the wrong files (using git reset ) and then perform a new commit.我认为您将不得不撤消错误推送错误文件的提交(使用git reset ),然后执行新的提交。 A commit is like a collection of project changes and has to be treated as a unit , thus single file changes are not regarded independently from the rest of the commit.一次提交就像一个项目更改的集合,必须被视为一个单元,因此单个文件的更改不会独立于提交的其余部分。 (Edit: This will only help you as long as your commits have not been merged yet.) (编辑:只要您的提交尚未合并,这只会对您有所帮助。)

Anyways, there is a way to revert only special files that have been added in a commit.无论如何,有一种方法可以仅还原已添加到提交中的特殊文件。 The key word here is Cherry Picking which "appl[ies] the change[s] each [commit] introduces, recording a new commit for each".这里的关键词是樱桃采摘,它“应用每个[提交]引入的更改[s],为每个记录一个新的提交”。

TL;DR TL; 博士

You probably ( probably , this can get complicated) want a particular mode of git checkout or, in Git 2.23 or later, git restore , here.您可能(可能,这可能会变得复杂)想要一种特定的git checkout模式,或者,在 Git 2.23 或更高版本中, git restore ,在这里。 You can extract particular files from particular commits this way, without actually changing commits.您可以通过这种方式从特定提交中提取特定文件,而无需实际更改提交。 The details get a little sticky, although the new git restore probably does exactly what you want, right out of the box, as it were:细节有点棘手,尽管新的git restore可能完全符合您的要求,开箱即用,就像它一样:

git restore <commit-hash> -- path/to/filename1 path/to/filename2

Long

Each commit in a Git repository is (or holds, really) a snapshot of all of your files. Git 存储库中的每个提交都是(或实际上持有)所有文件的快照

When you use:当您使用:

git checkout <commit-hash>

(or since Git 2.23, the same thing with git switch ), you ask Git to switch to the given commit as a detached HEAD . (或自 Git 2.23 起,与git switch相同),您要求 Git 切换到给定提交作为分离的 HEAD This reads the chosen commit into the index—the index, or staging area, holds your proposed next commit so switching to a commit requires filling it in from that commit—and in the process, adjusts the contents of your work-tree to match the commit you've switched-to.这会将选定的提交读入索引——索引或暂存区保存您提议的下一次提交,因此切换到提交需要从该提交中填充它——并且在此过程中,调整您的工作树的内容以匹配承诺你已经切换到。

When you use git checkout branch-name ( or again git swtich ), Git does the same thing, except that now the special name HEAD is attached to the branch name.当您使用git checkout branch-name (或再次使用git swtich )时,Git 做同样的事情,除了现在特殊名称HEAD附加到分支名称。 Git now remembers which branch you're on, and making a new commit will write the new commit's hash ID into that branch name. Git 现在会记住您所在的分支,并且进行新提交会将新提交的哈希 ID 写入该分支名称。

Well, that's all well and good, but now you want some particular file out of one particular commit, without switching commits at all.嗯,这一切都很好,但是现在您希望从一个特定的提交中提取一些特定的文件,而根本不需要切换提交。 This is where Git 2.23 and later are better, because there's a separate command for this, git restore .这是 Git 2.23 及更高版本更好的地方,因为有一个单独的命令git restore We'll get to that in a moment, but first let's talk more about git checkout .我们稍后会讨论这个问题,但首先让我们更多地讨论git checkout

The git checkout command is absurdly complex. git checkout命令极其复杂。 It has, depending on how you count, something like four to seven different modes of operation.根据您的计数方式,它有四到七种不同的操作模式。 The one we care about here is the one that checks out particular files from particular commits .我们在这里关心的是从特定提交中检出特定文件的那个。 That is, we want:也就是说,我们想要:

git checkout <commit-hash> -- <paths>

It's important to remember Git's index here, because this kind of git checkout first writes the files to the index .在这里记住 Git 的索引很重要,因为这种git checkout首先将文件写入索引 The paths argument you give, after the -- , determines which files come out of the chosen commit.您在--之后给出的paths参数确定哪些文件来自所选提交。 The -- itself is only there to make sure that these file names don't look like flags or branch names or whatever. --本身只是为了确保这些文件名看起来不像标志或分支名称或其他任何东西。 The commit-hash part can be anything acceptable to git rev-parse , as long as it names a commit or tree object internally; commit-hash部分可以是git rev-parse可接受的任何内容,只要它在内部命名提交或树对象; a commit hash is good here.提交哈希在这里很好。

Having copied the file(s) you named from the commit you named into the index, this git checkout goes on to write the files into your work-tree.将您从您命名的提交中命名的文件复制到索引中后,此git checkout继续将文件写入您的工作树。 Note that unlike a regular, safe git checkout , this particular mode will destroy the current contents of the named files even if they are not saved anywhere else.请注意,与常规、安全的git checkout ,这种特定模式将破坏命名文件的当前内容,即使它们没有保存在其他任何地方。 So if you have used git checkout a lot and are happy with the fact that it will tell you: no, I can't do that, I'll lose some files you might have forgotten to save , just remember that this form of git checkout is quite ruthless.因此,如果您经常使用git checkout并且对它会告诉您的事实感到满意:不,我不能这样做,我会丢失一些您可能忘记保存的文件,请记住这种形式的git checkout很无情。

In Git 2.23 and later, there is a separate command, git restore , that takes over this job.在 Git 2.23 及更高版本中,有一个单独的命令git restore来接管这项工作。 The new restore command can copy the file separately to the index, or to your work-tree, or both.新的restore命令可以将文件单独复制到索引,或复制到您的工作树,或两者兼而有之。 The default is just to copy to the work-tree, without affecting the index (and also without checking whether you've saved the work-tree file anywhere, so be careful with it!).默认值只是复制到工作树,而不影响索引(也不检查您是否已将工作树文件保存在任何地方,所以要小心!)。 So:所以:

git restore <commit-hash> -- path/to/filename1 path/to/filename2

will copy those files from that commit into your work-tree.将从该提交中复制这些文件到您的工作树中。 The updated files won't be staged for commit, as your index has not been altered at all.更新的文件不会暂存以进行提交,因为您的索引根本没有更改。 (As before, the -- is just in case a file name is something weird like --staged . If it's not anything like this, you can omit the -- . It's a good habit to get into though.) If everything looks good, you can git add and git commit as usual. (和以前一样, --只是为了防止文件名像--staged一样--staged 。如果不是这样的,你可以省略-- 。尽管如此,这是一个好习惯。)如果一切看起来都不错,你可以像往常一样git addgit commit

(If, after this, you want to use interactive rebase, or a soft reset and commit, to squash away some of the extra intermediate commits, that's a topic for another question—there are already a lot of answers to those questions.) (如果在此之后,您想使用交互式 rebase,或软重置和提交,来消除一些额外的中间提交,这是另一个问题的主题——这些问题已经有很多答案了。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM