102 lines
5.4 KiB
Markdown
Executable File
102 lines
5.4 KiB
Markdown
Executable File
---
|
||
title: sync-vault-lib.sh — pipefail + grep -q молча пропускал коммиты (492 файла застряли)
|
||
type: log
|
||
namespace: family
|
||
tags: [vault, sync, git, sync-script, pipefail, bug, fixed]
|
||
date: 2026-09-26
|
||
---
|
||
|
||
# `git status --porcelain | grep -q .` под `set -o pipefail` = коммиты молча не создаются
|
||
|
||
**Когда:** 2026-09-26 09:53 UTC, найдено и починено в cron-тике Taiga.
|
||
**Статус:** **фикс применён** (`/opt/data/sync-vault-lib.sh`), синк проверен.
|
||
|
||
## Симптом
|
||
|
||
Каждый тик печатал `Pushed ✓` ×3 и `Already up to date.`, exit 0, три SHA равны —
|
||
но рабочий каталог `/obsidian-syncthing` **не очищался**, и в нём накопилось
|
||
**492 изменения**, включая реальную правку пользователя:
|
||
|
||
- 491 файл — смена режима `100644 → 100755` (класс «mode-flip», см.
|
||
`2026-09-26-mode-flip-644-755.md`), включая папки `personal/` 199, `family/` 166, `work/` 108;
|
||
- `personal/plans/music-tags-review/1-needs-decision.md` — 4 чекбокса `- [ ]` → `- [x]`
|
||
(пользователь отмечал разобранные треки, вероятно с телефона).
|
||
|
||
То есть правки телефона **не доезжали до bare**, пока в клоне было мало изменений —
|
||
коммит делался; как только изменений становилось много, коммит перестал создаваться вообще.
|
||
|
||
## Причина
|
||
|
||
В `/opt/data/sync-vault-lib.sh`:
|
||
|
||
```bash
|
||
set -euo pipefail
|
||
...
|
||
if git status --porcelain | grep -q .; then # ← вот это
|
||
git add -A 2>/dev/null || true
|
||
...
|
||
git commit ... || true
|
||
else
|
||
true # ← «нечего коммитить», молча
|
||
fi
|
||
```
|
||
|
||
`grep -q` выходит сразу после первого совпадения. При большом выводе `git status`
|
||
(492 записи ≈ **23 КБ**) он продолжает писать в уже закрытый pipe, получает `SIGPIPE`,
|
||
и **`pipefail` объявляет весь pipeline упавшим (exit 141)**. `if` уходит в `else` —
|
||
ветка коммита не выполняется вообще. Ошибка полностью безмолвная: stderr у
|
||
`add`/`commit` погашен, `Pushed ✓` печатается как ни в чём не бывало.
|
||
|
||
Порог — размер вывода: у коммита из 2 файлов (09:44:37) условие было истинным,
|
||
у 492 файлов — ложным.
|
||
|
||
**Доказательство (воспроизведение):**
|
||
|
||
```bash
|
||
cd /obsidian-syncthing
|
||
bash -c 'set -euo pipefail; if git status --porcelain | grep -q .; then echo IF=TRUE; else echo IF=FALSE; fi'
|
||
# → IF=FALSE (492 записи)
|
||
bash -c 'set -eu; if git status --porcelain | grep -q .; then echo IF=TRUE; else echo IF=FALSE; fi'
|
||
# → IF=TRUE (то же состояние, без pipefail)
|
||
```
|
||
|
||
## Фикс
|
||
|
||
```bash
|
||
STATUS="$(git status --porcelain 2>/dev/null || true)" # подстановка не даёт SIGPIPE
|
||
if [ -n "$STATUS" ]; then
|
||
git add -A 2>/dev/null || true
|
||
MSG="$(printf '%s\n' "$STATUS" | awk 'NF{n++; if(n<=25) printf "%s ", $2} END{if(n>25) printf "(... +%d more)", n-25}' || true)"
|
||
git commit -m "[$STAMP] $LABEL: $MSG" 2>/dev/null || true
|
||
fi
|
||
```
|
||
|
||
Две правки в одном месте:
|
||
|
||
1. **условие** через `$( )` вместо `| grep -q` — нет пайпа, нет SIGPIPE;
|
||
2. **сообщение коммита** собирается одним `awk` (читает вход целиком) — раньше при массовом
|
||
flip в сообщение и в `/tmp/vault-sync.log` попадали все ~500 путей; теперь первые 25 + `(... +N more)`.
|
||
⚠️ Не заменять на `awk ... | head -25 | tr ...`: `head` рвёт пайп, `awk` получает SIGPIPE,
|
||
подстановка падает под `set -e` — тик завершается `exit 141` (проверено, первая версия фикса
|
||
так и падала).
|
||
|
||
## Проверка после фикса
|
||
|
||
```
|
||
синк: exit 0, Pushed ✓ ×3
|
||
bare = /vault = /obsidian-syncthing = 041885fa
|
||
git status --porcelain пуст в обоих клонах, маркеров конфликта 0
|
||
Push failed 0, Fetch failed 4 (все исторические, тик 09:40 до origin-guard)
|
||
safe.directory 3 записи (без дублей), insteadOf 2 (без дублей)
|
||
коммит 12a7566e: 492 файла, сообщение "... (+467 more)"
|
||
чекбоксы [x] в bare HEAD и в /vault = 8 → правка телефона доехала
|
||
```
|
||
|
||
## Связанное
|
||
|
||
- `2026-09-26-syncthing-clone-origin-url-flip.md` — flip `origin` в клоне (guard уже вшит)
|
||
- `2026-09-26-syncthing-clone-root-objects-acl-confusion.md` — root-объекты и ACL (разбор
|
||
другой сессии, решения за Alex): там же симптом «документ не доехал до телефона»
|
||
- `2026-09-26-mode-flip-644-755.md` — откуда берутся `100644 → 100755`
|
||
- `family/how-to/vault-git-sync.md`, skill `vault-sync-infrastructure`
|