Отмена мерзавца
Кто-нибудь знает, как легко отменить git rebase?
Единственный способ, который приходит на ум, - это делать это вручную:
- git checkout родительский коммит для обеих веток
- затем создайте временную ветку оттуда
- Вишня все коммиты вручную
- заменить ветку, в которой я перебазирован, на созданную вручную ветку
В моей нынешней ситуации это сработает, потому что я легко могу определить коммиты из обеих веток (одна была моей работой, другая была работой моего коллеги).
Однако мой подход кажется мне неоптимальным и подверженным ошибкам (скажем, я только что перебазировал с двумя своими ветками).
Есть идеи?
Уточнение: я говорю о ребазе, во время которого была воспроизведена группа коммитов. Не только один.
22 ответа
Самым простым способом было бы найти главный коммит ветки, как это было непосредственно перед тем, как начался ребаз в reflog...
git reflog
и сбросить текущую ветку к нему (с обычными предостережениями о том, чтобы быть абсолютно уверенным перед перезагрузкой с --hard
опция).
Предположим, старый коммит был HEAD@{5}
в журнале ссылок:
git reset --hard HEAD@{5}
В Windows вам может понадобиться процитировать ссылку:
git reset --hard "HEAD@{5}"
Вы можете проверить историю старого кандидата, просто сделав git log HEAD@{5}
(Windows: git log "HEAD@{5}"
).
Если вы не отключили повторные журналы для каждой ветви, вы можете просто git reflog branchname@{1}
в качестве ребаза отделяет головку ветки перед тем, как прикрепить к последней головке. Я бы дважды проверил это, хотя я не проверял это недавно.
По умолчанию все reflogs активируются для не пустых репозиториев:
[core]
logAllRefUpdates = true
На самом деле, rebase сохраняет вашу отправную точку ORIG_HEAD
так что это обычно так просто, как:
git reset --hard ORIG_HEAD
Тем не менее reset
, rebase
а также merge
все сохранить свой оригинал HEAD
указатель на ORIG_HEAD
так что, если вы выполнили какие-либо из этих команд после перезагрузки, которую вы пытаетесь отменить, вам придется использовать reflog.
Ответ Чарльза работает, но вы можете сделать это:
git rebase --abort
убирать после reset
,
В противном случае вы можете получить сообщение "Interactive rebase already started
".
Сброс ветвления к висячему объекту фиксации его старого наконечника, конечно, является лучшим решением, поскольку он восстанавливает предыдущее состояние, не затрачивая усилий. Но если вы потеряли эти коммиты (например, из-за того, что тем временем вы собрали мусор в своем хранилище или это новый клон), вы всегда можете перебазировать ветку снова. Ключом к этому является --onto
переключатель.
Допустим, у вас была тематическая ветка topic
, что ты разветвился master
когда кончик master
был 0deadbeef
совершить. В какой-то момент на topic
ветка, ты сделал git rebase master
, Теперь вы хотите отменить это. Вот как:
git rebase --onto 0deadbeef master topic
Это примет все коммиты на topic
которые не на master
и переиграть их поверх 0deadbeef
,
С --onto
Вы можете изменить свою историю практически в любую форму.
Повеселись.:-)
git reset --hard origin/{branchName}
является правильным решением для сброса всех ваших локальных изменений, сделанных с помощью rebase.
Если вы перенесли свою ветку в удаленный репозиторий (обычно это источник), а затем выполнили успешную перезагрузку (без слияния) (git rebase --abort
выдает "No rebase in progress") вы можете легко сбросить ветку, используя команду:
git reset --hard origin / {branchName}
Пример:
$ ~/work/projects/{ProjectName} $ git status
On branch {branchName}
Your branch is ahead of 'origin/{branchName}' by 135 commits.
(use "git push" to publish your local commits)
nothing to commit, working directory clean
$ ~/work/projects/{ProjectName} $ git reset --hard origin/{branchName}
HEAD is now at 6df5719 "Commit message".
$ ~/work/projects/{ProjectName} $ git status
On branch {branchName}
Your branch is up-to-date with 'origin/{branchName}.
nothing to commit, working directory clean
Я на самом деле помещаю резервный тег в ветку перед тем, как выполнить какую-либо нетривиальную операцию (большинство ребаз это тривиально, но я бы сделал это, если это выглядит где-то сложно).
Затем восстановление так же просто, как git reset --hard BACKUP
,
Если вы еще не выполнили ребаз и в середине, работает следующее:
git rebase --abort
Я удивлен, что здесь никто еще не упомянул. Rebase покидает старое состояние как ORIG_HEAD
, так что вы можете отменить последнюю перезагрузку, выполнив:
git reset --hard ORIG_HEAD
С помощью reflog
не работал для меня
То, что сработало для меня, было похоже на описанное здесь. Откройте файл в.git/logs/refs, названном в честь перебазированной ветви, и найдите строку, содержащую "rebase finsihed", что-то вроде:
5fce6b51 88552c8f Kris Leech <me@example.com> 1329744625 +0000 rebase finished: refs/heads/integrate onto 9e460878
Оформить второй коммит, указанный в строке.
git checkout 88552c8f
После того как я подтвердил, что это содержит мои потерянные изменения, я разветвился и облегченно вздохнул.
git log
git checkout -b lost_changes
Для нескольких коммитов помните, что любой коммит ссылается на всю историю, ведущую к этому коммиту. Поэтому в ответе Чарльза прочитайте "старый коммит" как "самый новый из старых коммитов". Если вы вернетесь к этому коммиту, то снова появится вся история, ведущая к этому коммиту. Это должно делать то, что вы хотите.
Для новичков / тех, кто слишком боится сделать полный сброс, вы можете извлечь коммит из reflog, а затем сохранить его как новую ветку.
git reflog
Найти коммит незадолго до того, как вы начали ребазинг. Возможно, вам придется прокрутить дальше вниз, чтобы найти его (нажмите Enter или PageDown). Запишите номер HEAD и замените 57:
git checkout HEAD@{57}
Просмотрите ветку /commitits, если она выглядит хорошо, создайте новую ветку, используя эту HEAD:
git checkout -b new_branch_name
Если вы успешно сделали ребазинг против удаленной ветки и не можете git rebase --abort
Вы все еще можете сделать некоторые трюки, чтобы спасти свою работу и не иметь принудительных толчков. Предположим, что ваша текущая ветка, которая была перебазирована по ошибке, называется your-branch
и отслеживает origin/your-branch
git branch -m your-branch-rebased
# переименовать текущую веткуgit checkout origin/your-branch
# оформить заказ до последнего состояния, известного как источникgit checkout -b your-branch
- проверять
git log your-branch-rebased
, сравнить сgit log your-branch
и определить коммиты, которые отсутствуют вyour-branch
git cherry-pick COMMIT_HASH
за каждый коммит вyour-branch-rebased
- подтолкнуть ваши изменения. Обратите внимание, что два местных филиала связаны с
remote/your-branch
и ты должен толкать толькоyour-branch
Следуя решению @Allan и @Zearin, я хотел бы просто сделать комментарий, но мне не хватает репутации, поэтому я использовал следующую команду:
Вместо того чтобы делать git rebase -i --abort
(обратите внимание на -i) Я должен был просто сделать git rebase --abort
(без -i).
Используя оба -i
а также --abort
в то же время заставляет Git показывать мне список использования / опций.
Итак, мой предыдущий и текущий статус ветки с этим решением:
matbhz@myPc /my/project/environment (branch-123|REBASE-i)
$ git rebase --abort
matbhz@myPc /my/project/environment (branch-123)
$
Другой способ , который не требует аппаратного сброса, - это создать новую ветку с желаемой начальной точкой.
Как и в случае с другими решениями, вы используете журнал ссылок, чтобы найти правильную отправную точку.
git reflog
(вы также можете использовать
git log -g
здесь для более подробной информации)
Затем вы отмечаете ссылку на фиксацию SHA (например:
e86a52b851e
).
Наконец, вы используете команду git branch.
git branch recover-branch e86a52b851e
Ссылка: https://git-scm.com/book/en/v2/Git-Internals-Main maintenance-and-Data-Recovery#_data_recovery
Если вы находитесь на ветке, вы можете использовать:
git reset --hard @{1}
Существует не только справочный журнал для HEAD (получен git reflog
), также есть reflogs для каждой ветви (полученные git reflog <branch>
). Итак, если вы на master
затем git reflog master
перечислит все изменения в этой ветке. Вы можете сослаться на эти изменения по master@{1}
, master@{2}
, так далее.
git rebase
обычно меняют HEAD несколько раз, но текущая ветка будет обновляться только один раз.
@{1}
просто ярлык для текущей ветви, так что он равен master@{1}
если вы на master
,
git reset --hard ORIG_HEAD
не будет работать, если вы использовали git reset
во время интерактивного rebase
,
Допустим, я перебазирую master в свою ветку Feature и получаю 30 новых коммитов, которые что-то ломают. Я обнаружил, что часто проще всего удалить плохие коммиты.
git rebase -i HEAD~31
Интерактивная перебазировка за последние 31 коммитов (не повредит, если вы выберете слишком много).
Просто возьмите коммиты, от которых вы хотите избавиться, и пометьте их "d" вместо "pick". Теперь коммиты эффективно удаляются, отменяя перебазирование (если вы удаляете только коммиты, которые вы только что получили при перебазировании).
Меня безмерно раздражает, что ни один из этих ответов не является полностью автоматическим, несмотря на то, что он должен быть автоматическим (по крайней мере, в большинстве случаев). Я создал набор псевдонимов, чтобы попытаться исправить это:
# Useful commands
#################
# Undo the last rebase
undo-rebase = "! f() { : git reset ; PREV_COMMIT=`git x-rev-before-rebase` && git reset --merge \"$PREV_COMMIT\" \"$@\";}; f"
# See what changed since the last rebase
rdiff = "!f() { : git diff ; git diff `git x-rev-before-rebase` "$@";}; f"
# Helpers
########
# Get the revision before the last rebase started
x-rev-before-rebase = !git reflog --skip=1 -1 \"`git x-start-of-rebase`\" --format=\"%gD\"
# Get the revision that started the rebase
x-start-of-rebase = reflog --grep-reflog '^rebase (start)' -1 --format="%gD"
У вас должна быть возможность настроить это, чтобы можно было довольно легко вернуться к произвольному количеству перемещений (жонглирование аргументами - самая сложная часть), что может быть полезно, если вы выполняете несколько перемещений в быстрой последовательности и на этом пути что-то испортили.
Предостережения
Он запутается, если какие-либо сообщения фиксации начинаются с "rebase (start)" (пожалуйста, не делайте этого). Вы можете сделать регулярное выражение более устойчивым, чтобы улучшить ситуацию, сопоставив что-то вроде этого для вашего регулярного выражения:
--grep-reflog "^rebase (start): checkout "
ВНИМАНИЕ: не проверено (может потребоваться корректировка регулярного выражения)
Причина, по которой я этого не сделал, состоит в том, что я не на 100% уверен, что перебазирование всегда начинается с оформления заказа. Кто-нибудь может это подтвердить?
[Если вас интересует значение null (:
) в начале функции, это способ настройки завершения bash для псевдонимов]
Я обычно делаю
git reset #commit_hash
до последней фиксации, где я думаю, что перебазирование не повлияло.
тогда git pull
Теперь ваша ветка должна совпадать с master, и в ней не должно быть перебазированных коммитов.
Теперь можно просто выбрать коммиты в этой ветке.
Я пробовал все предложения со сбросом и рефлогом безуспешно. Восстановление локальной истории IntelliJ решило проблему потерянных файлов
Если вы что-то испортили в git rebase, например git rebase --abort
пока у вас есть незафиксированные файлы, они будут потеряны и git reflog
не поможет Это случилось со мной, и вам нужно будет думать нестандартно здесь. Если вам повезло, как и мне, и вы используете IntelliJ Webstorm, тогда вы можете right-click->local history
и может вернуться к предыдущему состоянию вашего файла / папки, независимо от того, какие ошибки вы сделали с версионным программным обеспечением. Всегда хорошо иметь другой отказоустойчивый запуск.
Для отмены вы можете ввести следующую команду:
git -c core.quotepath=false rebase --abort