Как отменить «git commit --amend» вместо «git commit»

1296

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

Есть ли способ отменить этот последний коммит? Если я делаю что-то подобное git reset --hard HEAD^, первый коммит также отменяется.

(Я еще не подталкивал ни к каким удаленным каталогам)

Джеспер Рённ-Йенсен
источник

Ответы:

2293

Вам нужно создать новый коммит с теми же деталями, что и текущий HEADкоммит, но с родителем, как в предыдущей версии HEAD. git reset --softпереместит указатель ветвления так, чтобы следующий коммит происходил поверх другого коммита, чем текущий заголовок ветки.

# Move the current head so that it's pointing at the old commit
# Leave the index intact for redoing the commit.
# HEAD@{1} gives you "the commit that HEAD pointed at before 
# it was moved to where it currently points at". Note that this is
# different from HEAD~1, which gives you "the commit that is the
# parent node of the commit that HEAD is currently pointing to."
git reset --soft HEAD@{1}

# commit the current tree using the commit details of the previous
# HEAD commit. (Note that HEAD@{1} is pointing somewhere different from the
# previous command. It's now pointing at the erroneously amended commit.)
git commit -C HEAD@{1}
CB Bailey
источник
33
Очень круто, +1. Я даже сделал это со вторым последним изменением, git reflogчтобы найти правильный номер, например {2}.
JJD
179
Просто чтобы прояснить, первая команда - это настоящая «отмена». Он создает заголовок, рабочий каталог (без изменений) и состояние индекса до git commit --amend. Второе - это «переделать» в новый коммит. Это работает для любого git commit, а не только --amend.
cdunn2001
60
Так что, если вы не исправили новым сообщением о коммите, которое вам нужно спасти, вторая часть может быть обычной git commit.
Мэтт Монтег
18
По какой - то причине, я получаю сообщение об ошибке при запуске git reset --soft HEAD@{1}: fatal: ambiguous argument 'HEAD@1': unknown revision or path not in the working tree. Use '--' to separate paths from revisions. Когда я заменил HEAD@{1}эквивалентный хеш коммита, показанный в git reflog(спасибо JJD!), Этот ответ работал чудесно!
Тим Кэмбер
20
@TimArnold, в зависимости от вашей оболочки, вам может понадобиться заключить в одинарные или двойные кавычки HEAD@{1}. Если я, echo HEAD@{1}например, запускаю tcsh, то вывод получится HEAD@1потому, что скобки были интерпретированы tcsh. Если я использую одинарные кавычки, скобки сохраняются.
Кельвин
136

используйте ref-log :

git branch fixing-things HEAD@{1}
git reset fixing-things

После этого все внесенные вами ранее изменения должны быть сохранены только в вашей рабочей копии, и вы можете подтвердить их снова.

чтобы увидеть полный список предыдущих типов индексов git reflog

knittl
источник
7
Это также стирает индекс - все еще полезно, но выходит за рамки простого «отмены».
cdunn2001
3
есть ли разница между HEAD@{1}и HEAD~1?
neaumusic
15
@neaumusic: да! HEAD~1точно так же, как HEAD^и идентификаторы родителя текущего коммита. HEAD@{1}с другой стороны, относится к коммиту, на который HEAD указывал до этого, т.е. они означают разные коммиты, когда вы извлекаете другую ветку или изменяете коммит.
knittl
@knittl ну не удивительно, я не думал, что это было возможно раньше, спасибо еще раз, хорошая информация
neaumusic
9
Первый шаг является излишним. Простого git reset HEAD@{1}достаточно.
dwelle
79

Найти исправленные коммиты можно по:

git log --reflog

Примечание. Вы можете добавить --patchтекст коммитов для ясности. То же самое git reflog.

затем сбросьте ваш HEAD на любой предыдущий коммит в тот момент, когда он был в порядке:

git reset SHA1 --hard

Примечание: замените SHA1 вашим реальным хэшем коммита. Также обратите внимание, что эта команда потеряет все незафиксированные изменения, поэтому вы можете сохранить их раньше. В качестве альтернативы используйте --softвместо этого, чтобы сохранить последние изменения, а затем зафиксировать их.

Затем выберите другой коммит, который вам нужен:

git cherry-pick SHA1
kenorb
источник
26
Если вы это сделаете git reset SHA1 --soft, вы можете сохранить последние изменения, а затем зафиксировать их.
Прав
24

Вы всегда можете разделить коммит, из руководства

  • Запустите интерактивную перебазировку с помощью git rebase -i commit ^, где commit - это коммит, который вы хотите разделить. Фактически, подойдет любой диапазон фиксации, если он содержит этот коммит.
  • Пометьте коммит, который вы хотите разделить, с помощью действия «изменить».
  • Когда дело доходит до редактирования этого коммита, выполните git reset HEAD ^. В результате HEAD перематывается на единицу, и индекс следует за ним. Однако рабочее дерево остается прежним.
  • Теперь добавьте изменения в индекс, которые вы хотите иметь при первом коммите. Для этого вы можете использовать git add (возможно, в интерактивном режиме) или git-gui (или оба).
  • Зафиксируйте текущий текущий индекс с любым соответствующим сообщением.
  • Повторяйте последние два шага, пока ваше рабочее дерево не станет чистым.
  • Продолжите перебазирование с помощью git rebase --continue.
Аркаитц Хименес
источник
26
слишком сложно. git reflogэто все, что вам нужно
knittl
2
Да, много шагов, но каждый шаг несложен и прост в выполнении. Это сработало для меня и получило мой голос.
OzBandit
5
кроме того, этот ответ позволяет вам выборочно выбирать изменения, которые вы случайно «исправили», чтобы придать дополнительное значение подходу git reset --soft HEAD @ {1} (который, кстати, решил мою проблему)
Wiebe Tijsma
2
Вы также можете выборочно выбирать изменения с помощью метода reflog. Просто делай git resetвместо git reset --soft, потом делай git add --patch.
geekofalltrades
1
Это все еще переписывает историю и требует силового толчка. В зависимости от вашей ситуации это может быть или не быть проблемой.
Пайн
20

Возможно, стоит отметить, что если вы все еще находитесь в редакторе с сообщением о коммите, вы можете удалить сообщение о коммите, и оно прервет выполнение git commit --amendкоманды.

Джастин Шульц
источник
Это тот самый.
atilkan
Сохранено, но ^^
engineercoding
14

Может быть, можно использовать, git reflogчтобы получить два коммита до изменения и после изменения.

Затем используйте, git diff before_commit_id after_commit_id > d.diffчтобы получить разницу между до и после изменения.

Далее используйте, git checkout before_commit_idчтобы вернуться к перед фиксацией

И последнее использование, git apply d.diffчтобы применить реальные изменения, которые вы сделали.

Это решает мою проблему.

utzcoz
источник
11

Если вы передали коммит на удаленный, а затем ошибочно внесли изменения в этот коммит, это решит вашу проблему. Выполните git logкоманду a, чтобы найти SHA перед фиксацией. (предполагается, что удаленное имя называется источником). Теперь выполните эти команды, используя этот SHA.

git reset --soft <SHA BEFORE THE AMMEND>
#you now see all the changes in the commit and the amend undone

#save ALL the changes to the stash
git stash

git pull origin <your-branch> --ff-only
#if you issue git log you can see that you have the commit you didn't want to amend

git stash pop
#git status reveals only the changes you incorrectly amended

#now you can create your new unamended commit
Давид Сопко
источник
3
Это частный случай более общего вопроса, но он точно охватил мою насущную потребность.
dmckee --- котенок экс-модератора
8

Вы можете сделать ниже, чтобы отменить git commit —amend

  1. git reset --soft HEAD^
  2. git checkout files_from_old_commit_on_branch
  3. git pull origin your_branch_name

====================================

Теперь ваши изменения соответствуют предыдущим. Итак, вы сделали с отменой дляgit commit —amend

Теперь вы можете сделать git push origin <your_branch_name>, чтобы подтолкнуть к ветке.

Pratik
источник
3

Почти 9 лет опоздал, но не увидел, что этот вариант упоминал о том, что он совершал одно и то же (это своего рода комбинация нескольких из них, аналогично верхнему ответу ( https://stackoverflow.com/a/1459264/4642530 ) ,

Обыщи все отдельные головы на ветке

git reflog show origin/BRANCH_NAME --date=relative

Тогда найдите хэш SHA1

Сбросить до старого SHA1

git reset --hard SHA1

Тогда поднимите это назад.

git push origin BRANCH_NAME

Выполнено.

Это вернет вас обратно к старому коммиту полностью.

(Включая дату предыдущего перезаписанного отдельного коммита)

garrettmac
источник
Да, но обычно я хочу сбросить настройки, --softчтобы сохранить изменения. Я просто хочу, чтобы это совершалось отдельно
Хуан Мендес
2
  1. Оформить заказ на временную ветку с последним коммитом

    git branch temp HEAD@{1}

  2. Сбросить последний коммит

    git reset temp

  3. Теперь у вас будут все файлы вашего коммита, а также предыдущий коммит. Проверьте статус всех файлов.

    git status

  4. Сбросьте ваши файлы коммитов со стадии git.

    git reset myfile1.js (скоро)

  5. Прикрепите этот коммит

    git commit -C HEAD@{1}

  6. Добавить и зафиксировать ваши файлы для нового коммита.

Прияншу Чаухан
источник