Sync environment files across linked git worktrees
Project description
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 synccopies 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 envdoesn't understandKEY=VALUEsyntax, so it can't merge or diff values semantically — only whole-file conflict detection. --porcelainis 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.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file git_env-0.4.0.tar.gz.
File metadata
- Download URL: git_env-0.4.0.tar.gz
- Upload date:
- Size: 16.4 kB
- Tags: Source
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0029918b5174e88e1212ce72c8b474ddca962d1149238d027465664595ea0963
|
|
| MD5 |
1f128e3c14ea84485fb312ee41000487
|
|
| BLAKE2b-256 |
e5f54fde05f52baa8130c88abfab470b823b8ddee04572a03ff5bafc8bb3b8d6
|
File details
Details for the file git_env-0.4.0-py3-none-any.whl.
File metadata
- Download URL: git_env-0.4.0-py3-none-any.whl
- Upload date:
- Size: 22.1 kB
- Tags: Python 3
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7e2dce30076e66a1b5f808d558f161d3f8012bc4a6192b3b8cdc7974a3318bb0
|
|
| MD5 |
4a03b933413f92950d274adc3bcec2c1
|
|
| BLAKE2b-256 |
b58c0b2004beb914dcea80d813fd30efdbeabf6b57877161126e171ff5d5f111
|