Вот конфиг git, который я держу на каждой машине, одиннадцать глобальных настроек и двадцать алиасов, и каждый из них здесь потому, что убивает конкретное ежедневное раздражение. Забирайте всё целиком.
Некоторое время назад коллега увидел, как я пушу совершенно новую ветку, и остановил меня:
«Стоп, как это только что сработало? Без
--set-upstream?»
Одна строка конфига. Он годами набирал этот обходной путь вручную.
Именно этот разговор, причина существования этой статьи. Большинство разработчиков работают с git на заводских настройках, а заводские настройки git спроектированы не для вас. Они спроектированы ради совместимости. Git скорее прервёт вас тысячу раз, чем удивит хоть однажды.
Поэтому все тихо платят один и тот же ежедневный налог, чаще всего уже не замечая этого:
fatal: The current branch has no upstream branch, набирали исправление настолько часто, что оно въелось в моторную память- Коммиты слияния засоряют историю, потому что обычный pull делает merge
.DS_Storeвылезает в каждомgit statusв каждом проекте на Mac- Разрешение одного и того же конфликта при rebase уже в пятый раз
- Коммит в рабочий репозиторий с личным email
Каждая из этих проблем решается одной строкой, которую git поставляет выключенной.

Дальше, мой настоящий конфиг, тот, что путешествует со мной на каждую машину. Это не свалка всего, что поддерживает git: каждая строка здесь заслужила своё место, убив реальное раздражение. Два акта: сначала глобальные настройки, потому что они меняют поведение git везде; затем алиасы, потому что они меняют скорость, с которой вы им управляете.
🪪 Акт первый: Identity, настройка, которая может вас смутить
Начнём с той, у которой есть последствия:
git config --global user.name "Paurush Rai"
git config --global user.email "personal@email.com"Очевидно. Но вот часть, которую большинство разработчиков никогда не настраивает: автоматическая маршрутизация рабочего и личного.
Я держу код работодателя под ~/Developer/company/. Один блок в ~/.gitconfig:
[includeIf "gitdir:~/Developer/company/"]
path = ~/.gitconfig-workА ~/.gitconfig-work хранит ровно одну вещь:
[user]
email = work@company.com
Теперь identity определяется тем, где живёт репозиторий. Клонируете что угодно в company/ → рабочий email автоматически. Всё остальное → личный. Ничего не нужно запоминать, никакой настройки на репозиторий, никакого личного email в корпоративном журнале аудита, и никакого рабочего email на пет-проектах по выходным.
(Обратите внимание, чего нет в рабочем файле: имени. includeIf накладывается поверх глобального конфига, так что рабочий файл объявляет только то, что отличается, email. Всё остальное наследуется.)
Это важнее, чем кажется, потому что email коммита навсегда. Запушьте с неправильной identity, и это останется в истории навечно; GitHub припишет коммит тому аккаунту, которому принадлежит этот email. Identity должна быть свойством репозитория, а не того, что вы в последний раз запомнили набрать.
⚙️ Акт первый, продолжение: настройки поведения
Одиннадцать строк. Каждая убивает конкретное, повторяющееся раздражение.
Больше никогда не видеть «no upstream branch»
git config --global push.autoSetupRemote trueОшибка, которую заучил наизусть каждый разработчик, fatal: The current branch has no upstream branch, существует, чтобы git мог спросить разрешения сделать очевидную вещь. Эта настройка выдаёт разрешение навсегда. Первый пуш новой ветки просто работает. (Это та самая строка, что остановила моего коллегу на полуслове.)
Перестать создавать случайные merge-коммиты
git config --global pull.rebase trueОбычный git pull, это fetch плюс merge, из-за чего в истории заводятся коммиты вида "Merge branch 'main' of github.com...". С этой настройкой pull переигрывает ваши локальные коммиты поверх того, что пришло. Линейная история, без шума.
Новые репозитории стартуют на main
git config --global init.defaultBranch mainИначе каждый git init стартует на master, и вы переименовываете её позже. Одна строка, и больше никогда об этом не думать.
Перестать копить мёртвые ветки
git config --global fetch.prune trueКогда ветку удаляют на удалённом сервере (в основном, смёрженные PR), ваша локальная копия origin/that-branch остаётся навсегда. Эта настройка автоматически удаляет устаревшие ссылки при каждом fetch. Список ваших веток остаётся честным.
Забанить .DS_Store из каждого репозитория навсегда
git config --global core.excludesfile ~/.gitignore_global
echo ".DS_Store" > ~/.gitignore_globalГлобальный файл игноров применяется поверх собственного .gitignore каждого репозитория. Мусор macOS, файлы подкачки редактора, решается один раз, на уровне машины, вместо того чтобы решать это в каждом проекте заново.
Троица разрешения конфликтов
git config --global merge.conflictstyle zdiff3
git config --global rerere.enabled true
git config --global rebase.autostash trueТри настройки, которые вместе преображают разрешение конфликтов:
zdiff3 добавляет третью секцию в маркеры конфликта, исходный текст, с которого начинали обе стороны. Стандартные маркеры показывают «ваше» и «их» и заставляют угадывать намерение. Видеть общего предка, значит превратить угадывание в чтение.

rerere («reuse recorded resolution»), тёмная лошадка из всех одиннадцати. Git записывает, как вы разрешили каждый конфликт, и когда тот же конфликт возникает снова (rebase обожает повторять конфликты), он автоматически применяет ваше прежнее решение. Пятый одинаковый конфликт разрешается сам.
autostash избавляет от танца «cannot rebase: you have unstaged changes». Git прячет вашу работу в stash, делает rebase и восстанавливает её обратно. То, чем вы занимались, никуда не девается.
Качество жизни, две строки
git config --global diff.algorithm histogram
git config --global branch.sort -committerdateHistogram даёт заметно более чистые диффы, чем алгоритм Myers по умолчанию, меньше тех перепутанных блоков, где git сопоставляет несвязанные строки. А сортировка веток по недавней активности означает, что git branch показывает то, над чем вы действительно работаете в первую очередь, а не алфавитную археологию.
⌨️ Акт второй: Алиасы
Настройки меняют поведение git. Алиасы меняют вашу скорость. Я держу отобранные двадцать штук, сгруппированные по тому, что они экономят.

Ежедневные инструменты
st = status -sb
cm = commit -m
sw = switch
co = checkout
amend = commit --amend --no-editstatus -sb заслуживает отдельного слова: это статус в две строки. Состояние ветки плюс изменённые файлы, без абзацев советов, которые вы перестали читать много лет назад.
Ясность зрения
lg = log --oneline --graph --decorate -20
last = log -1 HEAD --stat
d = diff
ds = diff --staged
whoami = !echo "$(git config user.name) <$(git config user.email)>"git lg, это мой способ понять «где я нахожусь»: двадцать коммитов, нарисованных как граф. А git whoami замыкает круг Акта первого: выполните его в любом репозитории, и он покажет ровно ту identity, которой будет подписан следующий коммит. В company/ он показывает рабочий email, везде остальном, личный. Доверие, проверенное одной командой.
Отмена без страха
unstage = restore --staged
undo = reset --soft HEAD~1
wip = !git add -A && git commit -m "wip: checkpoint"undo отменяет коммит, ничего не разрушая, изменения возвращаются в staging. wip делает снимок всего перед рискованной операцией. Большая часть тревоги вокруг git, это незнание безопасного пути назад; вот они, эти безопасные пути.
Профессиональные приёмы
pf = push --force-with-lease
fixup = commit --fixup
ri = rebase -i --autosquash
cleanup = !git branch --merged | grep -vE "^\*|main|master" | xargs git branch -dpf, самый важный: --force-with-lease делает force-push только если удалённая ветка там же, где вы видели её в последний раз, то есть физически отказывается перезаписать работу коллеги, которая появилась в промежутке. Не бывает ситуации, где голый --force был бы лучшим выбором. Сделать из него алиас означает, что пальцы никогда не наберут опасную версию.
Пара fixup + ri, это workflow для чистой истории: закоммитить исправление, нацеленное на более ранний коммит, а затем один интерактивный rebase автоматически всё аккуратно сворачивает на место.
🎯 Часть, которая делает это постоянным
Вот в чём суть конфига: построить его, легко. Сохранить его, вот настоящая проблема. Конфиг, который живёт только на текущей машине, это конфиг, который вы будете восстанавливать по памяти на следующей.
Мой ~/.gitconfig, не отдельный файл, а симлинк в репозиторий dotfiles, который авто-коммитит и авто-пушит каждое изменение в момент, когда оно происходит. Когда я недавно добавил новый алиас, фоновый агент закоммитил его на GitHub раньше, чем я закончил его тестировать.
(Эта система, симлинки плюс агент launchd, без cron, без сторонних инструментов, отдельная история, которую я описал в другой статье.)
Итог: вся эта настройка, включая маршрутизацию identity и все двадцать алиасов, разворачивается на любой машине одним install.sh. Настроил один раз, храни вечно.
📋 Всё целиком, готово к копипасте
# identity (edit these!)
git config --global user.name "Your Name"
git config --global user.email "you@personal.com"
# behavior
git config --global init.defaultBranch main
git config --global push.autoSetupRemote true
git config --global pull.rebase true
git config --global fetch.prune true
git config --global core.excludesfile ~/.gitignore_global
echo ".DS_Store" > ~/.gitignore_global
git config --global merge.conflictstyle zdiff3
git config --global rerere.enabled true
git config --global rebase.autostash true
git config --global diff.algorithm histogram
git config --global branch.sort -committerdate
# aliases
git config --global alias.st "status -sb"
git config --global alias.cm "commit -m"
git config --global alias.sw "switch"
git config --global alias.co "checkout"
git config --global alias.amend "commit --amend --no-edit"
git config --global alias.lg "log --oneline --graph --decorate -20"
git config --global alias.last "log -1 HEAD --stat"
git config --global alias.d "diff"
git config --global alias.ds "diff --staged"
git config --global alias.unstage "restore --staged"
git config --global alias.undo "reset --soft HEAD~1"
git config --global alias.wip '!git add -A && git commit -m "wip: checkpoint"'
git config --global alias.pf "push --force-with-lease"
git config --global alias.fixup "commit --fixup"
git config --global alias.ri "rebase -i --autosquash"
git config --global alias.cleanup '!git branch --merged | grep -vE "^\*|main|master" | xargs git branch -d'
git config --global alias.root "rev-parse --show-toplevel"
git config --global alias.aliases "config --get-regexp ^alias"
git config --global alias.whoami '!echo "$(git config user.name) <$(git config user.email)>"'Плюс маршрутизация identity, если вы держите рабочий код в отдельной директории:
git config --global includeIf."gitdir:~/Developer/company/".path ~/.gitconfig-work
printf '[user]\n email = you@work.com\n' > ~/.gitconfig-workУрок
Ни одна из этих настроек не впечатляет сама по себе. В этом и суть.
Настройки git по умолчанию оптимизированы так, чтобы никогда никого не удивлять, а значит, они прерывают каждого. Каждая строка выше превращает одно повторяющееся прерывание в тишину, а тишина накапливается. Умножьте несколько сэкономленных секунд и один избежанный коммит не под тем email на каждый репозиторий, к которому вы когда-либо прикоснётесь, и эти полчаса настройки окажутся одним из самых высокоэффективных вложений времени в инструменты, какие вы только сделаете.
Мой коллега настроил push.autoSetupRemote в тот же день, как увидел это. Обходной путь, который он набирал годами, исчез с одной строкой, просто потребовалось увидеть, как кто-то не платит этот налог, чтобы понять, что это было необязательно.
Считайте эту статью тем самым моментом, в письменной форме. Наберите cat ~/.gitconfig и посмотрите, что там на самом деле есть.
Настройки по умолчанию, ради совместимости. Конфигурация, ради вас самих.
Что в вашем gitconfig?
Я всегда в поиске следующего исправления в одну строку. Если у вас есть настройка или алиас, заслужившие своё место, именно так я нашёл половину своих.


