📱

Get Our Mobile App

Take your business learning on the go!

Download on the App StoreGet it on Google Play

Как использовать Git? Практическое руководство | Уроки Git

Egor Repnikov20:45

Transcription

В данном видео мы разберём на практических примерах, что может Git и как его использовать. Всем привет! Меня зовут Егор.

Гид в современном мире разработки является одним из самых распространённых инструментов. Большая часть компаний и Open Source сообществ его использует, и, в принципе, в этом нет ничего странного. Система контроля версии сильно упрощает разработку, особенно если речь идёт о разработке в команде.

Несведущий человек может спросить: зачем это вообще нужно? Случаев использования может быть множество. Опять же, когда речь идёт о разработчиках, Git способен обеспечить удобоваримую параллельную разработку фич и постоянный актуальный доступ к кодовой базе. Версионирование кода помогает корректнее управлять проектом, отслеживать и откатывать изменения. Но и для одного разработчика это может быть очень полезно. Как минимум, при поиске работы потенциальному работодателю может быть важно увидеть код кандидата на том же GitHub, особенно если кандидат без коммерческого опыта разработки. В общем, Git реально является базой, и очень важно его знать.

В данном видео мы разберём наиболее необходимый и часто используемый функционал. Итак, давайте начнём. Начать стоит с настройки. Нужно выставить имя и email, которые будут записываться в коммиты, то есть в коммитах указывается автор. Мы на это ещё посмотрим подробнее. Используем мы для этого команду `git config`. К слову, отмечу несколько более редких случаев, когда нам требуется указать пользователя для отдельного проекта. Для этого используем ту же команду, но без флага `global`. Использовать команду нужно в директории проекта.

Инициализирует репозиторий командой `git init`. Создалась скрытая папка `.git` в ней и хранится информация о репозитории. Добавим тестовый файл с каким-нибудь произвольным контентом. Введём команду `git status` и видим, что только что добавленный файл имеет состояние `untracked`, то есть его история пока не отслеживается. Нужно его добавить. Для того, чтобы добавить его, нужно ввести команду `git add` и название файла. Данная команда переводит файл в состояние `staged`. Это состояние предшествует коммиту. Для создания коммита нужно использовать команду `git commit -m`, где в параметре `-m` указывается название коммита.

Давайте добавим ещё два файла: `second.js` и вложенный в папку `Folder` `ct.js`. `git status` также показывает, что новые файлы не отслеживаются. Добавим тоже. Но для того, чтобы не писать два раза `git add`, можно написать `git add .` или `git add -A`. Эти команды также проставят файлам статус `staged`, включая файлы, вложенные в папки. Также закоммитим результат.

Теперь разберём такое понятие, как удалённый репозиторий. Суть в том, что хранить всё на локальном компьютере может быть неудобно. Во-первых, так отпадает возможность командной разработки. Это несколько небезопасно, так как компьютер может сломаться и так далее. Кейсов может быть множество. Для нашего примера используем GitHub как самую популярную платформу для удалённых репозиториев. Шаги для других популярных решений вроде GitLab, Gitea, Bitbucket будут примерно такие же.

Для начала создадим репозиторий в сервисе. Как видите, GitHub нам сам предлагает инструкцию для использования с готовым репозиторием. Давайте разберём эти команды. Командой `git remote add origin` мы фактически указываем, где находится удалённый репозиторий, куда отправлять данные, откуда принимать и с чем актуализировать. Вторая команда выставляет ветку `main` главной веткой. Этот момент поясню отдельно. Вы главную ветку хоть "пеньком" можете назвать, это ни на что не повлияет, но исторически сложилось, что в Git для главной ветки используется слово `master`. Название для главной ветки `main` в GitHub - это исключительно политика, потому что якобы данное слово у некоторых людей ассоциируется с рабовладельческим. Нас, в принципе, данное слово не сильно пугает, поэтому давайте оставим `master`, будем следовать традициям. Соответственно, вводим `git push -u origin master`. Теперь наш код залит на удалённый репозиторий.

Стоит отметить, что до этого мы лишь добавляли новые файлы, но не редактировали существующие. Давайте попробуем изменить файл `first.js`. Также стоит отметить команду `git log`. Данная команда выводит список коммитов в ветке. Здесь можно увидеть и название коммита, и автора. Для этого и нужно было указать пользователя.

Бывают ситуации, когда нам не нужно, чтобы какие-то конкретные файлы попадали в коммиты и, соответственно, не попадали на постоянной основе, чтобы не нужно было вечно писать `git add` отдельные файлы. Для этого можно использовать `.gitignore` файл. Случаи могут быть разными. К примеру, зачастую среда разработки хранит свои внутренние конфигурационные файлы в папке проекта, им нечего делать в репозитории с исходным кодом. Давайте создадим данный файл и файл, который не должен фиксироваться системой контроля версий, назовём его `hidden.txt`. Введём `git status`. Файл имеет статус `untracked`. Теперь добавим новую запись в `.gitignore`. Опять введём `git status` и вывод пустой. Теперь этот файл будет игнорировать для коммитов. То же самое можно сделать и для директорий. Разберём этот пример: создаём директорию `hidden` и произвольный файл. Видим, что статус фиксирует его наличие. Добавляем запись в `.gitignore` и повторяем ввод `git status`. В результате содержимое директории игнорируется.

Закоммитить пустую папку. Для этого следует использовать файл-маркер `gitkeep`. То есть, фактически, с помощью этого вспомогательного файла мы указываем системе контроля версий отслеживать данную директорию. При появлении других файлов в данной директории `gitkeep` можно удалить, в нём не будет необходимости.

Ветки - это одна из ключевых функций Git, которая и обеспечивает возможность вести параллельную разработку. Это может быть параллельная разработка разных независимых фич для проекта вам самолично, или же ведение разработки несколькими членами команды в рамках одного большого проекта. Ветка - это фактически отдельная независимая область для хранения кода, которая впоследствии может быть слита с главной веткой. Как это может выглядеть? Допустим, у нас два члена команды с двумя независимыми задачами. Коммит в одну ветку могут вызвать конфликты, допустим, в рамках обеих задач затрагивается один и тот же файл. Отдельные области для разработки могут решить данную проблему, а код в главную ветку можно влить последовательно и подконтрольно.

Создадим новую ветку. Для этого используется команда `git checkout -b` и название ветки. Всё, теперь мы в новой ветке, созданной от ветки `master`. Если введём команду `git log`, то увидим коммиты, которые наследовались из ветки `master`. В свою очередь, на создание веток нет никаких ограничений, мы можем сделать новую ветку от только что созданной `test_branch`. Создадим новый файл `first_branch.txt` и закоммитим в новую ветку. Должен быть указан `origin`, то есть на том конце данная ветка ещё не существует, и нам нужно особым образом запушить её. Командой `git push --set-upstream origin master` (название ветки) мы устанавливаем дефолтную удалённую ветку для текущей локальной ветки. Для нас. Давайте посмотрим, появилась ли данная ветка в удалённом репозитории. Да, всё. `--set-upstream` параметр нужно указывать только при первом пуше, в дальнейшем в этом нет необходимости. Давайте на это посмотрим. Добавим новый файл и закоммитим. Всё прошло успешно, и на удалённом репозитории появился данный файл.

Теперь замёржим эту ветку в главную ветку `master`, чтобы там тоже были доступны все последние изменения. Для этого перейдём в ветку `master` командой `git checkout master` и введём `git merge first_branch`. Всё, теперь изменения из ветки `first_branch` влились в ветку `master`. Можем в этом убедиться командой `git log`. Никуда не делась, нужно её удалить. Командой `git branch -d` в ней теперь нет необходимости. Соответственно, вводим `git branch -d first_branch`. Но это удалит ветку лишь локально. Чтобы удалить её в удалённом репозитории, в догонку нужно написать команду `git push origin --delete` и название ветки, в нашем случае опять же `first_branch`. На удалённом репозитории осталась только ветка `master`.

Для дальнейшей темы обсуждения `merge conflicts` может понадобиться проставить ещё одну настройку Git. По умолчанию Git использует как редактор `nano`, но если вы предпочитаете `vim`, то выставить его в качестве дефолтного редактора можно с помощью команды `git config --global core.editor vim`.

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

Для разбора данной ситуации создадим новый файл `merge_conflict_resolving.js` с произвольным содержимым и закоммитим его. Теперь создадим новую ветку `git checkout -b merge_conflict_resolving_1` и изменим этот файл, закоммитим. Вернёмся в ветку `master` и создадим новую ветку `merge_conflict_resolving_2`, где изменим содержимое того же файла и закоммитим. Опять возвращаемся в `master` и мержим первую ветку `merge_conflict_resolving_1`. Всё прошло успешно, изменения применили. Теперь попробуем мержить вторую ветку `merge_conflict_resolving_2` и видим, что Git сообщает нам о конфликте. Здесь мы видим, что в верхней части указано, что находится в текущей ветке, а в нижней части - изменения из ветки, которую мы хотим мержить. Допустим, мы считаем, что изменения из нашей второй ветки более актуальны. Для этого нужно удалить лишний вариант вручную или использовать средства среды разработки. Далее будут следовать команды `git add` и `git merge --continue`. Теперь `merge` прошёл успешно, видим соответствующий коммит и изменения в файле.

В большинстве случаев при командной разработке `merge` как таковой будет происходить не вручную, а в удалённом репозитории через Pull Request или Merge Request. Название будет различаться в зависимости от удалённого репозитория. То есть, под капотом будет происходить всё то же самое, только через веб-интерфейс.

Давайте разберём подобный пример и заодно затронем новую команду `git pull`. Создаём новую ветку `pull_request_example` и добавляем новый файл `pull_request_example.txt`. Далее запушим это на удалённый репозиторий. В данном сообщении Git сам предлагает нам перейти по ссылке создания Pull Request. Воспользуемся ей. Здесь в настройках указано, что ветка будет мержиться в `master`. Это нас устраивает. Pull Request создан. Теперь мержим его и удаляем ветку. Теперь в удалённом репозитории ветка `master` более актуальная, чем локальная, потому что `merge` происходил не в локальном проекте. Вводим `git checkout master`. В `master` пока нет нового файла, и история коммитов пока старая. Итак, чтобы подгрузить данные с удалённого репозитория, используется команда `git pull`. Также не забудем удалить локальную ветку. Удалённую ветку удалять не надо, потому что мы это сделали через GitHub. Теперь появился новый файл, и `git log` показывает новый коммит.

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

Команда `git cherry-pick` нужна для копирования одного или нескольких коммитов из одной ветки в другую. От `merge` это отличается тем, что перенос более точечный. При `merge` ветки окажутся все коммиты, а при `cherry-pick` только те, что мы укажем. Использоваться это может в разных случаях, естественно, достаточно специфичных. Как пример, в проекте может возникнуть критическая ошибка, но в рамках выполнения какой-либо задачи в другой ветке эта ошибка может быть исправлена, и мы можем отдельный коммит вытащить из этой ветки раньше времени. Рассмотрим, как это работает. Создаём новую ветку `cherry_pick_example`, создаём новый файл `cherry_pick_example.js`, коммитим. Пушим? Обойдёмся без этого. Давайте для ясности добавим второй коммит: изменим содержимое `first.js`, закоммитим. Теперь в данной ветке у нас два коммита. Пробуем перенести только первый. Вводим `git log`, берём хэш коммита, переходим в ветку `master` и подставляем хэш в команду `git cherry-pick`.

`git stash` позволяет временно сохранить незавершённый код в кэш. К примеру, мы не хотим коммитить текущий код, но нужно что-то исправить. Добавляем изменения, вводим `git stash`. Изменения перенеслись в кэш, из которого мы в любой момент можем вернуть эти данные командой `git stash pop`. В некоторых случаях это может быть удобно. Допустим, мы наделали каких-то бесполезных изменений и хотим все их откатить разом, а не ходить по каждому файлу отдельно. Команда `git reset --hard` удалит все незавершённые данные.

`git revert` позволяет откатить коммит. Фактически, команда генерирует новый коммит, в котором откатываются все предыдущие изменения. В качестве параметра передаём хэш коммита. Посмотрим, как это выглядит. Также отмечу, что хэшей коммитов можно указать несколько, соответственно, создадутся несколько коммитов, которые откатывают изменения.

`git rebase` может быть достаточно опасным при неправильном использовании. Фактически, `rebase` может использоваться как `merge`, но он, грубо говоря, интегрирует коммиты в текущую ветку, а не наложит их сверху, как при `merge`. Он поменяет саму структуру ветки и историю коммитов. Это может добавить трудности при командной разработке. Давайте посмотрим, как будет выглядеть подобный аналог `merge` по старой схеме. Создаём ветку, новый файл и коммитим. Возвращаемся в ветку `master` и вводим `git rebase rebase_example`. И видим, что новый коммит появился в `master`. А подобной возможности нужно знать, но я рекомендую использовать `merge`.

У `rebase` есть и другая полезная особенность. Как я сказал ранее, `rebase` способен менять структуру ветки. Наиболее интересные особенности - это объединение, переименование и удаление коммитов. Перед тем, как начнём, повторю: подобные манипуляции с веткой достаточно рискованны, удалить что-то лишнее, к примеру, поэтому подобным лучше заниматься лишь в отдельных ветках. В главной ветке `rebase` должен быть под запретом. В том же GitHub и GitLab вообще можно отключить подобную возможность для ряда веток. После данного предостережения можем начать.

Создаём отдельную ветку и добавим три коммита. В одном создадим файл, а в последующих двух его обновим. Давайте сейчас сделаем следующее: последний, то есть третий коммит, мы удалим, первый и второй объединим, а затем переименуем. Вводим команду `git rebase -i HEAD~3`. В данном случае количество коммитов начиная с последнего. В данном случае мы добавили как раз три коммита. Как видите, Git сам предоставляет инструкцию: `pick` означает просто оставить коммит, `drop` - удалить. Давайте удалим последний. К слову, отмечу, можно использовать маркер `drop`, а можно удалить строчку из файла, и будет тот же результат. Теперь объединим второй коммит с первым. Для этого ставим маркер `squash` у второго коммита, а у первого нужно поменять название - это маркер `reword`. Вводим. Сейчас нужно поменять название. Здесь можно поменять сообщение при объединении коммитов при необходимости. Видим в истории теперь один коммит с указанным ранее названием, а в файле содержимое, которое было добавлено только во втором коммите.

Отмечу ещё одну интересную команду для изменения коммитов, которое может быть полезно в ряде случаев: `git commit --amend`. В данном случае `git commit --amend` позволяет добавить код в рамках предыдущего коммита с изменением сообщения. `git commit --amend --no-edit` оставит у коммита прошлое сообщение.

Поскольку `rebase` подразумевает изменение ветки, то может произойти ситуация, когда мы модифицируем коммиты, которые уже есть на удалённом репозитории. В данном случае команда `git push` не сработает. Фактически, это тот же конфликт. Единственное, что можно сделать в этой ситуации, если изменения обязательно должны быть залиты именно в текущем виде, это использовать команду `push --force`. `git push -f`. Само собой, это максимально рискованно, поэтому нужно несколько раз подумать перед тем, как использовать эту команду.

Под конец покажу ещё вариант вывода команды `git log`. Сейчас у нас уже набралась история коммитов, и это будет выглядеть нагляднее. Команда `git log --graph` показывает историю коммитов в виде графа с учётом мерж веток. Есть ещё один параметр `oneline`, если нам нужно видеть только название коммитов.

Консоль на утилиту `tig` с её помощью можно более удобным образом просматривать историю коммитов с кодом. Давайте установим и посмотрим, как это выглядит. На самом деле, есть ещё ряд моментов Git, которые я не упомянул, но при использовании Git на практике вы быстро закроете эти пробелы. Всю основную базу я в данном видео разобрал. Зачастую Git сам даёт рекомендации при вызове той или иной команды, советую их не игнорировать.

Надеюсь, данный материал был вам полезен. Подписывайтесь, если вам понравилось данное видео. А на этом я закончу. Всем пока!