Где живёт конфиг и как он читается
Git не хранит настройки в одном месте. Он склеивает их из нескольких файлов, и порядок чтения важен - что прочитано позже, то и перекрывает прежнее. Уровней четыре, от самого общего к самому частному:
- system - /etc/gitconfig, для всех пользователей машины. Трогаешь редко.
- global - ~/.gitconfig (или ~/.config/git/config), твой личный, на все репозитории. Тут живёт 90% настроек.
- local - .git/config внутри конкретного репозитория. Перекрывает global для этого проекта.
- worktree - .git/config.worktree, отдельный конфиг на ворктри, если включён extensions.worktreeConfig.
Код: Выделить всё
$ git config --list --show-origin --show-scope
global file:/home/dev/.gitconfig user.name=Anton K
global file:/home/dev/.gitconfig core.pager=delta
local file:.git/config user.email=anton@bigcorp.io
local file:.git/config remote.origin.url=git@github.com:bigcorp/api.git
Код: Выделить всё
$ git config --show-origin user.email
file:.git/config anton@bigcorp.io

git alias: сокращения, которые экономят часы
Псевдоним (alias) - это просто новое имя для команды. Настройка простых алиасов через git config alias делается так:
Код: Выделить всё
$ git config --global alias.st status
$ git config --global alias.co checkout
$ git config --global alias.br branch
$ git config --global alias.ci commit
Код: Выделить всё
[alias]
st = status
co = checkout
br = branch
ci = commit
Код: Выделить всё
[alias]
last = log -1 HEAD --stat
unstage = reset HEAD --
amend = commit --amend --no-edit
aliases = config --get-regexp ^alias\\.
Самый ценный из полезных алиасов git - красивый граф истории. Классический lg, который стоит у каждого второго инженера:
Код: Выделить всё
[alias]
lg = log --graph --abbrev-commit --decorate \
--format=format:'%C(bold blue)%h%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(auto)%d%C(reset)' \
--all
Код: Выделить всё
$ git lg
* a3f1c08 fix: validate token expiry - Anton (HEAD -> main, origin/main)
* 9b2e441 feat: add refresh endpoint - Maria
| * 7c5d0aa wip: cache layer - Anton (feature/cache)
|/
* 12ab9f0 chore: bump deps - bot
Shell-алиасы через восклицательный знак
Если алиас начинается с восклицательного знака, Git не дописывает его к git, а выполняет как команду shell из корня репозитория. Это открывает всё: пайпы, переменные, несколько команд, вызовы внешних утилит.
Код: Выделить всё
[alias]
cleanup = "!git branch --merged | grep -v '\\*\\|main\\|master' | xargs -n 1 git branch -d"
root = "!pwd"
save = "!git add -A && git commit -m 'WIP savepoint'"
pushf = "!git push --force-with-lease"
Код: Выделить всё
[alias]
ignore = "!f() { curl -sL https://www.toptal.com/developers/gitignore/api/$1 >> .gitignore; }; f"
includeIf git: разные личности по каталогу
Вот классическая боль. У тебя один компьютер, на нём рабочие репозитории компании и личные пет-проекты. В рабочих нужен корпоративный email и корпоративный ключ подписи, в личных - твой gmail. Раньше люди вручную дёргали git config user.email в каждом проекте и регулярно забывали. Современное решение 2026 года - условные включения includeIf git, они работают с Git 2.13 и давно стабильны.
Идея: в основном ~/.gitconfig ты подключаешь дополнительный файл по условию - если репозиторий лежит в определённом каталоге. Раскладка такая. Личные проекты держишь в ~/personal/, рабочие в ~/work/. Основной ~/.gitconfig:
Код: Выделить всё
[user]
name = Anton K
email = anton.personal@gmail.com
[includeIf "gitdir:~/work/"]
path = ~/.gitconfig-work
[includeIf "gitdir:~/personal/"]
path = ~/.gitconfig-personal
Код: Выделить всё
[user]
email = anton@bigcorp.io
signingkey = ~/.ssh/id_ed25519_work.pub
[gpg]
format = ssh
[commit]
gpgsign = true
Три критичных детали, на которых спотыкаются:
- Завершающий слеш обязателен. gitdir:~/work/ со слешем матчит каталог и всё, что внутри рекурсивно. Без слеша - только точное совпадение пути, что почти никогда не то, что нужно.
- Порядок имеет значение. includeIf подмешивается там, где он стоит в файле. Базовый email задавай выше includeIf, чтобы условный его перекрывал, а не наоборот.
- Условие проверяется по расположению .git, а не по cwd. Есть вариант gitdir/i для регистронезависимого сравнения (полезно на macOS и Windows), и onbranch для условия по имени ветки.
Несколько SSH-аккаунтов: ~/.ssh/config рядом
includeIf разруливает, какой email и ключ подписи в коммите. Но есть вторая половина задачи - каким SSH-ключом аутентифицироваться на сервере, когда у тебя два аккаунта GitHub (личный и рабочий), а хост у обоих один - github.com. SSH по умолчанию не знает, какой ключ подсунуть. Решается это не в gitconfig, а рядом, в ~/.ssh/config через host-алиасы:
Код: Выделить всё
Host github-work
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_work
IdentitiesOnly yes
Host github-personal
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_personal
IdentitiesOnly yes
Код: Выделить всё
$ git clone git@github-work:bigcorp/api.git
$ git clone git@github-personal:anton/blog.git
Связка получается красивая: ~/.ssh/config решает, под кем ты ходишь на сервер, includeIf решает, чьё имя и почта попадут в коммит. Чтобы они не разъезжались, держи рабочие репозитории физически в ~/work/ и клонируй их через github-work - тогда обе системы сработают согласованно.
Настройки качества жизни
Эти ключи не делают ничего магического по отдельности, но вместе превращают Git из сырого инструмента в отлаженный. Пройдёмся по тем, что реально стоит включить.
Код: Выделить всё
[rerere]
enabled = true
[pull]
rebase = true
[fetch]
prune = true
[diff]
colorMoved = zebra
[help]
autocorrect = prompt
[core]
pager = delta
- rerere.enabled - reuse recorded resolution. Git запоминает, как ты разрулил конфликт слияния, и при повторном таком же конфликте (типично во время долгого rebase) применит решение сам. Бесценно на больших ребейзах.
- pull.rebase - git pull будет делать rebase вместо merge, история остаётся линейной без мусорных merge-коммитов вида Merge branch main. Альтернатива - значение merges, чтобы сохранять реальные слияния. Без этой настройки свежий Git вообще ругается и просит выбрать стратегию явно.
- fetch.prune - при fetch чистит локальные remote-tracking ветки, которых уже нет на сервере. Без неё origin/feature-old висит призраком ещё долго после удаления ветки на GitHub.
- diff.colorMoved - подсвечивает переехавшие блоки кода другим цветом. Когда ты вырезал кусок и вставил в другое место файла, обычный diff покажет это как большое удаление плюс большое добавление; с zebra сразу видно, что код просто переехал.
- help.autocorrect - реагирует на опечатки. Значение prompt спросит, не имел ли ты в виду status, набрав statsu. Число, например 30, выполнит исправленную команду автоматически через 3 секунды. Лично я предпочитаю prompt - меньше сюрпризов.
- core.pager - чем листать длинный вывод. По умолчанию less; ниже поставим delta для красивых диффов.
Код: Выделить всё
[push]
autoSetupRemote = true
delta как pager: красивые диффы
delta - сторонняя утилита (ставится через пакетный менеджер: brew, apt, cargo), которая перехватывает вывод diff/blame/grep и рисует его с подсветкой синтаксиса, номерами строк и аккуратными рамками. Базовая настройка:
Код: Выделить всё
[core]
pager = delta
[interactive]
diffFilter = delta --color-only
[delta]
navigate = true
line-numbers = true
[merge]
conflictStyle = zdiff3
Мини-лаба: собери свой .gitconfig
Повторяем руками по шагам.
- 1. Посмотри текущее состояние: git config --list --show-origin --show-scope. Запомни, что уже задано.
- 2. Добавь четыре простых алиаса: git config --global alias.st status и аналогично co, br, ci. Проверь git st.
- 3. Добавь граф: открой ~/.gitconfig в редакторе и впиши секцию alias с lg из урока. Выполни git lg в любом репозитории с историей.
- 4. Сделай личность по каталогу. Создай ~/work/ и ~/personal/. В ~/.gitconfig добавь два блока includeIf со слешами на конце. Заведи ~/.gitconfig-work с рабочим email. Склонируй любой репозиторий в ~/work/ и проверь git config user.email - должен быть рабочий.
- 5. Настрой multi-SSH. В ~/.ssh/config добавь host-алиас github-work с IdentitiesOnly yes. Проверь связь: ssh -T git@github-work.
- 6. Включи качество жизни: rerere.enabled, pull.rebase, fetch.prune, diff.colorMoved, help.autocorrect, push.autoSetupRemote. Перечитай git config --list и убедись, что всё на месте.
Забирай целиком, подставь свои имя/почту и каталоги:
Код: Выделить всё
[user]
name = Anton K
email = anton.personal@gmail.com
[includeIf "gitdir:~/work/"]
path = ~/.gitconfig-work
[init]
defaultBranch = main
[core]
pager = delta
editor = nvim
[pull]
rebase = true
[fetch]
prune = true
[push]
autoSetupRemote = true
[rerere]
enabled = true
[help]
autocorrect = prompt
[diff]
colorMoved = zebra
algorithm = histogram
[merge]
conflictStyle = zdiff3
[interactive]
diffFilter = delta --color-only
[delta]
navigate = true
line-numbers = true
[alias]
st = status -sb
co = checkout
sw = switch
br = branch
ci = commit
last = log -1 HEAD --stat
unstage = reset HEAD --
amend = commit --amend --no-edit
aliases = config --get-regexp ^alias\\.
lg = log --graph --abbrev-commit --decorate \
--format=format:'%C(bold blue)%h%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(auto)%d%C(reset)' \
--all
cleanup = "!git branch --merged | grep -v '\\*\\|main\\|master' | xargs -n 1 git branch -d"
pushf = "!git push --force-with-lease"
Контрольные вопросы
- 1. Чем shell-алиас (с восклицательным знаком) отличается от обычного, и зачем в нём оборачивать тело в функцию f?
- 2. Почему в gitdir:~/work/ завершающий слеш критичен, и по чему именно проверяется условие includeIf - по текущему каталогу или по расположению .git?
- 3. Что делает IdentitiesOnly yes в ~/.ssh/config и какая беда случается без неё при двух аккаунтах GitHub?
- 4. Правда ли, что push.autoSetupRemote включён по умолчанию в 2026? Как проверить на своей машине?
Хороший .gitconfig - это разовая инвестиция с пожизненной отдачей. Алиасы режут рутину, includeSif раскладывает личности по каталогам без ручных переключений, ~/.ssh/config с host-алиасами разводит несколько аккаунтов по ключам, а настройки качества жизни (rerere, prune, colorMoved, delta) делают повседневную работу тише и приятнее. Главный навык на будущее - git config --show-origin: когда что-то ведёт себя странно, ты всегда знаешь, какой файл и какой уровень виноват.