Практическое руководство по использованию Git. Часть 1

1
1914
views

Перевод первой части статьи «How to Use Git and Git Workflows – a Practical Guide».

Все говорят, что разработчику нужно знать Git — и это правда. Но будем откровенны: Git не так прост.

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

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

Я считаю, что как и многие другие вещи в программировании, Git лучше всего изучать на практике. Начните пользоваться инструментом — и постепенно освоите и основы, и продвинутый функционал.

В этом руководстве мы именно так и поступим. Чтобы изучить Git с нуля и понять, как с его помощью осуществляется коллективная работа, мы разберем несколько практических примеров.

Мы будем использовать простые команды и разбирать стоящие за ними концепции — но только в том объеме, который способствует лучшему пониманию.

Безусловно, в этой статье мы не сможем рассмотреть все аспекты работы с Git, но более сложные вещи вы освоите самостоятельно в процессе работы.

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

https://www.atlassian.com/git/tutorials/using-branches/git-checkout

В первой части статьи мы рассмотрим следующие темы:

Я советую вам испытывать все описываемые команды самостоятельно, на вашей машине. Итак, начнем!

Как установить Git и настроить аккаунт на GitHub

Начнем с рутинных, но необходимых вещей.

Если вы уже установили Git на своей машине, завели аккаунт на GitHub (или GitLab, или Bitbucket) и настроили SSH-соединение, то этот раздел можно пропустить.

Если нет, для начала нужно установить Git.

В этом туториале мы будем использовать GitHub. Зарегистрироваться на этом сайте можно по ссылке.

Заведя аккаунт, нужно создать SSH-ключ. Это позволит вам отправлять ваш код с вашей локальной машины на GitHub (при помощи ключа GitHub будет проверять, что вы — это вы). Это не сложно: просто следуйте инструкции.

Как создать новый репозиторий на GitHub

Следующее, что нам нужно сделать, это создать репозиторий на GitHub.

Это просто. Нажмите кнопку «New» на вашей домашней странице:

Затем выберите имя для репозитория и укажите, будет он приватным или публичным. Опционально можно добавить файл README. Затем нажмите кнопку «Create repository».

Свой репозиторий я назвал practical-git-tutorial. В нем содержатся все шаги, описанные в этом руководстве, так что при желании можете пользоваться им как справочником.

Как клонировать Git-репозиторий

Чтобы начать работать, нам нужно «клонировать» репозиторий. Клонирование означает скачивание из источника всего кода проекта, а также метаданных. Источником в нашем случае является GitHub.

Чтобы клонировать репозиторий, используйте команду git clone <URL>.

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

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

$ git clone git@github.com:johnmosesman/practical-git-tutorial.git
Cloning into 'practical-git-tutorial'...
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 6 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (6/6), done.

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

Давайте перейдем в папку проекта (при помощи команды cd):

$ cd practical-git-tutorial/
/practical-git-tutorial (main)$

Мы перешли в папку проекта (это обычная папка, такая же, как все остальные). Ваш терминал может выводить рядом с именем папки слово (main).

Ветки в Git

Наличие (main) рядом с именем папки означает, что в настоящее время мы находимся в ветке под названием main. Что такое ветка (англ branch)? Считайте, что это копия проекта в определенный момент времени, которую можно изменять независимо от других веток.

Рассмотрим пример. Если бы мы писали книгу, у нас могли бы быть следующие ветки:

  • main branch (главная ветка)
  • table-of-contents branch (ветка содержания)
  • chapter-1 branch (ветка первой главы)
  • chapter-2 branch (ветка второй главы)
  • и так далее.

Ветка main — главная. Это место, где мы будем собирать все содержимое воедино, в законченную книгу.

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

Допустим, я работаю над Главой 1, а вы — над Главой 2. Мы можем создать две разные ветки, chapter-1 и chapter-2, каждая из которых будет отдельной копией текущего состояния книги.

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

Когда один из нас закончит работу над своей главой, он сможет добавить содержимое этой главы обратно в ветку main. Когда мы оба закончим работу, в ветке main будет и Глава 1, и Глава 2.

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

Примечание. Главная ветка может называться не main, а master: это зависит от проекта. Функциональной разницы нет.

Как посмотреть состояние проекта в Git

Работая над проектами, мы довольно часто проверяем их состояние и смотрим, какие изменения были внесены.

Чтобы просмотреть статус нашего проекта, мы используем команду git status:

(main)$ git status
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean

Давайте разберем вывод команды.

Первое, что нам сообщает git status, это то, что мы находимся в ветке main:

On branch main

Второе предложение немного интереснее:

Your branch is up to date with 'origin/main'.

Git сообщает, что наша ветка полностью соответствует некоему origin/main.

Это новая концепция. origin (или remote) — удаленный источник, т. е., находящийся не на вашей локальной машине.

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

Вернемся на момент к нашему примеру с книгой. Если бы я писал Главу 1 на своей машине, а вы — Главу 2 на вашей машине, мы могли бы добавить себе компьютеры друг друга в качестве удаленных источников, чтобы обмениваться внесенными изменениями. Ведь одно из главных достоинств Git — контролируемое сотрудничество с другими людьми.

На практике сообщество разработчиков в целом пришло к выводу, что лучше не обмениваться изменениями в стиле «все-со-всеми», а иметь единый источник истины для кода. Текущее состояние кодовой базы в этом источнике считается «правильным», а все остальные с ним сверяются. По соглашению это место называется origin (англ. начало, источник).

В нашем случае origin — репозиторий на GitHub.

На самом деле мы можем посмотреть, что у нас считается origin, запустив команду git remote -v (-v означает «verbose»: информация будет выводиться «многословно», подробно).

(main)$ git remote -v
origin  git@github.com:johnmosesman/practical-git-tutorial.git (fetch)
origin  git@github.com:johnmosesman/practical-git-tutorial.git (push)

Эта команда выводит список всех наших удаленных репозиториев. Мы видим, что у нас есть удаленный репозиторий с именем origin, а его Git URL указывает на наш репозиторий на Github.com. Это было настроено автоматически, когда мы запустили git clone.

Но вернемся к выводу команды git status:

Your branch is up to date with 'origin/main'.

Когда мы запросили статус проекта, Git ответил, что наша локальная ветка main полностью соответствует ветке main в нашем origin (GitHub).

Git «увидел», что в origin, который мы клонировали, в качестве главной ветки была ветка main. И команда git clone автоматически создала для нас локальную ветку main.

В общем, на нашей локальной машине не внесено никаких изменений, которых нет на GitHub, и наоборот. Наша локальная ветка main и ветка main в репозитории на GitHub идентичны.

Когда мы внесем какие-нибудь изменения, в выводе команды git status сообщение уже будет другим. Оно будет отражать разницу между нашим локальным репозиторием и репозиторием origin (GitHub).

Последнее сообщение git status касается состояния локального проекта:

nothing to commit, working tree clean

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

Подобьем итоги. Выполнив команду git status, мы узнали следующие вещи:

  • мы находимся в ветке main
  • наша локальная ветка main идентична ветке main в origin (GitHub)
  • мы еще не внесли никаких изменений в проект.

Как сделать первый коммит

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

Продолжая аналогию с написанием книги, давайте создадим новый файл chapter-1.txt и вставим в него какой-то текст.

(Вы можете выполнить в терминале команды, указанные ниже, или просто создать файл в любом текстовом редакторе).

(main)$ touch chapter-1.txt
(main)$ echo "Chapter 1 - The Beginning" >> chapter-1.txt
(main)$ cat chapter-1.txt
Chapter 1 - The Beginning

Мы создаем файл chapter-1.txt при помощи команды touch и вставляем в него текст «Chapter 1 — The Beginning» при помощи команды echo и оператора перенаправления. Чтобы проверить результат, выводим содержимое файла на экран при помощи команды cat.

У нас есть простой текстовый файл с маленьким текстом.

Давайте теперь запустим git status и посмотрим, что изменится в выводе.

(main)$ git status
On branch main
Your branch is up to date with 'origin/main'.

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        chapter-1.txt

nothing added to commit but untracked files present (use "git add" to track)

Да, кое-что определенно изменилось. Мы видим раздел, описывающий «Untracked files» («Неотслеживаемые файлы»), в котором есть наш файл chapter-1.txt.

Прежде чем Git начнет отслеживать изменения в файле, нам сначала нужно сказать ему делать это. И, как написано в конце сообщения, для этого можно использовать команду git add:

(main)$ git add chapter-1.txt

(Можно не указывать конкретное имя, а просто поставить точку. Таким образом будут «захвачены» все изменения в директории).

Давайте еще раз проверим статус проекта:

(main)$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   chapter-1.txt

john:~/code/practical-git-tutorial (main)$

Сообщение снова изменилось. Теперь в нем говорится, что у нас есть некоторые изменения, готовые к «закреплению» — коммиту.

В Git коммит — это сохраненный кусочек работы. Но это сохранение отличается от сохранения файла в текстовом редакторе.

Можно считать, что коммит — это завершенная идея или юнит работы.

Вернемся к аналогии с книгой. Если бы мы продолжили писать первую главу книги, это могло бы выглядеть так:

  • Написать название главы. /Нажать кнопку «Сохранить» в редакторе/
  • Написать первый абзац. /Снова нажать кнопку «Сохранить»/
  • Написать второй абзац. /«Сохранить»/
  • Написать последний абзац. /«Сохранить»/

Мы «сохранили» документ четыре раза, но после этих четырех сохранений у нас на руках первый черновик нашей главы, а этот черновик — один «юнит» нашей работы.

Мы хотим сохранить этот файл на своем компьютере. Но кроме этого мы также хотим обозначить, что это — юнит завершенной работы (несмотря на то, что всего лишь черновик). Это кусочек труда, который стоит того, чтобы его хранить. Возможно, мы вернемся к нему в будущем и отредактируем. А может, соединим этот черновик с черновиком всей книги.

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

Чтобы закоммиттить наши изменения, сначала их нужно внести в зону стейджинга (подготовки) при помощи команды git add.

(О стейджинге мы еще поговорим).

Затем нам нужно завершить сохранение изменений при помощи команды git commit.

Считается хорошим тоном добавлять описание того, какие именно изменения вы внесли и, что более важно, — зачем вы это сделали.

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

Давайте закоммитим новый файл с сообщением. Чтобы добавить сообщение, припишем к команде флаг -m (от message).

(main)$ git commit -m "New chapter 1 file with chapter heading"
[main a8f8b95] New chapter 1 file with chapter heading
 1 file changed, 1 insertion(+)
 create mode 100644 chapter-1.txt

Мы сохранили этот кусочек работы и можем увидеть это, просмотрев лог Git при помощи команды git log:

(main)$ git log
commit a8f8b95f19105fe10ed144fead9cab84520181e3 (HEAD -> main)
Author: John Mosesman <johnmosesman@gmail.com>
Date:   Fri Mar 19 12:27:35 2021 -0500

    New chapter 1 file with chapter heading

commit 2592324fae9c615a96f856a0d8b8fe1d2d8439f8 (origin/main, origin/HEAD)
Author: John Mosesman <johnmosesman@users.noreply.github.com>
Date:   Wed Mar 17 08:48:25 2021 -0500

    Update README.md

commit 024ea223ee4055ae82ee31fc605bbd8a5a3673a0
Author: John Mosesman <johnmosesman@users.noreply.github.com>
Date:   Wed Mar 17 08:48:10 2021 -0500

    Initial commit

Глядя на этот лог, мы видим, что у нас в истории проекта есть три коммита.

Последний из них — тот, который мы только что сделали. Мы видим наше сообщение коммита.

До него было еще два коммита: один был сделан, когда я инициализировал проект, а второй — когда обновил файл README.md на GitHub.

Обратите внимание, что к каждому коммиту прилагается длинная строка чисел и букв:

commit a8f8b95f19105fe10ed144fead9cab84520181e3 (HEAD -> main)

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

После SHA коммитов мы видим еще пару интересных вещей:

  • (HEAD -> main) рядом с последним коммитом
  • (origin/main, origin/HEAD) рядом с предпоследним коммитом.

Это говорит нам о текущем статусе нашей ветки и удаленных веток (насколько он нам известен).

Что касается последнего коммита, мы видим, что HEAD указывает на нашу локальную ветку main (HEAD -> main). HEAD — это указатель на текущую ветку.

Это имеет смысл. Мы только сделали коммит и пока больше ничего не делали. Мы все еще во временной точке этого коммита.

Если мы посмотрим на предыдущий коммит, начинающийся с 25923, мы увидим рядом с ним (origin/main, origin/HEAD). Это говорит нам о том, что в origin (GitHub) HEAD указывает на наш предыдущий коммит.

В общем, наша машина думает, что последнее изменение в локальной ветке main — коммит с добавленной Главой 1. Также наша машина думает, что на GitHub последнее изменение — коммит, которым я обновил README перед написанием этой статьи.

И это понятно: мы же не сообщали GitHub-у о самом последнем нашем коммите. GitHub все еще думает, что репозиторий соответствует текущему состоянию проекта.

Конец первой части. В следующей части мы отправим наш новый коммит на GitHub.

1 КОММЕНТАРИЙ

ОСТАВЬТЕ ОТВЕТ

Please enter your comment!
Please enter your name here