Git за пределами основ: рабочие процессы, экономящие часы каждую неделю
Interactive rebase, cherry-pick, bisect, worktrees, спасение через reflog и стратегии ветвления, которые реально работают. Команды Git, которые я использую каждый день, а большинство разработчиков о них не знает.
Большинство разработчиков выучивают пять команд Git и на этом останавливаются. add, commit, push, pull, merge. Может быть checkout и branch, если чувствуют себя смелыми. Этого хватает на первый год. Потом в вашей ветке 47 коммитов с сообщениями «fix» и «wip» и «please work», вы случайно откатываете что-то, что не следовало, и проводите 40 минут на Stack Overflow, пытаясь отменить неудачный мёрж.
Я использую Git годами. Не время от времени — интенсивно. Множество веток, множество репозиториев, множество участников, весь день, каждый день. Далее — команды и рабочие процессы, которые я реально использую. Не те, что хорошо выглядят в туториале. Те, что экономят мне реальное время каждую неделю.
Interactive Rebase: наведите порядок прежде, чем кто-то увидит#
В вашей ветке двенадцать коммитов. Половина из них — «fix typo». Один говорит «undo previous commit». Другой — «actually fix it this time». Вы собираетесь открыть PR. Никому не нужно видеть эту историю.
Interactive rebase — это способ переписать историю в вашей ветке перед тем, как поделиться ею. Он позволяет объединять коммиты, менять сообщения, переупорядочивать их или полностью удалять.
Базовая команда#
git rebase -i HEAD~5Это открывает редактор, показывая последние 5 коммитов от старых к новым:
pick a1b2c3d Add user authentication endpoint
pick d4e5f6g Fix typo in auth middleware
pick h7i8j9k Add rate limiting
pick l0m1n2o Fix rate limit bug
pick p3q4r5s Update auth testsКаждая строка начинается с команды. Измените pick на одну из следующих:
squash(илиs) — Объединить этот коммит с предыдущим, совместив сообщенияfixup(илиf) — То же, что squash, но отбросить сообщение этого коммитаreword(илиr) — Оставить коммит, но изменить его сообщениеdrop(илиd) — Удалить этот коммит полностьюedit(илиe) — Приостановить rebase на этом коммите, чтобы можно было его изменить
Реальная сессия очистки#
Вот что я делаю на практике. Та грязная история выше превращается в:
pick a1b2c3d Add user authentication endpoint
fixup d4e5f6g Fix typo in auth middleware
pick h7i8j9k Add rate limiting
fixup l0m1n2o Fix rate limit bug
pick p3q4r5s Update auth testsСохраняем и закрываем. Теперь у вас три чистых коммита вместо пяти. Исправление опечатки вошло в коммит аутентификации. Исправление бага rate limit вошло в коммит rate limit. Ваш ревьюер видит чистую, логичную последовательность.
Переупорядочивание коммитов#
Вы можете буквально переставить строки. Если коммит с тестами должен идти перед коммитом rate limiting, просто переместите строку:
pick a1b2c3d Add user authentication endpoint
pick p3q4r5s Update auth tests
pick h7i8j9k Add rate limitingGit воспроизведёт ваши коммиты в новом порядке. Если будут конфликты, он приостановится и даст вам их разрешить.
Ярлык Autosquash#
Если вы знаете, что коммит является исправлением для предыдущего, отметьте это при коммите:
git commit --fixup=a1b2c3dЭто создаёт коммит с сообщением fixup! Add user authentication endpoint. Затем при rebase:
git rebase -i --autosquash HEAD~5Git автоматически переставит fixup-коммиты прямо под их цели и отметит их как fixup. Вы просто сохраняете и закрываете. Без ручного редактирования.
Я использую это постоянно. Это самый быстрый способ итерировать в ветке, сохраняя финальную историю чистой.
Золотое правило#
Никогда не делайте rebase коммитов, которые были запушены в общую ветку. Если другие люди основали работу на этих коммитах, переписывание истории создаст реальные проблемы. Делайте rebase своих feature-веток перед мёржем. Никогда не делайте rebase main.
Если вы уже запушили свою feature-ветку и нужно сделать rebase:
git push --force-with-leaseФлаг --force-with-lease безопаснее, чем --force. Он откажется пушить, если кто-то другой запушил в ту же ветку с момента вашего последнего fetch. Он не предотвратит все проблемы, но поймает самую частую.
Cherry-Pick: хирургический перенос коммитов#
Cherry-pick берёт конкретный коммит из одной ветки и применяет его к другой. Не мёрж. Не rebase. Просто один коммит, чисто применённый.
Когда я реально это использую#
Самый частый сценарий: я исправил баг в своей feature-ветке, но исправление нужно также в main или release-ветке прямо сейчас. Я не хочу мёржить всю свою feature-ветку. Мне нужно только это одно исправление.
# Находим хеш коммита с исправлением
git log --oneline feature/user-auth
# a1b2c3d Fix null pointer in session validation
# Переключаемся на main и cherry-pick'аем
git checkout main
git cherry-pick a1b2c3dГотово. Исправление на main как новый коммит с теми же изменениями.
Cherry-Pick без коммита#
Иногда вы хотите применить изменения, но не коммитить сразу. Может быть, вы хотите объединить несколько cherry-pick'ов в один коммит или немного модифицировать изменения:
git cherry-pick --no-commit a1b2c3dИзменения подготовлены (staged), но не закоммичены. Вы можете модифицировать их, добавить ещё изменений, потом закоммитить когда готовы.
Cherry-Pick диапазона#
Нужно несколько последовательных коммитов? Используйте синтаксис диапазона:
git cherry-pick a1b2c3d..f6g7h8iЭто cherry-pick'ает всё после a1b2c3d до f6g7h8i включительно. Обратите внимание, что сам a1b2c3d исключён. Если вы хотите включить его:
git cherry-pick a1b2c3d^..f6g7h8iОбработка конфликтов#
Конфликты при cherry-pick работают как конфликты мёржа. Когда они возникают:
# Git скажет вам, что есть конфликт
# Исправьте конфликтующие файлы, затем:
git add .
git cherry-pick --continueИли если передумали:
git cherry-pick --abortИмейте в виду: cherry-pick'нутые коммиты создают новые хеши. Если вы потом мёржите оригинальную ветку, Git обычно достаточно умён, чтобы обработать дублирование. Но если вы агрессивно cherry-pick'аете между ветками, которые в итоге будут мёржиться, могут появиться неожиданные конфликты. Используйте хирургически, а не как стратегию мёржа.
Git Bisect: бинарный поиск багов#
Что-то сломалось. Вы знаете, что две недели назад работало. С тех пор было 200 коммитов. Какой из них сломал?
Можно проверять каждый коммит вручную. Или можно использовать git bisect, который применяет бинарный поиск, чтобы найти точный сломавший коммит за log2(n) шагов. Для 200 коммитов это примерно 7–8 проверок вместо 200.
Ручной способ#
# Начинаем bisect
git bisect start
# Отмечаем текущий коммит как плохой (баг здесь есть)
git bisect bad
# Отмечаем заведомо хороший коммит (бага здесь не было)
git bisect good v2.1.0Git переключается на коммит посередине между хорошим и плохим. Тестируйте. Затем:
# Если баг существует на этом коммите:
git bisect bad
# Если баг не существует на этом коммите:
git bisect goodGit каждый раз сужает диапазон вдвое. После 7–8 шагов он говорит:
a1b2c3d4e5f6g7h is the first bad commit
commit a1b2c3d4e5f6g7h
Author: Some Developer <dev@example.com>
Date: Tue Feb 18 14:23:01 2026 +0300
Refactor session handling to use async middlewareТеперь вы точно знаете, какой коммит ввёл баг. Когда закончите:
git bisect resetЭто вернёт вас туда, где вы были до начала.
Автоматический способ (вот где настоящая сила)#
Если у вас есть тест, который может обнаружить баг, вы можете автоматизировать весь процесс:
git bisect start
git bisect bad HEAD
git bisect good v2.1.0
git bisect run npm test -- --grep "session validation"Git автоматически переключает коммиты, запускает тест и отмечает их как хорошие или плохие на основе кода возврата. Ноль — хороший, не ноль — плохой. Уходите, возвращайтесь, и он сообщит вам точный коммит.
Можно использовать любой скрипт:
git bisect run ./test-regression.shГде test-regression.sh:
#!/bin/bash
npm run build 2>/dev/null || exit 125 # 125 означает "пропустить этот коммит"
npm test -- --grep "session" || exit 1 # 1 означает "плохой"
exit 0 # 0 означает "хороший"Код возврата 125 — особый: он говорит bisect пропустить этот коммит (полезно, если коммит не компилируется). Это одна из тех функций, которая кажется нишевой, пока не понадобится, и тогда экономит целый день.
Реальная сессия Bisect#
Вот как это выглядит на практике:
$ git bisect start
$ git bisect bad HEAD
$ git bisect good abc1234
Bisecting: 97 revisions left to test after this (roughly 7 steps)
[def5678...] Commit message here
$ npm test -- --grep "login flow"
# Тесты не проходят
$ git bisect bad
Bisecting: 48 revisions left to test after this (roughly 6 steps)
[ghi9012...] Another commit message
$ npm test -- --grep "login flow"
# Тесты проходят
$ git bisect good
Bisecting: 24 revisions left to test after this (roughly 5 steps)
...
# После ~7 итераций:
abc1234def5678ghi is the first bad commitСемь шагов, чтобы найти иголку в стоге из 200 коммитов.
Git Worktrees: несколько веток без stash#
Это самая недооценённая функция Git. Я уверен, что большинство разработчиков не знают о её существовании.
Проблема: вы глубоко в feature-ветке. Файлы изменены повсюду. Тут кто-то говорит «можешь быстро глянуть этот продакшен-баг?» У вас три варианта:
- Stash всё, переключить ветки, исправить, переключить обратно, pop stash. Надеяться, что ничего не пойдёт не так.
- Закоммитить незаконченную работу с сообщением «wip». Некрасиво, но функционально.
- Клонировать репозиторий заново в другую директорию.
Или вариант 4: worktrees.
Что такое Worktree#
Worktree — это вторая (или третья, или четвёртая) рабочая директория, связанная с тем же репозиторием. У каждого worktree своя checked out ветка, свои рабочие файлы, свой индекс. Но они разделяют одни и те же данные .git, поэтому вы не дублируете весь репозиторий.
Добавление Worktree#
# Вы на feature/user-auth, глубоко в работе
# Нужно исправить баг на main:
git worktree add ../hotfix-session mainЭто создаёт новую директорию ../hotfix-session с checked out main. Ваша текущая директория остаётся точно как была. Ничего не stash'нуто, ничего не закоммичено, ничего не нарушено.
cd ../hotfix-session
# Исправляем баг
git add .
git commit -m "Fix null pointer in session validation"
git push origin main
cd ../my-project
# Продолжаем работу над фичей как ни в чём не бывалоСоздание новой ветки в Worktree#
git worktree add ../hotfix-nav -b hotfix/nav-crash mainЭто создаёт worktree И создаёт новую ветку hotfix/nav-crash на основе main.
Управление Worktree#
# Список всех worktree
git worktree list
# /home/dev/my-project abc1234 [feature/user-auth]
# /home/dev/hotfix-session def5678 [main]
# Удалить worktree когда закончили
git worktree remove ../hotfix-session
# Если директория была уже удалена:
git worktree pruneПочему это лучше, чем Stash#
Stash подходит для быстрых переключений контекста. Но worktree лучше для всего, что занимает больше пяти минут:
- Ваша IDE остаётся открытой на feature-ветке. Без переиндексации, без потери позиции прокрутки.
- Вы можете запускать тесты в обеих директориях одновременно.
- Нет риска конфликтов stash или забыть, что вы stash'нули.
- Можно иметь долгоработающий dev-сервер в одном worktree и чистую сборку в другом.
Я обычно держу два-три активных worktree: мою основную feature-ветку, worktree с main для быстрых проверок и иногда worktree для ревью, где я проверяю чей-то PR.
Единственная оговорка#
Нельзя иметь одну и ту же ветку checked out в двух worktree. Это by design — предотвращает конфликтующие изменения одной и той же ветки в двух местах. Если попробуете, Git откажет.
Reflog: кнопка «отменить» для всего#
Вы сделали hard reset и потеряли коммиты. Удалили ветку. Сделали rebase и всё пошло ужасно не так. Вы думаете, что работа потеряна.
Нет. Git почти никогда не удаляет ничего по-настоящему. Reflog — ваша страховочная сеть.
Что такое Reflog#
Каждый раз, когда HEAD перемещается — каждый коммит, checkout, rebase, reset, merge — Git записывает это в reflog. Это журнал всех мест, где побывал ваш HEAD, по порядку.
git reflogВывод:
a1b2c3d (HEAD -> main) HEAD@{0}: reset: moving to HEAD~3
f4e5d6c HEAD@{1}: commit: Add payment processing
b7a8c9d HEAD@{2}: commit: Update user dashboard
e0f1g2h HEAD@{3}: commit: Fix auth token refresh
i3j4k5l HEAD@{4}: checkout: moving from feature/payments to mainКаждая запись имеет индекс (HEAD@{0}, HEAD@{1} и т.д.) и описание того, что произошло.
Восстановление после Hard Reset#
Вы случайно выполнили git reset --hard HEAD~3 и потеряли три коммита. Они прямо тут, в reflog:
# Смотрим, что потеряли
git reflog
# Коммит перед reset — это HEAD@{1}
git reset --hard f4e5d6cВсе три коммита вернулись. Кризис предотвращён.
Восстановление удалённой ветки#
Вы удалили ветку с незамёрженной работой:
git branch -D feature/experimental
# О нет, там было две недели работыКоммиты всё ещё существуют. Найдите их:
git reflog | grep "feature/experimental"
# Или просто просмотрите reflog в поисках последнего коммита той ветки
# Нашли. Пересоздаём ветку на этом коммите:
git branch feature/experimental a1b2c3dВетка вернулась со всеми коммитами.
Восстановление после неудачного Rebase#
Вы сделали rebase и всё пошло наперекосяк. Конфликты повсюду, неправильные коммиты, хаос:
# Reflog показывает, где вы были до rebase
git reflog
# a1b2c3d HEAD@{0}: rebase (finish): ...
# ...
# f4e5d6c HEAD@{5}: rebase (start): checkout main
# b7a8c9d HEAD@{6}: commit: Your last good commit
# Возвращаемся к моменту до начала rebase
git reset --hard b7a8c9dВы вернулись ровно туда, где были до начала rebase. Как будто его не было.
30-дневная страховочная сеть#
По умолчанию Git хранит записи reflog 30 дней (90 дней для достижимых коммитов). После этого они могут быть удалены сборщиком мусора. Так что у вас есть месяц, чтобы осознать ошибку. На практике этого более чем достаточно.
Можно проверить время жизни:
git config gc.reflogExpire
# default: 90.days.ago (для достижимых)
git config gc.reflogExpireUnreachable
# default: 30.days.ago (для недостижимых)Если вы параноик, увеличьте:
git config --global gc.reflogExpireUnreachable "180.days.ago"Личное правило#
Перед любой деструктивной операцией — hard reset, force push, удаление ветки — я запускаю git log --oneline -10. Мысленно запоминаю текущий HEAD. Это занимает две секунды и не раз спасало меня от паники, которой не нужно было быть.
Правильный Stash: это не просто git stash#
Большинство людей используют stash так:
git stash
# делаем что-то
git stash popЭто работает, но это эквивалент кидания всего в коробку с надписью «вещи». Когда у вас три stash'а, вы понятия не имеете, что в каком.
Называйте свои Stash'и#
git stash push -m "WIP: user auth form validation"Теперь при просмотре stash'ей:
git stash list
# stash@{0}: On feature/auth: WIP: user auth form validation
# stash@{1}: On main: Quick fix attempt for nav bug
# stash@{2}: On feature/payments: Experiment with Stripe webhooksВы видите точно, что содержит каждый stash.
Включение неотслеживаемых файлов#
По умолчанию git stash stash'ит только отслеживаемые файлы. Новые файлы, которые вы ещё не добавили, остаются:
# Stash всё, включая новые файлы
git stash push --include-untracked -m "WIP: new auth components"
# Или даже включить игнорируемые файлы (редко нужно)
git stash push --all -m "Full workspace snapshot"Я использую --include-untracked почти каждый раз. Оставлять новые файлы при переключении веток вызывает путаницу.
Частичный Stash#
Об этом большинство не знает. Можно stash'ить конкретные файлы:
# Stash только конкретные файлы
git stash push -m "Just the auth changes" src/auth/ src/middleware.tsИли использовать patch-режим для stash'инга конкретных фрагментов внутри файлов:
git stash push --patch -m "Partial: only the validation logic"Git пройдёт по каждому изменению интерактивно и спросит, хотите ли вы его stash'нуть. y — да, n — нет, s — разбить фрагмент на меньшие части.
Apply против Pop#
# Pop: применить и удалить из списка stash
git stash pop stash@{2}
# Apply: применить, но оставить в списке stash
git stash apply stash@{2}Я использую apply, когда не уверен, что stash применится чисто. Если будет конфликт, stash сохранится. С pop, если конфликт, stash тоже остаётся в списке (многие этого не знают), но мне нравится явное намерение apply.
Просмотр содержимого Stash#
# Посмотреть, какие файлы изменены в stash
git stash show stash@{0}
# Посмотреть полный diff
git stash show -p stash@{0}Создание ветки из Stash#
Если ваш stash вырос во что-то более существенное:
git stash branch feature/auth-validation stash@{0}Это создаёт новую ветку от коммита, где вы изначально stash'нули, применяет stash и удаляет его. Чисто.
Стратегии ветвления: какая реально работает#
Есть три основных стратегии ветвления. Каждая имеет контекст, где блестит, и контексты, где причиняет боль.
Gitflow#
Классика. main, develop, feature/*, release/*, hotfix/*. Создана Vincent Driessen в 2010.
# Feature-ветка
git checkout -b feature/user-auth develop
# ... работа ...
git checkout develop
git merge --no-ff feature/user-auth
# Release-ветка
git checkout -b release/2.1.0 develop
# ... финальные исправления ...
git checkout main
git merge --no-ff release/2.1.0
git tag -a v2.1.0 -m "Release 2.1.0"
git checkout develop
git merge --no-ff release/2.1.0
# Hotfix
git checkout -b hotfix/session-fix main
# ... исправление ...
git checkout main
git merge --no-ff hotfix/session-fix
git checkout develop
git merge --no-ff hotfix/session-fixКогда работает: Мобильные приложения, десктопное ПО, всё с именованными версионированными релизами и одновременной поддержкой нескольких версий. Если вы выпускаете v2.1 и v3.0 и нужно патчить обе, Gitflow это обрабатывает.
Когда не работает: Веб-приложения с непрерывным развёртыванием. Если вы деплоите в продакшен 5 раз в день, церемониальность release-веток и develop-веток — чистый оверхед. Большинство веб-команд, принявших Gitflow, заканчивают с develop-веткой, которая постоянно сломана, и release-ветками, которые никто не понимает.
GitHub Flow#
Просто. У вас есть main. Вы создаёте feature-ветки. Открываете PR. Мёржите в main. Деплоите main.
git checkout -b feature/user-auth main
# ... работа ...
git push origin feature/user-auth
# Открываете PR, получаете ревью, мёржите
# main всегда готов к деплоюКогда работает: Маленькие и средние команды, выпускающие веб-приложения. Непрерывный деплой. Если main всегда деплоится, это всё, что нужно. Это то, что использует этот сайт.
Когда не работает: Когда нужно поддерживать несколько release-версий, или когда перед деплоем длинный цикл QA. GitHub Flow предполагает, что main быстро уходит в продакшен.
Trunk-Based Development#
Все коммитят в main (»транк«) напрямую или через очень короткоживущие ветки (менее дня). Никаких долгоживущих feature-веток.
# Короткоживущая ветка (мёржится в тот же день)
git checkout -b fix/auth-token main
# ... маленькое, сфокусированное изменение ...
git push origin fix/auth-token
# PR ревьюится и мёржится за часы, не дниКогда работает: Высокопроизводительные команды с хорошим CI/CD, комплексными тестовыми наборами и feature-флагами. Google, Meta и большинство крупных технологических компаний используют trunk-based development. Это заставляет делать маленькие, инкрементальные изменения и устраняет merge hell.
Когда не работает: Команды без хорошего покрытия тестами или CI. Если мёрж в транк означает деплой непротестированного кода, вы будете постоянно ломать продакшен. Также нужны feature-флаги для всего, что занимает больше дня:
# Feature-флаг в коде
if (featureFlags.isEnabled('new-checkout-flow')) {
renderNewCheckout();
} else {
renderLegacyCheckout();
}Моя рекомендация#
Для большинства команд веб-разработки: начните с GitHub Flow. Он простой, работает и не требует инструментария сверх того, что GitHub/GitLab уже предоставляет.
Если команда вырастает за 15–20 инженеров и вы деплоите несколько раз в день, посмотрите на trunk-based development с feature-флагами. Инвестиция в инфраструктуру feature-флагов окупается за счёт снижения конфликтов мёржа и более быстрой итерации.
Если вы выпускаете версионированное ПО (мобильные приложения, CLI-инструменты, библиотеки): Gitflow или упрощённая версия. Вам действительно нужны те release-ветки.
Не выбирайте стратегию, потому что блог-пост сказал, что она лучшая. Выбирайте ту, что соответствует тому, как вы реально выпускаете продукт.
Git Hooks: автоматизируйте то, что постоянно забываете#
Git hooks — это скрипты, которые запускаются автоматически в определённые моменты рабочего процесса Git. Они локальны на вашей машине (не пушатся на remote), что означает, что нужен способ поделиться ими с командой.
Хуки, которые реально важны#
pre-commit — Запускается перед каждым коммитом. Используйте для линтинга и форматирования:
#!/bin/bash
# .git/hooks/pre-commit
# Запуск ESLint только для подготовленных файлов
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|ts|tsx)$')
if [ -n "$STAGED_FILES" ]; then
echo "Running ESLint on staged files..."
npx eslint $STAGED_FILES --quiet
if [ $? -ne 0 ]; then
echo "ESLint failed. Fix errors before committing."
exit 1
fi
fi
# Запуск Prettier для подготовленных файлов
if [ -n "$STAGED_FILES" ]; then
echo "Running Prettier..."
npx prettier --check $STAGED_FILES
if [ $? -ne 0 ]; then
echo "Prettier check failed. Run 'npx prettier --write' first."
exit 1
fi
ficommit-msg — Валидирует формат сообщения коммита. Идеально для обеспечения conventional commits:
#!/bin/bash
# .git/hooks/commit-msg
COMMIT_MSG=$(cat "$1")
PATTERN="^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?: .{1,72}"
if ! echo "$COMMIT_MSG" | grep -qE "$PATTERN"; then
echo "Invalid commit message format."
echo "Expected: type(scope): description"
echo "Example: feat(auth): add session refresh endpoint"
echo ""
echo "Valid types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert"
exit 1
fipre-push — Запускается перед push'ем. Используйте для тестов:
#!/bin/bash
# .git/hooks/pre-push
echo "Running tests before push..."
npm test
if [ $? -ne 0 ]; then
echo "Tests failed. Push aborted."
exit 1
fiHusky против нативных хуков#
Нативные хуки живут в .git/hooks/. Проблема: директория .git не отслеживается Git, поэтому нельзя поделиться хуками через репозиторий. Всем приходится настраивать их вручную.
Husky решает это. Он хранит конфигурации хуков в репозитории и настраивает .git/hooks автоматически при npm install:
npx husky initЭто создаёт директорию .husky/. Добавляйте хуки как файлы:
# .husky/pre-commit
npx lint-stagedВ комбинации с lint-staged вы получаете быстрые, целевые pre-commit проверки:
{
"lint-staged": {
"*.{js,ts,tsx}": ["eslint --fix", "prettier --write"],
"*.css": ["prettier --write"],
"*.json": ["prettier --write"]
}
}Это запускает ESLint и Prettier только для файлов, которые вы реально коммитите. Не для всей кодовой базы. Быстро.
Когда пропускать хуки#
Иногда нужно закоммитить без запуска хуков. Экстренные хотфиксы, work-in-progress коммиты в своей ветке:
git commit --no-verify -m "WIP: debugging production issue"Используйте это экономно. Если вы регулярно пропускаете хуки, ваши хуки, вероятно, слишком медленные или слишком строгие.
Алиасы, экономящие время#
Мой .gitconfig эволюционировал годами. Вот алиасы, которые выжили — те, которые я реально использую ежедневно, а не те, что добавил, потому что выглядели умно.
Красивый лог#
Стандартный git log многословный. Этот даёт чистое, цветное, графическое представление:
git config --global alias.lg "log --oneline --graph --all --decorate"Использование:
git lg
# * a1b2c3d (HEAD -> main) Fix session validation
# | * d4e5f6g (feature/payments) Add Stripe integration
# | * h7i8j9k Update payment models
# |/
# * l0m1n2o Merge PR #42
# * p3q4r5s Add user dashboardЯ запускаю это 20 раз в день. Самый быстрый способ понять состояние вашего репозитория.
Отменить последний коммит#
Сохранить изменения, просто отменить коммит:
git config --global alias.undo "reset HEAD~1 --mixed"Использование:
git undo
# Коммит исчез, но все изменения остались в рабочей директорииЯ использую это, когда коммичу слишком рано, забываю файл или хочу реструктурировать изменения.
Снять всё со stage#
git config --global alias.unstage "reset HEAD --"Использование:
git unstage src/auth/session.ts
# Файл снят со stage, но изменения сохраненыСписок всех алиасов#
Потому что вы забудете, что настроили:
git config --global alias.aliases "config --get-regexp ^alias\\."Ещё алиасы, которые я использую#
# Показать, что вы собираетесь коммитить
git config --global alias.staged "diff --staged"
# Короткий status
git config --global alias.st "status -sb"
# Amend без изменения сообщения
git config --global alias.amend "commit --amend --no-edit"
# Показать последний коммит
git config --global alias.last "log -1 HEAD --stat"
# Pull с rebase вместо merge
git config --global alias.up "pull --rebase --autostash"
# Удалить ветки, которые были замёржены в main
git config --global alias.cleanup "!git branch --merged main | grep -v '^[ *]*main$' | xargs git branch -d"Алиас up особенно хорош. pull --rebase сохраняет историю линейной вместо создания merge-коммитов при каждом pull. --autostash автоматически stash'ит и восстанавливает ваши изменения, если есть незакоммиченные файлы. Это то, чем pull должен был быть по умолчанию.
Алиас cleanup удаляет локальные ветки, которые были замёржены в main. Со временем у вас накапливаются десятки устаревших веток. Запускайте это еженедельно.
Команды, которые я использую ежедневно#
Это не алиасы и не продвинутые функции. Это просто команды, которые я запускаю постоянно и о которых, кажется, многие разработчики не знают.
Ультимативная команда лога#
git log --oneline --graph --allПоказывает каждую ветку, каждый мёрж, всю топологию вашего репозитория в компактном виде. Первое, что я запускаю, когда подтягиваю изменения. Отвечает на вопрос «что сейчас происходит в этом репозитории?»
Diff подготовленных изменений#
git diff --stagedПоказывает, что будет закоммичено. Не что изменено в рабочей директории — а что реально подготовлено (staged). Я всегда запускаю это перед коммитом. Всегда. Ловит случайные включения, debug-операторы, console.log, которых не должно быть.
# Неподготовленные изменения
git diff
# Подготовленные изменения
git diff --staged
# Оба
git diff HEADПоказать конкретный коммит#
git show a1b2c3dПоказывает полный diff одного коммита. Полезно при обзоре истории, понимании, что коммит реально изменил.
# Показать только изменённые файлы
git show --stat a1b2c3d
# Показать конкретный файл на конкретном коммите
git show a1b2c3d:src/auth/session.tsПоследнее невероятно полезно. Можно посмотреть любой файл в любой точке истории без checkout этого коммита.
Blame с диапазоном строк#
git blame -L 42,60 src/auth/session.tsПоказывает, кто последним изменял строки 42–60. Полезнее, чем blame всего файла, что обычно подавляет.
# Показать также сообщение коммита, а не только хеш
git blame -L 42,60 --show-name src/auth/session.ts
# Игнорировать изменения пробелов (очень полезно)
git blame -w -L 42,60 src/auth/session.ts
# Показать коммит перед blamed (копнуть глубже)
git log --follow -p -- src/auth/session.tsФлаг -w важен. Без него blame припишет строки тому, кто последним переформатировал файл, а это редко тот, кого вы ищете.
Поиск строки по всей истории#
# Найти, когда функция была добавлена или удалена
git log -S "validateSession" --oneline
# Найти, когда появился regex-паттерн
git log -G "session.*timeout" --oneline-S («кирка») находит коммиты, где количество вхождений строки изменилось. -G находит коммиты, где diff соответствует regex. Оба мощны для археологии — выяснения, когда что-то было введено или удалено.
Показать, что изменилось между двумя точками#
# Что изменилось между двумя ветками
git diff main..feature/auth
# Что изменилось в этой ветке с момента отделения от main
git diff main...feature/auth
# Список только изменённых файлов
git diff main...feature/auth --name-only
# Stat-представление (файлы + вставки/удаления)
git diff main...feature/auth --statДве точки против трёх имеет значение. Две точки показывает разницу между вершинами обеих веток. Три точки показывает, что изменилось на правой стороне с момента отделения от левой. Три точки — обычно то, что нужно при обзоре feature-ветки.
Очистка неотслеживаемых файлов#
# Посмотреть, что будет удалено (пробный прогон)
git clean -n
# Удалить неотслеживаемые файлы
git clean -f
# Удалить неотслеживаемые файлы и директории
git clean -fd
# Удалить неотслеживаемые и игнорируемые файлы (ядерный вариант)
git clean -fdxВсегда запускайте сначала с -n. git clean -fdx удалит ваши node_modules, .env, артефакты сборки — всё, что не отслеживается Git. Полезно для действительно чистого старта, но разрушительно.
Восстановление одного файла из другой ветки#
# Получить версию файла из main без переключения веток
git restore --source main -- src/config/database.tsИли из конкретного коммита:
git restore --source a1b2c3d -- src/config/database.tsЭто чище старого синтаксиса git checkout main -- path/to/file, и не влияет на HEAD.
Собираем всё вместе: реальный рабочий процесс#
Вот как выглядит типичный день с этими инструментами:
# Утро: смотрим, что происходит
git lg
git fetch --all
# Начинаем фичу
git checkout -b feature/session-refresh main
# Работаем, коммитим инкрементально
git add -p # Подготавливаем конкретные фрагменты, не целые файлы
git commit -m "Add token refresh endpoint"
git commit -m "Add refresh token rotation"
git commit -m "Fix: handle expired refresh tokens"
git commit -m "Add integration tests"
# Прерывание: нужно исправить продакшен-баг
git worktree add ../hotfix main
cd ../hotfix
# ... исправляем, коммитим, пушим, PR замёржен ...
cd ../my-project
git worktree remove ../hotfix
# Возвращаемся к работе над фичей
git commit -m "Fix edge case in token validation"
# Готовы к PR: чистим историю
git rebase -i main
# Объединяем fix-коммиты, переформулируем для ясности
# Пушим и открываем PR
git push -u origin feature/session-refresh
# Что-то сломалось в staging? Находим какой коммит:
git bisect start
git bisect bad HEAD
git bisect good main
git bisect run npm test
# Ой, я hard-reset'нул не то
git reflog
git reset --hard HEAD@{2}Каждая из этих команд занимает секунды. Вместе они экономят часы. Не гипотетические часы — реальные часы, каждую неделю, которые иначе ушли бы на распутывание проблем Git, ручной поиск багов или потерю работы из-за переключений контекста.
Git — инструмент, который вознаграждает глубину. Базовые знания проведут через день. Но команды в этом посте — то, что разделяет «я использую Git» от «Git реально делает меня быстрее». Изучайте их постепенно. Выберите одну новую технику на этой неделе и используйте, пока не станет мышечной памятью. Потом выберите другую.
Ваше будущее «я», глядящее на продакшен-баг в 11 вечера, будет благодарно вам за знание git bisect.