From 313cb2f643642314733ae22db7b54edb6db58019 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Sat, 27 Jun 2026 19:22:48 -0400 Subject: [PATCH] chore: enable canvas, disable bookmarks, document single-tier config Enable the canvas core plugin and disable bookmarks in core-plugins.json. Rewrite the README to describe the collapsed single-tier global config (the per-vault fork tier and bookmarks.json are retired) and add a comprehensive git submodule how-to. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01CsXyUHsjzgFfz2EWPrSQ2U --- README.md | 204 +++++++++++++++++++++++++++++++++------------- core-plugins.json | 4 +- 2 files changed, 151 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 9de0e81..f33889f 100644 --- a/README.md +++ b/README.md @@ -1,88 +1,182 @@ -# Obsidian config — global + per-vault fork model +# Obsidian config — shared global config via git submodule -Two-tier Obsidian configuration: +A single, shared Obsidian configuration used by **every** vault. -- **Global config** — `ssh://git@gitea.bchen.dev:2222/brendan/obsidian-global-config.git` — - shared settings (plugins, hotkeys, appearance, theme) used by **every** vault. -- **Per-vault config** (this repo, e.g. `obsidian-config`) — a *fork* of the global repo: - the global config **plus** files specific to one vault that must sync with that vault - but never be published globally. - -Each vault's `.obsidian` is a git submodule pointing at that vault's per-vault repo, which -has the global repo configured as its `upstream` remote. +Each vault's `.obsidian/` directory is a **git submodule** pointing at one repo — +the global config: ``` -vault/.obsidian ──submodule──► obsidian-config (per-vault, downstream) - │ remote: upstream - ▼ - obsidian-global-config (shared, upstream) +ssh://git@gitea.bchen.dev:2222/brendan/obsidian-global-config.git (remote: origin) ``` +``` +vault-A/.obsidian ─┐ +vault-B/.obsidian ─┼──submodule──► obsidian-global-config (one source of truth) +vault-C/.obsidian ─┘ +``` + +Change a setting once, push it, and pull it down in every other vault. There is no +per-vault tier and no vault-specific fork — everything tracked here is shared +everywhere. Anything that should *not* be shared is machine-local and gitignored. + +> **History:** this config used to be two-tier (a per-vault fork with the global repo as +> `upstream`). That has been collapsed — the per-vault repo and its remote are gone, and +> the global repo is now the single `origin`. If you ever need the old vault-specific +> data (e.g. the former `bookmarks.json`), it still lives in the retired +> `obsidian-config` repo's history. + ## Tiers -| Tier | Examples | Lives in | +| Tier | Examples | Tracked? | |---|---|---| -| **Global** (shared everywhere) | `app.json`, `appearance.json`, `hotkeys.json`, `templates.json`, `daily-notes.json`, `core-plugins.json`, `community-plugins.json`, `types.json`, `themes/**`, plugin code, most plugin `data.json` | global upstream → flows to every vault | -| **Vault-specific** (synced, never published) | `bookmarks.json` | per-vault repo only — never pushed upstream | -| **Machine-local** (not synced) | `workspace.json`, `workspaces.json`, `workspace-mobile.json`, `.DS_Store` | gitignored everywhere | +| **Shared** (everywhere) | `app.json`, `appearance.json`, `hotkeys.json`, `templates.json`, `daily-notes.json`, `core-plugins.json`, `community-plugins.json`, `types.json`, `themes/**`, plugin code, plugin `data.json` | ✅ tracked in this repo → flows to every vault | +| **Machine-local** (not synced) | `workspace.json`, `workspace-mobile.json`, `workspaces.json`, `.DS_Store` | ❌ gitignored (see `.gitignore`) — stays on the one machine | + +If a setting starts diverging per-machine, add it to `.gitignore`. There is no longer a +per-vault tier — keep everything tracked truly shareable. ## Daily workflow -Run from a vault root (where `.obsidian` is the submodule). +Run these from a **vault root** (the directory that contains `.obsidian` as a submodule). +All `git -C .obsidian …` commands operate inside the submodule. -**Change a vault-specific setting** (a bookmark) — stays in this vault: +### Change a setting and publish it to all vaults ```bash +# 1. Make the change (edit in Obsidian, or edit the JSON directly) git -C .obsidian add -A -git -C .obsidian commit -m "..." -git -C .obsidian push -git add .obsidian && git commit -m "bump config" && git push # record the pointer +git -C .obsidian commit -m "Describe the config change" +git -C .obsidian push # publish to the global repo (origin) + +# 2. Record the new submodule pointer in this vault's repo +git add .obsidian && git commit -m "Bump obsidian config" && git push ``` -**Pull global changes down** — conflict-free; never touches your vault-specific files: +### Pull the latest global config into a vault ```bash -git -C .obsidian fetch upstream -git -C .obsidian merge upstream/main -git -C .obsidian push # save merged result to this vault's repo -git add .obsidian && git commit -m "pull global config" && git push +git -C .obsidian pull # fast-forward to the latest global config +# or, equivalently, from the vault root: +git submodule update --remote .obsidian + +git add .obsidian && git commit -m "Bump obsidian config" && git push # record the pointer ``` -**Change a global setting** — make it in a clone of the **global** repo, push there, then run -the pull-down above in each vault: +> Restart Obsidian (or reload the vault) after pulling config so it re-reads the JSON. + +--- + +# Working with git submodules — comprehensive how-to + +`.obsidian` being a submodule means the **vault repo** stores only a *pointer* (a commit +SHA) to the config repo, not its files. The two repos have independent histories. This +section covers everything you'll need. + +## Mental model + +- The **vault repo** tracks your notes **plus** a pointer entry for `.obsidian`. +- The **config repo** (`obsidian-global-config`) holds the actual `.obsidian` files. +- `git status` in the vault shows `.obsidian` as a single line: + - `modified: .obsidian (new commits)` → the submodule is checked out at a different + commit than the pointer recorded in the vault repo. Fix by committing the pointer + (`git add .obsidian && git commit`) or by resetting the submodule. + - `modified: .obsidian (modified content)` → there are uncommitted edits *inside* the + submodule. +- `.gitmodules` (in the vault root) maps the submodule path → its URL. `git submodule + sync` copies that URL into the vault's `.git/config` for local use. + +## Cloning a vault that uses this submodule ```bash -git -C /path/to/obsidian-global-config add -A -git -C /path/to/obsidian-global-config commit -m "..." -git -C /path/to/obsidian-global-config push +# Clone the vault and its submodule in one step: +git clone --recurse-submodules + +# Already cloned without --recurse-submodules? Initialize after the fact: +git submodule update --init --recursive ``` -> Promoting a *global* change you happened to make **inside a vault** isn't a plain push -> (your per-vault history has vault-specific commits in the way) — cherry-pick it onto an -> `upstream`-tracking branch and push that. Editing global settings in the global clone -> avoids this entirely. +## Adding the config to a brand-new vault -## Which files are vault-specific +Do this **before opening the vault in Obsidian** — Obsidian auto-creates a default +`.obsidian/` on first open, which blocks `submodule add`. Delete any auto-created one first. -The current list is just `bookmarks.json`. To make a plugin's settings -vault-specific, add `plugins//data.json` to the denylist and remove it from the -global repo. The denylist is enforced by a `pre-receive` hook on the global repo, which -rejects any push that touches a vault-specific path (so a stray cherry-pick can't publish -private data). See the project notes for the hook script. +```bash +cd /path/to/new-vault +rm -rf .obsidian # only if Obsidian already created a default one +git submodule add ssh://git@gitea.bchen.dev:2222/brendan/obsidian-global-config.git .obsidian +git commit -m "Add shared obsidian config submodule" +``` -## Bootstrapping a new vault +On first open the vault starts in **Restricted Mode** — click **Trust author and enable +plugins** to load the configured community plugins. -- **Only needs the global config** → point the submodule straight at the global repo: +## Pulling submodule updates - ```bash - git submodule add ssh://git@gitea.bchen.dev:2222/brendan/obsidian-global-config.git .obsidian - ``` +```bash +# Update this one submodule to the latest commit on its tracked branch: +git submodule update --remote .obsidian -- **Needs its own private tracked config** → create an empty gitea repo for it, seed it from - the global repo, add it as the submodule, and set `upstream` to the global repo. (Ask to - have this scripted.) +# Update every submodule in the repo: +git submodule update --remote --merge -> Do this **before opening the vault in Obsidian** — Obsidian auto-creates a default -> `.obsidian/` on first open, which blocks the submodule add. Delete it first if present. -> On first open the vault starts in **Restricted Mode** — click **Trust author and enable -> plugins** to load the configured community plugins. +# Plain `git pull` in the vault updates the *pointer* but NOT the submodule contents. +# Follow it with this to actually check out the recorded commit: +git submodule update --init --recursive +``` + +> Tip: `git config --global submodule.recurse true` makes `git pull` / `git checkout` +> update submodules automatically. + +## Making and pushing changes from inside the submodule + +```bash +cd .obsidian +git checkout main # submodules often land in DETACHED HEAD — see gotchas +# … make changes … +git add -A && git commit -m "…" +git push # to origin (the global config repo) +cd .. +git add .obsidian && git commit -m "Bump obsidian config" # record the new pointer +git push +``` + +## Changing the submodule's remote URL + +```bash +# Edit the URL in .gitmodules (vault root), then sync it into .git/config: +git submodule sync .obsidian +# Verify the submodule's own remotes: +git -C .obsidian remote -v +``` + +## Inspecting submodule state + +```bash +git submodule status # shows SHA + path for each submodule +git -C .obsidian remote -v # the submodule's remotes +git -C .obsidian branch -vv # branch + upstream tracking +git -C .obsidian log --oneline -5 +``` + +## Removing a submodule (de-init) + +```bash +git submodule deinit -f .obsidian # unregister + clear working tree +git rm -f .obsidian # remove from index + .gitmodules +rm -rf .git/modules/.obsidian # delete the cached git dir +git commit -m "Remove obsidian config submodule" +``` + +## Gotchas + +- **Detached HEAD:** `git submodule update` checks out a specific commit, leaving the + submodule in detached-HEAD state. Run `git checkout main` inside `.obsidian` before + committing, or your commit won't be on a branch. +- **Pointer vs contents:** committing in the submodule is only half the job — you must + also `git add .obsidian` in the vault to record the new pointer, or other clones won't + get the change. +- **Two pushes:** changes need a push from inside `.obsidian` (to the config repo) **and** + a push of the vault repo (to record the pointer). +- **Restart Obsidian** after pulling config changes so it reloads the JSON. +- **Don't track machine-local files** — keep `workspace*.json` and `.DS_Store` in + `.gitignore` so per-machine state never syncs. diff --git a/core-plugins.json b/core-plugins.json index b20bef3..be0aa38 100644 --- a/core-plugins.json +++ b/core-plugins.json @@ -4,7 +4,7 @@ "switcher": true, "graph": true, "backlink": true, - "canvas": false, + "canvas": true, "outgoing-link": true, "tag-pane": true, "footnotes": false, @@ -16,7 +16,7 @@ "command-palette": true, "slash-command": false, "editor-status": true, - "bookmarks": true, + "bookmarks": false, "markdown-importer": false, "zk-prefixer": false, "random-note": false,