Перейти к основному содержимому

Git для начинающих: полное практическое руководство

· 17 мин. чтения

Задача: Медиана двух отсортированных массивов

Даны два отсортированных массива размерами n и m. Найдите медиану слияния этих двух массивов.
Временная сложность решения должна быть O(log(m + n)) ...

ANDROMEDA

1. Обзор

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

Мы начнем с установки и настройки, а затем создадим наш первый локальный репозиторий. Далее мы узнаем, как фиксировать изменения и синхронизировать их с удаленным репозиторием.

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

2. Что такое Git?

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

В отличие от своего основного конкурента — SVN , Git также реализует систему распределенного рабочего процесса. Это означает, что у каждого разработчика, работающего с Git, есть локальная копия всего репозитория. Git также позволяет работать асинхронно без постоянного подключения к центральному репозиторию.

3. Установка Git

Мы можем установить Git на самые распространенные операционные системы, такие как Windows, Mac и Linux. Фактически, на большинстве компьютеров Mac и Linux Git установлен по умолчанию.

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

$ git version
git version 2.24.3 (Apple Git-128)

Кроме того, Git поставляется со встроенными инструментами с графическим интерфейсом для фиксации ( git-gui ) и просмотра ( gitk ). Существует также множество сторонних инструментов или плагинов IDE, которые расширяют возможности.

4. git help — удобное руководство

Прежде чем мы создадим наш первый репозиторий, давайте запустим команду git help . Он отображает полезную информацию о самом Git :

$ git help
usage: git [--version] [--help] [-C <path>] [-c <name>=<value>]
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
[-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
<command> [<args>]
...

Мы также можем проверить руководство для конкретной команды несколькими способами:

$ git --help init
$ git help init
$ git init --help

Все три приведенных выше варианта возвращают одинаковый результат.

С опцией -g мы также можем получить доступ к списку внутренних руководств для развития наших навыков :

$ git help -g
The common Git guides are:
attributes Defining attributes per path
cli Git command-line interface and conventions
core-tutorial A Git core tutorial for developers
...
$ git help core-tutorial

Чтобы распечатать учебник, нам нужно указать его имя в качестве параметра.

5. git config — Настройка Git

После установки Git мы можем легко настроить его с помощью команды git config , которая позволяет управлять параметрами .

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

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

Локальная конфигурация специфична для одного репозитория, и это уровень по умолчанию, который использует Git, когда мы не передаем какой-либо параметр команде git config .

Уровни рабочего дерева и файлов — это более продвинутые уровни конфигурации, которые можно применить к одной ветке или файлу в репозитории.

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

В качестве примера давайте настроим наше имя пользователя, используемое в истории коммитов:

$ git config --global user.name "ForEach User"

Мы только что установили наше имя глобально.

Чтобы переопределить параметр для одного репозитория, мы можем использовать флаг –local в его каталоге.

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

$ git config -l
user.name=ForEach User

Мы можем выполнить команду git –help config , чтобы получить подробную информацию обо всех доступных параметрах.

6. Создание репозитория

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

6.1. git init — инициализировать новый репозиторий

Если мы решим инициализировать новый репозиторий, нам нужно использовать команду git init . Он превращает текущий каталог в репозиторий Git и начинает отслеживать его содержимое:

$ mkdir simple-repo; cd simple-repo; git init
Initialized empty Git repository in /simple-repo/.git/

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

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

$ git remote add origin https://github.com/foreach/tutorials.git

Мы только что добавили новый удаленный сервер с именем origin и подключили его к официальному репозиторию ForEach GitHub.

6.2. git clone — клонировать внешний репозиторий

Иногда мы хотим внести свой вклад в существующий репозиторий. Во-первых, нам нужно загрузить существующий репозиторий локально.

Команда git clone клонирует репозиторий в новый каталог :

$ git clone https://github.com/foreach/tutorials.git
Cloning into 'repo'...

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

Кроме того, клонированный репозиторий уже настроен и подключен к внешнему источнику:

$ cd tutorials
$ git remote -v
origin https://github.com/foreach/tutorials.git (fetch)
origin https://github.com/foreach/tutorials.git (push)

Git будет использовать эти исходные ссылки для управления любыми дальнейшими изменениями.

7. Рабочий процесс Git

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

Наш локальный репозиторий состоит из трех разных деревьев, поддерживаемых Git.

Первый — рабочий каталог, в котором хранятся актуальные версии файлов.

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

Наконец, мы можем применить и сохранить наши изменения в локальном репозитории с помощью команды git commit . Фиксация изменений обновляет HEAD репозитория, который всегда указывает на последнюю сделанную нами фиксацию.

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

./f027c5c440873b163149bd5fc7c330a0.png

  1. Внесение изменений

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

8.1. git status — Показать текущие изменения

Давайте создадим простой файл и добавим его в наш репозиторий. После этого мы выполняем команду git status и анализируем ее вывод:

$ "Hello World" >> myfile.txt
$ git status
On branch master
Your branch is up to date with 'origin/master'.

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

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

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

Далее в выводе отображается состояние рабочего дерева — список измененных в данный момент файлов со статусом их обслуживания. Как мы видим, файл myfile.txt в настоящее время находится в области рабочего каталога и не отслеживается Git.

8.2. git add — отслеживать изменения

Чтобы начать отслеживать изменения, нам нужно переместить их в индекс с помощью команды git add :

$ git add myfile.txt
$ git stage *

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

В качестве альтернативы мы также можем использовать команду git stage , которая является синонимом команды git add .

Давайте теперь проверим статус:

$ git status
On branch master
Your branch is up to date with 'origin/master'.

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

Как мы видим, Git начал отслеживать наши файлы.

8.3. git restore & gitignore — отменить отслеживание изменений

Git позволяет удалять файлы из индекса . Если мы по ошибке переместили в него свои изменения и хотим временно отключить их отслеживание, используем git restore :

$ git restore -S myfile.txt
$ git status
On branch master
Your branch is up to date with 'origin/master'.

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

Мы только что снова переместили наш файл в рабочую область и исключили его из дальнейших коммитов до тех пор, пока мы снова не подготовим его. Флаг -S ( –staged ) указывает Git восстановить только индекс репозитория .

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

8.4. git commit — сохранить изменения

Давайте вернём последние изменения и снова переместим наш файл в Staging Area :

$ git add myfile.txt

Теперь пришло время сохранить нашу работу, поэтому нам нужно сделать коммит.

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

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

$ git commit -m "My first commit"
[master 8451901] My first commit
1 file changed, 1 insertion(+)
create mode 100644 myfile.txt

Мы только что создали наш первый коммит локально.

Команда git commit содержит множество дополнительных опций для выполнения более сложных операций, которые мы можем проверить с помощью команды git commit –help .

Наиболее полезным является флаг -m , который указывает сообщение фиксации, описывающее изменения, сделанные в текущем моментальном снимке.

Наконец, давайте проверим статус:

$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)

nothing to commit, working tree clean

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

8.5. git log & git show — проверка коммитов

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

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

$ git log
commit 845190154ed7a491a6143669c4ce88058fb93f8a (HEAD -> master)
Author: ...
Date: ...

My first commit

commit 9a1e11ec981b41e4b4b9c245a7a96cd6707f4705 (origin/master, origin/HEAD)
...

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

Каждая запись содержит общие метаданные, такие как идентификатор коммита (уникальная контрольная сумма SHA-1), автор, дата и заданное сообщение.

Когда мы хотим углубиться в отдельный коммит, мы печатаем его детали, используя команду git show , за которой следует запрошенный идентификатор коммита:

$ git show 845190154ed7a491a6143669c4ce88058fb93f8a
commit 845190154ed7a491a6143669c4ce88058fb93f8a (HEAD -> master)
Author: ...
Date:...

My first commit

diff --git a/myfile.txt b/myfile.txt
new file mode 100644
index 0000000..557db03
--- /dev/null
+++ b/myfile.txt
@@ -0,0 +1 @@
+Hello World

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

8.6. git stash `` — Отложить изменения

Команда git stash временно откладывает сделанные нами изменения , возвращая рабочий каталог в соответствие с фиксацией HEAD . Это позволяет нам быстро переключать контекст и начинать работать над чем-то другим.

Давайте создадим еще один файл и добавим его в Staging Area . После этого запускаем git stash :

$ touch myfile-2.txt; git add *
$ git stash push
Saved working directory and index state WIP on master: 8451901 My first commit

Теперь давайте попробуем перечислить файл:

$ ls myfile-2.txt
ls: myfile-2.txt: No such file or directory

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

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

$ git stash list
stash@{0}: WIP on master: 8451901 My first commit

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

Чтобы проверить его детали, мы используем опцию show :

$ git stash show
myfile-2.txt | 0
1 file changed, 0 insertions(+), 0 deletions(-)

На выходе выводится информация об изменениях, хранящихся в последнем тайнике.

Наконец, если мы хотим восстановить изменения, мы используем опцию pop :

$ git stash pop
...
$ ls myfile-2.txt
myfile-2.txt

Мы только что удалили одно спрятанное состояние из списка сундуков и применили его поверх текущего состояния.

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

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

9.1. git commit –amend — добавить дополнительные изменения в коммит

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

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

Давайте рассмотрим последний коммит:

$ git show --summary
commit 845190154ed7a491a6143669c4ce88058fb93f8a (HEAD -> master)
Author: ...
Date: ...

My first commit

create mode 100644 myfile.txt

Получив наш файл my-file2.txt из тайника, давайте зафиксируем его, используя опцию исправления :

$ git commit --amend
[master 0ed9f03] My first commit
2 files changed, 1 insertion(+)
create mode 100644 myfile-2.txt
create mode 100644 myfile.txt

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

9.2. git rebase — повторное применение коммитов

Более продвинутый способ изменения коммитов — использование команды git rebase . Он повторно применяет коммиты из истории поверх другой базы , что позволяет нам изменять их на лету.

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

$ touch myfile-3.txt
$ git add *
$ git commit -m "My second commit"

Теперь у нас должно быть два одиночных коммита — Мой первый коммит и Мой второй коммит .

Начнем перебазировать оба коммита в интерактивном режиме:

git rebase -i HEAD~2

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

pick 82d8635 My first commit
pick 6d58108 My second commit

# Rebase 9a1e11e..82d8635 onto 9a1e11e (2 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# d, drop <commit> = remove commit
...

Вверху у нас есть список коммитов ребазинга, за которым следует руководство. У нас есть много вариантов здесь. Мы можем изменить порядок, поменяв местами строки, или перефразировать сообщение коммита, или сжать их в одно, отредактировать или даже удалить один коммит. Строки инструкций будут применяться сверху вниз.

9.3. git reset — откат к определенному состоянию

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

$ git reset 82d8635

Он отменяет все фиксации после указанной фиксации , сохраняя изменения локально и перемещая их в Staging Area . Но если мы хотим отказаться от всех рабочих изменений, мы можем использовать флаг –hard .

10. Синхронизация репозитория

После локальной работы над репозиторием пришло время опубликовать наши изменения.

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

10.1. git fetch — обновить ссылки

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

В этом нам поможет команда git fetch :

$ git fetch

Это загружает объекты и ссылки из исходного репозитория .

Следует отметить, что это действие никогда не модифицирует текущее рабочее дерево . Это связано с тем, что мы можем просматривать обновленную историю коммитов только для нашего репозитория. Если мы найдем какие-либо ожидающие изменения, мы должны пойти дальше.

10.2. git merge — применить входящие изменения

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

Обновим нашу ветку:

$ git merge origin/master

Команда git merge — очень мощный инструмент. Он загружает все новые изменения из данной ссылки и объединяет их с текущим рабочим деревом, выбирая правильную стратегию слияния. Многие изменения будут применены автоматически, даже если модификации существуют в одних и тех же файлах.

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

10.3. git pull — обновить и применить сразу

Команда git pull — это не что иное, как git fetch и git merge , объединенные в одно:

$ git pull origin/master

Он проверяет заданную ветку на наличие последних изменений и объединяет их с текущей веткой так же, как это делают git fetch и git merge . Это наиболее распространенный способ обновления текущей ветки.

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

10.4. git push — Публикация локальных коммитов

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

Выполним команду git push :

$ git push origin master

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

Наконец, проверяем историю:

$ git log
commit 6d5810884c3ce63ca08084959e3a21405a1187df (HEAD -> master, origin/master, origin/HEAD)
Author: ...
Date: ...
My second commit

Были сделаны! Мы только что отправили наши изменения в удаленный репозиторий.

11. Ветвление Git

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

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

11.1. git ветка — Управление ветками

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

$ git branch new-branch

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

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

$ git branch --list --all
* master
new-branch
remotes/origin/HEAD -> origin/master
remotes/origin/master

Если мы хотим удалить локальную ветку, мы выполняем:

$ git branch -d new-branch

11.2. git checkout — изменить текущую ветку

Если мы хотим переключить текущую ветку, мы используем функции git checkout или git switch :

$ git switch new-branch
Switched to branch 'new-branch'
$ git checkout master
Switched to branch 'master'

Мы только что перешли с master на new-branch, а затем снова вернулись к master , используя обе команды.

Хотя обе работают одинаково, команда git switch просто позволяет переключать ветки. Напротив, git checkout — более сложная команда, позволяющая нам дополнительно управлять рабочими файлами дерева, сбрасывать ветки или возвращать файлы к определенным версиям.

12. Заключение

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

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