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