= Git: основные команды
:title-separator: {sp}|
:category: Программирование
:tags: программирование, git
:toc:

== Ссылки

* https://githowto.com/ru/changes_not_files[GitHowTo]
* https://git-scm.com/book/ru/v2[ProGit]
* https://github.com/maxliscia/git-pocket[Git Pocket Guide]

== Установка

В Debian/Ubuntu:

[source,sh]
----
sudo apt-get install git
----

== Термины

[width="100%",cols="18%,20%,62%",options="header",]
|===
|Термин |Англ |Определение
|Рабочий каталог |working tree, working directory |Набор файлов в
текущем каталоге

|Репозиторий |repository, repo |Контейнер, хранящий историю изменений
файлов проекта

|Индекс |index, staging area |Область между рабочим каталогом и
репозиторием, в котором осуществляется подготовка к фиксации

|SHA-1 |SHA-1 |Уникальный идентификатор, отражающий информацию об
истории

|Ветка |branch |Именованная последовательность в истории изменений

|Фиксация (коммит) |commit |Набор файлов, записанных в историю
одновременно

|`HEAD` |`HEAD` |Имя ссылки на последнюю фиксацию в текущей ветке

|Метка |tag |Именованная ссылка на некоторую фиксацию в истории
|===

== Состояния

Файлы в рабочем каталоге могут отслеживаться системой контроля версий
(tracked) или нет (untracked). Отслеживаемые файлы, которые на диаграмме
обозначены зелёным фоном, могут быть неизменёнными (unmodified),
изменёнными (modified) или подготовленными к фиксации (staged).


[.text-center]
.Состояния
[plantuml]
----
@startuml
participant untracked as "Неотслеживамые\n(untracked)"
box "Отслеживаемые"
participant staged as "Подготовленные к фиксации\n(staged)" #55FF55
participant unmodified as "Неизменённые\n(unmodified)" #99FF99
participant modified as "Изменённые\n(modified)" #77FF77
end box

untracked -> staged : git add
staged -> unmodified : git commit
unmodified -> modified : редактирование
modified -> staged : git add
modified -> untracked: git rm --cached
unmodified -> untracked: git rm --cached
staged -> untracked: git rm --cached
@enduml
----

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


[.text-center]
.Команды
[plantuml]
----
@startuml
participant workspace as "Рабочий каталог\n(working dir)"
participant index as "Индекс\n(index)" #77FF77
participant local as "Локальный репозиторий\n(local repository)" #FF7777
participant remote as "Удалённый репозиторий\n(remote repository)" #7777FF

workspace -> local : git commit -a
workspace -> index : git add (-u)
index -> local : git commit
local -> remote : git push

== Обновление с сервера ==

remote -> workspace : git pull (rebase)
remote -> local : fetch

== Откат изменений ==
local -[#red]> workspace : git checkout HEAD
index -[#red]> workspace : git checkout

== Сравнение ==
local -[#blue]> workspace : git diff HEAD
index -[#blue]> workspace : git diff

@enduml
----

== Настройка

[width="100%",cols="15%,35%,50%",options="header",]
|===
|Команда |Ключи |Описание
|`git config` |`--global user.name "John Doe"` |Имя текущего
пользователя

|`git config` |`--global user.email "mail@example.com"` |Почта текущего
пользователя

|`git config` |`--list` |Вывод текущей конфигурации

|`git config` |`--global --list` |Вывод глобальной конфигурации
|===

== Инициализация

[width="100%",cols="15%,35%,50%",options="header",]
|===
|Команда |Ключи |Описание
|`git init` |`<dir>` |Создать пустой репозиторий в каталоге `<dir>`

|`git clone` |`<giturl>` `<dir>` |Создать в каталоге `<dir>` копию
репозитория, находящегося по адресу `<giturl>`

|`git clone` |`--recursive <giturl>` `<dir>` |Создать в каталоге `<dir>`
копию репозитория, находящегося по адресу `<giturl>`, с учётом
подмодулей
|===

== Подмодули

[width="100%",cols="15%,25%,60%",options="header",]
|===
|Команда |Ключи |Описание
|`git submodule` |`add <giturl> <dir>` |Добавить в каталог `<dir>`
текущего рапозитория подмодуль, находящийся по адресу `<giturl>`

|`git submodule` |`update --recursive --remote` |Обновить подмодули

|`git submodule` |`sync --recursive` |Заменить адреса подмодулей на
указанные в файле `.gitmodules`
|===

Удаление подмодуля:

[source,sh]
----
git submodule deinit <path/to/submodule>
git rm <path/to/submodule>
----

== Фиксация

[width="100%",cols="15%,25%,60%",options="header",]
|===
|Команда |Ключи |Описание
|`git add` |`<filename>` |Подготовить файл `<filename>` к фиксации

|`git commit` | |Зафиксировать подготовленные файлы

|`git commit` |`-a` |Зафиксировать все отслеживаемые файлы, которые были
изменены

|`git rm` |`<filename>` |Удалить файл из индекса и рабочего каталога

|`git rm` |`-f <filename>` |Force deletion of files from disk

|`git rm` |`--cached <filename>` |Untrack file (without deleting)
|===

== Информация

[width="100%",cols="15%,25%,60%",options="header",]
|===
|Команда |Ключи |Описание
|`git status` |`-s`            |Вывод информации о рабочем каталоге в
краткой форме

|`git log` |`--oneline` |Вывод журнала изменений в краткой форме

|`git ls-files` | |Вывод списка отслеживаемых и подготовленных файлов
|===

== $push branches (see tags for pushing tags)

[width="100%",cols="15%,25%,60%",options="header",]
[cols=",,",options="header",]
|===
|Команда |Ключи |Описание
|`git push` |`<remotename> <branchname>` |Push branch to remote
|`git push` |`<remotename> --all` |Push all branches to remote
|`git push` |`--d <remotename> <branchname>` |`--delete` remote branch
|===

== $remote

* Remote connections are like bookmarks named after remote repos
* `git clone` automatically creates a remote connection usually called
`origin`

[width="100%",cols="15%,25%,60%",options="header",]
[width="100%",cols="36%,26%,38%",options="header",]
|===
|Команда |Ключи |Описание
|`git remote` |`-v` |List remote repository endpoints

|`git branch` |`-r` |List remote repository branches

|`git remote` |`add <name> <url>` |Create namespaced connection to a
remote repository

|`git remote` |`rename <oldname> <newname>` |Rename connection

|`git remote` |`rm <name>` |Remove connection

|`git remote` |`add origin <url>` |Set remote origin
|===

Reference: https://git-scm.com/docs/git-remote

*Remove remote origin:*

[source,shell]
----
# Remove `origin` settings from .git/config
git remote rm origin

# Remove `FETCH_HEAD` which still points to remote
git rm .git/FETCH_HEAD
----

== $fetch-pull

[width="100%",cols="15%,25%,60%",options="header",]
|===
|Команда |Ключи |Описание
|`git fetch` |`<remote>` |Fetch all branches from remote (without merge)
|`git fetch` |`<remote> <branch>` |Fetch specific branch
|`git merge` |`<remote>/<branch>` |Merge fetched remote
|`git pull` |`<remote>` |Fetch and merge in one command
|===

Reference: https://git-scm.com/docs/git-fetch,
https://git-scm.com/docs/git-pull

== $branch

[cols=",,",options="header",]
|===
|Команда |Ключи |Описание
|`git branch` | |List branches

|`git branch` |`<branchname>` |Create new branch

|`git checkout` |`<sha-1>` |Switch to branch, or commit

|`git branch` |`-m <branchname> <newname>` |Rename branch

|`git merge` |`<branchname>` |Merge changes from `<branchname>` to
current branch

|`git branch` |`-d <branchname>` |`--delete` branch
|===

Reference: https://git-scm.com/docs/git-branch

== $diff

[cols=",,",options="header",]
|===
|Команда |Ключи |Описание
|`git diff` | |Compare *`working directory`* and *`index`*
| |`–-cached` |Compare *`index`* and *`latest commit`*
| |`HEAD` |Compare *`latest commit`* and *`working directory`*
| |`--stat` |Optional short format
| |`<sha-1> <sha-1>` |2 points in time to compare
| |`<dir> | <file>` |Compare whole directory or limit to file
|===

Reference: https://git-scm.com/docs/git-diff

*Examples:*

[source,shell]
----
## compare changes made to README.md between working tree (default) and latest commit (HEAD)
$ git diff --stat HEAD ./path/README.md

## compare changes made to README.md between 2 specific points in time (e.g. 2 commits)
$ git diff --stat a649900 24bdd58 ./path/README.md
----

== $tag

[width="100%",cols="36%,26%,38%",options="header",]
|===
|Команда |Ключи |Описание
|`git tag` | |List tags

|`git tag` |`<v1.0.0>` |Create tag, from latest commit, lightweight

|`git tag` |`-a <v1.0.0> -m "<msg>"` |Create tag, with `--annotate`,
from latest commit

|`git tag` |`-a <v1.0.0> -m "<msg>" <SHA-1>` |Create tag, with
`--annotate`, from specific commit

|`git tag` |`-d  <v1.1.0>` |`--delete` tag

|`git show` |`<v1.0.0>` |Show tag data and message

|`git checkout` |`<v1.0.0>` |Switch to specific point tag (not editable)

|`git push` |`<remote> <tag>` |Push specific tag to `<remote>`
(recommended)

|`git push` |`<remote> --tags` |Push all tags to `<remote>` (only if
necessary)
|===

Инициализация репозитория в каталоге `dir`:

[source,sh]
----
git init dir
----

Клонирование репозитория `repo`, принадлежащего пользователю `user`, с
сервера `gitlab.2` в каталог `dir`:

[source,sh]
----
git clone git@gitlab.2:user/repo.git dir
----

Просмотр состояния рабочего каталога и репозитория:

[source,sh]
----
git status
----

Краткая форма вывода состояния:

[source,sh]
----
git status -s
----

Добавление файла `README.md` под версионный контроль и подготовка к
фиксации:

[source,sh]
----
git add README.md
----

Удаление файла `README.md` из индекса репозитория:

[source,sh]
----
git rm --cached README.md
----

Зафиксировать файлы, подготовленные к фиксации:

[source,sh]
----
git commit
----

Зафиксировать все отслеживаемые файлы, которые были изменены:

[source,sh]
----
git commit -a
----

Отправить все ветки репозитория на сервер с меткой `origin`:

[source,sh]
----
git push origin
----
