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"

Инициализация репозитория
Теперь вы готовы к работе с 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-project
. Две другие области сохраняют свое содержимое внутри папки .git
в понятном и удобном для git формате, но не понятном для человека.
Считайте Рабочий Каталог
песочницей, где вы можете опробовать изменения перед тем, как сделаете коммит. На данном этапе вы можете в любой момент стереть все изменения и вернуться к последнему коммиту. Они не будут сохранены в истории git.
Чтобы сохранить изменения – сделать коммит. Необходимо сначала добавить содержимое Рабочего Каталога
в так называемый Индекс
– это черновик коммита. Только файлы из Индекс
попадут в коммит.
Без добавления файла в Индекс
у нас не получится создать коммит, проверьте это сами с помощью комманды:
$ git commit -m "Первый коммит"
На ветке main
Начальный коммит
Неотслеживаемые файлы:
file.txt
ничего не добавлено в коммит, но есть неотслеживаемые файлы
Для добавления файлов в Индекс
используется следующая команда:
git add file.txt
Когда у вас много файлов, вы можете добавить их все разом git add --all
.

Теперь сделаем наш первый коммит, выполнив команду 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
, то мы не увидим никаких изменений, так как все три области одинаковые.

Теперь мы хотим внести изменения в файл и сделать новый коммит. Мы пройдём через ту же процедуру: сначала отредактируем файл в нашем рабочем каталоге. Давайте называть эту версию файла 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 локально и не потерять изменения, об остальных командах читайте в остальных статьях.
Если все получилось, переходите к следующему уроку 👇
