如何重置“整个回购”?

时间:2019-01-16 22:08:15

标签: git

如何重置整个回购,即所有分支在给定提交之前都返回其最后一个状态?

tl; dr

假设我有一个带有四个分支的仓库:master,一个,两个和三个:

git init
echo a > a.txt && git add . && git commit -m "added a"
git checkout -b one
echo b > b.txt && git add . && git commit -m "added b"
git checkout master
git checkout -b two
echo c > c.txt && git add . && git commit -m "added c"
git checkout -b three
echo d > d.txt && git add . && git commit -m "added d"

这是我的树(由于简单,三个只是两个的快进):

git checkout master
git --no-pager log --graph --oneline --decorate --all
* a7457d6 (one) added b
| * a7e62c6 (three) added d
| * c84c43b (two) added c
|/  
* 6ec61a1 (HEAD -> master) added a

然后我犯了一些错误,后来想删除,例如误合并分支。

git merge --strategy=ours one -m "accidentally lose ability to merge one"

我不经意间将其传播到其中一些分支(两个和三个):

git checkout two
git merge master -m "propagating problem to two"
git checkout three
git merge master -m "propagating problem to three"

树现在看起来像:

git checkout master
git --no-pager log --graph --oneline --decorate --all
*   83448b1 (three) propagating problem to three
|\  
* | a7e62c6 added d
| | *   f33efb4 (two) propagating problem to two
| | |\  
| |/ /  
|/| /   
| |/    
| *   69bee42 (HEAD -> master) accidentally lose ability to merge one
| |\  
| | * a7457d6 (one) added b
| |/  
* | c84c43b added c
|/  
* 6ec61a1 added a

检查reflog,我们可以看到我哪里出了问题以及受到的影响:

git --no-pager reflog --oneline --all
69bee42 HEAD@{6}: checkout: moving from three to master
83448b1 refs/heads/three@{1}: merge master: Merge made by the 'recursive' strategy.
83448b1 HEAD@{7}: merge master: Merge made by the 'recursive' strategy.
a7e62c6 (three) HEAD@{8}: checkout: moving from master to three
69bee42 HEAD@{9}: checkout: moving from two to master
f33efb4 refs/heads/two@{1}: merge master: Merge made by the 'recursive' strategy.
f33efb4 HEAD@{10}: merge master: Merge made by the 'recursive' strategy.
c84c43b (two) HEAD@{11}: checkout: moving from master to two
69bee42 refs/heads/master@{1}: merge one: Merge made by the 'ours' strategy.
69bee42 HEAD@{12}: merge one: Merge made by the 'ours' strategy.
6ec61a1 (HEAD -> master) HEAD@{13}: checkout: moving from three to master
6ec61a1 (HEAD -> master) refs/heads/master@{2}: commit (initial): added a
a7457d6 (one) refs/heads/one@{0}: commit: added b
6ec61a1 (HEAD -> master) refs/heads/one@{1}: branch: Created from HEAD
a7e62c6 (three) refs/heads/three@{2}: commit: added d
c84c43b (two) refs/heads/three@{3}: branch: Created from HEAD
c84c43b (two) refs/heads/two@{2}: commit: added c
6ec61a1 (HEAD -> master) refs/heads/two@{3}: branch: Created from HEAD
a7e62c6 (three) HEAD@{14}: commit: added d
c84c43b (two) HEAD@{15}: checkout: moving from two to three
c84c43b (two) HEAD@{16}: commit: added c
6ec61a1 (HEAD -> master) HEAD@{17}: checkout: moving from master to two
6ec61a1 (HEAD -> master) HEAD@{18}: checkout: moving from one to master
a7457d6 (one) HEAD@{19}: commit: added b
6ec61a1 (HEAD -> master) HEAD@{20}: checkout: moving from master to one
6ec61a1 (HEAD -> master) HEAD@{21}: commit (initial): added a

所以我回头看看我的错误是在(第一个)69bee42。现在,我想回滚整个时间段的整个仓库。

我能看到的唯一方法是盯着reflog,看看我污染了哪些树枝,然后将其单独打回到折断之前的状态。因为此示例是人为简单的,所以这些状态均为〜1:

git checkout master
git reset --hard master~1
git checkout two
git reset --hard two~1
git checkout three
git reset --hard three~1

[[编辑:从我不需要需要更新的分支中branch -f更加优雅:

git checkout one
git branch -f master master~1
git branch -f two two~1
git branch -f three{,~1} # get cute with bash {} expansion

]]

在现实生活中,这非常容易出错:

  1. 由于一些额外的合并,〜1处什么也没有,所以我重置为SHA1哈希。
  2. 如果您使用哈希,则git不能帮助您确保提交的内容位于您要重置的分支的历史记录中。重置为相对时间也是有问题的,因为好的提交和错误的提交之间的间隔非常小。

Git: Roll back to much earlier version暗示了上述逐分支解决方案。

1 个答案:

答案 0 :(得分:1)

您实际上是在以正确的方式进行操作:必须使用git resetgit branch -f来强制每个分支名称指向所需的提交,并且找到该提交的位置在适当的位置分支的引用日志。 (我建议尽可能多地使用git branch -f。无法用git branch -f修复的唯一分支是 current 分支,无论是什么分支;在那里,您必须使用{{1 }},可能使用git reset-包含所有--hard的含义。)

您可以使用一些快捷方式,即reflog语法允许使用git reset --hard,例如name@{when}master@{1.hour.ago}three@{yesterday}模式可转换为如果您在很早以前运行该名称便会产生的任何哈希ID。您也可以使用绝对时间。 (但是您在回答中指出,这里的时间窗口不是很大,因此并不是那么有用。)

要查看带有日期的现有reflog,请使用@{relative time offset}git reflog --date=local(还有更多选项;请参见the documentation

相关问题