Files
obsidian-vault/family/documents/vault-sync/2026-09-26-syncthing-clone-origin-url-flip.md
T

6.6 KiB
Executable File
Raw Blame History

tags, date
tags date
vault
git
syncthing
incident
2026-09-26

2026-09-26 — Флип origin у клона /obsidian-syncthing (Syncthing переписал .git/config)

Симптом

Первый за всю историю лога (21646 строк) провал fetch в тике 09:40:31:

[2026-09-26 09:40:31] [syncthing] Fetch failed (origin offline?) — skipping merge/push
[2026-09-26 09:40:31] [taiga-vault] Pushed ✓
[2026-09-26 09:40:31] [syncthing-out] Fetch failed (origin offline?) — skipping merge/push

Тик завершался exit 0 (graceful skip в sync-vault-lib.sh), поэтому cron-задача рапортовала «успех», хотя фазы 1 и 3 не выполнялись вообще:

  • фаза 1 (syncthing): правки с телефона коммитились локально, но не доезжали до bare;
  • фаза 3 (syncthing-out): правки агента/Eagle не доезжали до клона → и дальше по Syncthing не уходили на телефон.

Состояние на момент обнаружения: bare и /vault = 5c939ed8, /obsidian-syncthing = 66d4d626 (отстал на один коммит Eagle), git status --porcelain пуст в обоих, маркеров конфликта 0. Данные не потеряны — git идемпотентен, отставание догоняется одним прогоном.

Причина

remote.origin.url клона /obsidian-syncthing оказался

/mnt/RED_2TB/storage/git/obsidian-vault.git

— хост-путь, которого в контейнере hermes-taiga нет (/mnt пуст, mount-ов на /mnt/RED_2TB нет; bare смонтирован как /vault.git). Проба вручную:

$ git -C /obsidian-syncthing fetch origin main
fatal: '/mnt/RED_2TB/storage/git/obsidian-vault.git' does not appear to be a git repository

До 09:35:33 фаза 1 успешно фетчила и мерджила (reflog клона: merge origin/main: Fast-forward в 09:06, 09:12, 09:25, 09:35), а в 09:36:19 файл .git/config был перезаписан — mtime == ctime == 2026-09-26 09:36:19, владелец uid 950 (наши git-процессы в контейнере пишут файлы клона как root: index, ORIG_HEAD, refs/remotes/origin/main — root, а сам config — 950:950, -rw-rw----). Значит записал внешний процесс, а не наш контейнер.

Это тот же механизм, что и .sync-conflict-файлы внутри .git (от 2026-05-29: config.sync-conflict-…, index.sync-conflict-…, FETCH_HEAD.sync-conflict-…): каталог .git клона /obsidian-syncthing сам синкается Syncthing'ом с других устройств. Старая конфликт-копия конфига (2026-05-15) содержит совсем другой remote — телефонный:

[remote "origin"] url = https://git.mallexxx.duckdns.org/git_admin/obsidian-vault.git
[user] name = alex phone S25

То есть у клона периодически «прилетает» origin с чужого устройства, и внутри контейнера этот адрес не резолвится. Сегодня прилетел хост-путь.

Что сделано (Taiga, 2026-09-26 09:42–09:43 UTC)

  1. Клону вернули контейнерно-корректный remote: origin = file:///vault.git (тот же bare, что и у /vault).

  2. В /opt/data/sync-vault.sh добавлен идемпотентный guard (рядом с блоками safe.directory и git identity) — он выполняется в начале каждого тика:

    • если remote.origin.url клона ≠ file:///vault.git → git remote set-url origin file:///vault.git;
    • в глобальный конфиг (/root/.gitconfig) добавляется правило перезаписи url."file:///vault.git".insteadOf для обоих известных чужих адресов (хост-путь /mnt/RED_2TB/storage/git/obsidian-vault.git и Gitea-URL https://git.mallexxx.duckdns.org/git_admin/obsidian-vault.git) — на случай, если Syncthing перепишет конфиг посреди тика, уже после guard'а.

    Побочных эффектов нет: других репозиториев, использующих эти два адреса, в контейнере нет (проверено grep по git-конфигам).

Проверка (после правки)

  • bash -n /opt/data/sync-vault.sh → syntax OK;
  • три прогона, все exit 0, Pushed ✓ ×3, новых Fetch failed нет (в логе остались только 4 исторические строки тика 09:40);
  • три SHA равны: bare = /vault = /obsidian-syncthing = 3edbddf5;
  • git status --porcelain пуст в обоих клонах, маркеров конфликта 0, Push failed 0;
  • idempotency guard'а: два прогона подряд → правила insteadOf не дублируются (2 шт.);
  • функциональный тест перезаписи: временный клон с origin = /mnt/RED_2TB/storage/git/obsidian-vault.git → git fetch origin main проходит (REWRITE_OK), т.е. даже чужой адрес теперь разрешается в локальный bare.

Осталось за владельцем (Taiga молча не делает)

  • Корень класса — .git клона /obsidian-syncthing синкается Syncthing'ом между устройствами (отсюда и .sync-conflict-* внутри .git, и флип remote'а). Правильное лечение — исключить .git/ из синка этого folder'а на стороне Syncthing (.stignore в корне folder'а или в конфиге демона). Это правка чужой инфраструктуры, решение Алексея. Пока guard в sync-vault.sh держит fetch рабочим независимо от содержимого .git/config.
  • Конфиги /root/.gitconfig и /opt/data/sync-vault.sh живут в контейнере: скрипт персистентен (/opt/data = ZFS), глобальный конфиг — нет, но он пересоздаётся при каждом тике самим скриптом.