142 lines
7.1 KiB
Markdown
Executable File
142 lines
7.1 KiB
Markdown
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
|
|
```
|
|
|
|
Перестановка шагов = падение. Подробности ниже.
|
|
|
|
```bash
|
|
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` (форк) мержа не содержит вообще.
|
|
|
|
```bash
|
|
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 # ← доказательство
|
|
```
|
|
|
|
**Как доказывать факт мержа** (не по доке, а фактически):
|
|
```bash
|
|
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` = список веток под снос
|
|
|
|
Один вызов отвечает «какие ветки уже в апстриме»:
|
|
```bash
|
|
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` — перед любым решением выгружать содержимое, а не удалять вслепую:
|
|
```bash
|
|
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. Проверка результата
|
|
|
|
```bash
|
|
git worktree list # только рабочий каталог
|
|
git branch -a # main + remotes, без дев-веток
|
|
git status --short # пусто или объяснимые untracked
|
|
```
|