Гайд по Git : Часть 1 : Как сделать коммит в Git

Отвечаю на вопросы: как сделать коммит, создать новую ветку, вмержить одну ветку в другую.

· 10 минуты на чтение
Гайд по Git : Часть 1 : Как сделать коммит в Git

Git - это консольная утилита, для отслеживания и ведения истории изменения файлов в проекте. Чаще всего его используют для кода, но можно и для других файлов.

Я пользовался git еще в универе, было удобно писать диплом и не плодить 100 копий файлов с названиями: “диплом (новый)”, “диплом (новее нового)”, “диплом (новые правки)”.

Мои статьи основаны на отличном интерактивном курсе LearnGitBranching. Но у него есть существенный недостаток, они не используют настоящий Git в процессе обучения. Из-за этого ряд важных нюансов упускается.

В моей статье будет обучение на настоящем Git. Просто повторяйте команды за мной, чтобы попрактиковаться.

Спонсор поста

Что такое VCS?

Системы контроля версий (СКВ, VCS, Version Control Systems) позволяют откатить проект до старой версии, сравнивать, анализировать или сливать свои изменения в репозиторий.

Репозиторием называют хранилище вашего кода. Git работает локально и все ваши репозитории хранятся в определенных папках на жестком диске. Также есть хостинги репозиториев, такие как GitHub и GitLab, которые позволяют хранить проект в интернете.

VCS позволяют нескольким разработчикам работать над одним проектом и сохранять внесённые изменения независимо друг от друга. При этом каждый участник команды видит над чем работают коллеги.

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

Установка git

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

Для установки на Windows нажимаем на кнопку ниже, скачиваем и устанавливаем.

Для Mac OS открываем терминал и пишем, если установлен Brew:

brew install git

Если Brew не установлен, то вводим эту команду.

git --version

После этого появится окно, где предложит установить Command Line Tools (CLT). Соглашаемся и ждем установки. Вместе с CLT установиться и git

Для Linux открываем терминал и вводим следующую команду.

# Debian или Ubuntu
sudo apt install git

# CentOS
sudo yum install git

Настройка

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

Открываем терминал (Linux и MacOS) или консоль (Windows) и вводим следующие команды.

git config --global user.name "ваше_имя"
git config --global user.email "адрес_почты@email.com"
Подписываем коммиты GPG ключом
Чтобы проверить, что коммиты действительно получены из доверенного источника, в Git есть несколько способов подписать и проверить исходники, используя GPG.

Инициализация репозитория

Теперь вы готовы к работе с Git локально на компьютере. Для начала создадим папку, которая будет нашим обучающим проектом.

$ mkdir git-project
$ cd git-project

Чтобы создать локальный репозиторий, необходимо выполнить команду:

$ git init

Инициализирован пустой репозиторий Git в /home/upagge/Documents/git-project/.git/

Посмотрим, какие файлы находятся сейчас в папке git-project:

$ ls -al

итого 20
drwxrwxr-x  3 upagge upagge  4096 мая 25 13:04 .
drwxr-xr-x 12 upagge upagge 12288 мая 25 13:02 ..
drwxrwxr-x  7 upagge upagge  4096 мая 25 13:04 .git

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

$ ls -l .git/

итого 56
drwxrwxr-x 2 upagge upagge 4096 мая 25 13:04 branches
-rw-rw-r-- 1 upagge upagge   92 мая 25 13:04 config
-rw-rw-r-- 1 upagge upagge   73 мая 25 13:04 description
-rw-rw-r-- 1 upagge upagge   23 мая 25 13:04 HEAD
drwxrwxr-x 2 upagge upagge 4096 мая 25 13:04 hooks
drwxrwxr-x 2 upagge upagge 4096 мая 25 13:04 info
drwxrwxr-x 4 upagge upagge 4096 мая 25 13:04 objects
drwxrwxr-x 4 upagge upagge 4096 мая 25 13:04 refs

Коммиты

Коммит это одно из базовых понятий в Git. Если объяснять простым языком, то коммит это огромная копия вашего проекта в момент времени, когда этот коммит был сделан.

Но на самом деле git пытается быть лёгким и быстрым, так что он не просто слепо копирует весь проект каждый раз, а ужимает коммит в набор изменений или «дельту» между текущей версией и предыдущей. Это позволяет занимать меньше места.

Также Git хранит всю историю о том, когда какой коммит был сделан и кем. Это очень важно, можно посмотреть из-за кого упало приложение и навалять ему 😄

Файлы в репозитории могут находиться в 3 различных “областях”.

  • HEAD
  • Индекс
  • Рабочий каталог

Наш проект сейчас пуст. Давайте создадим наш первый файл:

echo "12345" > file.txt
Три области Git

На данном этапе только область “Рабочий каталог” содержит данные.

Рабочий Каталог это ваша папка с файлами, в данном случае это git-project. Две другие области сохраняют свое содержимое внутри папки .git в понятном и удобном для git формате, но не понятном для человека.

Считайте Рабочий Каталог песочницей, где вы можете опробовать изменения перед тем, как сделаете коммит. На данном этапе вы можете в любой момент стереть все изменения и вернуться к последнему коммиту. Они не будут сохранены в истории git.

Чтобы сохранить изменения – сделать коммит. Необходимо сначала добавить содержимое Рабочего Каталога в так называемый Индекс – это черновик коммита. Только файлы из Индекс попадут в коммит.

Без добавления файла в Индекс у нас не получится создать коммит, проверьте это сами с помощью комманды:

$ git commit -m "Первый коммит"

На ветке main

Начальный коммит

Неотслеживаемые файлы:
  file.txt

ничего не добавлено в коммит, но есть неотслеживаемые файлы

Для добавления файлов в Индекс используется следующая команда:

git add file.txt

Когда у вас много файлов, вы можете добавить их все разом git add --all.

Выполнение команды git add

Теперь сделаем наш первый коммит, выполнив команду git commit. Тем самым мы сохраним содержимое области Индекс как неизменяемый снимок в области HEAD. Обязательно нужно описать суть внесенных вами изменений, для этого используется флаг -m:

$ git commit -m "Первый коммит"

[main (корневой коммит) 06f7fc0] Первый коммит
1 file changed, 1 insertion(+)
create mode 100644 file.txt

Все, коммит готов. И файл попал в область HEAD. HEAD будет родителем следующего созданного коммита. Как правило, самое простое считать HEAD снимком вашего последнего коммита. Возможно пока не совсем понятно что такое HEAD, но о нем мы еще поговорим.

Если сейчас выполнить git status, то мы не увидим никаких изменений, так как все три области одинаковые.

git commit

Теперь мы хотим внести изменения в файл и сделать новый коммит. Мы пройдём через ту же процедуру: сначала отредактируем файл в нашем рабочем каталоге. Давайте называть эту версию файла v2 и обозначать зеленым цветом. Допишем в файл цифры 67890.

echo "67890" >> file.txt
Измененный файл в рабочем каталоге

Теперь посмотрим, какие изменения произошли в git:

$ git status

На ветке main
Изменения, которые не в индексе для коммита:
  (используйте «git add <файл>…», чтобы добавить файл в индекс)
  (используйте «git checkout -- <файл>…», чтобы отменить изменения
   в рабочем каталоге)

	изменено:      file.txt

нет изменений добавленных для коммита
(используйте «git add» и/или «git commit -a»)

Нам подсказывают, что изменения не попадут в коммит, пока мы не сделаем git add. Выполним эту комманду чуть позже, пока добавим новый файл:

echo "qwerty" > new-file.txt

Еще раз проверяем статус репозитория:

git status

На ветке main
Изменения, которые не в индексе для коммита:
  (используйте «git add <файл>…», чтобы добавить файл в индекс)
  (используйте «git checkout -- <файл>…», чтобы отменить изменения
  в рабочем каталоге)

	изменено:      file.txt

Неотслеживаемые файлы:
  (используйте «git add <файл>…», чтобы добавить в то, что будет включено в коммит)

	new-file.txt

нет изменений добавленных для коммита
(используйте «git add» и/или «git commit -a»)

Новый файл появился в списке не отслеживаемых, то есть в Рабочем Каталоге. Давайте добавим в отслеживание этот файл, а так же измененный ранее.

$ git add file.txt new-file.txt
$ git status

На ветке main
Изменения, которые будут включены в коммит:
(используйте «git reset HEAD <файл>…», чтобы убрать из индекса)

	изменено:      file.txt
	новый файл:    new-file.txt

Теперь у нас есть изменения, которые будут включены в коммит. Значит пора сделать второй коммит:

$ git commit -m "Второй коммит"

[main b66f9c7] Второй коммит
2 files changed, 2 insertions(+)
create mode 100644 new-file.txt

Я уже упоминал, что у коммитов есть родитель, который указывает на предыдущий коммит. Из таких цепочек складывается “ветка”. О ветках мы поговорим в следующей статье. Пока достаточно знать, что по умолчанию у нас уже есть ветка main. Эта цепочка также является нашей историей проекта, вы можете вернуться к предыдущим коммитам в любой момент.

Для удобства восприятия, визуализируем наши коммиты. Кружочки это коммиты, а стрелочки между ними указывают на родителей.

Просмотр истории коммитов

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

$ git log

commit b66f9c79eb37de0beb099b04cc7f7f4979a98a83 (HEAD -> main)
Author: uPagge 
Date:   Tue Jun 15 21:50:01 2021 +0300

    Второй коммит

commit 06f7fc0a337bb6105f6a792fcc93eb2dda2a4dda
Author: uPagge 
Date:   Tue Jun 15 21:48:54 2021 +0300

    Первый коммит

Помимо автора и даты, у каждого комита есть идентификатор, который называется hash. Пример: 2934ee19f4d4ca37ff9bea9dc8208ef5362d789e. Необязательно использовать такую длинную запись, git поймет и по первым 5 символам, какой hash вам нужен.

Команда git log имеет очень большое количество опций для поиска коммитов по разным критериям.

Одним из самых полезных аргументов является -p или --patch, который показывает разницу, внесенную в каждый коммит. Можно ограничить количество записей в выводе команды, используя параметр -2:

$ git log -p -2

commit b66f9c79eb37de0beb099b04cc7f7f4979a98a83 (HEAD -> main)
Author: uPagge 
Date:   Tue Jun 15 21:50:01 2021 +0300

    Второй коммит

diff --git a/file.txt b/file.txt
index e56e15b..cbb384f 100644
--- a/file.txt
+++ b/file.txt
@@ -1 +1,2 @@
12345
+67890
diff --git a/new-file.txt b/new-file.txt
new file mode 100644
index 0000000..19f0805
--- /dev/null
+++ b/new-file.txt
@@ -0,0 +1 @@
+qwerty

commit 06f7fc0a337bb6105f6a792fcc93eb2dda2a4dda
Author: uPagge 
Date:   Tue Jun 15 21:48:54 2021 +0300

    Первый коммит

diff --git a/file.txt b/file.txt
new file mode 100644
index 0000000..e56e15b
--- /dev/null
+++ b/file.txt
@@ -0,0 +1 @@
+12345 

Навигация

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

Detaching HEAD

Давайте разберемся, как нам откатиться к более старой версии нашего репозитория.

У git есть указатели на коммиты, своеобразный ярлыки, которые перемещаются от коммита к коммиту. Одним из таких ярлыков-указателей является HEAD.

HEAD - это символическое имя текущего выбранного коммита. По сути это, тот коммит, над которым мы в данным момент работаем.

Указатели могут ссылаться на другие указатели, обычно HEAD указывает на имя ветки. Но это можно изменить, например указать hash нужного коммита, чтобы откатиться к нему.

Создадим еще один файл и сделаем третий коммит:

$ echo "new new file" > three-file.txt
$ git add three-file.txt
$ git commit -m "Третий коммит"

[main b64191a] Третий коммит
1 file changed, 1 insertion(+)
create mode 100644 three-file.txt
git log -2

commit b64191a1a53c7f28429042c1fe00702c15354fab (HEAD -> main)
Author: uPagge 
Date:   Tue Jun 15 21:52:20 2021 +0300

    Третий коммит

commit b66f9c79eb37de0beb099b04cc7f7f4979a98a83
Author: uPagge 
Date:   Tue Jun 15 21:50:01 2021 +0300

    Второй коммит 

Мы видим, что HEAD сейчас указывает на main, main это тоже указатель, обозначающий ветку. То есть HEAD указывает на main, который в свою очередь указывает на коммит dac5bb87a. Отделение (detaching) HEAD означает лишь присвоение его не ветке, а конкретному коммиту.

Представим, что нам надо посмотреть, как выглядел наш репозиторий после второго коммита. Для этого используем команду checkout и хэш второго коммита. Кстати, можно не указывать его целиком, достаточно первых 5 символов.

$ git checkout b66f9

Примечание: переход на «b66f9».

Вы сейчас в состоянии «отделённого HEAD». Вы можете осмотреться, сделать
экспериментальные изменения и закоммитить их, также вы можете отменить
изменения любых коммитов в этом состоянии не затрагивая любые ветки и
не переходя на них.

Если вы хотите создать новую ветку и сохранить свои коммиты, то вы
можете сделать это (сейчас или позже) вызвав команду checkout снова,
но с параметром -b. Например:

git checkout -b <имя-новой-ветки>

HEAD сейчас на b66f9c7 Второй коммит

Таким образом мы переключились на состояние второго коммита, в котором у нас еще не было файла three-file.txt, проверим это:

$ ls

file.txt  new-file.txt

Вызвав git log видим, что HEAD теперь указывает на второй коммит:

$ git log

commit b66f9c79eb37de0beb099b04cc7f7f4979a98a83 (HEAD)
Author: uPagge 
Date:   Tue Jun 15 21:50:01 2021 +0300

    Второй коммит

commit 06f7fc0a337bb6105f6a792fcc93eb2dda2a4dda
Author: uPagge 
Date:   Tue Jun 15 21:48:54 2021 +0300

    Первый коммит

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

$ git log --all

commit b64191a1a53c7f28429042c1fe00702c15354fab (master)
Author: uPagge 
Date:   Tue Jun 15 21:52:20 2021 +0300

    Третий коммит

commit b66f9c79eb37de0beb099b04cc7f7f4979a98a83 (HEAD)
Author: uPagge 
Date:   Tue Jun 15 21:50:01 2021 +0300

    Второй коммит

commit 06f7fc0a337bb6105f6a792fcc93eb2dda2a4dda
Author: uPagge 
Date:   Tue Jun 15 21:48:54 2021 +0300

    Первый коммит

Вернем указатель HEAD на main:

$ git checkout main

Предыдущая позиция HEAD была b66f9c7 Второй коммит
Переключено на ветку «main»

$ ls

file.txt  new-file.txt  three-file.txt

Относительные ссылки

Передвигаться по коммитам при помощи указания хешей неудобно. Поэтому Git поддерживает относительные ссылки. С относительными ссылками можно начать с какого-либо удобного места и двигаться от него.

Относительные ссылки - мощный инструмент, но мы разберем два простых способа использования:

  • Перемещение на один коммит назад ^
  • Перемещение на n коммитов назад ~<num>

Для начала рассмотрим оператор каретки ^. Когда мы добавляем его к имени указателя, Git воспринимает это как команду найти родителя выбранного коммита. Так что main^ означает “первый родитель ветки main”. main^^ означает прародитель (родитель родителя) main.

Давайте переключимся на коммит выше main:

$ git checkout main^

Примечание: переход на «main^».
...
HEAD сейчас на b66f9c7 Второй коммит

Да, мы снова попали на второй коммит, то есть HEAD сейчас вновь указывает на второй коммит.

Может показаться, нужно еще раз вызвать команду git checkout main^, чтобы попасть на первый коммит. Но это не так, указатель main остался на третьем коммите, мы сдвинули HEAD. Поэтому git checkout main^ запросит снова родителя третьего коммита.

Чтобы попасть на первый коммит, можно использовать указатель HEAD. Попробуем перейти к первому коммиту:

$ git checkout HEAD^

Предыдущая позиция HEAD была b66f9c7 Второй коммит
HEAD сейчас на 06f7fc0 Первый коммит

Вернемся на третий коммит:

$ git checkout main

Предыдущая позиция HEAD была 06f7fc0 Первый коммит
Переключено на ветку «main»

Оператор ~

Предположим, нужно переместиться на много шагов назад. Было бы неудобно печатать ^ несколько раз, так что Git поддерживает также оператор тильда ~.

Опционально к тильде можно добавить количество родительских коммитов, через которые нужно пройти. Посмотрим, как это работает.

$ git checkout HEAD~2

Примечание: переход на «HEAD~2».
...
HEAD сейчас на 06f7fc0 Первый коммит

Мы переместились на первый коммит. Вернемся:

$ git checkout main

Заключение

Это была первая статья по обучению git. Мы установили git и научились работать с некоторыми командами для создания коммитов и навигации по ним.

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

Если все получилось, переходите к следующему уроку 👇

Ветки и слияние веток в Git
В этой статье разбираемся с понятием веток в git. Как смержить одну ветку в другую, и чем отличается Merge от Rebase.

Struchkov Mark
Struchkov Mark
Задавайте вопросы, если что-то осталось не понятным👇