Как удалить конкретную ревизию в истории 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>