如何重置整个回购,即所有分支在给定提交之前都返回其最后一个状态?
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
]]
在现实生活中,这非常容易出错:
Git: Roll back to much earlier version暗示了上述逐分支解决方案。
答案 0 :(得分:1)
您实际上是在以正确的方式进行操作:必须使用git reset
或git 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。