Blog
Take Your Claude Code Setup to a New Mac
You spend months teaching Claude how you work — your skills, your commit style, the quirks of your stack. Then you open a fresh laptop and it's a stranger again. All of that knowledge was sitting in one folder the whole time: ~/.claude.
Here's how to put that folder under git so your setup follows you anywhere — and, just as importantly, which parts you must keep out of git so you don't leak a credential.
June 2026 · Alex Miller

What's Actually in ~/.claude
Before you sync anything, it helps to know what you're looking at. The folder mixes three very different kinds of things: portable config, durable memory, and machine-local state you should never copy.
~/.claude/ ├── CLAUDE.md ← your global preferences ├── settings.json ← model, hooks, statusline, plugins ├── settings.local.json ← per-machine permissions (DO NOT SYNC) ├── keybindings.json ├── statusline-command.sh ├── hooks/ ← global hooks ├── skills/ ← global skills ├── projects/ ← session transcripts + memory (500 MB+) │ └── <encoded-repo-path>/ │ └── memory/ ← what Claude has learned └── history.jsonl ← your prompt history # auth lives in the macOS Keychain, not a file here
Two lines in there are traps. settings.local.json holds permission grants tied to paths on this specific machine. And your login isn't a file at all — Claude Code keeps it in the macOS Keychain, which is why copying the folder never carries your auth. That's a feature. Secrets can't leak by accident.
Split It Into Two Repos
The cleanest setup uses two repos, because the stuff in ~/.claude answers to two different owners.
A private dotfiles repo
Your global config, skills, and hooks. The things you want on every machine, in every project. One repo, cloned once per Mac.
The project's own repo
A project's memory belongs with that project's code, so it commits straight into the repo it describes — and travels with a normal git clone.
The Trick: Move the File, Symlink It Back
You can't put ~/.claude itself in git without dragging along 500 MB of transcripts and a pile of secrets. So you move just the pieces you want into a repo, and leave a symlink behind. Claude Code follows the link and never knows the difference.
# Move the real file into a repo, then symlink it back.
# ~/.claude keeps working — it just points at the repo now.
REPO=~/dev/claude-dotfiles/claude
mkdir -p "$REPO"
for it in CLAUDE.md settings.json keybindings.json \
statusline-command.sh stop-hook-label.sh hooks skills; do
mv ~/.claude/$it "$REPO/$it"
ln -s "$REPO/$it" ~/.claude/$it
doneOne gotcha if you're on zsh: an unquoted $VAR in a for loop doesn't word-split the way it does in bash. Spell out the list literally, as above, and it behaves on both shells.
Prove There Are No Secrets
This is the step people skip and regret. A dotfiles repo is the easiest place in the world to accidentally publish an API key. Run a guard before every push, and put settings.local.json and .env in your .gitignore first.
# Before you ever push, prove no secrets snuck in. git ls-files | grep -E "settings.local.json|credentials|\.env" \ && echo "STOP — secret staged" \ || echo "clean"
One Script for the New Mac
Mine sits at github.com/fotoflo/claude-dotfiles. On a fresh laptop the whole restore is three lines:
# Mine lives here. It's private — these are my real skills and # prefs — so this is the pattern, not a repo you can clone. # Fork your own and swap the URL. git clone https://github.com/fotoflo/claude-dotfiles ~/dev/claude-dotfiles ~/dev/claude-dotfiles/install.sh claude # log in — auth comes from the Keychain, not the repo
That last line — install.sh — backs up anything already sitting in ~/.claude, then lays down the symlinks. Run it twice and nothing breaks, so it's safe to re-run any time you add a skill on one machine and want it on the other. Here's the whole script:
#!/usr/bin/env bash
# install.sh — run once on the new Mac after cloning the repo.
set -euo pipefail
C="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/claude"
DEST="$HOME/.claude"
BK="$DEST/.backup-$(date +%s)"
for it in CLAUDE.md settings.json keybindings.json \
statusline-command.sh stop-hook-label.sh hooks skills; do
[ -e "$C/$it" ] || continue
[ -L "$DEST/$it" ] && rm "$DEST/$it"
[ -e "$DEST/$it" ] && { mkdir -p "$BK"; mv "$DEST/$it" "$BK/"; }
ln -s "$C/$it" "$DEST/$it"
done
echo "Done. Now run 'claude' and log in."Want to start clean? I split mine in two: a private repo for my actual config and skills, and a public template you can fork — github.com/fotoflo/claude-dotfiles-template. Same install.sh, generic example skills, none of my client-specific ones. Fork it, drop in your own, and you're running in under a minute.
Memory Rides With the Code
The memory folder is the part that makes a new laptop feel like home. It's where Claude records what it has learned about a project — conventions, decisions, the reasons behind them. Because that knowledge is specific to one codebase, it belongs inside that codebase's repo.
Same move as before: shift the real folder into the repo, symlink the path Claude reads from, commit. Now a teammate who clones the project inherits everything Claude figured out — for free.
# Memory is project-specific, so it belongs with the project. # Move it into the repo, symlink the path Claude reads from. REPO=~/dev/myapp mv ~/.claude/projects/-Users-me-dev-myapp/memory "$REPO/.claude/memory" ln -s "$REPO/.claude/memory" ~/.claude/projects/-Users-me-dev-myapp/memory cd "$REPO" && git add .claude/memory && git commit -m "chore: vendor Claude memory"
What About the Conversations?
Config and memory get you 90% of the way. If you also want to reopen an actual thread on the other Mac — scroll back, hit --resume — that's the transcripts, and those are a sync problem rather than a git one. They're big and they change every keystroke.
Point Syncthing or iCloud at ~/.claude/projects/. Two rules: clone the project to the same absolute pathon both machines, and don't run a live session against the same file from two Macs at once.
# Clone the project to the SAME absolute path on the new Mac # (the transcript folder name is derived from it). git clone <repo-url> ~/dev/myapp # Drop the synced session file into the matching folder, then: claude --resume # pick the conversation
The Whole Thing, in Order
Make a private dotfiles repo
Move CLAUDE.md, settings.json, skills, and hooks into it. Symlink each one back into ~/.claude.
Gitignore the landmines
settings.local.json, .credentials.json, and any .env. Then run the secret guard before your first push.
Vendor memory into each project
Move the project's memory folder into its repo, symlink the path Claude reads from, and commit it.
On the new Mac: clone and run install.sh
It backs up and symlinks. Then run `claude` and log in — auth comes from the Keychain, not the repo.
Optional: sync transcripts
Syncthing or iCloud on ~/.claude/projects/ if you want to resume conversations, not just config.
Want your whole team set up like this?
I run AI workshops on Claude Code — skills, workflows, and the setup that makes AI-assisted development actually stick across a team.
Book a 30-Minute Call