Как удалить конкретную ревизию в истории git?

Предположим, что ваша история git выглядит так:

1 2 3 4 5

1-5 - отдельные изменения. Вам нужно удалить 3, сохраняя при этом 1, 2, 4 и 5. Как это можно сделать?

Есть ли эффективный метод, когда есть сотни исправлений после того, который нужно удалить?

Ответ 1

Чтобы объединить версии 3 и 4 в одну ревизию, вы можете использовать git rebase. Если вы хотите удалить изменения в версии 3, вам нужно использовать команду редактирования в режиме интерактивной переадресации. Если вы хотите объединить изменения в одну ревизию, используйте сквош.

Я успешно использовал эту технику сквоша, но мне никогда не приходилось удалять ревизию раньше. Документация git -rebase в разделе "Разделение коммитов", надеюсь, даст вам достаточно идеи, чтобы понять это. (Или кто-то другой может знать).

В git документации:

Запустите его с самой старой фиксацией, которую вы хотите сохранить как есть:

git rebase -i <after-this-commit>

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

pick deadbee The oneline of this commit
pick fa1afe1 The oneline of the next commit
...

Описание oneline - это исключительно для вашего удовольствия; git -rebase не будет смотреть на них, но на имена фиксации ( "deadbee" и "fa1afe1" в этом примере), поэтому не удаляйте и не редактируйте имена.

Заменив команду "pick" командой "edit", вы можете сообщить git -rebase, чтобы остановить после применения этой фиксации, чтобы вы могли редактировать файлы и/или сообщение фиксации, и продолжайте перезагрузку.

Если вы хотите сбросить две или более коммитов в одну, замените команду "pick" на "squash" для второго и последующего фиксации. Если у коммитов были разные авторы, он будет атрибут сжатой фиксации автору первого коммита.

Ответ 2

Вот способ удаления неинтерактивно определенного <commit-id>, зная только <commit-id>, который вы хотите удалить:

git rebase --onto <commit-id>^ <commit-id> HEAD

Ответ 3

Per этот комментарий (и я проверил, что это правда), радио-ответ очень близок, но оставляет git в состоянии отдельной головы. Вместо этого удалите HEAD и используйте это, чтобы удалить <commit-id> из ветки, в которой вы находитесь:

git rebase --onto <commit-id>^ <commit-id>

Ответ 4

Как отмечено выше, git -rebase (1) является вашим другом. Предполагая, что коммиты находятся в вашей ветке master, вы должны сделать:

git rebase --onto master~3 master~2 master

До:

1---2---3---4---5  master

После:

1---2---4'---5' master

Из git -rebase (1):

Диапазон коммитов также может быть удалено с rebase. Если у нас есть следующая ситуация:

E---F---G---H---I---J  topicA

то команда

git rebase --onto topicA~5 topicA~3 topicA

приведет к удалению фиксирует F и G:

E---H'---I'---J'  topicA

Это полезно, если F и G были испорчены в некоторых или не должны быть частью topicA. Обратите внимание, что аргумент --onto и параметр может быть любым valid commit-ish.

Ответ 5

Если все, что вы хотите сделать, это удалить изменения, внесенные в версию 3, вы можете использовать git revert.

Git revert просто создает новую ревизию с изменениями, которые отменят все изменения в версии, которую вы возвращаете.

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

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

Ответ 6

Все ответы до сих пор не затрагивают конечную проблему:

Есть ли эффективный метод, когда есть сотни исправлений после того, который будет удален?

Далее следуют шаги, но для справки предположим следующую историю:

[master] -> [hundreds-of-commits-including-merges] -> [C] -> [R] -> [B]

С: совершить только после фиксации, которую нужно удалить (очистить)

R: Конец, который нужно удалить

В: совершить только перед фиксацией, которую нужно удалить (базой)

Из-за ограничения "сотен ревизий" я предполагаю следующие предварительные условия:

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

Это довольно ограничительный набор ограничений, но есть интересный ответ, который действительно работает в этом случае.

Вот шаги:

  • git branch base B
  • git branch remove-me R
  • git branch save
  • git rebase --preserve-merges --onto base remove-me

Если конфликтов действительно нет, тогда это не должно продолжаться. Если есть конфликты, вы можете разрешить их и rebase --continue или решить просто жить с смущением и rebase --abort.

Теперь вы должны быть на master, у которого больше нет фиксации R. Филиал save указывает на то, где вы были до этого, в случае, если вы хотите согласовать.

Как вы хотите, чтобы все остальные перешли к вашей новой истории, зависит от вас. Вам нужно будет познакомиться с stash, reset --hard и cherry-pick. И вы можете удалить ветки base, remove-me и save

Ответ 7

Итак, вот сценарий, с которым я столкнулся, и как я его решил.

[branch-a]

[Hundreds of commits] -> [R] -> [I]

здесь R - это фиксация, которую мне нужно удалить, а I - это одно коммит, который появляется после R

Я сделал повторную фиксацию и раздавил их вместе

git revert [commit id of R]
git rebase -i HEAD~3

Во время интерактивного сквоша перевязки последние 2 совершают.

Ответ 8

Я тоже приземлился в подобной ситуации. Используйте интерактивную rebase, используя приведенную ниже команду и при выборе, отпустите 3-ей фиксацию.

git rebase -i remote/branch

Ответ 9

Ответы rado и kareem ничего не делают для меня (появляется только сообщение "Текущая ветка обновляется" ). Возможно, это происходит, потому что символ "^" не работает в консоли Windows. Однако, согласно этому комментарию, замена '^' на ~ ~ 1 решает проблему.

git rebase --onto <commit-id>^ <commit-id>