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

Кто-нибудь знает, как легко отменить 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
Другие вопросы по тегам