Skip to main content

git-env

Copies untracked env files to your worktrees

Example

cd ~/repos/myproject                         # primary worktree, has .env
git worktree add ~/worktrees/my-feature      # create a linked worktree
cd ~/worktrees/my-feature
git env sync                                 # copies .env from the primary
$ git env sync
synced  .env
2 files synced, 0 unchanged, 0 conflicts skipped

Run it again later to pick up any changes made in the primary. By default, sync won't clobber a file that has diverged locally — pass --force if you want the primary's copy to win.

Installation

uv tool install git-env

Or, with pipx:

pipx install git-env

This installs a git-env executable on your PATH, which git picks up automatically as the env subcommand:

git env --version

Syncing

git env sync copies env files from the primary worktree into the linked worktree you're standing in. The primary worktree is the original clone — the one whose .git is a real directory, not a file. sync locates it via git rev-parse --git-common-dir.

Files are matched by glob against the entire primary worktree tree. .gitignore is not consulted (env files are normally gitignored, which is the point), but a .envsyncignore file at the primary root is. A file that doesn't exist at the destination is copied. A file that's byte-identical is skipped silently. A file that differs is skipped with a warning and a one-line diff summary, unless --force is given.

Writes are atomic: each file is written to <dest>.envsync.tmp then renamed over the destination. Mode bits (including the executable bit) are preserved; mtimes are not, so a synced file's timestamp tells you when it was synced.

sync must run from a linked worktree — it refuses to run from the primary itself, and refuses on bare repositories.

git env sync [--dry-run] [--force] [--verbose | --quiet]
             [--pattern <glob>]... [--path <subdir>]
Flag Short Meaning
--dry-run -n Print what would happen; change nothing.
--force -f Overwrite local files that differ from the primary, backing up the previous content first.
--verbose -v Print every file considered, including skips.
--quiet -q Suppress non-error output.
--pattern <glob> Override configured patterns for this run. Repeatable.
--path <subdir> Restrict the sync to a subdirectory of the worktree.

--verbose and --quiet are mutually exclusive.

Exit codes

Code Meaning
0 Success, or dry-run with nothing to change.
1 Sync completed but one or more files were skipped due to conflicts.
2 Refused to run (not a git worktree, bare repo, invoked from the primary worktree, or the primary has uncommitted changes to tracked env files).
3 Usage error (bad flag, unknown or reserved subcommand).
4 I/O error while copying.

These codes are part of the contract — scripts can rely on them.

Configuration

All keys live under env.sync.* and are read with standard git config precedence (system → global → local → worktree):

Key Type Default Meaning
env.sync.patterns multi-value .env Globs to sync. Multi-value, so additional git config --add calls append rather than replace.
env.sync.exclude multi-value .env.example, .env.sample, .env.template Patterns never synced, even if they match patterns (typically committed templates).
env.sync.followSymlinks bool false Follow symlinked env files instead of skipping them.
env.sync.maxFileSize int (bytes) 1048576 Files larger than this are skipped with a warning.
env.sync.onConflict enum skip skip, overwrite, or prompt.
env.sync.backup bool true Whether --force writes a <dest>.envsync.bak backup before overwriting.

Set per-repo in the primary worktree's .git/config, or per-worktree in that worktree's own config:

git config env.sync.onConflict overwrite
git config --add env.sync.patterns ".env.local"

.envsync

An optional key=value file at the primary worktree root, for pinning patterns into version control so the whole team gets the same defaults without everyone running git config. Same keys as above, minus the env.sync. prefix:

patterns=.env
exclude=.env.example
followSymlinks=false

Repeated keys accumulate (for multi-value settings). git config values, if set, always take precedence over .envsync.

.envsyncignore

A gitignore-syntax file at the primary worktree root. Paths it matches are excluded from sync regardless of env.sync.patterns — use it to opt a specific file or directory out without changing the glob patterns themselves.

Shell completion

git env --install-completions bash   # print a snippet for your rc file
git env --install-completions zsh --write   # install the completion file directly
git env --install-completions fish

Supported shells: bash, zsh, fish. Without --write, the command prints what to add to your shell config; with --write, it installs the completion file to a standard location for that shell.

FAQ

Why not just symlink the env files instead? A symlink means there's only ever one copy, so editing the file in a linked worktree edits the primary too — that defeats the purpose of having isolated worktrees in the first place (e.g. running two branches with different API keys or feature flags side by side). git env sync gives each worktree its own independent copy, seeded from the primary, that you can then diverge from intentionally.

Does this work with bare repositories? No. git env sync requires a primary worktree with a real working tree to copy from. Bare repos are detected and rejected with exit code 2.

What about secrets in env files? git env sync only ever copies bytes between worktrees on your local filesystem — it doesn't transmit, log, or store file contents anywhere else, and it never touches git history (env files are normally gitignored and stay that way). The usual rules still apply: don't commit secrets, and be mindful that --force backups (<dest>.envsync.bak) leave a second copy of the previous content on disk.

Can I sync changes back from a worktree to the primary? Not yet. sync is one-way (primary → linked). A git env push for the reverse direction is planned but not implemented in v1 — see Limitations.

Limitations

  • One-way sync only. git env sync copies primary → linked. There is no bidirectional sync in v1; pushing changes from a linked worktree back to the primary isn't supported yet.
  • Submodules aren't traversed. Env files inside submodules are not discovered or synced.
  • Single primary per invocation. The tool assumes one primary worktree and doesn't support syncing between two linked worktrees directly.
  • No format parsing. Env files are treated as opaque bytes; git env doesn't understand KEY=VALUE syntax, so it can't merge or diff values semantically — only whole-file conflict detection.
  • --porcelain is reserved but not implemented. Passing it is a usage error in v1, by design, so scripts don't silently depend on output that may change later.

push, diff, status, list, edit, and check are reserved for future official subcommands and will error if invoked, so a third-party git-env-<name> script doesn't collide with them later.

Metadata

Release files for git-env 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for git-env 0.5.0
File Size Uploaded
git_env-0.5.0.tar.gz 16.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for git-env 0.5.0
File Interpreter ABI Platform
git_env-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 39.0 kB

Release files / git_env-0.5.0.tar.gz

Download URL git_env-0.5.0.tar.gz
Size 16.7 kB
Tags Source
SHA-256 checksum
How to use checksums
7fd77a44e662988c6f4f16d61ac6b98ad41057a06d7303ae85e81c672a475988
BLAKE2b-256 checksum
How to use checksums
016203648cd85fc1d0e53e68aa6b11656f542ef4aea4c4cefff63665fd11eca9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / git_env-0.5.0-py3-none-any.whl

Download URL git_env-0.5.0-py3-none-any.whl
Size 22.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6798c009c83b1b2a9ad674a8ab0034585d4ca1e355bf02b48bcb299f466749bb
BLAKE2b-256 checksum
How to use checksums
f3f01aeb13284582057f245a7c1b97a4f749a638f40502918027f365f47674de
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.5.0 This release

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page