Могу ли я удалить коммит git, но сохранить изменения?

В одной из веток разработки я внес некоторые изменения в свою кодовую базу. Прежде чем я смог завершить функции, над которыми я работал, мне пришлось переключить мою текущую ветку на master, чтобы продемонстрировать некоторые функции. Но простое использование «мастера проверки git» сохранило изменения, которые я также внес в свою ветку разработки, тем самым нарушив некоторые функции мастера. Итак, что я сделал, так это зафиксировал изменения в моей ветке разработки с сообщением фиксации «временная фиксация», а затем проверил мастер для демонстрации.

Теперь, когда я закончил демонстрацию и вернулся к работе над своей веткой разработки, я хотел бы удалить «временную фиксацию», которую я сделал, сохраняя при этом сделанные мной изменения. Это возможно?


person tanookiben    schedule 02.04.2013    source источник
comment
В следующий раз: git stash   -  person Matt Ball    schedule 02.04.2013
comment
@MattBall, не обязательно. Хотя git stash - хороший инструмент, незавершенные коммиты - тоже вполне законное устройство.   -  person kostix    schedule 03.04.2013
comment
Это отличный ресурс от Github: Как отменить (почти) что угодно с Git   -  person jasonleonhard    schedule 03.02.2017
comment
@MattBall @kostix Да, stash особенно плохо подходит для долгосрочного хранения, учитывая, что это один глобальный стек для всего репо. Я хочу иметь возможность сохранять изменения в ветке, а затем переходить в другие ветки, делать все, что угодно, возвращаться через несколько дней, не беспокоясь о том, что я мог использовать git stash в какой-то другой ветке тем временем.   -  person Alec    schedule 19.12.2017
comment
В отношении stash стоит отметить то, что он полностью локален и будет подвержен потере кода из-за повторного удаления или восстановления, а также сбоя или потери оборудования. ИМО, это действительно должно использоваться только для очень краткосрочной WIP. Любите коммит WIP перед тем, как отправиться в отпуск: П ... назовите это коммитом мозговой свалки!   -  person ϹοδεMεδιϲ    schedule 23.07.2018
comment
Я бы сразу git checkout -b temp-need-to-run-demo-now создал временную ветку, а затем git commit, но, конечно, это не значит, что это ответ :)   -  person user776686    schedule 07.04.2019


Ответы (11)


Это так просто:

git reset HEAD^

Примечание: некоторые оболочки рассматривают ^ как специальный символ (например, некоторые оболочки Windows или ZSH с включенным глобусом), поэтому в таких случаях вам, возможно, придется процитировать "HEAD^".

git reset без --hard или --soft перемещает ваш HEAD на указанную фиксацию без изменения каких-либо файлов. HEAD^ относится к (первой) родительской фиксации вашей текущей фиксации, которая в вашем случае является фиксацией перед временной.

Обратите внимание, что другой вариант - продолжить как обычно, а затем в следующей точке фиксации вместо этого запустить:

git commit --amend [-m … etc]

вместо этого будет редактировать самую последнюю фиксацию с тем же эффектом, что и выше.

Обратите внимание, что это (как и почти каждый ответ git) может вызвать проблемы, если вы уже отправили плохую фиксацию в место, откуда кто-то другой мог ее вытащить. Постарайтесь избежать этого

person Gareth    schedule 02.04.2013
comment
Это, безусловно, самый простой вариант, но если вы уже отправили свою фиксацию на удаленный компьютер, а кто-то другой ее вытащил, я бы очень не решился сделать что-либо, кроме как извиниться. - person atw13; 02.04.2013
comment
@sicophrenic, не упустите возможность прочитать об этом Reset Demystified повод. - person kostix; 03.04.2013
comment
Я собирался опубликовать этот точный ответ. Отличная работа. Простой git reset HEAD^ сделает свое дело! - person TheBuzzSaw; 03.04.2013
comment
Я получаю More? после этого. Что бы я ни набирал в этом приглашении, я получаю fatal: ambiguous argument 'HEADwhateverItypedIn': unknown revision or path not in the working tree. - person DaAwesomeP; 27.01.2015
comment
@DaAwesomeP звучит так, будто вы используете оболочку, которая рассматривает ^ как специальный символ. Вы можете указать ссылку "HEAD^" или использовать альтернативный синтаксис HEAD~1 без кавычек - person Gareth; 27.01.2015
comment
@Gareth, чтобы отметить, это в Windows 8.1 в обычной командной строке (а не в git bash / shell, который также включен). - person DaAwesomeP; 29.01.2015
comment
Работал на меня, хотя пришлось ускользать от персонажа git reset HEAD\^ - person cevaris; 02.02.2015
comment
В Windows мне пришлось использовать git reset "HEAD^" - person Rob; 29.03.2016
comment
Проклятие! я не читал комментарии и сделал git reset parent commit после прочтения "HEAD^ refers to the (first) parent commit of your current commit, which in your case is the commit before the temporary one." и потерял все свои изменения. к счастью, их было немного. - person Polynomial Proton; 15.07.2016
comment
Если это не сработает, см. stackoverflow.com/questions/14203952/git-reset-asks -больше - person danwellman; 10.10.2016
comment
@TheUknown, что ты имеешь в виду потерянный? Это не теряет никаких ваших изменений? Вы можете просто вернуться к сбросу коммита? (через 30+ дней он может быть удален, если ни одна ветвь не указывает на него или любого из его дочерних элементов) - person Johny Skovdal; 07.12.2016
comment
Не знал, что можно добавлять / удалять файлы с помощью amend - person captain; 12.01.2017
comment
@kostix FWIW, эта ссылка была изменена на git-scm.com/ book / en / v2 / Git-Tools-Reset-Demystified - person hughjdavey; 10.10.2018
comment
Если вы похожи на меня и забыли взять копию сообщения фиксации, фиксация все еще существует, и вы можете сделать git show <commit-SHA>, чтобы просмотреть и скопировать свое потрясающее сообщение фиксации. (В моем случае для перемещения фиксации из одной ветки в другую.) - person cedricdlb; 26.10.2018
comment
Прошло несколько лет, и я все время возвращаюсь к этому! Большой! - person Juan; 12.03.2019
comment
Если вы привыкли работать с git через Visual Studio и не размещать свои изменения, обратите внимание, что git commit --amend будет принимать только поэтапные изменения. - person ejohnson; 22.03.2019
comment
Последующий вопрос: как мне как-то запомнить эту (относительно простую) команду? Я могу только вспомнить, как его искать, хотя, наверное, уже сотни раз запускал эту команду. - person Andrew Koster; 15.08.2019
comment
Я использую git уже 8 лет, и мне все еще нужно это найти - person Hubert Grzeskowiak; 10.09.2019
comment
Для тех, кто делает это на удаленном компьютере, вы можете впоследствии сделать git push -f <remote> <repository>, чтобы удалить фиксацию с удаленного - person Garth; 25.10.2019
comment
HEAD^^ также работает в Windows - person CoffeeTableEspresso; 08.12.2020

Есть два способа справиться с этим. Что проще, зависит от вашей ситуации

Сброс

Если фиксация, от которой вы хотите избавиться, была последней фиксацией, и вы не выполнили никакой дополнительной работы, вы можете просто использовать git-reset

git reset HEAD^

Возвращает вашу ветку к фиксации непосредственно перед вашим текущим HEAD. Однако на самом деле это не меняет файлы в вашем рабочем дереве. В результате изменения, которые были в этой фиксации, отображаются как измененные - это похоже на команду «uncommit». Фактически, у меня есть псевдоним для этого.

git config --global alias.uncommit 'reset HEAD^'

Тогда вы можете просто использовать git uncommit в будущем для резервного копирования одного коммита.

Сдавливание

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

git rebase -i <ref>

Я говорю выше, потому что хочу прояснить, что это может быть любое количество обратных коммитов. Запустите git log и найдите коммит, от которого хотите избавиться, скопируйте его SHA1 и используйте его вместо <ref>. Git переведет вас в интерактивный режим перебазирования. Он покажет все коммиты между вашим текущим состоянием и тем, что вы поместили вместо <ref>. Итак, если <ref> 10 коммитов назад, он покажет вам все 10 коммитов.

Перед каждым коммитом будет слово pick. Найдите коммит, от которого хотите избавиться, и измените его с pick на fixup или squash. Использование fixup просто отбрасывает фиксированное сообщение и объединяет изменения с его непосредственным предшественником в списке. Ключевое слово squash делает то же самое, но позволяет вам редактировать сообщение фиксации вновь объединенной фиксации.

Обратите внимание, что коммиты будут повторно зафиксированы в том порядке, в котором они отображаются в списке при выходе из редактора. Итак, если вы сделали временную фиксацию, затем выполнили другую работу в той же ветке и завершили эту функцию в более поздней фиксации, то использование rebase позволит вам повторно отсортировать фиксации и сжать их.

ВНИМАНИЕ!

Перебазирование изменяет историю - НЕ делайте этого с любыми коммитами, которыми вы уже поделились с другими разработчиками.

Хранение

В будущем, чтобы избежать этой проблемы, рассмотрите возможность использования git stash для временного хранения незавершенной работы.

git stash save 'some message'

Это сохранит ваши текущие изменения в стороне в вашем списке тайников. Выше представлена ​​наиболее явная версия команды stash, позволяющая в комментарии описать, что вы прячете. Вы также можете просто запустить git stash и ничего больше, но сообщение не будет сохранено.

Вы можете просмотреть свой список тайников с помощью ...

git stash list

Это покажет вам все ваши тайники, какие ветки они были сделаны, а также сообщение и в начале каждой строки, а также идентификатор этого тайника, который выглядит так stash@{#}, где # - его позиция в массиве тайников.

Чтобы восстановить тайник (что можно сделать в любой ветке, независимо от того, где изначально был создан тайник), вы просто запускаете ...

git stash apply stash@{#}

Опять же, # это позиция в массиве тайников. Если тайник, который вы хотите восстановить, находится в позиции 0, то есть если это был самый последний тайник. Затем вы можете просто запустить команду, не указывая позицию тайника, git предположит, что вы имеете в виду последнюю: git stash apply.

Так, например, если я обнаружу, что работаю не с той веткой, я могу запустить следующую последовательность команд.

git stash
git checkout <correct_branch>
git stash apply

В вашем случае вы немного больше перемещались по веткам, но та же идея все еще применима.

Надеюсь это поможет.

person eddiemoya    schedule 03.04.2013
comment
git config --global alias.uncommit reset HEAD^ просто отменяет фиксацию псевдонимов для сброса. Вместо этого сделайте git config --global alias.uncommit 'reset HEAD^' - person mernst; 15.04.2015
comment
Обратите внимание, что вам придется использовать ^^ вместо ^ при использовании в командной строке Windows. - person Pramod B R; 01.04.2019

Я думаю ты ищешь это

git reset --soft HEAD~1

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

person Sudhanshu Jain    schedule 09.03.2018
comment
Спасибо. Это сработало для меня. Вызов git reset HEAD^ в Windows просто запрашивает «Еще»? - что бы это ни значило - person Tyron; 21.10.2018
comment
@Tyron ^ - это escape-символ в DOS. В паре с новой строкой она служит подсказкой для продолжения предыдущей команды. Ввод git reset HEAD^^ должен работать в Windows. - person trk; 09.01.2019

Да, вы можете удалить свою фиксацию, не удаляя изменения:

git reset @~
person DURGESH    schedule 23.08.2016
comment
Это действительно то, что я хочу, и, на мой взгляд, это будет принятый ответ, большое спасибо! - person Carlos Liu; 13.06.2018
comment
Интересный компактный синтаксис, я его раньше не видел и не использовал. Чем это отличается от git reset --soft или git reset --keep? - person user776686; 07.04.2019
comment
со мной это не сработало: git reset @ ~ commit_hash коммит остается как есть и никогда не удаляется - person JAHelia; 11.08.2020
comment
Идеально !! каждый раз, когда я запускаю эту команду, она отменяет мою фиксацию один за другим в обратном порядке - person Gentle; 27.04.2021

Вы ищете либо git reset HEAD^ --soft, либо git reset HEAD^ --mixed.

Существует 3 режима команды сброса, как указано в документации.:

git reset HEAD^ --soft

отменить git commit. Изменения все еще существуют в рабочем дереве (папка проекта) + индекс (--cached)

git reset HEAD^ --mixed

отменить git commit + git add. Изменения все еще существуют в рабочем дереве

git reset HEAD^ --hard

Как будто вы никогда не вносили этих изменений в кодовую базу. Изменения ушли из рабочего дерева.

person Bar Horing Amir    schedule 06.06.2019
comment
Лучший ответ со всеми основными деталями. - person Alliswell; 20.12.2020

2020 Простой способ:

git reset <commit_hash>

(Хеш фиксации последней фиксации, которую вы хотите сохранить).

Если фиксация была нажата, вы можете сделать:

git push -f

Вы сохраните незафиксированные изменения локально

person Xys    schedule 04.06.2020
comment
Когда вы говорите, что это «простой способ 2020 года», подразумевается, что этот метод был невозможен в предыдущих версиях? Если да, то какая версия Git требуется для использования этого метода? - person tomhughes; 24.11.2020

В моем случае я уже нажал на репо. Ой!

Вы можете отменить конкретную фиксацию, сохранив изменения в ваших локальных файлах, выполнив следующие действия:

git revert -n <sha>

Таким образом, я смог сохранить нужные мне изменения и отменить фиксацию, которая уже была отправлена.

person Wim Feijen    schedule 18.05.2017
comment
В моем случае мне нужно было вернуть то, что я уже отправил в удаленный репозиторий. Даже больше ай! Лучше всего было git revert bad-commit-sha, затем git revert -n revert-commit-just-created-sha, а затем оттуда отремонтировать. Вы меня на полпути. Спасибо! - person TinkerTenorSoftwareGuy; 21.07.2017
comment
Кажется, это наоборот, не так ли? Он создает новые изменения, которые соответствуют отмене изменений, сделанных в выбранной фиксации. Если бы вы зафиксировали эти новые изменения, вы бы отменили работу, которую действительно хотели сохранить. - person svaens; 27.04.2018
comment
Следовательно, опция -n, которая оставляет изменения, которые будет выполняться при возврате, поэтапно, но не зафиксировано. Затем вы можете выбрать, какие из этих изменений возврата оставить или отменить. - person youngmit; 15.06.2020

Для тех, кто использует zsh, вам нужно будет использовать следующее:

git reset --soft HEAD\^

Объясняется здесь: https://github.com/robbyrussell/oh-my-zsh/issues/449

Если URL становится мертвым, важная часть:

Избегайте ^ в вашей команде

В качестве альтернативы вы можете использовать HEAD ~, чтобы вам не приходилось каждый раз его избегать.

person Greg Hilston    schedule 18.08.2018
comment
Я никогда не могу вспомнить эту команду, и мне нужно погуглить, чтобы найти свой собственный ответ хахахаха - person Greg Hilston; 26.11.2018
comment
git reset HEAD^ у меня работает в zsh, возможно, это было исправлено. - person Ben Kolya Mansley; 25.03.2019
comment
@BenKolyaMansley Какую версию zsh вы используете? - person Greg Hilston; 25.03.2019
comment
Я использую zsh 5.3 (x86_64-apple-darwin18.0) - person Ben Kolya Mansley; 27.03.2019
comment
Привет всем, снова я, я много раз смотрел на это, я закидывал это в свой .zshrc, что многим может быть полезно сделать. Пример: github.com/GregHilston/toolbox/blob/master/dot/ zshrc # L147 - person Greg Hilston; 27.03.2021

Использование git 2.9 (а именно 2.9.2.windows.1) git reset HEAD^ запрашивает дополнительные сведения; не уверен, что здесь ожидается ввод. См. Снимок экрана ниже

введите здесь описание изображения

Нашли другое решение git reset HEAD~#numberOfCommits, с помощью которого мы можем выбрать количество локальных коммитов, которые вы хотите сбросить, сохранив ваши изменения нетронутыми. Следовательно, мы получаем возможность отбросить все локальные коммиты, а также ограниченное количество локальных коммитов.

См. Ниже снимки экрана, показывающие git reset HEAD~1 в действии:  введите описание изображения здесь

введите здесь описание изображения

person nkharche    schedule 22.11.2017
comment
Вероятно, вам нужно избежать символа ^ - попробуйте git reset HEAD ^ или git reset HEAD \ ^ - person Mark Fisher; 20.03.2018
comment
В дополнение к этому, команда git reset HEAD ^^ работает, поскольку одиночный ^ рассматривается как новая строка. - person John; 12.07.2018

Еще один способ сделать это.

Добавьте фиксацию поверх временной фиксации, а затем выполните:

git rebase -i

Чтобы объединить два коммита в один (команда откроет текстовый файл с явными инструкциями, отредактируйте его).

person Ruslan Osipov    schedule 02.04.2013
comment
Технически правильно, но далеко не так элегантно, как простое выполнение git reset HEAD^. Здесь у Git rebase много места для ошибки. - person TheBuzzSaw; 03.04.2013

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

В этом случае я делаю следующее:

git checkout HEAD~1 <path_to_file_to_put_in_different_commit>
git add -u
git commit --amend --no-edit
git checkout HEAD@{1} <path_to_file_to_put_in_different_commit>
git commit -m "This is the new commit"

Конечно, это хорошо работает даже в середине rebase -i с опцией редактирования в коммите для разделения.

person Louis Caron    schedule 12.06.2020