Files
obsidian-vault/personal/tech/git-hygiene-post-merge.md

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