macOS configuration, shared across machines and used to set up new ones.
- Install homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"New machine, everything at once:
sh dotfiles.sh allOr one step at a time. The order matters: zsh installs oh-my-zsh, home
symlinks on top of it, and mac restarts Finder and the Dock, so it goes last.
sh dotfiles.sh brew
sh dotfiles.sh zsh
sh dotfiles.sh home
sh dotfiles.sh claude
sh dotfiles.sh codex
sh dotfiles.sh iterm
sh dotfiles.sh macOptional, builds a Python interpreter and takes a while:
sh dotfiles.sh pythonhome replaces files in ~. Anything that already exists and is not one of
our symlinks is moved to <name>.bak first.
- gitconfig
- vimrc
- zshrc, zshenv, zprofile
- tmux, including the status bar
- iTerm2 settings: profile, colors, fonts, key bindings
- Claude Code status line and hooks
- brew dependencies
- macos preferences
- Claude Code and Codex (via their official installers, not brew)
iTerm2 keeps everything in one preferences domain rather than a dotfile, so it
is copied in and out of config/iterm2/ instead of symlinked:
sh dotfiles.sh iterm # repository -> this machine
sh dotfiles.sh iterm export # this machine -> repositoryiTerm2 must be quit before importing. It holds the whole domain in memory and writes it back when it quits, which would undo the import a few minutes later. The script detects a running iTerm2 and does nothing, so run it from Terminal.app, or quit iTerm2 and run it from any other terminal.
The previous settings are saved to
~/Library/Preferences/com.googlecode.iterm2.plist.bak first. To go back:
defaults import com.googlecode.iterm2 \
~/Library/Preferences/com.googlecode.iterm2.plist.bakAfter changing something in iTerm2's settings window, run the export and
commit the diff. The export drops window positions, Sparkle's update
bookkeeping and everything iTerm2 marks NoSync, so the tracked file holds
configuration only. Importing merges, it never deletes: a key removed from the
tracked file keeps its old value on a machine that already had it.
Nothing machine-specific belongs in a tracked file. Four escape hatches:
| What | Where | Tracked |
|---|---|---|
| Extra shell config | ~/.zshrc.local |
no |
| Extra packages | Brewfile.local (see .example) |
no |
| Work git identity | ~/.gitconfig_work |
no |
| Machine-only git config | ~/.gitconfig.local |
no |
~/.gitconfig_work is applied to any repository under ~/Code/Work via
includeIf. It is deliberately untracked, since this repository is public.
Create it by hand on each machine:
[user]
email = you@example.com
name = Malek Rezgui~/.gitconfig.local is included last, so it overrides anything above it. It is
where a single machine's git setup goes: a credential helper for several
GitHub accounts, per-directory identities, anything holding an absolute path or
an employer address. Absent on most machines, and silently skipped when so.