Git sync
If your vault — or a mounted external folder — is a git repository, the tree drives it: fetch, pull, commit and push, without opening a terminal. Nothing is hidden behind an automatic background sync: every operation that touches your files is a click.
Requirements
Znote uses the git already installed on your machine, so it inherits your existing setup:
gitavailable on thePATH(on macOS, the Xcode command line tools are enough).- The vault folder is inside a repository — created the usual way (
git init,git remote add origin …). Znote never creates a repository or a branch for you. - An identity (
git config --global user.name/user.email) and working credentials: an SSH key or a credential helper.
Git runs non-interactive: it can never pop a password prompt into a window that has nowhere to type. A missing credential fails immediately with a readable message instead of hanging.
A repository created while Znote is open is picked up within 20 seconds — no restart needed.
The buttons
They sit on the All files root row (and on the row of each mounted folder that is its own repository):
| Button | What it does |
|---|---|
| ⟳ Refresh | Re-reads the files from disk and runs git fetch. Nothing on disk changes — the tree simply starts showing what moved on both sides. |
| ↓ N | Pulls the N incoming commits. |
| ↑ N | Commits the N local changes and pushes them. |
The ⟳ button appears on hover, like the other row actions. The ↓ and ↑ only appear when there is something to do — they are the notification.
↑ stays greyed out while ↓ shows commits to pull. Pushing on top of incoming commits would require a merge, and a merge can move a note to the trash, so Znote asks you to pull first rather than doing it behind your back.
Reading the tree
Each note carries a one-letter marker:
| Marker | Meaning |
|---|---|
| A (green) | New — not committed yet |
| M (orange) | Modified here |
| D (red) | Deleted here |
| I (blue) | Modified on the remote — a pull will bring it |
| B (red) | Modified on both sides — pulling will move your version to the trash |
A folder shows the most urgent marker found below it, so a change stays visible on a collapsed branch.
The local status refreshes every 20 seconds and whenever the window comes back to the foreground. The network is only ever touched when you click ⟳, ↓ or ↑.
Conflicts: the remote wins
When a note was changed both here and on the remote, pulling asks for confirmation and lists the files. If you confirm:
- Your local version is copied into the Znote trash — a normal trashed note, restorable from the Trash row.
- The remote version replaces it on disk.
- A toast names what was moved: "Pulled 1 commit from origin/main — Reunion was replaced by the remote version, your local note is in the trash".
The same policy applies when the merge itself conflicts (both sides committed). Nothing is ever overwritten without a copy being kept first.
Non-markdown files in conflict (images, PDFs…) cannot become trashed notes: their local version is backed up under .znote/trash/conflicts/<timestamp>/ instead. An encrypted secrets sidecar follows that same rule — the remote wins — and a pull that changes one is picked up on the spot: an open note shows the new value without being reopened.
Restoring a conflicted note
The trashed copy remembers where it came from, so Restore puts it back at its original path — next to the remote version, which keeps its own name.
What gets committed
Commit messages are generated from what actually changed — Znote: update 3 files, with the paths in the body, or Znote: add "Meeting notes" for a single note.
Znote writes a .znote/.gitignore the first time it sees the repository:
trash/ # the corbeille, and the conflict backups above
settings.json # open tabs, last note, folder mounts
metadata.json # note ids, pins, run permissions, trash pointers
*.tmpThese four are a local index, not content. metadata.json in particular must not be shared: each machine's scan drops the ids it does not see locally, so two machines would rewrite the file in turn — an endless ping-pong of commits — and the per-note "may run code" permission would travel with it.
If your vault already tracked those files, Znote removes them from the index (git rm --cached) the next time it opens the vault: the files stay on your disk, they simply stop being pushed.
Your notes, your attachments/ folder and .znote/files/ (images pasted into a note) are versioned — they are content. So are the shared secrets sidecars (<Note>.secrets.yaml): they are encrypted, and they are meant to travel with the note.
Secrets and private files
A second, separate block goes into the vault's own .gitignore the first time you push, covering what must never leave the machine:
# >>> znote secrets (managed) >>>
local-* # private notes, and their sidecar
*.local.secrets.yaml # private values inside an otherwise shared note
# <<< znote secrets <<<Renaming an already committed note to local-… would not have been enough — .gitignore only governs untracked files — so Znote also runs git rm --cached on what the rules now cover and tells you. The files stay on disk; versions already pushed stay in the history.
Before a commit, the notes about to be committed are scanned for plaintext credentials, and the push stops if any are found. See Secrets for the whole picture: how a note points at an encrypted value, and how to read those values without Znote.
A vault inside a larger repository
A vault that lives in a sub-folder of a bigger repository (docs/ of a code project, say) only ever stages its own folder. Status counters and markers are scoped the same way, so unrelated work in the repository is never counted or committed by Znote.
One exception is worth knowing: if you had already staged files elsewhere in that repository, they ride along in the commit — git commits an index, not a folder. The toast says so explicitly when it happens.
External folders
A mounted external folder that is its own repository gets its own buttons, its own branch and its own remote. Two repositories can therefore live side by side in the same window, and a commit started from one row never touches the other.
A folder mounted inside the vault's own repository gets no buttons: the All files row already drives that repository.
Conflicts in an external folder are backed up to the vault's trash — that is where the trash lives, whatever the repository of origin. Restoring puts the note at the vault root, not back into the mounted folder.
What Znote does not do
- It never creates, switches or merges branches — do that from a terminal; Znote always works on the current branch.
- It never pulls, pushes or fetches on its own.
- It does not resolve conflicts hunk by hunk: the policy is whole-file, remote wins, local copy in the trash.
