Как я могу сбросить весь репо , т.е. все ветви вернутся в свое последнее состояние перед данным коммитом?
ТЛ; др
Предположим, у меня есть репо с четырьмя ветками: master, one, two и three:
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
Изучая рефлог, мы можем увидеть, где я ошибся и что на это повлияло:
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. Теперь я хочу откатить весь репо к тому времени.
Единственный способ, которым я мог это сделать, - это наблюдать за рефлогом, чтобы увидеть, какие ветви я загрязнил, и по отдельности откатить их в состояние до того, как они были сломаны. Поскольку этот пример искусственно прост, все эти состояния ~ 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, поэтому я перешел на хэши SHA1.
- Если вы используете хэши, git не поможет вам убедиться, что ваш коммит был в истории ветви, которую вы сбрасываете. Сброс на относительное время также был проблематичным, потому что интервалы между хорошими и плохими коммитами были очень малы.
Git: откат к более ранней версии подразумевает вышеупомянутое решение по ветвям.