Содержание

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

Гид для самых маленьких чайников :D

Backend Java Developer
Backend Java Developer
Oбновлена 2021-06-15

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

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

Это статья первая из курса по обучению Git. Остальные статьи курса:

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

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

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

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

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

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

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

Установим git:

  • Windows. Проходим по этой ссылке, скачиваем и устанавливаем.

  • Для Mac OS. Открываем терминал и пишем:

    #Если установлен Homebrew
    brew install git
      
    #Если нет, то вводим эту команду.
    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

Три области Gir

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

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


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 Add Two

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


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

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

Gir Commit Three

Я уже упоминал, что у коммитов есть родитель, который указывает на предыдущий коммит. Из таких цепочек складывается “ветка”. О ветках мы поговорим в следующей статье. Пока достаточно знать, что по умолчанию у нас уже есть ветка 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, важно понять различные способы перемещения по коммитам вашего проекта.

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

У 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 Второй коммит 

Визуализация Detaching

Таким образом мы переключились на состояние второго коммита, в котором у нас еще не было файла 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 поддерживает относительные ссылки. С относительными ссылками можно начать с какого-либо удобного места и двигаться от него.

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

  • Перемещение на один коммит назад ^
  • Перемещение на коммитов назад ~<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 локально и не потерять изменения, об остальных командах читайте в остальных статьях.

Комментарии