Отмена изменений в Git

Reading time ~7 minutes

Обширная и достаточно сложная тема. Однако необходимая, если стоит задача изучить и иметь возможность работать в Git.

Эта тема касается отмены изменений в Git. Так как эта система контроля версий представляет из себя три ветки - рабочая директория, буфер, репозиторий - то будут рассмотрены все три способа.

Отмена изменений в рабочей директории

Для отмены изменений в рабочей директории необходимо выполнить одну команду:

git checkout -- name_of_file

Данная команда говорит Git вернуть состояние файла

1
name_of_file
до того, каким оно было зафиксировано в репозитории. Другими словами, произойдет сброс изменений файла в рабочей директории проекта.

Обязательным условием использования команды

1
checkout
является двойной дефис. Это “говорит” команде
1
checkout
, что в данном случае нужно оставаться в текущей ветке (branche).

Отмена изменений в буфере

Для отмены изменений в буфере нужно использовать команду:

git reset HEAD name_of_file

Эта команда “вернет” файл

1
name_of_file
из буфера в рабочую директорию.

Отмена изменений в репозитории

Что касается репозитория, то здесь ситуация намного сложнее. Связано это с тем, что все коммиты в репозитории взаимосвязаны между собой.

Нельзя просто так взять и изменить какой-либо определенный коммит - все они находятся в своеобразной цепочке и один коммит проистекает из предыдущего коммита.

Другими словами, у каждого коммита есть свой коммит-родитель и коммит-потомок. Изменить какой-либо коммит - это значит вырвать его из этой цепочки взаимосвязей. Тем самым нарушится целостность всего репозитория и работа Git также нарушится. Ведь дело в том, что сама суть системы контроля версий Git основана на такой взаимосвязанной цепочке коммитов - один коммит проистекает из другого, второй из первого, третий из второго и так далее.

Как же поступить в данной ситуации?

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

Опция amend

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

Соответственно, раз у этого коммита нет потомков, то изменение этого коммита не повлечет за собой нарушение целостности всей цепочки коммитов. Измениться только этот коммит - и только.

Изменить крайний коммит можно просто перезаписав его. Для этой цели служит ключ

1
--amend
. Команда в целом представляет из себя следующее:

git commit --amend -m "Add some commit"

Как только будет выполнена подобная команда, крайний коммит (тот, на котором находится указатель HEAD) будет перезаписан и это очень легко проследить - достаточно сравнить контрольную сумму SHA этого коммита до и после изменений.

Команда checkout

С помощью команды

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

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

Команда

1
checkout
может заставить указатель HEAD вернуться назад, на предыдущее место остановки - на предыдущий коммит. И необязательно этот коммит должен быть предыдущим - он может быть любым более ранним.

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

Команда возврата к нужному коммиту выглядит таким образом:

git checkout -- 4a2f59a32bd1074c42 name_of_file

Можно указать только часть SHA-суммы, так как это показано выше.

И тогда Git произведет сброс состояния (которые были сделаны в последнем коммите) файла

1
name_of_file
в буфер. Если ввести команду
1
git status
то увидим, что в буфере будут изменения, готовые для коммита. Это те самые изменения файла
1
name_of_file
, которые мы “выбросили” из репозитория.

Дальше выполняем “выброс” изменений файла

1
name_of_file
из буфера в рабочую директорию, чтобы мы смогли внести правки в этот файл:

git reset HEAD name_of_file

Все, что будет внесено в этот файл, а затем закоммичено - будет иметь уже другую контрольную сумму. А следовательно - это уже будет совсем другой коммит.

Зеркалирование коммитов

Одной из разнообразных команд Git для внесения изменений в репозиторий является команда

1
revert
.

Суть ее очень проста - она отменяет все изменения, зафиксированные в крайнем коммите; и создает новый коммит, который является потомком предыдущего коммита, но в тоже время его зеркальной копией, противоположностью.

Синтаксис команды revert также прост:

git revert 4a2f59a32bd1074c42

где

1
4a2f59a32bd1074c42
- это часть SHA-суммы крайнего коммита.

Команда reset

Команда

1
reset
также может изменять репозиторий Git. Делает она это путем смещения указателя HEAD на нужный коммит.

После такого смещения HEAD все более поздние коммиты никуда не пропадут из репозитория - они все еще там. Но теперь любое закоммиченное изменение произведет перезапись более поздних коммитов - и тогда они будут потеряны навсегда.

Ситуация очень похожа на то образное сравнение цепочки коммитов Git с магнитофонной лентой. Стоит отмотать ленту на 10-20 минут назад и снова начать запись, то все более поздние записи (относительно этого места) будут перезаписаны.

У команды

1
reset
есть три ключа, которые незначительно видоизменяют ее работу:

  • -soft
  • -mixed
  • -hard

Команда

1
git reset --soft 4a2f59a32bd1074c42
произведет смещение указателя HEAD на указанный коммит 4a2f59a32bd1074c42. При этом Git произведет сброс изменений в буфер. В результате буфер и рабочая директория будут идентичными между собой; а репозиторий будет переведен в более ранее состояние.

Команда

1
git reset --mixd 4a2f59a32bd1074c42
аналогична предыдущей. Также будет произведено смещение указателя HEAD. Но теперь Git сбросит изменения в рабочую директорию. Репозиторий и буфер будут идентичными друг другу и находиться в более раннем состоянии.

Команда

1
git reset --hard 4a2f59a32bd1074c42
самая “жесткая”. Также будет произведено смещение указателя HEAD на указанный коммит, но при этом будет произведен сброс всех трех деревьев Git - репозитория, буфера и рабочей директории. Все три будут идентичными друг другу.

Команда clean

В системе Git существует команда

1
clean
для удаления не отслеживаемых файлов в рабочей директории.

Разница между командой

1
git rm
и
1
clean
заключается в том, что при удалении файлов из рабочей директории первой командой они вроде как удаляются. Но на самом деле удаленные файлы все еще остаются в системе Git. Просто они помещены в раздел мусора и являются теперь мусором.

Команда

1
clean
как раз и выполняет очистку мусора в системе. Она удаляет все не отслеживаемые и неиспользуемые файлы.

Но даже это она делает очень осторожно. Если просто запустить

1
git clean
, то ничего не произойдет. Система Git скажет, что команду
1
clean
необходимо запускать либо с ключом
1
-n
, либо с ключом
1
-f
.

Запуск

1
git clean -n
- это тестовый запуск команды удаления. Ничего удалено не будет - система лишь покажет, что она собирается удалить, какие файлы.

А вот запуск

1
git clean -f
произведен реальное удаление всех файлов, которые Git считает мусором. И в этом случае эти файлы будут потеряны навсегда.

На этом все.


MongoDB - создание документа

![MongoDB]({{site.url}}/images/uploads/2017/05/mongodb-logo.jpg "MongoDB")Приступили к самому основному - операциям создания, чтения, изм...… Continue reading

MongoDB - документы

Published on May 23, 2017