7.1 KiB
Executable File
Git-гигиена после мержа PR (форк → апстрим)
Создан 2026-09-29. Извлечено из уборки после мержа
baldaworks/balda#8+normahq/runtime#4/#5/#7. Связано: personal/plans/balda-mm-pr-plan, personal/plans/balda-runtime-rebuild-mm-validation, personal/tech/balda-go-fx-pitfalls.
1. Порядок операций (он обязателен)
worktree remove --force → worktree prune → branch -D → push origin --delete → remote prune
Перестановка шагов = падение. Подробности ниже.
cd <repo>
git checkout main && git pull --no-rebase <remote> main # merge, НЕ rebase
git worktree list # 🔴 СНАЧАЛА посмотреть
git worktree remove --force <path-with-gone-dir> && git worktree prune
git branch -D <merged-branches>
git push origin --delete <merged-branches>
git remote prune origin && git remote prune upstream
git status --short # ?? — untracked, разобрать отдельно
📌 AGENTS.md Балды: синхронизация общих веток — через merge (git pull --no-rebase), не rebase.
2. 🔴 Питфолл: git-регистрация worktree ≠ путь на диске
Самый дорогой сюрприз. git worktree list показывает пути, которых уже нет:
/private/tmp/balda-mm-pr8-review 06b2b8d0 (detached HEAD) ← каталог снесён из /tmp
.git/worktrees/<name> остаётся жить. Такая регистрация держит ветку, и удаление падает
даже при отсутствующем каталоге:
error: cannot delete branch 'fix/openai-tool-call-roundtrip-v2' used by worktree at
'/Users/admin/Developer/runtime-local' # ← это про ТЕКУЩИЙ worktree, не про мёртвый
Из двух провалов в одной сессии:
codex/mattermost-prod-fix— держал мёртвый/private/tmp/balda-mm-pr8-prod-fix;fix/openai-tool-call-roundtrip-v2— держал активный worktree, где я стоял сам.
Лечение: git worktree remove --force <path> (работает и для несуществующего каталога —
просто убирает регистрацию) → git worktree prune → и только потом git branch -D.
⚠️ Проверять git worktree list ДО удаления веток. Иначе получишь список «неудаляемых» веток
и будешь думать, что они не смержены.
⚠️ git worktree remove на /private/tmp/... уходит на апрув — это удаление каталога, а не
безобидная мета-операция. Нормально, просто ожидаемо.
📌 Ещё: git checkout main невозможен, пока стоишь в worktree удаляемой ветки — отсюда
второй cannot delete branch ... used by worktree в логе.
3. ⚠️ git pull origin main → Already up to date — это НЕ «всё чисто»
После мержа PR в апстрим ветка форка остаётся на старом коммите, а мерж живёт только в
upstream/main. origin (форк) мержа не содержит вообще.
git pull --no-rebase origin main # → Already up to date ← вводит в заблуждение
git log --oneline -1 upstream/main # ← здесь правда
git merge-base --is-ancestor <pr-head> upstream/main && echo MERGED # ← доказательство
Как доказывать факт мержа (не по доке, а фактически):
gh api repos/<upstream>/pulls/<N> --jq '{merged, merge_commit_sha, head_sha, base_sha}'
git merge-base --is-ancestor <head_sha> upstream/main && echo YES || echo NO
Чтобы догнать апстрим локально — git merge upstream/main (не rebase).
4. git branch -r --merged upstream/main = список веток под снос
Один вызов отвечает «какие ветки уже в апстриме»:
git branch -r --merged upstream/main
⚠️ В списке появятся ветки, которые не твои (upstream/feature/*, upstream/docs/*) —
их не трогать. Смотреть на origin/* (свой форк).
5. Что не трогать
| Объект | Почему |
|---|---|
__dolt_remote_info__ |
служебная ветка апстрима (Dolt), появляется в fetch, восстановится сама |
untracked рабочие инструменты (tools/*.py, deltas-*.json) |
не в git, значит и не «мусор». Проверять git ls-files tools/ — пусто ≠ удалить |
| файлы вне PR, нужные для локальной сборки | go.mod, Dockerfile.release, go.sum, app.go, preflight.go — checkout -- ./stash/reset --hard их снесёт |
6. Stash
git stash list — перед любым решением выгружать содержимое, а не удалять вслепую:
git stash show --name-only stash@{N}
git stash show -p stash@{N} > /path/patch.diff # восстановимо после drop
git stash drop — только с явного подтверждения. Дата в сообщении stash (whale-20260623)
говорит, как давно он лежит; старый stash обычно надо сначала прочитать, потом решать.
7. 📌 Реакция Alex: бэкап не предлагать
Предложение «сделать бэкап веток перед сносом» получило резкий отказ. Смерженный код восстанавливается из апстрима — бэкапить нечего. Бэкап в этом контексте = лишний шаг, который читается как неуверенность.
| Предлагать бэкап | НЕ предлагать |
|---|---|
| Продовые конфиги, БД, данные без копии | Смерженные git-ветки |
| Хостовые файлы вне git | Worktree-регистрации в /tmp |
navidrome.db (правило: согласовывать КАЖДЫЙ раз) |
untracked-мусор на смерженных коммитах |
Формулировать так: «ветка смержена в upstream/main (bf4a6ae4), сношу» — без слова «бэкап».
8. Проверка результата
git worktree list # только рабочий каталог
git branch -a # main + remotes, без дев-веток
git status --short # пусто или объяснимые untracked