Git - Отменить выдвинутые коммиты

610

У меня есть проект в удаленном хранилище, синхронизированный с локальным хранилищем (разработка) и серверным (прод). Я вносил некоторые зафиксированные изменения, уже переданные удаленному и извлеченные с сервера. Теперь я хочу отменить эти изменения. Таким образом, я мог бы просто git checkoutвыполнить коммит до изменений и зафиксировать новые, но я предполагаю, что будут проблемы с их повторным удалением. Любое предложение о том, как мне поступить?

Маноло
источник
5
Возможный дубликат Как вернуть репозиторий Git к предыдущему коммиту?
Тревор Бойд Смит

Ответы:

735

Вы можете отменить отдельные коммиты с помощью:

git revert <commit_hash>

Это создаст новый коммит, который отменит изменения указанного вами коммита. Обратите внимание, что он отменяет только этот конкретный коммит, а не коммит после этого. Если вы хотите вернуть диапазон коммитов, вы можете сделать это следующим образом:

git revert <oldest_commit_hash>..<latest_commit_hash>

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

Посмотрите man-страницу git-revert для получения дополнительной информации о git revertкоманде. Также посмотрите на этот ответ для получения дополнительной информации о возврате коммитов.

gitaarik
источник
22
Другими словами, он возвращается к <самыми старым_коммитам>;)
Дэвид
7
@mr_jigsaw Использованиеgit log
gitaarik
2
В документации git говорится, что команда revert отменяет коммиты между первым и последним коммитами (включая первый и последний). Смотрите документацию
Онур Демир,
4
@aod правильный, этот ответ необходимо обновить. текущий git API для возврата <oldest_commit_hash>был включен в список
возвращений
4
с git 2.17.2 revert <old> .. <new> не включает в себя старый, но включает в себя <new>
chingis
353

Решение, которое не оставляет следов «отмены».

ПРИМЕЧАНИЕ: не делайте этого, если кто-то уже вытащил ваши изменения (я бы использовал это только в моем личном репо)

делать:

git reset <previous label or sha1>

это будет повторно оформлять все обновления локально (поэтому в git status будут перечислены все обновленные файлы)

затем вы «делаете свою работу» и повторно фиксируете свои изменения (Примечание: этот шаг не является обязательным)

git commit -am "blabla"

В данный момент ваше локальное дерево отличается от удаленного

git push -f <remote-name> <branch-name>

подтолкнет и заставит удаленное устройство рассмотреть это нажатие и удалит предыдущее (указание remote-name и branch-name не обязательно, но рекомендуется избегать обновления всех ветвей с флагом обновления).

!! обратите внимание, что некоторые теги могут указывать на удаленный коммит !! как к удалению-а-удаленного тегу

Джо_
источник
4
-a все отслеживаемые файлы будут зафиксированы -m сообщение о
коммите
3
Именно то, что я искал. Кто-то сделал ошибочный коммит, толкнул и снова вернул его обратно. Из-за этого ветви не могли быть объединены, и я хотел, чтобы репозиторий снова находился в правильном состоянии (и, в любом случае,
удалял
1
Какой «предыдущий лейбл или sha1» я должен использовать? Должен ли я ввести последний «правильный» или предыдущий и повторить все изменения, сделанные последним правильным?
elysch
3
перед самым ошибочным коммитом
jo_
1
Именно то, что мне нужно. Я подтолкнул ветку к мастеру по ошибке. И в результате у меня было много мусорных коммитов за всю историю коммитов. Я только что сделал git push -fдля последнего правильного коммита и удаленной истории! Спасибо!
Антон Рыбалко
193

Что я делаю в этих случаях:

  • На сервере переместите курсор назад к последнему известному удачному коммиту:

    git push -f origin <last_known_good_commit>:<branch_name>
    
  • Локально, сделайте то же самое:

    git reset --hard <last_known_good_commit>
    #         ^^^^^^
    #         optional
    



Смотрите полный пример ветки, my_new_branchкоторую я создал для этой цели:

$ git branch
my_new_branch

Это недавняя история после добавления некоторых вещей в myfile.py:

$ git log
commit 80143bcaaca77963a47c211a9cbe664d5448d546
Author: me
Date:   Wed Mar 23 12:48:03 2016 +0100

    Adding new stuff in myfile.py

commit b4zad078237fa48746a4feb6517fa409f6bf238e
Author: me
Date:   Tue Mar 18 12:46:59 2016 +0100

    Initial commit

Я хочу избавиться от последнего коммита, который уже был передан, поэтому я запускаю:

$ git push -f origin b4zad078237fa48746a4feb6517fa409f6bf238e:my_new_branch
Total 0 (delta 0), reused 0 (delta 0)
To git@github.com:me/myrepo.git
 + 80143bc...b4zad07 b4zad078237fa48746a4feb6517fa409f6bf238e -> my_new_branch (forced update)

Ницца! Теперь я вижу, что файл, который был изменен в этом коммите ( myfile.py), отображается как «не подготовленный для коммита»:

$ git status
On branch my_new_branch
Your branch is up-to-date with 'origin/my_new_branch'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   myfile.py

no changes added to commit (use "git add" and/or "git commit -a")

Поскольку я не хочу этих изменений, я просто перемещаю курсор назад также локально:

$ git reset --hard b4zad078237fa48746a4feb6517fa409f6bf238e
HEAD is now at b4zad07 Initial commit

Итак, теперь HEAD находится в предыдущем коммите, как локальном, так и удаленном:

$ git log
commit b4zad078237fa48746a4feb6517fa409f6bf238e
Author: me
Date:   Tue Mar 18 12:46:59 2016 +0100

    Initial commit
Федорки "ТАК прекратить вредить"
источник
10
Это правильный ответ! это именно то, что нужно было сделать в моем случае, благодаря чему сегодня узнал что-то новое :)
Egli Becerra
2
Это правильный ответ! Это касается принудительных (!) Коммитов!
Powtac
3
Если вы действительно хотите отменить изменения (как если бы вы никогда не оттолкнули их), это является правильным ответом.
Джейкоб
1
Идеальный ответ. Сработало как положено!
Рафи Алхамд
2
Кто-нибудь купит этому человеку пиво!
Джей Небхвани
72

Вы можете ОБРАТИТЬ (или вы также можете назвать это УДАЛИТЬ ) Git Commit ОБА Локально и Удаленно, если вы выполните шаги, указанные ниже, через командную строку git.

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

git log --oneline --decorate --graph

Вы получите как следующий скриншот введите описание изображения здесь

Если вы также проверите удаленный (через веб-интерфейс), вы увидите, что это будет так же, как показано ниже

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

Согласно скриншоту, вы находитесь на коммите e110322, однако вы хотите вернуться к 030bbf6 ОБА ЛОКАЛЬНО и УДАЛЕННО .

Выполните следующие шаги, чтобы УДАЛИТЬ / ОТМЕНИТЬ коммиты локально + удаленно


Первый локальный возврат к фиксации id 030bbf6

git reset --hard 030bbf6

с последующим

git clean -f -d

Эти две команды очищают принудительный сброс до фиксации стадии 030bbf6, как показано ниже на снимке

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

Теперь, если вы запустите git status, вы увидите, что вы ДВА КОМИТЕТА BEHIND из удаленной ветви, как показано ниже введите описание изображения здесь

Запустите следующую, чтобы обновить ваши индексы (если есть какие-либо обновления). Рекомендуется попросить всех разработчиков не принимать какие-либо запросы на извлечение в основной удаленной ветви.

git fetch --all

Как только вы закончите с этим, вы должны принудительно нажать этот коммит , используя символ + перед веткой, как показано ниже. Я использовал здесь в качестве основного филиала, вы можете заменить его на любой

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

git push -u origin +master

теперь, если вы видите веб-интерфейс remote, то коммит там также должен быть отменен.

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

vibs2006
источник
61

Это удалит ваши заданные коммиты

git reset --hard 'xxxxx'

git clean -f -d

git push -f
Mo D Genesis
источник
7
Этот метод правильно переписывает историю, чтобы удалить фиксацию. Великолепно
Джесси Реза Хорасани
2
Этот ответ Эль Магнифеко!
правдаадъюстр
3
Люблю ответ, спасибо :)
Дзенис Х.
1
Просто чтобы прояснить (я не был уверен): коммит будет текущим коммитом после операции. Не последний, чтобы удалить.
Maik639
21
git revert HEAD -m 1

В приведенной выше строке кода. «Последний аргумент представляет»

  • 1 - возвращает один коммит.

  • 2 - возвращает последние два коммита.

  • n - возвращает последние n коммитов.

Вам нужно нажать после этой команды, чтобы применить эффект на пульте. У вас есть другие варианты, такие как указание диапазона коммитов для возврата. Это один из вариантов.


Позже использовать git commit -am "COMMIT_MESSAGE" то git pushилиgit push -f

Сириш Ярлагадда
источник
8
Это не так - параметр -m указывает номер родительского элемента, к которому следует вернуться (обычно 1, если вы хотите отменить входящие, "их", изменения, в отличие от 2 для изменений слиянием, "наш" - для слияния 2 фиксаций). Это не имеет ничего общего с количеством возвращенных коммитов - если вы хотите восстановить диапазон коммитов, используйтеgit revert ref1..ref2
Null Reference
1
Не дал желаемого эффекта
Сэм Тьюк,
4

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

git reset <commit_hash>

(Хэш последнего коммита, который вы хотите сохранить).

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

Если вы хотите нажать еще раз, вы должны сделать:

git push -f
XYS
источник
0

Вот мой путь:

Допустим, ветка называется develop.

# Create a new temp branch based on one history commit
git checkout <last_known_good_commit_hash>
git checkout -b develop-temp

# Delete the original develop branch and 
# create a new branch with the same name based on the develop-temp branch
git branch -D develop
git checkout -b develop

# Force update this new branch
git push -f origin develop

# Remove the temp branch
git branch -D develop-temp
backslash112
источник
0

Вы можете сделать что-то вроде

git push origin +<short_commit_sha>^:<branch_name>
avrsanjay
источник