Skip to content

Repository files navigation

overgit

Layer one git repository on top of another, in a single working directory.

You have a repo you don't control: a team project, a vendor drop, a fork you'd rather not carry patches against. overgit gives you a second git repository, the overlay. It shares that working directory and applies on top of the first one, the base. Your tweaks get committed, pushed and cloned onto the next machine, and the base's git status never sees them.

 your working directory
┌─────────────────────────────────────────┐
│ overlay .overgit/.git ← your repo │ add · override · delete
├─────────────────────────────────────────┤
│ base .git ← their repo │
└─────────────────────────────────────────┘

The overlay is an ordinary git repository in .overgit/, with your project root as its work-tree. Plain git works on it: git -C .overgit log.

Install

Requires Bun ≥ 1.1 and a system git ≥ 2.30. Linux and macOS.

git clone <this-repo> overgit
cd overgit
bun install
bun link # puts overgit in ~/.bun/bin

Usage

In a clone of any git repository:

$ overgit init --remote git@example.com:me/my-overlay.git
$ overgit hooks install # put the overlay back after every pull or checkout
$ overgit add config/app.toml # override: the base tracks it, you changed it
$ overgit add scripts/dev.sh # add: the base has never heard of it
$ overgit rm OLDNOTES.md # delete: gone for you, still there for the base
$ overgit commit -m 'my local dev setup'
$ overgit push -u origin main
$ git status --porcelain
$

Empty. Not "ignored", not "stashed": the base repo genuinely believes it is clean, and a git add -A && git commit in it captures nothing of yours.

On a new machine, overgit clone --base <base-url> <overlay-url> <dir> rebuilds the same tree, file modes and all.

overgit --help lists every command, and overgit help <command> explains one in full.

When upstream moves

This is the one situation worth walking through before you meet it, because git's own error message points the wrong way.

Someone changes config/app.toml upstream, the file you override. Plain git pull refuses, and it is right to:

$ git pullerror: Your local changes to the following files would be overwritten by merge:	config/app.tomlPlease commit your changes or stash them before you merge.Aborting

Do not commit or stash. Unmount the overlay, do your git, mount it again:

$ overgit detachdetached 3 paths: restored OLDNOTES.md restored config/app.toml removed scripts/dev.shthe work-tree is now pristine base content — run any git command you like,then `overgit attach` to mount the overlay again
$ git pullUpdating a2afb9b..0624468Fast-forward config/app.toml | 1 + 1 file changed, 1 insertion(+)
$ overgit attachchanged 6 paths: removed OLDNOTES.md wrote override config/app.toml work-tree content differed wrote add scripts/dev.sh missing from the work-tree hid from base OLDNOTES.md hid from base config/app.toml exclude block .git/info/exclude regenerated the managed block

While detached the work-tree is a byte-exact pristine base checkout, so every git command behaves normally. Your overlay content is safe in the overlay repository the whole time. overgit hooks install automates the reattach; the detach is the part you run yourself.

Now merge upstream into your override. overgit sync does a true three-way merge, using the blob you forked from as the merge base:

$ overgit sync1 path conflicted config/app.toml the work-tree files hold conflict markers. Edit them, then `overgit resolve <path>...` and `overgit sync --continue`. Shortcuts: `overgit resolve --keep <path>` keeps yours, `overgit resolve --take-upstream <path>` takes the base's.

A conflict blocks nothing else. Every other path still syncs, status and doctor keep working, and the base stays clean even with conflict markers sitting in the work-tree. Edit the file, then overgit resolve <path> and overgit sync --continue.

Known limitations

They are real, so here is the whole list.

limitationwhyremedy
git pull / git checkout in the base abort when upstream touches a file you overridegit refuses to overwrite a differing skip-worktree fileovergit detach, git, overgit attach; or overgit hooks install
git clean -xffd (double -f) deletes .overgit/double -f cleans even directories that are repositorieskeep the overlay pushed; overgit clone restores it
git clean -xfd and git stash -a remove overlay-added filesadded files are ignored from the base's point of viewovergit apply puts them back
filenames containing a newline, or ending in CR, cannot be addednot representable as an exact gitignore patternnone, a git format limit; overrides and whiteouts are unaffected
binary files cannot be three-way mergedgit refuses when any of the three sides is binarysurfaced as a decision: --keep / --take-upstream
work-tree commands refuse while the base has core.sparseCheckoutgit would clear the skip-worktree bits and leak every overridedisable sparse checkout, then overgit doctor --fix
overgit hooks install skips hooks written in another languageappending shell to a Python hook would break itadd overgit apply || true to it by hand
one overlay per work-treestacking is not supportednone
submodules are not overlaidovergit refuses paths inside a submodulenone
the base's index bits are shared statea skip-worktree bit you set yourself is reported as unowned, never adoptednone

Every row except the first three announces itself when you hit it, with the remedy in the message. The git clean rows cannot: overgit is not running, so it never gets to speak. Keep the overlay pushed.

Development

bun install
bun test
bun run typecheck

Licence

MIT.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages