logo
Ещё

Что такое Commit в Git и чем он отличается от git push

Git – доминирующая система контроля версий в IT на данный момент. Основная причина – в том, что в Git заложен крайне мощный функционал, с помощью которого можно решать самые сложные конфликты слияния функционалов, реализованных разными разработчиками (git merge). Кроме того, Git – действительно быстрый и легковесный, внутри себя он использует сложную схему запоминания изменений, исключающую дублирование информации (насколько это возможно, по крайней мере). В Git commit – самая частая команда, которой пользуются разработчики, потому что именно коммит является «логической единицей версионирования». Ниже мы объясним, что на самом деле значит коммит в локальных репозиториях и как выполнить коммит – а также как отменить коммит, если что-то пошло не так. Мы предполагаем, что вы уже имеете базовое представление о Git, поэтому начнем не с «Что такое репозиторий и ветка master», а сразу с устройства локального репозитория. Кроме того, здесь мы не будем рассматривать команды Git, не относящиеся напрямую к коммитам – git pull, git branch, git checkout, git rebase, cherry-pick и так далее.

В чем разница между add, commit и push

Итак, вы скачали из удаленного репозитория клон проекта, создали свою ветку и работаете в ней. Например, вы хотите создать новый функционал для приложения – для этого вам нужно создать пару новых файлов, затем – наполнить их кодом, после этого – локально протестировать, что все работает. На текущий момент в вашем локальном репозитории хранится копия репозитория с GitHub, отличий (кроме новой ветки) пока что нет. Копия хранится в папке на вашем компьютере, и это важно, потому что в деле участвуют сразу 2 файловые системы: собственная файловая система вашей операционной системы и файловая система Git, которая отслеживает все изменения в файлах рабочей папки (кроме тех файлов, которые вы с помощью .gitignore пометили как неотслеживаемые).

Итак, вы создали новый файл и наполнили его кодом. Что изменилось? В вашей файловой системе на локальном компьютере появился новый файл, в котором хранятся какие-то данные. Вы его видите и можете открыть. Git его тоже видит, но в свою файловую систему не вносит – этот процесс может запустить только пользователь (хотя Git говорит, что этот файл «modified»). Если вы хотите внести новый или измененный файл в файловую систему, вам нужно ввести команду «git add имя_файла» – тогда Git добавит его «у себя». Файл получит статус «staged» – подготовлен ко внесению в ветку (что и будет являться коммитом).

Вот эта область, в которую вы вносите файлы командой git add, называется «индексом». Индекс можно рассматривать как песочницу – можете добавлять новые файлы, можете удалять их командой git rm, и никто вам ничего не скажет, потому что идет нормальный процесс разработки. То, что происходило в staging area, остается в staging area. Но постепенно в индексе (кстати, еще одно название – область подготовленных файлов) скапливаются файлы, которые составляют «правильную» реализацию той фичи, которую вы сейчас делаете. Рано или поздно вы поймете, что часть функционала (или весь функционал) больше не нужно будет менять – здесь и выходит на сцену git commit. Commit – это команда, создающая логический узел на ветке разработки. После того, как вы создадите коммит, в него перенесутся все новые файлы, которые находились в staging area – и эти файлы станут видны всем, кто скачает ваш репозиторий. А если вы сделаете git push, то ваш локальный репозиторий пошлет в центральный репозиторий все новые коммиты – и тогда ваши изменения увидят все, кто склонирует центральный репозиторий.

Кратко:

  • git add добавляет измененные или новые файлы в staging area – в вашу локальную песочницу.
  • git commit создает логический узел (коммит) на основе данных, находящихся в staging area – ожидается, что в коммите находится какой-то работающий функционал, который вы не собираетесь менять (по крайней мере, в ближайшее время).
  • git push отправляет все ваши коммиты в центральный репозиторий (и на всеобщее обозрение).

Схематически это можно представить так:

Рисунок1

Если пока не до конца поняли – вот вам аналогия: вы пишете главу книги, и начинаете с черновика – что-то в нем пишете, что-то зачеркиваете, снова пишете и переписываете. В черновике крайне сложно разобраться, потому что везде начеркано, но на то он и черновик. В конце концов вы привели текст в черновике к состоянию, которое вас удовлетворяет, после чего – переписываете слово в слово черновик на чистовик. Написав еще пару глав таким же образом, вы отправляете текст редактору на проверку. Так вот, черновик – это staging area (git add), чистовик – это коммит (git commit), передача текста редактору – это пуш в центральный репозиторий (git push).

Как сделать коммит

git commit -m “сообщение”

В сообщении нужно указать, что этот коммит в себе несет, обычная формула: «this commit…», например: «this commit adds a login button». Если вы не укажете сообщение в команде git commit – Гит откроет стандартный для вашей ОС редактор текста и попросит вас ввести сообщение, коммитить без пояснительного сообщения нельзя.

Как удалить commit

А вот тут все сложно. Пока файлы находятся в staging area, вы можете удалять их простым git rm; если же вы уже закоммитили изменения, просто так «выдрать» их из ветки не получится. Какие есть варианты:

  • git revert индекс/тэг. Представим, что у нас идут последовательно коммит А и коммит Б (Б – последний в ветке). Вы решили отменить коммит Б и делаете git revert, после чего git, недолго думая, делает git commit A – получается цепочка «коммит А, коммит Б, коммит А», данные нового коммита перезаписались данными старого. Самый безопасный способ отменить коммит, но не удаляет данные из репозитория и не всегда применим.
  • git reset --soft HEAD~1. В Git есть такое понятие, как HEAD – указатель на текущий коммит. Soft-reset отменяет 1 (или столько, сколько указано после знака «~») коммит, возвращая все закоммиченные файлы в staging area. По большому счету – тот же git revert, но в истории коммитов будет меньше информации (что хорошо).
  • git reset --hard HEAD~1. Полностью удалить коммит – или несколько (HEAD~2, HEAD~3 и так далее) коммитов из ветки. Вообще полностью. Безвозвратно. Крайне опасная вещь, если кто-то уже скачал ваш репозиторий и работает в вашей же ветке – можно заработать большие проблемы, если другой человек запушит вашу удаленную ветку. Пользуйтесь с осторожностью.
  • git commit --amend -m “сообщение”. Позволяет изменить сообщение последнего коммита.

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

К слову, если вы уже удалили коммит (даже с --hard) – его все еще можно восстановить, хотя об этом знают далеко не все. Дело в том, что в Гит есть чудесная команда git reflog, которая показывает все состояния, в которых находился HEAD (и все коммиты, на которые он указывал). После того, как вы найдете нужное состояние, можно будет перейти на него по ссылке из самого reflog (git checkout) – и вы окажетесь в счастливом прошлом, в котором чудовищной ошибки не произошло. Правда, reflog хорошо работает ровно до тех пор, пока ваши изменения не попали в центральный репозиторий – там все становится сложнее.

Кратко о главном

  • git add нужен для того, чтобы добавлять изменения в «песочницу» (staging area), git commit нужен для того, чтобы добавлять изменения в ветку Git, git push нужен для того, чтобы посылать изменения в центральный репозиторий.
  • git commit всегда нужно делать с сообщением, оно должно содержать информацию о том, что добавляет коммит – Git не даст вам сделать коммит с пустым сообщением.
  • Удалить коммит – довольно сложная задача. Безопасные способы – git revert и git reset --soft HEAD~1. Опасный способ (почти полное удаление) – git reset --hard HEAD~1.