Search...Search plugins and themes...
⌘K
Sign in
  • Get started
  • Download
  • Pricing
  • Enterprise
  • Account
  • Obsidian
  • Overview
  • Sync
  • Publish
  • Canvas
  • Mobile
  • Web Clipper
  • CLI
  • Learn
  • Help
  • Developers
  • Changelog
  • About
  • Roadmap
  • Blog
  • Resources
  • System status
  • License overview
  • Terms of service
  • Privacy policy
  • Security
  • Community
  • Plugins
  • Themes
  • Discord
  • Forum / 中文论坛
  • Merch store
  • Brand guidelines
Follow us
DiscordTwitterBlueskyThreadsMastodonYouTubeGitHub
© 2026 Obsidian
GitHub

Publish to Git Repo

Novelty LiuNovelty Liu29 downloads

Incrementally publish notes and their images to a GitHub repository (co-located assets, git-tree atomic commit).

Add to Obsidian
  • Overview
  • Scorecard
  • Updates6

Translations: 中文

An Obsidian plugin that publishes vault notes together with their images to a GitHub repository. Incremental, atomic, and safe to re-run.

Flag a note with gh-publish: true and run Publish to Git Repo — the note (and any images it embeds) lands in your repo as clean Markdown + image files. It is pure file sync: it does not render or host a website. Pair it with any static-site generator (Jekyll, Hugo, Eleventy, Astro, …) or simply use the repo as a Markdown archive.

  • Co-located assets — a note with images becomes a folder (index.md + images/); a plain-text note stays a single .md file.
  • Incremental — only new and changed notes are uploaded; unchanged notes are skipped, so re-running is cheap and idempotent.
  • Atomic — every change in a run lands in a single commit. If a run is interrupted or fails, nothing partial is committed.
  • Re-runnable — running the command again never duplicates or re-uploads unchanged content.

Design inspired by oleeskild/obsidian-digital-garden, but lighter: it only syncs files from your vault to a GitHub repo.

Publish preview: review every change before a single file is uploaded


Quick start

1. Create a target repository

Create a (public or private) GitHub repository to hold your published notes. You don't need to initialize it with a README.

2. Create a GitHub token

Create a token with Contents: Read and write permission for that repository.

  • Fine-grained token (recommended): Repository access → your repo; Repository permissions → Contents: Read and write.
  • Classic token: check the repo scope.

3. Install the plugin

  • From Community plugins (when available): Settings → Community plugins → Browse → search "Publish to Git Repo" → Install → Enable.
  • Manually: copy main.js, manifest.json, and styles.css into <vault>/.obsidian/plugins/publish-to-git-repo/, then enable the plugin in Settings → Community plugins.

4. Configure the settings

Settings → Publish to Git Repo:

Field Example
GitHub token your token from step 2
Repository owner/repo
Branch main (default)
Base path notes (optional)
Default storage path posts (required)

5. Flag a note and publish

Add this to a note's frontmatter:

---
gh-publish: true
---

Then run the Publish to Git Repo command (Command palette: Cmd/Ctrl+P). You'll see a preview of what will change before anything is uploaded.

That's it — your note and its images are now in your repo. Run the command again any time; only what changed gets uploaded.


Commands

Command What it does
Publish to Git Repo Scans all gh-publish: true notes, shows a preview, and uploads the new/changed ones in one commit.
Mark/unmark for publishing Toggles gh-publish on the active note. Marking also seeds an empty gh-path for you to fill in; unmarking removes both (a gh-path value you typed is kept). Only available when a Markdown note is open.

You can assign hotkeys to both via Settings → Hotkeys.


Settings

Setting Required Notes
GitHub token ✅ Needs Contents: Read and write (classic token: check repo)
Repository ✅ owner/repo
Branch — Defaults to main
Base path — Root prefix inside the repo, e.g. notes
Default storage path ✅ Relative to the base path, e.g. posts
Published base URL — When set, the plugin writes gh-published-url back into each note; left unset otherwise

⚠️ The token is stored in plaintext in <vault>/.obsidian/plugins/publish-to-git-repo/data.json. Don't share that vault or its data.json in public environments.


Frontmatter flags

---
gh-publish: true              # allow this note to be published
gh-path: essays/2024          # optional, relative to base path; overrides the default storage path for this note
gh-published: true            # managed by the plugin — written back after publishing
gh-published-url: https://... # managed by the plugin — publish link (only when Published base URL is set)
---
  • gh-publish — the master switch. Only notes where this is exactly true are published. Prefer the Mark/unmark for publishing command to set it: it writes boolean true (not the string "true", which the plugin ignores) and also adds an empty gh-path placeholder.
  • gh-path — optional per-note sub-folder. If empty or whitespace-only, the note uses the default storage path instead — an empty value does not publish to the repo root. The Mark/unmark for publishing command adds it as an empty placeholder when marking, so you can fill it in; unmarking removes that empty placeholder (a value you set is preserved).
  • gh-published / gh-published-url — written back automatically by the plugin after each successful publish to record status. You don't need to set or edit them. Changing or removing them does not trigger or prevent a republish — republishing is driven by content comparison, not these flags.

Path layout

Whether a note becomes a folder depends on whether it embeds images or other assets:

Case Path in repo
Note without images/assets {base}/{default path or gh-path}/{note name}.md
Note with images/assets {base}/{default path or gh-path}/{note name}/index.md
Embedded images & assets {...}/{note name}/images/{asset name}

{base} is the base path if set. The note name is sanitized (path-illegal characters removed; CJK characters and spaces preserved).

For gh-published-url, the filename marker is the note name with the .md extension and any leading YYYY-MM-DD- date prefix removed — kept as-is, no slugification.

Example. With base path notes and default storage path posts, the note 2024-03-01-hello.md containing one image ![[pic.png]]:

notes/posts/hello/index.md
notes/posts/hello/images/pic.png

The same note with no images becomes a single file:

notes/posts/hello.md

Removing the last image from a note switches its layout from a folder (hello/index.md) to a flat file (hello.md) on the next publish. The old folder is not deleted automatically — see Known limitations.


Link rewriting

In your vault In the repo
![[pic.png]] ![](images/pic.png)
[[wikilinks]] Kept as-is (not processed)
![[note]] note embeds Not processed

Image embeds are resolved through Obsidian's metadata cache (not text matching), so a literal ![[...]] inside a code block is never touched. Embeds the plugin can't resolve are reported as failures — never silently dropped.


How it works

This section explains the publish flow and the guarantees behind it. (For the code architecture and internals, see README-developers.md.)

The publish flow

  1. Scan. Find every note with gh-publish: true.
  2. Read the remote. Fetch the repo's current file tree once (file paths and their hashes — no file contents downloaded).
  3. Classify. For each note, build exactly what would be published, compute its hash, and compare it with the remote. Each note falls into one bucket:
    • new — not in the repo yet → will be uploaded
    • changed — content differs from the repo → will be uploaded
    • unchanged — identical to the repo → skipped
    • failed — an embed couldn't be resolved → blocked, but doesn't stop the rest
  4. Preview. A confirmation modal shows the buckets and the target repo/branch before anything is uploaded.
  5. Upload. After you confirm, a progress card runs: the new/changed files are uploaded, then everything is committed in one atomic commit.
  6. Write back. On success, gh-published (and gh-published-url, if configured) is written back into each note's frontmatter.
  7. Report. A summary modal shows what was published.

Guarantees

  • Idempotent & incremental. The plugin hashes what would be published and compares it to the remote, so unchanged notes are never re-uploaded and running twice never duplicates content. Edits made to the repo outside the plugin are respected too — the plugin re-aligns to the real remote state on every run.
  • Atomic. All changes in a run land in a single commit. If a step fails before the commit, nothing is committed — there's no half-uploaded state.
  • Stable output. Published Markdown is produced deterministically: plugin-owned gh-* frontmatter is stripped and embeds rewritten consistently, so a note's bytes don't drift between runs and its content hash stays stable.
  • Safe to cancel. The progress card's Stop button cancels cooperatively. Already-uploaded blobs that weren't committed are ignored by Git; the next run picks up naturally.
  • Co-located images. Each note carries its own copy of the images it uses — no shared image folders, no broken links.

Known limitations

  • Shared images are duplicated. The same image referenced by several notes is copied into each note's images/ folder (one copy per note).
  • Layout-switch leftovers. When a note loses its last image and switches from a folder to a flat file, the old folder stays in the repo. Clean it up manually.
  • No delete sync. Removing gh-publish or deleting a note does not remove the corresponding file from the repo.
  • Wikilinks & note embeds. [[wikilinks]] are kept as-is, and ![[note]] note embeds are not expanded.
  • Large images on mobile. Images are base64-encoded for upload; very large images are memory-heavy on mobile — compress them first.
  • Token is plaintext. Stored in data.json in your vault (the settings page warns about this).

For developers

Build steps, architecture, and internals live in README-developers.md (English only).

HealthExcellent
ReviewSatisfactory
About
Publish vault notes flagged gh-publish: true and their images to a GitHub repo, storing each note as a folder with index.md and a co-located images/ directory. Sync incrementally by comparing git blob SHAs, commit atomically via the Git Trees/Blobs API, rewrite embeds to raw.githubusercontent URLs for deterministic output, and update frontmatter with publish status and URL.
GitPublishingAttachments
Details
Current version
1.3.2
Last updated
4 days ago
Created
Last week
Updates
6 releases
Downloads
29
Compatible with
Obsidian 1.8.7+
Platforms
Desktop, Mobile
License
MIT
Report bugRequest featureReport plugin
Author
Novelty LiuNovelty Liusatlxq
GitHubsatlxq
  1. Community
  2. Plugins
  3. Git
  4. Publish to Git Repo

Related plugins

Share Note

Instantly share/publish a note, with the full theme and content exactly like you see in Obsidian. Data is shared encrypted by default, and only you and the person you send it to have the key.

Quartz Syncer

Manage and publish your notes to Quartz, the fast, batteries-included static-site generator.

MP Publisher

Preview with custom CSS themes and publish articles to WeChat Official Accounts with one click.

Social Archiver

Archive social media posts from various platforms.

TTRPG Tools - Publish

Generates publish.js/publish.css and a publish-assets manifest for TTRPG Tools: Maps (Zoom Map).

Git

Integrate Git version control with automatic backup and other advanced features.

Ink

Handwriting and drawing directly between paragraphs using a digital pen, stylus, or Apple pencil.

Fit

Minimalist File gIT (FIT) to sync your files across mobile and desktop devices using GitHub.

Pixel Perfect Image

Pixel perfect 100% image resizing, copy to clipboard, show image in Finder/Explorer, and much more.

Local Images Plus

A reincarnation of Local Images to download images in Markdown notes to local storage.