Commit, pull, push and sync your vault with Git, without ever being asked for credentials. Side panel with the changed files, guided setup, automatic backups and warnings for large files.
An Obsidian desktop plugin (Windows, macOS, Linux) that keeps your vault under Git, built on simple-git.
↑n / ↓n for commits to push / pull). Click it for a menu with the commands.ssh-agent) or on your configured credential helper.The plugin is desktop-only (isDesktopOnly: true).
00TIA/pluginobsidianGit.main.js, manifest.json and styles.css from the latest release into <vault>/.obsidian/plugins/mercurio-git-sync/, restart Obsidian and enable the plugin in Settings → Community plugins.To build it from the source code instead:
npm install
npm run build
then copy main.js, manifest.json and styles.css as above.
The settings tab starts with the same steps as a checklist that ticks itself as you complete them.
git init (branch main unless you configured init.defaultBranch) and creates:.gitignore for Obsidian (workspace layout files, .trash/, OS files);.gitattributes with * text=auto eol=lf: Obsidian writes LF line endings on every OS, so notes keep LF in the repository and in the vault, also on Windows with core.autocrlf=true.Select the Git icon in the left ribbon (or run Open Git panel) to open the panel in the right sidebar:
The panel and the status bar refresh every 30 seconds, after every operation, and 2 seconds after files change in the vault.
| Command | What it does |
|---|---|
| Commit | Stages every change in the vault and commits it with the message built from the configured format (with a timestamp). |
| Commit with custom message… | Same, but opens a dialog with the generated message, which you can edit. Enter confirms. |
| Pull | git pull --no-rebase from the upstream branch. |
| Push | Pushes the current branch; the first time it sets the upstream (origin, or the only remote). |
| Sync | Commit, then pull, then push. Stops before pushing if the pull produces conflicts. |
| Abort merge | After a conflicting pull, goes back to the state before the pull (git merge --abort), after confirmation. Your commits are kept. |
| Initialize repository | See Getting started. |
origin, or the remote of the current upstream branch). Adds origin if there is no remote yet.git config --local); your global Git configuration is never modified. Leave empty to use the global values, which are shown as hints.{{date}}, {{hostname}}, {{numFiles}} (default vault backup: {{date}}), date format in Moment.js syntax (default YYYY-MM-DD HH:mm:ss) and a preview.When Obsidian is started from the macOS Dock (or from a desktop launcher on Linux) it does not get your shell's PATH, so Git installed with Homebrew & co. is "not in PATH". The plugin looks for Git in this order:
/opt/homebrew/bin, /usr/local/bin, /opt/local/bin (MacPorts), Nix, Xcode / Command Line Tools. /usr/bin/git is used only when the developer tools are installed, so the "install Xcode tools" dialog never pops up;Program Files\Git\cmd, Program Files (x86)\Git\cmd, %LOCALAPPDATA%\Programs\Git\cmd, Scoop;/usr/bin, /usr/local/bin, /snap/bin, Nix, Linuxbrew, ~/.local/bin;$SHELL -lc 'command -v git').Git's folder and the existing common locations are appended to the PATH of the processes started by Git, so git-lfs, gh auth git-credential and similar tools work too.
The plugin never shows username, password or passphrase prompts, and prevents Git, ssh and credential helpers from doing so:
GIT_TERMINAL_PROMPT=0 and -c core.askPass= (empty): Git asks nothing, not even through graphical SSH_ASKPASS programs;GCM_INTERACTIVE=never and -c credential.interactive=false: Git Credential Manager (and Git 2.46+) only use saved credentials;core.sshCommand, GIT_SSH, GIT_SSH_COMMAND), ssh runs with -o BatchMode=yes (no passphrase, no host key confirmation); your own command is used as is;So credentials must already be available:
ssh-agent (on macOS ssh-add --apple-use-keychain, on Windows the OpenSSH Authentication Agent service) and the host already in known_hosts (connect once from a terminal, e.g. ssh -T [email protected]);osxkeychain, libsecret, …). Pull once from a terminal to save them.When authentication fails, a notice explains what to configure, with Git's error message.
Pull always merges (--no-rebase), whatever pull.rebase says. If the merge produces conflicts:
Git: N conflicted;<<<<<<< / ======= / >>>>>>> markers stay in the files: keep the right content, remove the markers, then run Commit (or Sync) to conclude the merge. The commit is refused while markers remain;Note: Git cannot put markers in binary files (images, PDFs); the local version stays unless you replace it before committing.
Before a commit, the plugin looks for new or modified files of at least the size limit (50 MB by default) that Git LFS does not handle:
Once committed, a file stays in the history for good (removing it means rewriting history), and GitHub rejects files over 100 MB. Files tracked by Git LFS are never reported.
Git LFS itself is not applied automatically by file size: it stores in LFS only the files matching the patterns in .gitattributes. To use it:
git lfs install # once per computer (e.g. after brew install git-lfs)
cd /path/to/vault
git lfs track "*.pdf" "*.mp4" "*.zip" # once per vault: writes .gitattributes
Then commit .gitattributes (Commit / Sync). From then on the plugin handles LFS transparently: matching files are stored as LFS pointers on commit and uploaded on push, also when Obsidian is started from the Dock (the folder of git-lfs is added to the PATH). LFS authentication follows the same non-interactive rules as Git.
Keep in mind:
git lfs track stay in the normal Git history (moving them needs git lfs migrate, which rewrites history);git config --local, for the remote URL and the commit author). To find the Git executable it checks the common install locations and, on macOS and Linux, asks your login shell where git is.git, xcode-select -p on macOS (to check that Apple's developer tools, which provide /usr/bin/git, are installed) and, when Git is not found otherwise, your login shell with command -v git;.gitignore and .gitattributes when it initializes a repository.LC_ALL=C) so errors are recognised reliably.GIT_OPTIONAL_LOCKS=0: it never takes index.lock and does not disturb other Git programs working on the same repository.user.name / user.email), existing index.lock, repository owned by another user (safe.directory), unreachable remote, push rejected because the remote has new commits, detached HEAD.gpg-agent/pinentry, not from the plugin.npm install
npm run dev # watch mode
npm run build # typecheck + production build (main.js)
npm run lint # ESLint with eslint-plugin-obsidianmd
npm test # unit and integration tests with real Git
@types/node is listed under dependencies rather than devDependencies: the community directory review type-checks the code without installing devDependencies, and without the Node.js types every use of fs, path or os is reported as unsafe. It contains only type declarations, so nothing from it ends up in main.js. npm run lint also runs the type-aware rules that review reports.
Layout:
src/
main.ts plugin lifecycle, timers
commands.ts command registration
git-controller.ts connects the Git service to Obsidian: locking, notices, status bar, automation
settings.ts settings tab
settings-data.ts settings model and validation
commit-message.ts commit message format
messages.ts user-facing texts
git/
git-service.ts commit / pull / push / sync / init / remote / author / abort merge (no Obsidian dependency)
git-env.ts non-interactive environment for Git and ssh
locate-git.ts Git executable lookup
errors.ts classification of Git errors
ui/ notices, commit and confirmation dialogs, status bar
test/ node:test + tsx
The integration tests create temporary repositories with a local remote and check, among other things: conflicts after a pull (nothing pushed, markers left in the files, merge concluded only by hand or aborted), no credential prompt against an HTTP server answering 401 (even with an SSH_ASKPASS program configured), ssh in BatchMode and core.sshCommand respected, timeouts of network operations, Git lookup with an empty PATH, remote and author settings, Git LFS commit and push, and the production bundle loaded with a fake obsidian module. CI runs them on Linux, macOS and Windows.
npm version patch # or minor / major: updates manifest.json, versions.json and creates the tag
git push --follow-tags # the Release workflow builds the plugin and creates a draft release
Without pushing a tag, the same draft can be created from GitHub: Actions → Release Obsidian plugin → Run workflow, with the version written in manifest.json.
Then open the draft on the GitHub Releases page and publish it: Obsidian offers the update to everyone who installed the plugin.
0BSD, see LICENSE. Based on obsidian-sample-plugin.