LEARN X · ЗА 14 МИН

Git

Git за 14 минут: настройка, три состояния, коммиты, ветки, удалёнки, конфликты, stash, rebase, теги и спасательные команды — всё в комментариях.

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

1. Что такое Git и настройка

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

git --version                       # проверить, что Git установлен

# Глобальная настройка (один раз на машину). Эти данные уйдут в каждый коммит:
git config --global user.name "Иван Петров"
git config --global user.email "[email protected]"

git config --global init.defaultBranch main   # имя ветки по умолчанию — main
git config --global core.editor "nano"        # редактор для сообщений коммитов
git config --list                   # показать всю текущую конфигурацию

# Создать новый репозиторий в текущей папке:
git init                            # появится скрытая папка .git — это и есть репозиторий

# В Git # начинает комментарий ТОЛЬКО в шелле, не внутри Git.
# Внутри сообщений коммитов строки, начинающиеся с #, по умолчанию игнорируются.

2. Три состояния

Ключ к пониманию Git — три «зоны», через которые проходит файл. Изменение движется слева направо.

# 1) Рабочая директория (working directory) — ваши файлы на диске, как есть.
# 2) Индекс / staging area — "корзина" с тем, что попадёт в СЛЕДУЮЩИЙ коммит.
# 3) Репозиторий (.git) — зафиксированная история коммитов, снимки во времени.

# Путь изменения:
#   правка файла   ->  git add   ->  git commit
#   (working dir)      (индекс)      (репозиторий)

# Файл бывает в одном из состояний:
#   untracked  — Git его ещё не отслеживает (новый файл)
#   modified   — отслеживается, изменён, но не добавлен в индекс
#   staged     — изменения добавлены в индекс, ждут коммита
#   committed  — зафиксирован в истории

# Идея: индекс позволяет собрать коммит ВРУЧНУЮ из части изменений,
# а не фиксировать всё подряд.

3. Базовый цикл

90% повседневной работы — это три команды: посмотреть, добавить, зафиксировать.

git status                          # что изменено, что в индексе, на какой ветке
git status -s                       # короткий вид: ?? новый, M изменён, A добавлен

git add file.txt                    # добавить один файл в индекс
git add src/                        # добавить целую папку
git add .                           # добавить ВСЕ изменения в текущей папке
git add -p                          # добавить по кускам (интерактивно, по hunk'ам)

git restore --staged file.txt       # убрать файл из индекса (изменения на диске целы)

git commit -m "Добавил форму логина" # зафиксировать индекс в историю
git commit -am "Правка стилей"      # add + commit для уже отслеживаемых файлов
git commit                          # без -m: откроется редактор для длинного сообщения

# Хорошее сообщение: повелительное наклонение, по сути.
# "Исправь падение при пустом вводе", а не "исправлено падение".

4. Просмотр истории

git log                             # полная история коммитов (q — выйти)
git log --oneline                   # по одной строке на коммит
git log --oneline --graph --all     # ASCII-граф всех веток — очень наглядно
git log -5                          # последние 5 коммитов
git log --author="Иван"             # коммиты конкретного автора
git log -p file.txt                 # история изменений конкретного файла

git diff                            # что изменено, но НЕ добавлено в индекс
git diff --staged                   # что в индексе и попадёт в коммит
git diff main feature               # разница между двумя ветками
git diff HEAD~1 HEAD                 # разница между предпоследним и последним коммитом

git show                            # показать последний коммит целиком (с дельтой)
git show a1b2c3d                    # показать конкретный коммит по хешу
git show HEAD:file.txt              # содержимое файла на момент последнего коммита

# HEAD — указатель на текущий коммит. HEAD~1 — на один назад, HEAD~2 — на два.

5. Отмена изменений

Три разные команды для трёх разных ситуаций. Главное — не путать их.

# Отменить правки в рабочей директории (вернуть файл как в последнем коммите):
git restore file.txt                # ВНИМАНИЕ: несохранённые правки потеряются

# Убрать из индекса, но оставить правки на диске:
git restore --staged file.txt

# git reset двигает HEAD/ветку назад. Три режима:
git reset --soft HEAD~1             # отменить коммит, изменения остаются в индексе
git reset HEAD~1                    # (--mixed, по умолчанию) изменения в рабочей директории
git reset --hard HEAD~1            # отменить коммит И стереть изменения (ОПАСНО)

# git revert — безопасная отмена: создаёт НОВЫЙ коммит, отменяющий старый.
git revert a1b2c3d                 # история не переписывается — годится для общих веток

# Правило: reset --hard и переписывание истории — только на ЛОКАЛЬНЫХ ветках.
# Для уже запушенного — только revert.

6. Ветки

Ветка — это дешёвый подвижный указатель на коммит. Создавайте ветку под каждую задачу.

git branch                          # список локальных веток (* — текущая)
git branch -a                       # включая удалённые ветки

git branch feature-login            # создать ветку (но остаться на текущей)
git switch feature-login            # переключиться на ветку (современный способ)
git switch -c feature-login         # создать И сразу переключиться

# Старый универсальный способ (то же самое через checkout):
git checkout feature-login          # переключиться
git checkout -b feature-login       # создать и переключиться

git branch -d feature-login         # удалить влитую ветку
git branch -D feature-login         # удалить принудительно (даже невлитую)
git branch -m новое-имя            # переименовать текущую ветку

# Слить ветку feature в main:
git switch main                     # сначала встать на ветку-приёмник
git merge feature-login             # влить feature-login в main
# Fast-forward — если main не уходил вперёд, указатель просто сдвигается.
# Merge-коммит — если обе ветки расходились, Git создаст коммит-слияние.

7. Удалённые репозитории

Удалёнка (remote) — копия репозитория на сервере (GitHub, GitLab). По умолчанию называется origin.

git clone https://github.com/user/repo.git   # скачать репозиторий целиком
git clone https://github.com/user/repo.git mydir  # в свою папку

git remote -v                       # показать привязанные удалёнки
git remote add origin https://github.com/user/repo.git  # привязать удалёнку

git fetch origin                    # скачать изменения с сервера, НЕ сливая их
git pull origin main                # fetch + merge: скачать и сразу влить в текущую ветку
git pull --rebase origin main       # то же, но через rebase (история линейнее)

git push origin main                # отправить свои коммиты на сервер
git push -u origin feature-login    # первый пуш новой ветки + связать с удалённой
git push                            # дальше можно просто так (если связь уже есть)

# fetch безопасен — он ничего не меняет в ваших ветках, только обновляет
# знание о том, что есть на сервере. pull = fetch, а потом сразу слияние.

8. Разрешение конфликтов

Конфликт возникает, когда merge/rebase не может автоматически объединить правки одной и той же строки. Это нормально — Git просит вас выбрать.

git merge feature-login
# Auto-merging app.js
# CONFLICT (content): Merge conflict in app.js

git status                          # покажет файлы с пометкой "both modified"

# Внутри конфликтного файла Git вставляет маркеры:
# <<<<<<< HEAD
#   ваша версия (текущая ветка)
# =======
#   версия из вливаемой ветки feature-login
# >>>>>>> feature-login

# Что делать: открыть файл, удалить маркеры <<< === >>>,
# оставить нужный итоговый код (можно смешать обе версии).

git add app.js                      # пометить конфликт как решённый
git commit                          # завершить слияние (сообщение уже готово)

git merge --abort                   # передумали — откатить слияние целиком
git rebase --abort                  # то же для прерванного rebase

9. Прятки (stash)

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

git stash                           # спрятать все незакоммиченные изменения
git stash push -m "черновик формы" # спрятать с подписью
git stash -u                        # включая новые (untracked) файлы

git stash list                      # список пряток: stash@{0}, stash@{1}, ...
git stash show -p stash@{0}          # посмотреть содержимое прятки

git stash pop                       # вернуть последнюю прятку и удалить её из списка
git stash apply stash@{1}           # вернуть конкретную, НЕ удаляя из списка
git stash drop stash@{0}            # удалить прятку
git stash clear                     # удалить все прятки

# Типичный сценарий: git stash -> git switch main -> срочный фикс -> вернуться -> git stash pop

10. История и правки

Git позволяет переписывать ещё не опубликованную историю — делать её чище. На общих ветках это опасно.

git commit --amend -m "Точное сообщение"  # переписать ПОСЛЕДНИЙ коммит
git commit --amend --no-edit        # добавить забытый файл в последний коммит (git add сначала)

# Rebase — перенести коммиты ветки поверх другой ветки (линейная история):
git switch feature-login
git rebase main                     # "пересадить" свои коммиты на свежий main

# Интерактивный rebase — почистить последние N коммитов:
git rebase -i HEAD~3                # откроется список: pick / squash / reword / drop
# pick   — оставить коммит
# reword — оставить, но изменить сообщение
# squash — слить с предыдущим
# drop   — выкинуть коммит

# Cherry-pick — перенести ОДИН коммит из другой ветки сюда:
git cherry-pick a1b2c3d

# Золотое правило: НЕ переписывайте историю (rebase, amend) коммитов,
# которые уже запушены и которые видят другие. Только локальное.

11. Теги и .gitignore

Теги отмечают важные точки (релизы). .gitignore говорит Git, что не отслеживать.

git tag v1.0.0                      # лёгкий тег на текущий коммит
git tag -a v1.0.0 -m "Релиз 1.0"   # аннотированный тег (с автором и сообщением)
git tag                             # список тегов
git show v1.0.0                     # информация о теге
git push origin v1.0.0              # теги пушатся отдельно!
git push origin --tags              # отправить все теги сразу

# .gitignore — файл со списком того, что Git должен игнорировать:
#   node_modules/      # папки целиком
#   *.log              # по маске
#   .env               # секреты — НИКОГДА не коммитить
#   build/
#   .DS_Store

git check-ignore -v file.log        # проверить, какое правило игнорирует файл
git rm --cached secret.env          # перестать отслеживать уже добавленный файл

12. Полезное

Команды, которые однажды спасут вам день.

git reflog                          # журнал ВСЕХ перемещений HEAD — спасательный круг.
# Даже после reset --hard коммит виден здесь: git reset --hard a1b2c3d вернёт его.

git bisect start                    # бинарный поиск коммита, где сломалось
git bisect bad                      # текущий коммит — плохой
git bisect good v1.0.0              # этот — точно рабочий
# Git будет прыгать по серединам; помечайте good/bad, пока не найдёт виновника.
git bisect reset                    # завершить поиск

git blame file.txt                  # кто и в каком коммите менял каждую строку
git clean -fd                       # удалить неотслеживаемые файлы и папки (ОПАСНО)

# Типовые сценарии:
# Отменить локальные правки файла:        git restore file.txt
# Запушить новую ветку:                   git push -u origin имя-ветки
# Подтянуть свежий main в свою ветку:      git switch main && git pull && git switch - && git merge main
# Вернуть случайно удалённый коммит:        git reflog  ->  git reset --hard ХЕШ
# Срочно переключиться с черновиком:        git stash  ->  ...  ->  git stash pop
Поддержать проект