30+ типичных ошибок Git и как из них выбираться

Рейтинг: 64.6% · 7 голосов
Самый подробный курс по Git - от первого коммита до объектной базы и packfiles. Терминал и первый репозиторий, ветки и слияние, конфликты, удалённая работа и форджи (GitHub, GitLab), rebase и переписывание истории, спасение кода через reflog и fsck, stash и worktree, bisect и blame, подмодули и монорепо, хуки и подпись коммитов, внутреннее устройство и масштаб. Для новичков и профи. Актуально на 2026 (Git 2.54, дорожная карта 3.0).
Ответить
Аватара пользователя
Pavel_Git
Сообщения: 52
Зарегистрирован: 11 май 2026, 05:31

30+ типичных ошибок Git и как из них выбираться

Сообщение Pavel_Git »

Оглавление курса (52)
  1. Что такое контроль версий и почему победил Git
  2. Установка Git на Windows, macOS и Linux и первая настройка
  3. Терминал и как спрашивать у Git: выживание в командной строке
  4. Ментальная модель Git: три состояния, коммит-снимок и граф
  5. Первый репозиторий: init, add, commit, status
  6. Просмотр изменений: status, diff и индексация по частям
  7. История проекта: git log, диапазоны и поиск
  8. .gitignore: что не должно попасть в репозиторий
  9. .gitattributes: окончания строк, бинарники и атрибуты
  10. Ветки как указатели: branch, switch и detached HEAD
  11. Слияние веток: fast-forward против трёхстороннего merge
  12. Конфликты слияния: анатомия и уверенное разрешение
  13. Практикум: твой первый день с Git от нуля до push
  14. Удалённые репозитории: remote, fetch, базовый pull и refspec
  15. git push: безопасная отправка и защита от перезаписи
  16. Клонирование: shallow, partial clone, bundle и backfill
  17. GitHub, GitLab и форки: pull request, rulesets и защита веток
  18. Рабочие процессы команды: GitFlow, GitHub Flow, trunk-based
  19. Merge queue и merge trains: безопасное вливание в trunk
  20. Культура code review и Conventional Commits
  21. git reset: три дерева и отмена индексации и коммитов
  22. git revert: безопасная отмена в общей истории
  23. Правка последнего коммита: git commit --amend
  24. git rebase: пересадка истории и золотое правило
  25. Интерактивный rebase: переписать серию коммитов
  26. git cherry-pick: перенос отдельных коммитов
  27. Stacked diffs: цепочка зависимых PR
  28. Массовое переписывание истории: git filter-repo и git replace
  29. git reflog: журнал, который помнит всё
  30. Восстановление утерянных коммитов, веток и файлов
  31. Откат опасных операций: reset --hard, сломанный rebase, плохой merge
  32. git stash: тайник для незавершённой работы
  33. git worktree: несколько рабочих деревьев одного репозитория
  34. git bisect: бинарный поиск регрессии
  35. git blame, mailmap и археология кода
  36. Теги, релизы, archive и bundle
  37. Подмодули: внешние репозитории внутри проекта
  38. Subtree, монорепозитории и sparse-checkout
  39. Git LFS и большие файлы
  40. Вспомогательные команды: clean, mv, rm, restore
  41. Псевдонимы и кастомизация .gitconfig
  42. Хуки Git: автоматизация на клиенте и сервере
  43. Подпись коммитов и тегов: SSH, GPG и Sigstore
  44. Внутреннее устройство I: объектная база Git
  45. Внутреннее устройство II: ссылки, HEAD, notes и reftable
  46. Внутреннее устройство III: индекс, packfiles, gc и обслуживание
  47. От SHA-1 к SHA-256: переход на новый хеш
  48. Огромные репозитории: partial clone, FSMonitor, Scalar
  49. Git в CI/CD: shallow, кеш, merge_group и безопасность
  50. Куда движется Git: дорожная карта 3.0 и экосистема
  51. Профессиональный setup: собираем рабочее окружение Git
  52. 30+ типичных ошибок Git и как из них выбираться (вы здесь)
Зачем тебе аварийный справочник по Git

Рано или поздно ты увидишь красную строку в терминале и сердце ёкнет. Закоммитил не туда. Сделал reset --hard и работа исчезла. Запушил .env с боевым паролем. Git ответил fatal каким то загадочным текстом. В эти секунды важно не паниковать и не делать резких движений, потому что почти всё в Git обратимо - данные живут в .git/objects и в reflog ещё недели после того, как ты думаешь, что их уже нет.

Этот урок - финальный troubleshooting-компендиум курса. Формат каждого пункта жёсткий и одинаковый: симптом - причина - диагностика - лечение - профилактика. Это и шпаргалка под рукой, и материал, который удобно кинуть джуну в чат, когда он прибежал с горящими глазами. Здесь собраны самые частые ошибки git и проблемы git с решением, с реальными fatal-сообщениями, чтобы ты гуглил не текст ошибки, а сразу делал. Команды выверены под современный Git серии 2.5x (актуальная линия к середине 2026 - около 2.54). Где надо, отмечу нюансы свежих версий.

Один принцип перед стартом, выучи его как мантру: сначала reflog, потом думаем. git reflog - это журнал, куда Git пишет каждое перемещение HEAD и веток: коммит, reset, rebase, checkout, merge. Пока объект упомянут в reflog, сборщик мусора его не трогает. Поэтому 90 процентов "я всё потерял" лечится одной командой просмотра журнала и одним reset обратно.

Изображение

Потеря работы: reset, rebase, force-push, stash

X1. reset --hard съел незакоммиченное и закоммиченное.
Симптом: сделал git reset --hard, рабочая копия откатилась, нужных коммитов нет.
Причина: --hard двигает ветку и затирает рабочее дерево под новый коммит. Закоммиченное при этом не удаляется физически - на него просто перестаёт указывать ветка.
Диагностика и лечение:

Код: Выделить всё

$ git reflog
a1b2c3d (HEAD -> main) HEAD@{0}: reset: moving to HEAD~3
9f8e7d6 HEAD@{1}: commit: add payment retry
3c2b1a0 HEAD@{2}: commit: wip invoice parser
$ git reset --hard 9f8e7d6
HEAD is now at 9f8e7d6 add payment retry
В reflog HEAD@{1} - состояние ДО reset, на него и возвращаемся. Если коммитов не было (затёрлись только незакоммиченные правки в файлах, которых ещё не было в индексе) - вот это реально не вернуть, reflog хранит только коммиты. Профилактика: перед --hard делай git stash или хотя бы git add . - тогда blob'ы осядут в objects и найдутся через fsck.

X2. Сломал rebase, конфликты, всё перепуталось.
Симптом: посреди git rebase каша из конфликтов, непонятно где ты.
Причина: rebase переписывает коммиты по одному поверх новой базы, на каждом может встать конфликт.
Лечение - аварийный выход:

Код: Выделить всё

$ git rebase --abort
Это возвращает ветку ровно туда, где она была до rebase, через сохранённый ORIG_HEAD. Если rebase уже завершился и только потом ты понял, что напортачил:

Код: Выделить всё

$ git reset --hard ORIG_HEAD
Git перед каждой опасной операцией (rebase, merge, reset) кладёт прежнюю позицию в ref ORIG_HEAD. Профилактика: rebase делай на чистом дереве, мелкими порциями, и используй git rebase --update-refs (с 2.38), если переписываешь стек веток.

X3. Случайный force-push снёс чужие коммиты на сервере.
Симптом: запушил с --force, у коллег пропали коммиты на удалёнке.
Причина: git push --force затирает удалённую ветку безусловно, даже если кто то успел запушить после тебя.
Лечение: коммиты не исчезли - они есть в reflog у того, чей push ты затёр, и в любом свежем клоне. Восстанови SHA у коллеги (git reflog или git log на его машине) и запушь правильное состояние. На сервере с включённым reflog поможет и серверный журнал.
Профилактика: никогда не пуш --force вслепую. Используй:

Код: Выделить всё

$ git push --force-with-lease
--force-with-lease проверяет, что удалённая ветка указывает туда, куда ты её последний раз видел. Если кто то успел запушить - push отклонится, а не снесёт чужое.

X4. Потерял stash.
Симптом: сделал git stash pop, словил конфликт или случайно git stash drop, заначка пропала из git stash list.
Причина: stash - это объекты-коммиты, на которые после drop никто не ссылается по имени.
Диагностика:

Код: Выделить всё

$ git fsck --no-reflogs --unreachable | grep commit
unreachable commit 5d41402abc4b2a76b9719d911017c592...
$ git show 5d41402
$ git stash apply 5d41402
fsck перебирает .git/objects и находит висячие (unreachable) коммиты. Stash-коммит узнаётся по сообщению вида "WIP on main". Применяешь его как обычный stash. Профилактика: с Git 2.51 появились git stash export и git stash import - сериализуй важные заначки в ref и переноси/бэкапь как ветку.

Не туда закоммитил, не туда запушил

X5. Закоммитил в неправильную ветку.
Симптом: сделал коммит в main, а хотел в feature.
Причина: забыл переключиться перед commit.
Лечение (коммит ещё не запушен):

Код: Выделить всё

$ git switch -c feature        # создаём целевую ветку от текущего состояния
$ git switch main
$ git reset --hard HEAD~1       # откатываем main на один коммит назад
Если коммитов несколько или они вперемешку, аккуратнее через cherry-pick:

Код: Выделить всё

$ git switch feature
$ git cherry-pick a1b2c3d       # переносим нужный коммит на feature
$ git switch main
$ git reset --hard HEAD~1
cherry-pick копирует коммит как новый поверх текущей ветки. Профилактика: настрой в приглашении шелла показ ветки (starship, oh-my-zsh) - половина таких ошибок от того, что не видишь, где ты.

X6. Запушил секрет (пароль, токен, ключ).
Это самая болезненная git push ошибка. Действуй по порядку и не пропускай шаги.
Симптом: в коммите уехал .env, id_rsa или AWS-ключ.
Причина: файл не был в .gitignore, попал в индекс и улетел на сервер.
Лечение:
1. Сначала ротация. Считай секрет скомпрометированным в ту же секунду. Отзови токен, смени пароль, перевыпусти ключ. Переписывание истории НЕ отменяет того, что секрет уже прочитали боты, сканирующие GitHub в реальном времени.
2. Вычисти из всей истории. Рекомендованный инструмент 2026 - git filter-repo (сторонний, ставится отдельно), не legacy filter-branch:

Код: Выделить всё

$ git filter-repo --invert-paths --path .env
3. Запушь переписанную историю:

Код: Выделить всё

$ git push --force-with-lease --all
$ git push --force-with-lease --tags
4. Все коллеги делают свежий клон или git reset --hard на новую историю - merge после переписывания ЗАПРЕЩЁН, иначе старые коммиты с секретом вернутся.
Профилактика: pre-commit-хук с gitleaks/trufflehog, .gitignore на секреты с первого дня, push protection на платформе.

Состояние HEAD и отклонённый push

X7. Detached HEAD - паника.
Симптом:

Код: Выделить всё

$ git checkout a1b2c3d
You are in 'detached HEAD' state. ...
HEAD detached at a1b2c3d
Причина: ты переключился на конкретный коммит или тег, а не на ветку. HEAD указывает прямо на коммит, мимо refs/heads. Это не поломка - это нормальный режим "посмотреть/поэкспериментировать". Опасность одна: коммиты, сделанные здесь, ни к одной ветке не привязаны и потеряются при переключении.
Лечение - просто уйти обратно:

Код: Выделить всё

$ git switch -
git switch - возвращает на предыдущую ветку (как cd -). Если ты успел тут накоммитить и хочешь сохранить:

Код: Выделить всё

$ git switch -c rescue-branch
Профилактика: для просмотра старого состояния это штатно, не бойся. Просто не коммить в detached без последующего switch -c.

X8. push отклонён: rejected non-fast-forward.
Самый частый git push rejected.
Симптом:

Код: Выделить всё

 ! [rejected]        main -> main (non-fast-forward)
error: failed to push some refs to 'origin'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally.
Причина: на сервере появились коммиты, которых нет у тебя (коллега запушил). Твоя ветка разошлась с удалённой, fast-forward невозможен.
Лечение - подтянуть и переналожить своё:

Код: Выделить всё

$ git fetch origin
$ git rebase origin/main      # или git pull --rebase
... разруливаешь конфликты, если есть ...
$ git push
Не делай git push --force в ответ на rejected - это типовая катастрофа: ты сотрёшь чужие коммиты вместо того, чтобы их сохранить. Force тут лечит симптом и создаёт проблему X3. Профилактика: привыкни к pull --rebase (git config pull.rebase true), чтобы история была линейной без лишних merge-коммитов.

X9. Перепутал merge и rebase.
Симптом: хотел линейную историю, а получил merge-коммит "Merge branch main"; или наоборот - переписал общую ветку rebase'ом и всем поломал.
Причина: merge сохраняет обе линии и создаёт коммит слияния, rebase переписывает твои коммиты поверх чужих (новые SHA).
Правило: rebase - только для своих ещё не запушенных или личных веток. Общую ветку, которую тянут другие, не rebase'ь. Откатить ошибочный merge до push:

Код: Выделить всё

$ git reset --hard ORIG_HEAD
fatal-сообщения, окончания строк и аутентификация

X10. fatal: not a git repository.
Симптом:

Код: Выделить всё

$ git status
fatal: not a git repository (or any of the parent directories): .git
Причина: ты не внутри репозитория - нет папки .git ни здесь, ни выше по дереву. Либо ты не в той директории, либо репозиторий не инициализирован, либо случайно удалил .git.
Лечение: проверь, где ты, и поднимись/спустись в нужную папку. Если репо реально новый - инициализируй. На 2026 в ванильном Git ветка по умолчанию всё ещё master с подсказкой, поэтому main ставим руками:

Код: Выделить всё

$ git rev-parse --show-toplevel    # покажет корень репо, если ты внутри
$ git init -b main                 # если репо нужно создать
Это разруливает кучу запросов git fatal и git not a git repository. Профилактика: не запускай git-команды в случайных папках, смотри на приглашение шелла.

X11. Конфликт в бинарном файле.
Симптом: при merge конфликт в .png, .pdf, собранном артефакте - руками такое не сольёшь.
Причина: Git мержит построчно, бинарь строк не имеет.
Лечение - выбрать одну из версий целиком:

Код: Выделить всё

$ git checkout --ours  assets/logo.png    # моя версия (текущая ветка)
$ git checkout --theirs assets/logo.png   # их версия (входящая)
$ git add assets/logo.png
Внимание к терминам: при rebase значения ours/theirs меняются местами относительно merge, потому что rebase накладывает твои коммиты поверх чужой базы. Профилактика: бинарники - в Git LFS, а не в обычную историю; .gitattributes пометь их как binary.

X12. Огромный файл попал в историю, репо распух.
Симптом: клон тянется минутами, .git весит гигабайты из за дампа базы или видео.
Причина: большой blob закоммичен и теперь сидит в каждом срезе истории.
Лечение - вырезать из истории или перенести в LFS:

Код: Выделить всё

$ git filter-repo --strip-blobs-bigger-than 50M
# либо перенос в LFS:
$ git lfs migrate import --include="*.psd,*.mp4"
После filter-repo - force-with-lease и пересинк у всех, как в X6. Профилактика: лимит на размер файла в pre-receive хуке, крупные ассеты сразу в LFS.

X13. Слетели окончания строк (CRLF/LF), весь файл в diff.
Симптом: git diff показывает, что изменён весь файл, хотя ты правил одну строку; в команде Windows плюс Linux/Mac вечные конфликты переносов.
Причина: разные ОС пишут конец строки по разному (CRLF против LF), а в репо нет единого правила.
Лечение - зафиксировать политику и перенормализовать:

Код: Выделить всё

$ printf '* text=auto\n' > .gitattributes
$ git add --renormalize .
$ git commit -m "normalize line endings"
.gitattributes с text=auto говорит Git хранить LF в репозитории и подставлять родной перенос при checkout. --renormalize применяет это ко всем уже закоммиченным файлам. Профилактика: .gitattributes в репо с первого коммита - это надёжнее, чем настройка core.autocrlf у каждого по своему.

X14. Отвалилась аутентификация при push/clone.
Симптом:

Код: Выделить всё

remote: Support for password authentication was removed.
fatal: Authentication failed for 'https://github.com/...'
# либо по SSH:
git@github.com: Permission denied (publickey).
Причина: пароли по HTTPS давно отменены - нужен Personal Access Token (PAT) или SSH-ключ. По SSH - ключ не добавлен в агент или не загружен на платформу.
Лечение:

Код: Выделить всё

$ ssh -T git@github.com            # проверка SSH-доступа
$ ssh-add ~/.ssh/id_ed25519        # подгрузить ключ в агент
# для HTTPS - вместо пароля ввести PAT, а лучше сохранить:
$ git config --global credential.helper store   # или osxkeychain / manager
Профилактика: один ED25519-ключ на машину плюс ssh-агент; для CI - короткоживущие токены, а не вечные.

Игнор, амменд и ещё пачка быстрых рецептов

X15. Забыл .gitignore - файл уже отслеживается.
Симптом: добавил node_modules/ в .gitignore, а Git всё равно его видит в изменениях.
Причина: ignore работает только для НЕотслеживаемых файлов. Что уже в индексе - игнор не выкинет.
Лечение:

Код: Выделить всё

$ git rm -r --cached node_modules
$ git commit -m "stop tracking node_modules"
--cached убирает из индекса, не удаляя файл с диска. Профилактика: .gitignore до первого git add.

X16. Опечатка в последнем коммите / забыл файл.

Код: Выделить всё

$ git add forgotten_file.py
$ git commit --amend --no-edit     # дописать файл в последний коммит
$ git commit --amend               # поправить сообщение
Только если коммит ещё не запушен в общую ветку - amend меняет SHA.

X17. fatal: refusing to merge unrelated histories.
Причина: сливаешь две истории без общего предка (например, локальный init и удалённый репо с README).
Лечение: git pull origin main --allow-unrelated-histories. Профилактика: клонируй существующий репо, а не делай init поверх.

X18. error: Your local changes would be overwritten by checkout.
Причина: незакоммиченные правки мешают переключению. Лечение: git stash, переключись, git stash pop. Либо git switch -m для слияния правок в новую ветку.

X19. Удалил ветку, а в ней были коммиты.
git reflog покажет последний SHA удалённой ветки, git branch имя SHA воссоздаст её.

X20-X30 кратко (симптом - лечение):
  • fatal: remote origin already exists - git remote set-url origin URL.
  • detached после git pull в пустой ветке - git switch -c.
  • "nothing to commit" хотя файлы менялись - они в .gitignore, проверь git check-ignore -v файл.
  • Случайно git add лишнего - git restore --staged файл (убрать из индекса, правки сохранить).
  • Хочу выкинуть локальные правки в файле - git restore файл.
  • Тег создал не на том коммите - git tag -d имя, потом git tag имя SHA.
  • Завис git из за крупного коммита - git maintenance run (с 2.54 геометрическая стратегия по умолчанию, gc - легаси).
  • "fatal: pathspec did not match any files" - опечатка в имени файла или он не в индексе.
  • Откатить уже запушенный коммит без переписывания истории - git revert SHA (создаёт обратный коммит).
  • Большой репо медленно тянется - partial clone: git clone --filter=blob:none URL.
  • index.lock мешает - оборвался прошлый процесс; убедись что git не запущен и удали .git/index.lock.
Мини-лаба: сломай и почини сам

Повтори руками, 10 минут, на тестовом репо:
  • 1. git init -b main, сделай три коммита (a, b, c).
  • 2. git reset --hard HEAD~2 - "потеряй" b и c.
  • 3. git reflog, найди SHA коммита c, git reset --hard на него - верни всё. Прочувствуй, что reset --hard обратим.
  • 4. git switch -c feature, накоммить, вернись git switch main, сделай git checkout SHA - поймай detached HEAD и выйди git switch -.
  • 5. Создай файл secret.txt с фейковым токеном, закоммить. Вычисти git filter-repo --invert-paths --path secret.txt и проверь git log -- secret.txt - история пуста.
  • 6. Положи в .gitignore слово build, но сначала закоммить build/ - убедись, что игнор не работает, и почини через git rm -r --cached build.
Контрольные вопросы
  • 1. Ты сделал git reset --hard и потерял два коммита. Какая команда покажет, куда вернуться, и почему данные ещё живы?
  • 2. В ответ на "rejected non-fast-forward" коллега советует git push --force. Почему это опасно и что сделать вместо?
  • 3. В историю уехал боевой токен. Перечисли шаги по порядку - что делать ПЕРВЫМ и почему одного filter-repo мало?
  • 4. Чем --force-with-lease безопаснее обычного --force?
Итог
Запомни три якоря, и большинство аварий перестанет быть страшными: reflog возвращает почти любой коммит, ORIG_HEAD откатывает rebase/merge/reset, force-with-lease защищает удалёнку от слепого затирания. Текст fatal-сообщения - это не приговор, а подсказка: not a git repository значит "ты не там", non-fast-forward значит "сначала подтяни чужое". И главное правило при любой git ошибке: не делай резких --force, пока не посмотрел reflog. Сохрани этот компендиум - он экономит часы и нервы.
👍3 ❤️5 🔥2 😄 🤔2
Аватара пользователя
marius
Сообщения: 1
Зарегистрирован: 03 июн 2026, 19:01

Re: 30+ типичных ошибок Git и как из них выбираться

Сообщение marius »

Спасибо, X8 прямо про меня - я в ответ на rejected всегда жал force и однажды снес коллеге полдня работы. Теперь только fetch + rebase.
👍 ❤️ 🔥 😄 🤔
Аватара пользователя
truth34
Сообщения: 1
Зарегистрирован: 27 май 2026, 05:11

Re: 30+ типичных ошибок Git и как из них выбираться

Сообщение truth34 »

Вопрос по X6: если секрет уже улетел и его склонировали боты, то ротация обязательна даже после filter-repo? Правильно понял, что переписывание истории само по себе не спасает?
👍 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Профессиональный setup: собираем рабочее окружение Git

Все главы курса «Git профессионально: от первого коммита до внутреннего устройства»

Поделиться темой: ✈ Telegram VK
  • Похожие темы
Похожие запросы: Частые ошибки Git и как их исправить

Вернуться в «Git профессионально: от первого коммита до внутреннего устройства»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 0 гостей