Изменение автора и дат коммита
У коммита есть автор и коммиттер, у каждого своя дата. Последний чини через --amend --author или --reset-author; даты — через GIT_AUTHOR_DATE или --date; много коммитов — через rebase --exec или git filter-repo. А .mailmap правит отображаемые имена без переписывания истории.
Ты забыл задать user.email перед неделей работы, и теперь каждый коммит приписан you@old-laptop.local. Или коммит ушёл под личным адресом, хотя должен был — под рабочим. Git записывает, кто написал каждый коммит и когда, — и в отличие от содержимого файлов, эта идентичность кажется неприкосновенной. Это не так. Git даёт точные инструменты, чтобы починить авторство одного коммита, диапазона или истории всего репозитория.
К концу этого урока ты сможешь менять автора и даты коммита — для последнего коммита, для многих и для целого репозитория — и будешь знать ровно, какие из этих операций переписывают историю, а какие лишь меняют то, как она отображается.
После этого урока ты сможешь отличать автора коммита от коммиттера, менять автора и даты последнего коммита, сбрасывать авторство по диапазону через rebase --exec, массово переписывать всю историю через git filter-repo --mail-map и использовать .mailmap, чтобы поправить отображаемые имена без переписывания истории.
Каждый коммит несёт две идентичности: автора и коммиттера, у каждой своя дата. Автор — это тот, кто изначально написал изменение; коммиттер — тот, кто последним его применил. Обычно это один человек, но они расходятся, когда кто-то применяет написанный тобой патч или когда rebase переигрывает твои коммиты (ты остаёшься автором, ребейзящий становится коммиттером). Посмотри оба:
git show --format=fuller HEAD
# Author: Jane Dev <jane@old.local> AuthorDate: ...
# Commit: Jane Dev <jane@work.com> CommitDate: ...Это различие важно, потому что большинство команд меняют только автора, если ты не воздействуешь и на коммиттера, — а git log по умолчанию показывает автора, так что обычно именно автора ты и хочешь починить.
Чини автора последнего коммита через --amend --author или прими свою текущую идентичность через --reset-author. Две повседневные правки для самого свежего коммита:
# Задать конкретного автора явно:
git commit --amend --author="New Name <new@email.com>" --no-edit
# Или проштамповать той идентичностью, что сейчас в твоём git config:
git commit --amend --reset-author --no-edit--author= переопределяет только поле автора; --reset-author устанавливает и автора, и коммиттера в твои текущие user.name / user.email (и сбрасывает дату автора на сейчас). --no-edit сохраняет существующее сообщение. Оба переписывают последний коммит в новый SHA, ровно как любой другой amend.
Задавай даты коммита переменными окружения GIT_AUTHOR_DATE / GIT_COMMITTER_DATE или --date для одной лишь даты автора. Даты — это просто поля метаданных, и их можно задать явно:
# --date затрагивает только дату АВТОРА:
git commit --amend --date="2026-05-01T09:00:00" --no-edit
# Задать ОБЕ даты, автора и коммиттера, экспортом переменных окружения:
GIT_AUTHOR_DATE="2026-05-01T09:00:00" \
GIT_COMMITTER_DATE="2026-05-01T09:00:00" \
git commit --amend --no-editЛовушка: --date двигает только дату автора, оставляя дату коммиттера нетронутой, так что две могут молча разойтись. Когда нужно выровнять обе, задавай обе переменные. Git принимает ISO-8601, RFC 2822 и относительные формы вроде "2 days ago".
Меняй автора у МНОГИХ коммитов интерактивным rebase или диапазоном rebase --exec. Для нескольких коммитов два подхода. Отметь каждую цель edit в интерактивном rebase и амендь на каждой остановке:
git rebase -i <base> # отметь коммиты для починки как 'edit'
# на каждой остановке:
git commit --amend --author="New Name <new@email.com>" --no-edit
git rebase --continueИли запусти команду автоматически по всему диапазону — здесь сбрасывая автора каждого коммита на твою текущую идентичность:
git rebase -r <base> --exec 'git commit --amend --reset-author --no-edit'--exec запускает заданную команду после переигрывания каждого коммита в диапазоне, так что ты чинишь десятки коммитов, не останавливаясь на каждом.
Для неверного email во всей истории используй git filter-repo — и предпочитай .mailmap, когда нужно только корректное отображение. Чтобы переписать авторство по всем коммитам (например, утёкший или неверный email в каждом), современный инструмент — git filter-repo:
# mailmap.txt сопоставляет старые идентичности новым, затем переписывает каждый коммит:
git filter-repo --mail-map mailmap.txtgit filter-branch исторически делал это, но устарел — он медленный и полон граблей; filter-repo — официально рекомендованная замена. Но если тебе нужно лишь, чтобы имена/email выглядели правильно в git log и git shortlog вообще без касания истории, добавь файл .mailmap в корень репозитория:
Correct Name <correct@email.com> <old@wrong.com>.mailmap переотображает идентичности только на момент показа — ни один SHA не меняется, ни форс-пуша, ни координации. Тянись к нему первым, когда всё, что тебе реально нужно, — корректность отображения.
▸Частая ошибка
Каждая техника здесь, кроме .mailmap, переписывает коммиты, порождая новые SHA. Это значит форс-пуш и полную проблему золотого правила: любой, кто подтянул старую историю, теперь расходится. Переписывание авторства по общему main — это скоординированная, объявленная операция, а никогда не случайная. Если твоя цель чисто косметическая («в списке контрибьюторов должно быть моё настоящее имя»), .mailmap доводит тебя до неё с нулевым переписыванием истории и нулевым риском для коллабораторов.
Целая фичевая ветка закоммичена под неверным email.
Ты сделал неделю работы, прежде чем заметил, что git config user.email всё ещё указывал на личный адрес. В ветке 12 коммитов, ни один не запушен, все авторства me@personal.com; должны быть me@company.com. Поскольку ничего не расшарено, переписывание безопасно.
git config user.email "me@company.com" # сначала починить config
git rebase -r origin/main \
--exec 'git commit --amend --reset-author --no-edit'
git show --format=fuller HEAD # проверить, что Author + Commit совпадают--reset-author штампует каждый коммит в диапазоне твоей теперь-корректной идентичностью, а --exec применяет это по всем 12 без ручных остановок. Каждый коммит получает новый SHA, и это нормально, потому что ветка никогда не пушилась. Если бы эти коммиты уже были на общей ветке, правильным инструментом был бы вместо этого .mailmap (чтобы починить отображаемое имя без переписывания) или тщательно скоординированный git filter-repo --mail-map плюс объявленный форс-пуш.
▸Почему это работает
Зачем вообще существует отдельная идентичность коммиттера? Потому что git разделяет, кто создал изменение, и кто поместил его в эту историю. Когда мейнтейнер применяет патч контрибьютора, справедливость требует, чтобы контрибьютор остался автором, а мейнтейнер записался коммиттером, — сохраняя признание и аудит-след того, кто реально приземлил код. Rebase пользуется тем же разделением: переигрывание твоих коммитов оставляет тебя автором, но делает того, кто ребейзит, коммиттером, — вот почему даты коммиттера часто выглядят новее дат автора.
У коммита есть автор и коммиттер, у каждого своя дата. Чини последний коммит через git commit --amend --author="…" или --reset-author (который принимает твою текущую идентичность config); задавай даты через --date (только автор) или переменные GIT_AUTHOR_DATE / GIT_COMMITTER_DATE (обе). Для многих коммитов отметь их edit в интерактивном rebase или запусти git rebase -r <base> --exec 'git commit --amend --reset-author --no-edit'. Для всей истории используй git filter-repo --mail-map (filter-branch устарел). Важно: каждый метод, кроме .mailmap, переписывает SHA и требует скоординированного форс-пуша — поэтому, когда нужно лишь корректное отображение, .mailmap — безопасный, сохраняющий историю выбор.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.