4.13. Справочник-шпаргалка по Git
Справочник-шпаргалка по Git
Часть 1. Архитектура и внутреннее устройство
1.1. Объектная модель Git
Git хранит все данные в виде неизменяемых объектов четырёх типов: blob, tree, commit, tag. Все объекты идентифицируются SHA-1-хешем их содержимого (в современных версиях Git допускается SHA-256, но пока не является стандартом).
- Blob (binary large object) — представляет содержимое файла без имени и метаданных. Имя файла хранится не в blob, а в tree.
- Tree — похож на директорию: содержит список имён файлов/поддиректорий и соответствующих им SHA-хешей blob или других tree. Обеспечивает структуру файловой системы на момент коммита.
- Commit — ссылается на один tree (состояние файловой системы), содержит метаданные: автора, дату, сообщение, а также ссылку на родительский(-ые) коммит(ы). Первоначальный коммит не имеет родителей; при слиянии — два или более.
- Tag — объект, содержащий ссылку на commit (или другой объект), имя, необязательное сообщение и цифровую подпись (если используется
git tag -s). Лёгкие теги (git tag v1.0) — это просто ссылки вrefs/tags/, не создающие отдельный объект.
Все объекты физически хранятся в .git/objects/: сначала 2 символа хеша — имя подкаталога, остальные 38 — имя файла. Для экономии места Git применяет zlib-сжатие и пакует объекты в pack-файлы (*.pack) при выполнении git gc, git repack или при передаче по сети (git push/fetch).
1.2. Ссылки (refs) и их роль
Ссылки в Git — это текстовые файлы, содержащие SHA-хеш (обычно 40-символьный). Они делятся на категории:
- HEAD — специальная ссылка, указывающая на текущую позицию: либо на ветку (
ref: refs/heads/main), либо напрямую на коммит (состояние detached HEAD). - Ветки (
refs/heads/*) — подвижные указатели на коммиты. При создании коммита ветка, на которую ссылается HEAD, автоматически продвигается вперёд. - Удалённые ветки (
refs/remotes/*) — зеркала веток с удалённых репозиториев, обновляются только приgit fetchилиgit pull. Не являются локальными ветками, но могут быть использованы как точки отсчёта (git checkout origin/main→ detached HEAD). - Теги (
refs/tags/*) — либо лёгкие (ссылки), либо аннотированные (отдельные объекты). - Заметки (notes) — механизм добавления произвольных метаданных к коммитам без изменения их хеша (
git notes add).
В .git/packed-refs хранятся сжатые ссылки — Git использует его для ускорения доступа при большом количестве веток/тегов.
1.3. Индекс (staging area): не просто «буфер»
Индекс — это бинарный файл (.git/index), описывающий точное состояние, которое будет зафиксировано в следующем коммите. Он содержит:
- пути файлов (в кодировке UTF-8);
- статусы (mode, тип);
- SHA-1 blob для каждого пути;
- временные метки и размеры (для быстрого определения изменился ли файл);
- этапы слияния (в случае конфликтов — 3 записи на файл: base, ours, theirs).
Индекс не является просто «списком добавленных файлов»: он хранит полное дерево, включая удалённые файлы и конфликты. Это позволяет Git мгновенно реконструировать коммит без перечитывания файловой системы.
Команда git status сравнивает три состояния:
HEAD↔ индекс → показывает, что будет закоммичено;- индекс ↔ рабочая директория → показывает, что можно добавить.
Команда git diff без аргументов показывает различия между индексом и рабочей директорией. git diff --cached (или --staged) — между HEAD и индексом.
Часть 2. Базовые операции и их семантика
2.1. Инициализация и клонирование
git init [--bare]
Создаёт новый репозиторий в текущей директории. Без --bare создаётся рабочая копия с подкаталогом .git. С --bare создаётся только содержимое .git — такой репозиторий нельзя использовать для редактирования, только для push/pull. Используется для «центральных» репозиториев на серверах.
git clone <url> [<dir>]
git clone --depth 1 <url> # shallow clone
git clone --branch <branch> <url>
git clone --recurse-submodules <url>
git clone выполняет четыре действия:
- Создаёт директорию и инициализирует репозиторий (
git init); - Добавляет remote
originпо указанному URL; - Загружает все объекты и ссылки (
git fetch); - Создаёт локальную ветку, соответствующую
remote.<origin>.HEAD(обычноmainилиmaster), и проверяет её.
Опция --depth N создаёт «мелкий» репозиторий — без полной истории, только последние N коммитов. Удобно для CI/CD или быстрой сборки, но ограничивает возможности git log, git blame, git bisect. Позже можно углубить историю: git fetch --unshallow.
2.2. Фиксация изменений: от add до commit
git add <pathspec>...
git add -u # только уже отслеживаемые (modified/deleted)
git add -A # все изменения (новые, изменённые, удалённые)
git add -p # интерактивное добавление по ханкам
git add читает содержимое файлов, создаёт blob-объекты (если их ещё нет), обновляет записи в индексе. Для больших файлов Git проверяет, изменились ли только временные метки и размер — если нет, пересчёт хеша пропускается (ускорение).
Важно: git add не удаляет физически файлы из рабочей директории — только помечает их на удаление в индексе. Файлы удаляются из файловой системы только при git rm --cached (если нужно убрать из индекса без удаления) или git rm (и из индекса, и с диска).
git commit [-m "message"] [--amend] [--no-edit] [-v]
Создаёт новый commit-объект:
- tree берётся из текущего состояния индекса;
- родитель — текущий
HEAD; - автор/коммитер — из
user.name/user.email; - сообщение — либо из
-m, либо из редактора (core.editor).
При --amend текущий HEAD перезаписывается: создаётся новый коммит с тем же родителем, что и предыдущий HEAD, но с обновлённым tree и/или сообщением. Предыдущий коммит остаётся в объектной базе, пока не будет удалён сборщиком мусора. Это локальная операция, безопасна до push; после — требует --force.
Флаг -v (--verbose) добавляет в редактор diff коммита — помогает проверить вносимые изменения перед фиксацией.
Часть 3. Ветвление, слияние и перебазирование
3.1. Модель ветвления в Git: указатели, а не копии
В отличие от систем, где ветка — это физическая копия файлов (например, SVN), в Git ветка — это подвижная ссылка на коммит. Создание ветки мгновенно, поскольку выполняется только запись ссылки в refs/heads/<name> и обновление HEAD (при переключении). Никакие файлы при этом не копируются и не дублируются.
git branch <branch-name> [<start-point>]
Создаёт новую ссылку <branch-name>, указывающую на коммит, заданный <start-point> (по умолчанию — текущий HEAD). Не переключает на неё.
git checkout <branch-name>
git switch <branch-name> # современная альтернатива
git switch -c <new-branch> # = git checkout -b
Команда checkout исторически совмещала переключение веток и восстановление файлов. В Git 2.23+ рекомендуется использовать switch для смены веток и restore для манипуляций с файлами — это повышает читаемость и безопасность.
При переключении Git:
- Обновляет
HEAD, чтобы он ссылался на новую ветку; - Обновляет индекс и рабочую директорию в соответствии с tree, на который указывает эта ветка;
- Если в рабочей директории есть несохранённые изменения, которые конфликтуют с целевым состоянием, переключение отклоняется — это защита от потери данных.
Состояние detached HEAD возникает, когда HEAD указывает напрямую на коммит (например, git checkout abc123 или git checkout origin/main). В этом режиме новые коммиты создаются, но ни одна ветка не продвигается. После переключения на ветку такие коммиты становятся «висячими» и могут быть потеряны при сборке мусора — если не сохранить их хеш, например, через git branch rescue abc123.
3.2. Слияние (merge): интеграция изменений
git merge <branch>
git merge --no-ff <branch>
git merge --squash <branch>
- Fast-forward (ff) — происходит, если текущая ветка является предком целевой. Git просто перемещает указатель вперёд, не создавая нового коммита. История остаётся линейной.
- True merge — создаётся новый коммит с двумя родителями: текущий
HEADи<branch>. Запускается, если ветки расходились. Это единственный способ сохранить информацию о параллельной разработке.
Флаг --no-ff всегда создаёт merge-коммит, даже если возможен fast-forward. Это полезно в workflow, где важна прозрачность: например, при интеграции feature-ветки в develop, чтобы в будущем можно было легко откатить всю функцию как один коммит (git revert -m 1 <merge-commit>).
Флаг --squash объединяет все изменения из <branch> в индекс текущей ветки, но не создаёт коммит и не сохраняет историю источника. Подходит для интеграции экспериментальных или «грязных» веток, когда детали промежуточных коммитов не важны.
Стратегии слияния
Git поддерживает несколько встроенных стратегий:
resolve— базовая стратегия для двух веток;recursive(по умолчанию) — обрабатывает самослияния (например, при rebase), поддерживает--ours,--theirs;octopus— для слияния более двух веток одновременно (только если возможно без конфликтов);ours— полностью игнорирует изменения из других веток, сохраняя текущее дерево; используется в специфических сценариях интеграции;subtree— пытается обнаружить, что одна ветка является поддеревом другой (редко используется вручную).
3.3. Перебазирование (rebase): линеаризация истории
git rebase <base>
git rebase -i <base>
git rebase --onto <newbase> <oldbase> <branch>
git rebase --continue / --abort / --skip
rebase переносит серию коммитов из одной линии истории на другую, пересоздавая их с новыми хешами. Это достигается путём:
- Временного сохранения патчей (
git format-patch); - Сброса ветки до
<base>; - Последовательного применения патчей с возможной адаптацией.
Поскольку хеши меняются, rebase меняет историю. Это допустимо только для локальных, неопубликованных коммитов. Публичная ветка после rebase требует git push --force-with-lease, что может нарушить работу других разработчиков.
Интерактивное перебазирование (-i)
Открывает редактор со списком коммитов (от старых к новым), где каждая строка имеет вид:
pick abc123 commit message
Доступные команды:
pick— применить коммит как есть;reword— изменить сообщение;edit— остановиться для редактирования (можно изменить файлы, сделатьgit add,git commit --amend);squash— объединить с предыдущим коммитом, сохраняя его сообщение;fixup— объединить, но отбросить своё сообщение;drop— пропустить коммит;exec <command>— выполнить shell-команду после коммита (например,make test).
Это мощный инструмент для «причёсывания» истории перед интеграцией: удаление отладочных коммитов, группировка связанных изменений, унификация формулировок.
rebase --onto: перенос поддиапазона
Сценарий: у вас есть feature, ответвившаяся от main, но часть изменений уже была вмёрджена в main через другую ветку. Вы хотите перебазировать только новые коммиты.
git rebase --onto main <last-common-commit> feature
Это отрежет цепочку коммитов после <last-common-commit> и наложит её на main.
3.4. Конфликты: диагностика и разрешение
Конфликт возникает, когда Git не может автоматически объединить изменения в одном и том же участке файла. Рабочая директория остаётся в состоянии конфликта, а в индексе появляются три записи на файл:
stage 1— общая база (<<<<<<< HEADв файле);stage 2— текущая ветка (ours);stage 3— входящие изменения (theirs).
Команды:
git status # показывает конфликтующие файлы
git diff --ours / --theirs / --base # сравнить с каждой стороной
git checkout --ours <file> # взять версию текущей ветки
git checkout --theirs <file> # взять версию входящей ветки
git add <file> # после ручного разрешения — пометить как resolved
git mergetool # запустить GUI-инструмент (meld, kdiff3 и др.)
После разрешения всех конфликтов — git add и git commit (для merge) или git rebase --continue (для rebase). Важно: git add удаляет записи stage 2 и 3, оставляя только stage 0 — это сигнал системе, что конфликт разрешён.
Часть 4. Работа с удалёнными репозиториями
4.1. Remote: концепция и управление
Remote — это сокращённое имя для URL репозитория, используемое в командах fetch, push, pull.
git remote add <name> <url>
git remote rename <old> <new>
git remote remove <name>
git remote set-url <name> <newurl>
git remote -v # показать все remotes с URL
Каждый remote имеет набор конфигурационных параметров в .git/config:
[remote "origin"]
url = https://github.com/user/repo.git
fetch = +refs/heads/*:refs/remotes/origin/*
pushurl = ... # отдельный URL для отправки
push = ... # правила по умолчанию (редко используется)
Запись fetch определяет, как и куда сопоставляются ссылки при git fetch. По умолчанию — все локальные ветки в refs/remotes/origin/.
4.2. Fetch и push: асимметричные операции
git fetch [<remote>] [<refspec>...]
git fetch --prune # удалить локальные ссылки на удалённые ветки, которых больше нет
git fetch --tags # явно получить теги (по умолчанию — не все)
fetch только загружает объекты и обновляет удалённые ветки, не затрагивая рабочую директорию. Это безопасная операция.
refspec — шаблон +<src>:<dst>, где + означает force-update. Примеры:
git fetch origin main:fixes/main— загрузитьorigin/mainи создать/обновить локальную веткуfixes/main;git fetch origin :temp— создать локальную веткуtemp, ссылающуюся на тот же коммит, что иHEADна origin (редко, но полезно для бэкапа).
git push [<remote>] [<refspec>...]
git push --force-with-lease # безопасный force-push
git push --set-upstream origin main # установить tracking
Без refspec push отправляет текущую ветку, но только если она имеет upstream-связь. Иначе — ошибка.
--force перезаписывает удалённую ветку, игнорируя её текущее состояние. --force-with-lease проверяет, что локальная копия удалённой ветки (origin/main) совпадает с её состоянием на сервере. Это предотвращает случайную перезапись чужих коммитов.
4.3. Pull: fetch + merge (или rebase)
git pull
git pull --rebase
git config pull.rebase true # сделать rebase по умолчанию
git pull — это ярлык для git fetch + git merge FETCH_HEAD. Вариант с --rebase делает git rebase, что сохраняет линейную историю.
Рекомендация: в feature-ветках — pull --rebase, чтобы избежать лишних merge-коммитов. В shared-ветках (например, main) — только fetch + ручное merge, чтобы явно контролировать интеграцию.
Часть 5. Отладка и восстановление состояния
5.1. git reflog: журнал изменений ссылок
reflog — это локальный журнал операций, затрагивающих HEAD, ветки и другие ссылки. Записи сохраняются локально и не передаются при push/fetch. По умолчанию хранятся 90 дней (для достижимых объектов) или 30 дней (для недостижимых — garbage-collectable). Это основной инструмент восстановления после «потери» коммитов.
git reflog
git reflog show <branch> # reflog для конкретной ветки
git reflog expire --expire=now --all && git gc # очистка (осторожно!)
Формат записи:
abc123 HEAD@{1}: commit: fix typo
def456 HEAD@{2}: checkout: moving from main to feature
Каждая запись содержит:
- SHA-1 состояния, в котором была ссылка после операции;
- номер операции (
@{n}); - тип операции и контекст.
Примеры восстановления
-
Случайный
git reset --hard
Был сделанgit reset --hard abc123, ноabc123был не тем коммитом.
→git reflog, ищем запись перед reset (например,HEAD@{1}), затем:git reset --hard HEAD@{1} -
Коммит в detached HEAD, который «исчез» после checkout
Послеgit checkout abc123,git commit,git checkout mainкоммит остался без ветки.
→git reflog, находимHEAD@{n}с нужным коммитом, затем:git branch rescue abc123 # или HEAD@{n} -
Отмена
git rebase, который сломал историю
→git reflog, находим запись до rebase (HEAD@{k}), затем:git reset --hard HEAD@{k}
Важно: reflog работает только с локальными операциями. Если репозиторий клонирован заново — reflog теряется.
5.2. git reset: изменение положения HEAD, индекса и рабочей директории
Команда reset имеет три режима, определяемых флагом (или отсутствием):
| Режим | HEAD | Индекс | Рабочая директория | Применение |
|---|---|---|---|---|
--soft | ✓ | ✗ | ✗ | Отмена коммита, но сохранение изменений в индексе (например, чтобы изменить сообщение или добавить файлы) |
--mixed (по умолчанию) | ✓ | ✓ | ✗ | Отмена коммита и выгрузка изменений из индекса в рабочую директорию (подготовка к повторной подготовке коммита) |
--hard | ✓ | ✓ | ✓ | Полный возврат в состояние коммита — все несохранённые изменения теряются |
git reset --soft HEAD~1 # отменить последний коммит, оставить изменения в индексе
git reset HEAD~2 # отменить два коммита, изменения — в рабочей директории
git reset --hard origin/main # синхронизировать локальную ветку с удалённой (опасно!)
⚠️ Предупреждение: --hard без предварительного бэкапа (например, git stash или git branch backup) может привести к безвозвратной потере данных.
5.3. git revert: безопасная отмена изменений
В отличие от reset, revert не перезаписывает историю. Он создаёт новый коммит, который вносит противоположные изменения по отношению к указанному коммиту.
git revert <commit>
git revert -n <commit> # подготовить изменения, но не коммитить
git revert -m 1 <merge-commit> # отменить merge (указать, какую родительскую ветку сохранить)
Для merge-коммита обязательна опция -m <parent-number> (1 — наша ветка, 2 — входящая). Без неё revert откажет: Git не знает, в какую сторону «откатывать» двухродительский коммит.
revert — единственный рекомендуемый способ отмены изменений в публичных ветках, поскольку он сохраняет целостность истории и не нарушает работу других разработчиков.
5.4. git cherry-pick: выборочное применение коммитов
Применяет изменения из одного или нескольких коммитов как новые коммиты в текущую ветку. Полезно при backport’е исправлений или извлечении отдельных функций без полного слияния.
git cherry-pick <commit>
git cherry-pick A^..B # диапазон (исключая A, включая B)
git cherry-pick --continue / --abort / --skip
Особенности:
- Создаются новые коммиты с новыми хешами;
- Если коммит уже был применён ранее (например, через
merge), Git распознаёт это по содержимому изменений и пропускает дубликат (applyс--skip); - При конфликтах — как при
rebase: разрешить,git add,git cherry-pick --continue.
Ограничение: cherry-pick не переносит связи — если коммит ссылался на issue или другой коммит через fixes #123, это не восстанавливается автоматически.
5.5. git bisect: бинарный поиск по истории для локализации ошибки
Автоматизирует процесс нахождения первого «плохого» коммита, в котором появился баг.
git bisect start
git bisect bad HEAD # текущее состояние — сломано
git bisect good v1.0 # известная хорошая точка
# Git переключается на середину → тестируем →
git bisect good # или bad
# Повторяем, пока не найдётся первый bad-коммит
git bisect reset # выйти
Можно автоматизировать с помощью скрипта:
git bisect run ./test.sh
где test.sh возвращает:
0— коммит хороший;1–124, 126–127— коммит плохой;125— пропустить (например, сборка сломана не по нашей вине).
bisect особенно эффективен в больших проектах с длинной историей — вместо линейного просмотра 1000 коммитов требуется ~10 шагов.
Часть 6. Продвинутые темы
6.1. git worktree: параллельные рабочие копии
Позволяет иметь несколько рабочих директорий, связанных с одним репозиторием — без клонирования. Каждая worktree имеет своё дерево файлов, но разделяет общую объектную базу и ссылки.
git worktree add ../feature-branch feature
git worktree list
git worktree remove ../feature-branch
git worktree prune # удалить "мёртвые" worktree (например, после rm -rf)
Ограничения:
- Нельзя иметь две
worktreeна одну и ту же ветку; - Нельзя удалить
main worktree(ту, где лежит.git); - Индексы и
HEAD— изолированы.
Практическое применение:
- Тестирование сборки на
main, пока ведётся разработка вfeature; - Поддержка нескольких версий (например,
v1-support,v2-dev) одновременно.
6.2. git submodule и git subtree: управление зависимостями
Submodule
Подмодуль — это отдельный репозиторий, встроенный в поддиректорию. В основном репозитории хранится только SHA-1 коммита подмодуля.
git submodule add <url> [<path>]
git submodule update --init --recursive
git submodule foreach 'git checkout main'
Плюсы: полная изоляция, чёткая привязка к версии.
Минусы: сложность обновления (git submodule update --remote), необходимость явного init, частые ошибки при клонировании (--recurse-submodules обязателен).
Subtree
Объединяет историю подпроекта в основную ветку через merge с --squash или subtree-стратегию.
git subtree add --prefix=lib/foo <repo> <ref>
git subtree push --prefix=lib/foo <repo> <branch>
Плюсы: единая история, нет внешних зависимостей при клонировании.
Минусы: история подпроекта «размазывается», сложнее выделить изменения, относящиеся только к нему.
Рекомендация: для статичных библиотек — subtree; для активно развивающихся, независимых компонентов — submodule или внешние пакетные менеджеры (npm, NuGet, pip).
6.3. git replace и git filter-repo: переписывание истории
git replace
Создаёт локальную замену одного объекта другим, без изменения хешей. Не влияет на push.
git replace <old-commit> <new-commit>
git replace -d <commit> # удалить замену
git filter-branch -- --all # применить замены рекурсивно (устаревшее)
Практическое применение: исправление авторства, исправление сообщений без пересборки всей истории.
git filter-repo (современная замена filter-branch)
Инструмент для безопасного, быстрого и гибкого переписывания истории. Требует установки отдельно (не входит в стандартную поставку).
Частые задачи:
- Удаление файла из всей истории:
git filter-repo --path secrets.json --invert-paths - Изменение email во всех коммитах:
git filter-repo --email-callback 'return email.replace(b"old", b"new")' - Удаление тегов, веток, уменьшение размера репозитория.
⚠️ После filter-repo необходимо сделать git push --force, и все участники должны переклонировать репозиторий.
Часть 7. Конфигурация, оптимизация и работа с большими репозиториями
7.1. Уровни конфигурации и приоритеты
Git поддерживает три уровня настроек, которые применяются иерархически (нижестоящий переопределяет вышестоящий):
| Уровень | Команда | Файл | Область действия |
|---|---|---|---|
| Системный | git config --system | /etc/gitconfig | Все пользователи системы |
| Глобальный | git config --global | ~/.gitconfig или ~/.config/git/config | Текущий пользователь |
| Локальный | git config | .git/config | Только текущий репозиторий |
Проверить, откуда берётся конкретная настройка:
git config --show-origin --get user.email
Ключевые параметры:
user.name,user.email— обязательны дляcommitиtag;core.editor— редактор для сообщений (vim,code --wait,nano);core.autocrlf— управление окончаниями строк:true(Windows): LF → CRLF при checkout, CRLF → LF при commit;input(Unix/macOS): CRLF → LF при commit, но не обратно;false: без преобразований (только если вы контролируете окружение явно);
core.safecrlf— предупреждение при невозможности обратного преобразования;core.filemode— учитывать ли биты исполнения (falseна Windows);core.precomposeunicode(macOS) — корректная работа с именами файлов в UTF-8 (включено по умолчанию с Git 2.20+);init.defaultBranch— имя ветки по умолчанию (main,master);pull.rebase,branch.autosetuprebase— поведение по умолчанию дляpull;credential.helper— кеширование учётных данных (например,cache,store,manager-coreна Windows).
Для временного использования параметров — переменные окружения:
GIT_AUTHOR_NAME="CI Bot" git commit --amend --no-edit
7.2. .gitattributes: управление поведением по файлам
Файл .gitattributes (в корне или поддиректориях) задаёт правила обработки файлов при операциях. Имеет приоритет над глобальными настройками.
Часто используемые директивы:
| Атрибут | Назначение |
|---|---|
text=auto | Git сам решает, текстовый ли файл; при checkout нормализует EOL |
text eol=lf | Принудительно использовать LF при checkout (для скриптов, .sh, .py) |
text eol=crlf | Принудительно CRLF (.bat, .cmd) |
binary | Отключает EOL-конверсию и diff (эквивалентно -text -diff) |
diff=custom | Использовать пользовательский драйвер diff (например, для JSON) |
merge=custom | Кастомная стратегия слияния (например, merge=union для списков) |
linguist-language=Python | Подсказка для GitHub Language Stats |
Пример:
*.sh text eol=lf
*.bat text eol=crlf
*.png binary
*.json diff=json
CHANGELOG diff
Для включения JSON-pretty diff:
git config diff.json.textconv "jq -S ."
Теперь git diff file.json будет показывать семантические различия, а не побайтовые.
7.3. Оптимизация и обслуживание репозитория
Сборка мусора и упаковка
Git автоматически запускает git gc при достижении порогов (по умолчанию — после 65536 loose-объектов или 50 pack-файлов), но в активных репозиториях стоит делать это вручную:
git gc --aggressive --prune=now
--aggressive— более тщательная упаковка (медленнее, но лучше сжатие);--prune=now— немедленно удалить объекты старше указанного времени (по умолчанию — 2 недели).
Для очень больших репозиториев:
git repack -d -l -A --window=250 --depth=250
-d— удалить старые pack-файлы;-l— использовать только локальное сжатие (без--delta-base-offset);-A— оставить достижимые loose-объекты;--window,--depth— параметры поиска дельт (выше — лучше сжатие, но дольше).
Анализ размера
Определить, что занимает место:
# Топ-10 файлов по общему размеру в истории
git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| awk '/^blob/ {print substr($0,6)}' \
| sort -k2 -n -r \
| head -n 10
# Размер по веткам
git branch --format='%(refname:short)' | while read b; do
echo -n "$b: "
git ls-tree -r -l $b | awk '{sum += $4} END {print sum/1024/1024 " MB"}'
done
Если обнаружены большие бинарники — рассмотрите git filter-repo для их удаления или переход на Git LFS.
7.4. Работа с большими репозиториями: sparse-checkout, partial clone
Sparse-checkout
Позволяет клонировать и обновлять только часть файловой структуры, экономя время и дисковое пространство.
git clone --filter=blob:none --sparse <url> # частичная загрузка объектов + sparse
cd repo
git sparse-checkout set dir1/ dir2/file.txt
git sparse-checkout init --cone # режим "cone" (иерархический, быстрее)
В режиме --cone (по умолчанию с Git 2.25+) правила применяются иерархически: если включена src/, то включаются все поддиректории. Можно использовать git sparse-checkout add/remove/set для управления.
Partial clone и promisor remotes
Git 2.19+ поддерживает частичную загрузку объектов:
git clone --filter=blob:none <url> # не загружать blob-объекты (только trees/commits)
git clone --filter=tree:0 <url> # не загружать деревья глубже корня
При необходимости объект подгружается «лениво» при checkout, log -p, grep. Особенно эффективно с серверами, поддерживающими uploadpack.allowFilter (GitHub, GitLab ≥ 12.5).
Для CI-сборок — значительное ускорение клонирования при сохранении полной истории.
Часть 8. Анти-паттерны и типичные ошибки
8.1. «Сломанный» .gitignore
-
Ошибка: добавить файл в индекс, потом прописать его в
.gitignore.
Последствие: файл продолжает отслеживаться.
Исправление:git rm --cached <file>
git add .gitignore
git commit -m "stop tracking <file>" -
Ошибка: игнорировать файлы через
git update-index --assume-unchangedили--skip-worktree.
Последствие: изменения не видны вstatus, но приcheckout,reset --hard— перезаписываются.
Когда допустимо: только для локальных override-файлов (например,appsettings.Development.json), и только если точно понимаете разницу междуassume-unchanged(оптимизация производительности) иskip-worktree(намеренное игнорирование изменений).
8.2. Использование git push --force без --with-lease
--force перезаписывает удалённую ветку независимо от её текущего состояния. Если другой разработчик успел запушить изменения — они потеряются.
Правильно:
git push --force-with-lease origin feature
# или ещё безопаснее — по ветке:
git push --force-with-lease=feature:feature
8.3. Смешивание merge и rebase в shared-ветках
Если ветка develop используется несколькими людьми, её нельзя перебазировать. Это нарушает линейность и приводит к дублированию коммитов при последующих pull.
Правило: переписывание истории допустимо только для локальных веток, не имеющих upstream или не опубликованных.
8.4. «Забытый» git add перед commit
Создаёт коммит без изменений, или с устаревшим состоянием индекса.
Профилактика:
- Использовать
git commit -aтолько для простых случаев (не работает с новыми файлами); - Настроить pre-commit hook для проверки непустого diff;
- Использовать
git statusперед каждымcommit.
8.5. Неправильное использование git stash
git stashбез--include-untrackedне сохраняет новые файлы;git stash popпосле конфликта может оставить изменения в индексе — лучшеgit stash apply, разрешить конфликты,git stash dropвручную;- Долгоживущие stash’и — плохая практика; для длительного хранения используйте ветки.