Repository navigation
Directory Layout and Kept Blocks
pull, push, sync and watch all use the same layout. Each page is a folder holding a Markdown file of the same name, and sub-pages are sub-folders:
docs/ # markdown_root
└── Parent Page/
├── Parent Page.md
├── Meeting Notes/
│ ├── Meeting Notes.md
│ └── 2026-11-03/
│ └── 2026-11-03.md
└── Roadmap/
├── Roadmap.md
└── Tasks.csv # a database on the Roadmap page
The parent page in your configuration is pulled too, as the top folder. Page titles are kept as they are in Notion, spaces included. Only characters that aren't allowed in file names are replaced.
-
By
notion_id, not by path. Each file records its page in thenotion_idfrontmatter field. If you move or rename a file, pull keeps writing to it where it is. -
New files go under the page of their folder. A file without a
notion_idbecomes a sub-page of the page that owns its folder. BothParent Page/Notes.mdandParent Page/Notes/Notes.mdbecome sub-pages of Parent Page. -
Missing folder pages are created. If a folder has no page file, a page named after the folder is created and saved as
Folder/Folder.md. -
Duplicate titles don't collide. Two sibling pages with the same title get separate folders, such as
Title (1a2b3c4d). -
Some paths are never synced: files outside
markdown_root, paths matchingexcluded_patterns, and hidden folders such as.gitand.notion-sync.
A page's file links to its sub-pages and exported databases. It doesn't include their content (see issue #7):
[📄 Meeting Notes](<./Meeting Notes/Meeting Notes.md>) <!-- notion:child_page 1e369015-c240-8021-a930-e3ee43ea0ef6 -->
[🗃️ Tasks](./Tasks.csv) <!-- notion:child_database 1e369015-c240-8021-a930-e3ee43ea0ef7 -->Some blocks are never deleted or changed by a push:
- sub-pages (
child_page) and databases (child_database) - images hosted by Notion
- blocks Markdown can't express: columns, synced blocks, templates, embeds, video, audio, files, PDFs, link previews, links to pages
- any block that contains one of the above
In your file, each kept block is a marker line:
<!-- notion:embed 1e369015-c240-8021-a930-e3ee43ea0ef8 -->When a kept block has text, such as a column list, you get a read-only copy of it between begin and end markers:
<!-- notion:column_list 1e369015-... begin -->
...text from the columns...
<!-- notion:end column_list 1e369015-... -->On push, these lines are not uploaded. They only mark where the kept block sits, so the content you write around them lands in the right place.
-
Edit kept blocks in Notion. Changes you make between
beginandendare not uploaded, and push warns you about them. - You can move marker lines to reorder your text around a kept block.
- Deleting a marker line doesn't delete the block in Notion.
Push compares Notion's blocks with your Markdown one block at a time, using only what Markdown can express (text, formatting, links, block type), and lines them up:
- Unchanged blocks are left alone.
-
Edited blocks of a type that can be updated are changed in place (
PATCH), and their children are compared the same way. Block IDs, comments and links to them survive. - Leftover blocks are inserted at the right position, or deleted.
Inserts happen first, then in-place updates, then deletes. If any step fails, the inserted blocks are deleted and updated blocks get their old content back.