我几天前从大师那里分道扬and,并对newbranch
进行了两次后续提交。
后来,我注意到我所做的第一次提交中的文件不应该被更改。我还没有向主分支推送任何东西,我的所有更改都在newbranch
。如何将newbranch
中的这个文件回滚到我最初从master分支时所处的位置?
答案 0 :(得分:2)
git checkout master -- filepath
答案 1 :(得分:1)
在new_branch上:
git checkout HEAD~1
git checkout HEAD~1 file_not_needed
git commit -a --amend
git cherry-pick new_branch
git branch -f new_branch HEAD
答案 2 :(得分:1)
根据您想要的结果,有几种不同的方法可以做到这一点。
answer from euphoria83执行此操作...
开始时,您有一个类似于此的提交历史记录(每个字母代表一个提交):
A - B - C <-- master
\
D
\
E <-- HEAD=newbranch
提交C
是您开始时掌握的内容。您做了一些更改并提交(创建D
),然后更多并提交(创建E
)。
现在你运行git checkout HEAD~1
。这会更新您的工作目录以匹配提交D,并为您提供一个“分离的HEAD”(法国大革命的阴影!),它直接指向提交D
,而不是包含分支名称:
A - B - C <-- master
\
D <-- HEAD
\
E <-- newbranch
(这种结帐形式git checkout <branch-name-or-rev>
告诉git更改HEAD
个名称。<rev>
在这种情况下,HEAD~1
,意思是“查找提交的内容名称并向后移动一个“,所以这表示从提交E
返回一个。由于这是一个特定的<rev>
而不是分支名称,它也具有”分离HEAD“效果。)< / p>
现在你运行git checkout HEAD~1 filename
。为了更明确地对git你可以说git checkout HEAD~1 -- filename
。 --
表示“其余参数是文件名,而不是分支或修订名称” - 如果省略--
,git会尝试猜测它是分支名称还是文件名。
这种checkout
形式带有-- filename
参数,它做了一些不同的事情:它表示像往常一样查找修订版HEAD~1
,但这一次,不要更改 HEAD
,只提取给定文件。由于HEAD
现在命名为修订版D
,因此git会再备份一个修订版C
,并从该修订版中提取文件filename
的版本并将其放入工作目录中
(请注意,分支名称master
也指定修订版C
,因此您此时可以编写git checkout master -- filename
。)
接下来,git commit -a --amend
告诉git添加任何已更改的文件 - 在这种情况下,您实际上并不需要它,但通常这会添加您已修复的其他文件 - 然后执行“修改提交“。 “修改提交”意味着“创建一个新提交,但使其父提交与我们的父提交相同”。这会创建一个新的commit-let,称之为D'
,其父级为C
。与往常一样,新提交成为HEAD
。由于HEAD
已分离,因此尚未移动分支名称。生成的提交树如下所示:
A - B - C <-- master
| \
\ D
\ \
\ E <-- newbranch
\
D' <-- HEAD
D'
中的文件与D
中的文件相同,但文件filename
除外,现在与修订版C
中的文件相同。
接下来,git cherry-pick newbranch
说要获取newbranch
命名的修订版 - 那是修订版E
- 并对HEAD修订版进行相同的更改,作为新提交(让我们称之为{ {1}}):
E'
现在您只需要为A - B - C <-- master
| \
\ D
\ \
\ E <-- newbranch
\
D' - E' <-- HEAD
提供分支名称。然后可以安全地删除HEAD
,或将其重命名,然后将newbranch
重命名为newnew
:
newbranch
(你可以稍微缩短一下,但让我们继续......)
有一种更简单的方法可以做同样的事情。只需运行git checkout -b newnew # now HEAD=newnew which points to E'
git branch -m newbranch oldnew # rename out of the way
git branch -m newnew newbranch # and rename newnew to newbranch
。这表示使用git rebase -i master
作为其“上游”来重新绑定当前分支(newbranch
)。也就是说,找到master
之后的所有提交(这意味着master
和D
)并将其重新绑定到E
(这是之前的位置)。如果没有master
,这将是愚蠢的变种-i
和D
,就像以前那样,这只是一种不做任何事情的昂贵方式 - 但是E
git打开了一系列命令的编辑。这些命令将-i
提交pick
和D
。将提交E
的{{1}}更改为pick
,然后写出文件。
然后,rebase将为您挑选D
并停止并允许您修改提交。在shell中,输入:
edit
和以前一样,并保存修改后的提交,后者变为D
。然后运行:
git checkout master -- filename
git commit -a --amend
Git现在将挑选提交D'
,提交提交git rebase --continue
。它现在全部完成,因此rebase完成,分支E
具有您想要的提交。
answer from ДМИТРИЙ МАЛИКОВ做了不同的事情。它包含相同的E'
(拼写为newbranch
)但没有类似rebase的序列。
所以,和以前一样,你从这开始:
git checkout master -- filename
然后,将版本filepath
中的A - B - C <-- master
\
D
\
E <-- HEAD=newbranch
版本提取到工作目录中。如果你现在filename
(或C
,在这个特例-git中同样的事情,这是因为上面的git commit
首先将提取的rev-C文件写入临时区域,你'将获得一个新的提交git commit -a
,将文件checkout
更改回F
中的方式。
换句话说,提交filename
和C
仍然会对文件进行更改;新提交D
将撤消更改。