Отмена мерзавца

3182

Как я могу легко отменить git rebase?

Мои текущие идеи - только ручные подходы:

  1. git checkout на коммит родитель для обеих ветвей
  2. Создайте временную ветку оттуда
  3. git cherry-pick все совершает вручную
  4. заменить ветку, в которой я перебазировал ветку, созданную вручную

В моей нынешней ситуации это сработает, потому что я легко могу определить коммиты из обеих веток (одна была моей, другая - моей коллегой).

Однако мой подход кажется мне неоптимальным и подверженным ошибкам (скажем, я только что перебазировал 2 моих собственных ветки).

Пояснение : я говорю о ребазе, во время которого была воспроизведена группа коммитов. Не только один.

webmat
источник
6
Также обратите внимание, что во время перебазировки вы можете исключить коммиты или раздавить их; эти изменения не могут быть обратимыми без указания указателя на исходные узлы или просеивания через reflog, поэтому вишня не будет работать.
Ноябрь

Ответы:

4339

Самый простой способ - найти главный коммит ветки, как это было непосредственно перед тем, как в 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
CB Bailey
источник
115
Git reflog великолепен, просто помните, что вы можете получить лучший форматированный вывод с помощью git log -g(совет от Скотта Чакона progit.org/book ).
Карми
60
@Zach: git rebase --abort( -iне имеет смысла --abort) для отказа от ребазинга, который не был завершен - либо потому, что были конфликты, либо потому, что он был интерактивным, либо и то, и другое; речь идет не об отмене успешного ребазинга, о котором идет речь. Вы должны либо использовать, rebase --abortлибо в reset --hardзависимости от того, в какой ситуации вы оказались. Вам не нужно делать то и другое.
CB Bailey
311
На всякий случай, сделайте резервную копию первого: git tag BACKUP. Вы можете вернуться к нему, если что-то пойдет не так:git reset --hard BACKUP
Колыпто
6
Если вы сделали много коммитов, то HEAD @ {#}, который вы ищете, будет иметь префикс commit:перед, а не rebase:. Звучит очевидно, но это немного смутило меня.
Деформация
4
Вступление в партию после случайной перебазировки: D. Разве не удастся git reset --hard ORIG_HEADсделать то же самое сразу после случайной перезагрузки?
Quaylar
1488

На самом деле, rebase сохраняет вашу отправную точку, ORIG_HEADпоэтому обычно это так просто:

git reset --hard ORIG_HEAD

Однако reset, rebaseи mergeвсе , кроме исходного HEADуказателя в ORIG_HEADтак, если вы сделали какие - либо из этих команд с Rebase вы пытаетесь отменить , то вы должны будете использовать reflog.

Пэт Нотц
источник
34
В случае, если ORIG_HEADон больше не используется, вы также можете использовать branchName@{n}синтаксис, где nнаходится n-ая предшествующая позиция указателя ветви. Так, например, если вы перебазируете featureAветвь на свою masterветку, но вам не нравится результат перебазировки, тогда вы можете просто сделать git reset --hard featureA@{1}сброс ветви обратно туда, где она была до перебазирования. Вы можете прочитать больше о синтаксисе ветки @ {n} в официальных документах Git для ревизий .
15
Это самое простое. Продолжайте с тем, git rebase --abortхотя.
Seph
1
@DaBlick это работало нормально для меня после полностью успешной перезагрузки без каких-либо конфликтов git 2.17.0.
dvlsg
4
И позвольте мне дополнить его: git reset --hard ORIG_HEADможно использовать несколько раз для отката снова и снова. Скажем, если A --- перебазировать в --- B --- перебазировать в --- C, теперь я нахожусь в C, я могу вернуться к A, используя два разаgit reset --hard ORIG_HEAD
CalvinChe
5
@Seph Можете ли вы объяснить, почему вы предлагаете продолжить git rebase --abort?
UpTheCreek
386

Ответ Чарльза работает, но вы можете сделать это:

git rebase --abort

убирать после reset.

В противном случае вы можете получить сообщение « Interactive rebase already started».

Аллан
источник
4
Этот удалил часть «| REBASE» в моей подсказке. +1
Wouter Thielen
62
Это был не вопрос. Вопрос состоит в том, как отменить законченную перезагрузку.
Арунав Саньял
2
Должен быть комментарий к ответу Чарльза, потому что он не отвечает на вопрос о его
Мурмел
Хм, я не должен был этого делать.
Виктор Сеч
1
Единственный, который работал на меня!
Александр Пикард
90

Сброс ветвления к висячему объекту фиксации его старого наконечника, конечно же, является лучшим решением, потому что он восстанавливает предыдущее состояние без каких-либо усилий. Но если вы потеряли эти коммиты (например, из-за того, что тем временем вы собрали мусор в своем хранилище или это новый клон), вы всегда можете перебазировать ветку снова. Ключом к этому является --ontoпереключатель.

Допустим, у вас была изобретательная ветка темы topic, которую вы разветвляли, masterкогда кончик masterбыл 0deadbeefкоммитом. В какой-то момент, находясь на topicветке, вы это сделали git rebase master. Теперь вы хотите отменить это. Вот как:

git rebase --onto 0deadbeef master topic

Это возьмет все коммиты topic, которые не включены, masterи воспроизведет их поверх 0deadbeef.

С помощью --ontoвы можете изменить свою историю практически в любую форму .

Радоваться, веселиться. :-)

Аристотель Пагальцис
источник
3
Я думаю, что это лучший вариант из-за его гибкости. Я разветвлял b1 от master, затем переназначил b1 в новую ветвь b2, затем хотел вернуть b1, чтобы он снова был основан на master. Я просто люблю мерзавца - спасибо!
ripper234
2
Это лучший вариант здесь! Он сохранил все изменения, которые у меня есть в моей текущей ветке, и удалил все ненужные!
Алисия Тан
Я бы сказал, что с комбинацией, --ontoи -iвы можете изменить свою историю практически в любой форме. Используйте Gitk (или Gitx на Mac), чтобы увидеть формы, которые вы делаете :-).
rjmunro
69

На самом деле я помещаю резервный тег в ветку перед тем, как выполнить какую-либо нетривиальную операцию (большинство ребаз это тривиально, но я бы сделал это, если это выглядит где-то сложно).

Тогда восстановление так же просто, как git reset --hard BACKUP.

Алекс Гонтмахер
источник
2
Я тоже так делаю. Также полезно сделать команду diff BACKUP..HEAD, чтобы убедиться, что вы изменили то, что хотели.
Пол Боне
5
Раньше я тоже так делал, но, поскольку мне стало легче с рефлогом, я больше не чувствую, что это необходимо. По сути, reflog делает это от вашего имени каждый раз, когда вы меняете HEAD.
Пит Ходжсон
4
Что ж, я предпочитаю значимые имена, потому что поиск нужного предмета в reflog иногда совсем не веселый.
Алекс Гонтмахер
12
На самом деле вам даже не нужно создавать резервную ветвь, вы можете просто использовать branchName@{n}синтаксис, вот nn-ая предшествующая позиция указателя ветки. Так, например, если вы перебазируете featureAветку на свою masterветку, но вам не нравится результат перебазировки, тогда вы можете просто сделать git reset --hard featureA@{1}сброс ветки обратно туда, где она была до перебазирования. Вы можете прочитать больше о branch@{n}синтаксисе в официальных документах Git для ревизий .
2
На самом деле, вам даже не нужно использовать синтаксис выше, согласно ответу Пэта Нотца , оригинал HEADветки временно хранится в ORIG_HEAD. Но техника использования резервной метки ветки тоже работает, это просто больше шагов.
68

Если вы перенесли ветку в удаленный репозиторий (обычно это источник), а затем выполнили успешную перезагрузку (без слияния) ( git rebase --abortвыдает «Нет выполнения в процессе»), вы можете легко сбросить ветку с помощью команды:

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
Maksym
источник
Это правильный ответ для меня. Rebase и commit до rebase имели одинаковые идентификаторы коммитов, и возвращение к HEAD {1} просто не вернет rebase!
Билл Коциас
23

С помощью 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
Kris
источник
3
Вау - по той ссылке: «Есть одно предупреждение: я потерял историю ветки, но в этом случае это не имело значения. Я был просто счастлив получить мои изменения». ?
ruffin
16

Для нескольких коммитов помните, что любой коммит ссылается на всю историю, ведущую к этому коммиту. Поэтому в ответе Чарльза прочитайте «старый коммит» как «самый новый из старых коммитов». Если вы вернетесь к этому коммиту, то снова появится вся история, ведущая к этому коммиту. Это должно делать то, что вы хотите.

Грег Хьюгилл
источник
11

Следуя решению @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)
$
Матеус Фелипе
источник
11

Если вы успешно перебазировали против удаленной ветки и не можете, 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
Сергей П. ака лазурь
источник
4

Допустим, я перебазирую master в свою ветку функций и получаю 30 новых коммитов, которые что-то ломают. Я обнаружил, что часто проще всего удалить плохие коммиты.

git rebase -i HEAD~31

Интерактивная перебазировка за последние 31 коммитов (не повредит, если вы выберете слишком много).

Просто возьмите коммиты, от которых вы хотите избавиться, и пометьте их «d» вместо «pick». Теперь коммиты эффективно удаляются, отменяя ребазинг (если вы удаляете только коммиты, которые вы только что получили при ребазинге).

Hardev
источник
3

Для новичков / тех, кто слишком боится сделать полный сброс, вы можете извлечь коммит из reflog, а затем сохранить его как новую ветку.

git reflog

Найти коммит незадолго до того, как вы начали ребазинг. Возможно, вам придется прокрутить дальше вниз, чтобы найти его (нажмите Enter или PageDown). Запишите номер HEAD и замените 57:

git checkout HEAD@{57}

Просмотрите ветку / commitits, если она выглядит хорошо, создайте новую ветку, используя эту HEAD:

git checkout -b new_branch_name
Эндрю
источник
2

Если вы находитесь на ветке, вы можете использовать:

git reset --hard @{1}

Существует не только справочный журнал для HEAD (полученный с помощью git reflog), но и для каждой ветки (также полученные с помощью 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.

devconsole
источник
1

Что я обычно делаю, так это git reset #commit_hash

до последнего коммита, где, я думаю, ребаз не имел никакого эффекта.

тогда git pull

Теперь ваша ветка должна совпадать точно так же, как master, и в ней не должно быть повторных коммитов.

Теперь можно просто выбрать коммиты на этой ветке.

mrigendra
источник
1

Я перепробовал все предложения с перезагрузкой и рефлогом, но безуспешно. Восстановление локальной истории IntelliJ решило проблему потерянных файлов

user3638751
источник
Спасибо! Никогда раньше не использовал локальную историю, но это оказалось для меня самым простым способом восстановить случайно перебранный код. Очень хорошая, но несколько скрытая особенность.
Магнус В.
0

git reset --hard origin / {branchName}

является правильным решением для сброса всех ваших локальных изменений, сделанных с помощью rebase.

Дамодар П
источник
1
если вы выполните жесткий сброс, origin/branchвы можете потерять изменения между HEAD и этой точкой. Вы не хотите этого вообще говоря.
bluesmonk
Это именно то, что я хотел в моем случае. Так что голосование.
Mandar Vaze
-4

Если вы что-то испортили в git rebase, например git rebase --abort, когда у вас есть незафиксированные файлы, они будут потеряны и git reflogне помогут. Это случилось со мной, и вам нужно будет думать нестандартно здесь. Если вам повезло, как и мне, и вы используете IntelliJ Webstorm, то вы можете right-click->local historyи можете вернуться к предыдущему состоянию ваших файлов / папок, независимо от того, какие ошибки вы допустили при работе с версионным программным обеспечением. Всегда хорошо иметь другой отказоустойчивый запуск.

stevek
источник
5
git rebase --abortпрерывает активную перебазировку, отмена не отменяет . Кроме того, использование двух VCS одновременно является плохой идеей. Это хорошая функция в программном обеспечении Jetbrains, но вы не должны использовать оба. Лучше просто изучать Git, особенно когда отвечаете на вопросы о переполнении стека, касающиеся Git.
dudewad