Engineering 22 min read

A .gitignore for Your Notes: What Belongs in the Vault, and What's Just Junk the OS Made

MMNMNOTE
gitgitignorenote-takingobsidianmarkdownplain-textlocal-firstvaultworkflowtutorials
Updated July 6, 2026

A note vault is what you wrote — the .DS_Store, Thumbs.db, desktop.ini, and .obsidian/workspace.json were written by the OS, the editor, and the plugins. Git already gives you three ignore files to declare the difference — the committed .gitignore, the per-repo .git/info/exclude, and the machine-global ~/.config/git/ignore — and most tutorials only cover the first.

Version-controlling a folder of Markdown is one workflow among several. Dropbox, iCloud, Syncthing, rsync, and every vendor's "sync" all have their own version of "don't sync me." The durable principle is the same across all of them: declare what is noise before you commit to a system. This piece teaches the version for the git workflow, and the discipline survives even if you never git init.

The three levels of ignore, and which file to use for which pattern

Git recognises three exclude-pattern sources by precedence, and this is the sentence most .gitignore tutorials skip: "Git normally checks gitignore patterns from multiple sources, with the following order of precedence, from highest to lowest…"1. The three sources are the committed .gitignore, the per-repo local .git/info/exclude, and the machine-global file at core.excludesFile, whose default resolves to ~/.config/git/ignore on most systems1 2.

Each file has a stated purpose in the same reference. Patterns which should be version-controlled and distributed to other clones go in .gitignore. Patterns specific to a repository but auxiliary to one user's workflow go in the per-repo $GIT_COMMON_DIR/info/exclude file. Patterns a user wants Git to ignore in all situations go in the file named by core.excludesFile, whose default is $XDG_CONFIG_HOME/git/ignore2.

The decision frame is the whole game. The specific patterns come later, and mostly come from a canonical community list you can copy from. Read once as a table:

LevelPathShared?Best used for
1. Committed.gitignore (in the repo)Yes, checked inPatterns everyone using this vault should honor — e.g. .obsidian/workspace.json
2. Per-repo local.git/info/exclude (in .git/)No, never leaves your cloneMachine-specific scratch folders; a personal notes.txt inside one repo
3. Global (per-machine)~/.config/git/ignore (or core.excludesFile)No, applies to every repo on your machineOS byproducts like .DS_Store and Thumbs.db; files the machine wrote, not you

That is the mechanic. The rest of this piece is what belongs at each level, why, and the mistakes that turn a .gitignore into a source of drift.

The middle level — .git/info/exclude — is the one nobody talks about, and the one long-time git users defend most warmly when it comes up. The day Nelson Figueroa's essay surfaced on Hacker News (600 points at time of writing), a commenter with the handle bryancoxwell wrote: "I use the ever living hell out of .git/info/exclude. Works great for scripts/Makefiles I only want locally and collaborators wouldn't care about or be able to use."3

Figueroa's own framing is why the essay resurfaced the mechanic: "I've been using Git for so long and I just realized you can ignore files at three different levels and not just with .gitignore. The three files you can use to ignore files are: .gitignore · .git/info/exclude · ~/.config/git/ignore."4

For a note vault the equivalents are obvious. A scratch/ folder for quick captures on this laptop, a test-notes/ you spun up to try a template, a personal todo.md you never meant to publish. None of that belongs in .gitignore; a collaborator forking your vault would be told to ignore your scratch, and none of it should be tracked. .git/info/exclude is where it lives.

The third level, the machine-global file, clears the most noise from git status. It applies to every repository on your machine at once, not just your vault, and that is exactly what you want for OS byproducts.

The OS-junk canon, and why it belongs in your global ignore

The files your machine writes into every folder are not vault content — they are the operating system narrating over the vault. Put them in the global ignore file once, per machine, and every repository on that machine inherits the discipline for free. This is the level most tutorials skip.

GitHub maintains github/gitignore, a CC0-licensed reference repository with 174,707 stars and 82,368 forks at time of writing5. It ships per-language templates and, more usefully, a Global/ folder that catalogs the byproducts each operating system writes. This is the canonical list; do not curate your own. The three files that matter for a notes vault are Global/macOS.gitignore, Global/Windows.gitignore, and Global/Linux.gitignore5.

Read as parallel columns:

macOS Finder writes: .DS_Store · .localized · __MACOSX/ · .AppleDouble · .LSOverride · Icon · ._* (resource forks) · .DocumentRevisions-V100 · .fseventsd · .Spotlight-V100 · .TemporaryItems · .Trashes · .com.apple.timemachine.donotpresent5.

Windows Explorer writes: Thumbs.db · Thumbs.db:encryptable · ehthumbs.db · ehthumbs_vista.db · *.stackdump · [Dd]esktop.ini · $RECYCLE.BIN/ · *.lnk5.

Linux file managers write: *~ (editor backup) · .fuse_hidden* · .directory (KDE) · .Trash-* · .nfs* · nohup.out5.

Nothing in any of those lists is user-authored content. Per the current Wikipedia entry, .DS_Store is "a file generated by macOS that stores custom attributes of its containing folder, such as folder view options, icon positions, and other visual information… It is created and maintained by the Finder application in every folder, and has functions similar to the file desktop.ini in Microsoft Windows."6

Microsoft's own Windows Shell documentation puts the same fact from the other side: "You can customize the appearance and behavior of an individual folder by creating a Desktop.ini file in that folder to specify different options."7 Both are per-folder state written by the file manager — not by you — and both live at the machine level.

Copy the three GitHub lists into ~/.config/git/ignore once. Nelson Figueroa puts the reasoning plainly: "Whatever filenames are added to this file are ignored globally at a machine-level. This file is not checked into Git and isn't associated with any particular repository. It's a great place to add files that you want to ignore in every git repository on your computer. For example, if you're on macOS, adding .DS_Store here would be ideal."8

A common alternative is to paste the same OS canon into every repo's .gitignore — so a collaborator on Windows is protected from your macOS byproducts and vice versa. That is defensible for a shared codebase. For a personal note vault, the global file is the honest home — the pattern is machine-conditional, and putting it in the global file lets your macOS collaborator's own global file carry their macOS canon.

For an Obsidian vault, the vendor tells you which files are volatile

If your vault is an Obsidian vault, the vendor documents exactly which files under .obsidian/ are per-machine state versus shareable configuration. The docs state: "Obsidian creates an .obsidian configuration folder in the root folder of the vault, which contains preferences specific to that vault, such as hotkeys, themes, and community plugins."9

So far, so shareable. But the same page names the specific files that update on every editor session and should not travel between clones: "The .obsidian/workspace.json and .obsidian/workspaces.json files store the current workspace layout and update whenever you open a new file. If you use Git to manage your vault, you might want to add these files to .gitignore."9

Those two lines are the whole Obsidian answer, and they belong in the committed .gitignore. They apply to every future clone of the vault, and any editor session opening the vault will regenerate them. .obsidian/ itself — minus the volatile pair — is the layer you probably want to keep in git: the hotkeys you spent an evening setting up, the community-plugin list, the CSS snippets that make the editor bearable. Version-control what you configured; ignore what the editor mutates.

A worked example — from a public forum answer where a real Obsidian user shared the .gitignore they landed on after trial and error10:

# Ignore volatile UI-related state
.obsidian/workspace.json
.obsidian/app.json
# Ignore system and editor files (more specific to what I do)
.vscode/
.idea/
.DS_Store
Thumbs.db

Two things to notice, and one thing to fix. The first two rules are correct in the committed .gitignore — they should apply to every clone. .vscode/ and .idea/ are borderline: for a solo note vault they probably belong one level down in .git/info/exclude; on a shared vault whose collaborators do not all use the same editor, keeping them out of the committed file is more honest.

And .DS_Store and Thumbs.db in the committed .gitignore are the fix. They are global patterns — the right home is ~/.config/git/ignore — so the discipline runs across every repository on that machine, not just this vault. The forum snippet is a fair starting point; the three-level frame is the improvement.

The retro-add trap — ignore rules do not help until you git rm --cached

The most common .gitignore frustration — adding a pattern to a file git already tracks, and watching the file keep showing up in every git status. This is a stated boundary: "Files already tracked by Git are not affected" by additions to a .gitignore, and "use git rm --cached to remove the file from the index."11

The retro-add trap is why git status in a week-old vault is louder than the vault itself. You committed the vault once — before you knew that .DS_Store existed. Every subsequent commit re-touches the same tracked file. Adding .DS_Store to a global ignore now does nothing for the copies already indexed — they are already inside git, and the ignore mechanism only stops new files from entering.

The two-step fix, for a note vault:

  1. Add the pattern to the correct level — .DS_Store goes in ~/.config/git/ignore; .obsidian/workspace.json goes in the repo's .gitignore.
  2. Untrack the existing copies without deleting them from disk — use git rm --cached <path> for a single file, or git rm -r --cached . followed by git add . to re-index the whole tree against the new rules.

The --cached flag matters — it removes the file from git's index but leaves the file on disk. For a vault, that is exactly the intent: you are telling git to stop tracking .DS_Store, not telling Finder to stop writing it. Commit the deletion with a plain message like "stop tracking OS byproducts (retro-ignore)". Every future git status on the vault will be quieter for it.

When git is still touching a file, ask git check-ignore -v

When you are certain a file should be ignored and it is not — or when the file is ignored and you want to know which of the three levels caught it — there is a canonical diagnostic. git check-ignore -v <pathname> prints, for each matching path, "the exclude pattern together with the path."12

The output format is documented verbatim as "<source> <COLON> <linenum> <COLON> <pattern> <HT> <pathname>"12 — it names the source file and the exact line number of the rule that matched.

The practical effect: run git check-ignore -v note.md on any file — git will tell you either "this file is being ignored by ~/.config/git/ignore:12:*~" (the global file, line twelve, pattern *~) or "no rule matched," which is why the file keeps showing up.

This is the fastest way to close the last mile of a stubborn git status. In a note vault where you are running three levels of ignore across a dozen file types, it is the diagnostic that pays back for itself the first time you use it — the rest of the piece is prevention; this is the debugger.

Common mistakes to avoid

Not every drift is loud. Five patterns quietly turn a .gitignore back into noise:

  1. Putting .DS_Store in every repo's .gitignore. It works — but you are writing the same three lines forever. Put it once in ~/.config/git/ignore and every repo on your machine inherits it.
  2. Committing the OS canon before untracking the existing files. A .gitignore addition does nothing to files git already tracks11. The two-step fix — add the pattern and git rm --cached — is the only path that actually clears git status.
  3. Ignoring the whole .obsidian/ folder. You lose your hotkeys, your community-plugin list, and your CSS snippets on the next clone. The vendor's own documentation names the two volatile files to ignore instead9 — keep the rest.
  4. Putting collaborator-specific patterns in a shared .gitignore. If only you use VS Code inside this vault, .vscode/ belongs in .git/info/exclude — otherwise every future collaborator is told to honor a pattern meaningless to their setup.
  5. Believing a global ignore protects data that has already been committed. It does not. A leaked .DS_Store in a public repository can expose folder names and hidden files, and no amount of retroactive ignoring pulls it out of git history. Ignore rules prevent accidents going forward — they do not remove yesterday's accidents.

Point five is the honest scope caveat for the whole piece. Git's ignore mechanism is a hygiene tool — not a security control. If a byproduct has already been committed and pushed, treat it as if you had emailed it.

The nested .gitignore question, briefly

A .gitignore file can live in any directory of a repository, and the patterns in it apply relative to that directory. The Pro Git book notes, as background, that the Linux kernel source repository contains "206" .gitignore files, one at various depths of the tree13. For a note vault you almost never need this.

One repo-committed .gitignore at the vault root, plus the two other levels, covers the whole vocabulary. Nested files are for monorepos with genuinely different rules per subtree.

If you are drawn to a nested file, ask instead whether the pattern belongs at a level higher than "this subfolder." A templates/ folder inside a vault whose contents you never want committed is a candidate; even there, one root-level templates/*.tmp rule is usually cleaner than a .gitignore inside templates/.

Frequently asked questions

Common questions on running a git-tracked note vault, answered from the primary references above.

What should I gitignore for my vault's GitHub repository? The vendor answer, for an Obsidian vault, is in Obsidian's own documentation: "The .obsidian/workspace.json and .obsidian/workspaces.json files store the current workspace layout and update whenever you open a new file. If you use Git to manage your vault, you might want to add these files to .gitignore."9 Put those two lines in the vault's committed .gitignore. Then put the OS canon (.DS_Store, Thumbs.db, desktop.ini) in ~/.config/git/ignore on each machine, and put anything machine-specific in .git/info/exclude.

Should I commit the .obsidian/ folder or not? Commit the parts that describe your configuration; ignore the parts that describe your last session. Hotkeys, themes, community-plugin lists, and CSS snippets are the reason you set up the vault the way you did; they belong in git. .obsidian/workspace.json and .obsidian/workspaces.json are per-session UI state that Obsidian rewrites whenever you open a file9; they belong in .gitignore. Community-plugin data.json files sometimes contain settings you want to keep and sometimes contain state you do not; decide per-plugin.

How do I stop .DS_Store from being committed to my repo? Put .DS_Store in ~/.config/git/ignore on your Mac. It applies to every repository on that machine, which is what you want, because .DS_Store is written by Finder into every folder Finder opens6, including remote mounts, and no repo is immune. If you have already committed some, add the pattern first, then untrack the existing copies with git rm --cached, and commit the removal. Future clones will not see the byproducts.

What is the difference between .gitignore and .git/info/exclude? .gitignore is committed and shared; patterns in it apply to every clone of the repository, so it is the right home for rules everyone using the vault should honor2. .git/info/exclude lives inside the .git/ directory, is never checked in, and applies only to your local clone; it is the right home for machine-specific or workflow-specific patterns no collaborator needs to know about2 8. In practice: .gitignore for the whole team; .git/info/exclude for you.

Where does git store the global gitignore file? Git reads it from whatever path is set in core.excludesFile in your ~/.gitconfig. Its default value is $XDG_CONFIG_HOME/git/ignore; "If $XDG_CONFIG_HOME is either not set or empty, $HOME/.config/git/ignore is used instead."2 On macOS and Linux without a custom XDG setup, that resolves to ~/.config/git/ignore. Running git config --global core.excludesFile will tell you the current path.

How do I know why git is ignoring my file? Run git check-ignore -v <path>. Its documented behaviour is to print "the exclude pattern together with the path"12 in a format that names the source file, the line number, the matching pattern, and your path. If nothing is printed, no rule matched; the reason your file is not ignored is that no ignore rule catches it. If something is printed, you know which of the three files to edit.

Does a global ignore protect files that are already committed? No. Git's own reference is explicit: "Files already tracked by Git are not affected" by adding a pattern to any level of ignore, and you have to "use git rm --cached to remove the file from the index."11 Ignore is preventative, not remedial. If a byproduct is in git history, especially in a public repository, treat it as if it had been published.

What if I do not use git for my notes at all? The durable idea is not .gitignore; it is the decision frame. Every syncing system has an equivalent "don't sync me" mechanism: Dropbox has .dropboxignore, iCloud has extended-attribute rules, Syncthing has .stignore, rsync has --exclude, most vendor sync clients have a settings panel. The names differ; the discipline is the same. A vault is what you wrote; the OS wrote everything else. Declare the difference in whichever file the tool you use will read.


The vault you keep is the one you declared. Git's three ignore files are the tersest way to make the declaration explicit, and a folder of plain Markdown on your own device inherits every one of these mechanics the moment you git init it. Steph Ango's line is worth keeping close to a .gitignore: "Apps are ephemeral, but your files have a chance to last."14 The mechanics of what belongs in the vault outlive the tool.

This piece is one entry in a small set on running a vault yourself; see frontmatter as vault-native metadata for what belongs inside a note, hard-wrap your notes so diffs are readable for the same diff-hygiene spine, and publish your notes as a static site for what happens after you push.

Nelson Figueroa's essay4 is why the three-level frame surfaced in public conversation in June 2026; the Git project and the github/gitignore maintainers wrote the underlying mechanics.

The plain file lives at mnmnote.com.

Footnotes

  1. gitignore(5). Git reference — order of precedence. Rendered by the Git project. https://git-scm.com/docs/gitignore 2

  2. gitignore(5). Git reference — which file to place a pattern in. Rendered by the Git project. https://git-scm.com/docs/gitignore 2 3 4 5

  3. bryancoxwell (Hacker News commenter). Comment on story 48583356 (Figueroa essay), 2026-06-18. https://news.ycombinator.com/item?id=48583356

  4. Figueroa, N. ".gitignore Isn't the Only Way To Ignore Files in Git." nelson.cloud, 2026-06-18. https://nelson.cloud/.gitignore-isnt-the-only-way-to-ignore-files-in-git/ 2

  5. github/gitignore. Global/macOS.gitignore, Global/Windows.gitignore, Global/Linux.gitignore. CC0-1.0. https://github.com/github/gitignore 2 3 4 5

  6. .DS_Store (Wikipedia article). https://en.wikipedia.org/wiki/.DS_Store 2

  7. Microsoft Learn. How to Customize Folders with Desktop.ini. Win32 Shell docs, last updated 2022-02-08. https://learn.microsoft.com/en-us/windows/win32/shell/how-to-customize-folders-with-desktop-ini

  8. Figueroa, N. ".gitignore Isn't the Only Way To Ignore Files in Git." nelson.cloud, 2026-06-18 (~/.config/git/ignore section). https://nelson.cloud/.gitignore-isnt-the-only-way-to-ignore-files-in-git/ 2

  9. Obsidian Help. How Obsidian stores data. Files-and-folders reference. https://help.obsidian.md/how-obsidian-stores-data 2 3 4 5

  10. ryyHardy (Obsidian Forum). "What should I gitignore for my vaults GitHub repository?" forum.obsidian.md, 2025-05-25. https://forum.obsidian.md/t/what-should-i-gitignore-for-my-vaults-github-repository/101077

  11. gitignore(5). NOTES — files already tracked, git rm --cached remedy. https://git-scm.com/docs/gitignore 2 3

  12. git-check-ignore(1). Options — the -v output format. https://git-scm.com/docs/git-check-ignore 2 3

  13. Chacon, S., and Straub, B. Pro Git, 2nd ed., §2.4 "Ignoring Files." https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository

  14. Ango, S. "File over app." stephango.com, 2023-07-01. https://stephango.com/file-over-app