View script Copied!
Stage a git repository inside another git repository without treating it as a submodule.
Git has a feature called submodules for linking one repo into another: when you git add a directory containing its own .git, git records only a pointer to a specific commit in the inner repo, not the actual files. Submodules are powerful but add workflow friction (everyone cloning the outer repo needs to run git submodule update to fetch the inner contents; updates require coordinated commits in both repos).
If you don't want that coordination overhead -- e.g. you've vendored a third-party library, you want to snapshot an external project's source into your tree, or you're copying code you'll modify independently -- you want the nested directory's files tracked as plain files in the outer repo. This script makes that work by temporarily moving the inner .git directory aside so git sees regular files during git add, then putting the inner .git back.
From your outer repository's working tree:
$ git-add-nonsub vendor/some-lib
[INF][git-add-nonsub] Backup created at /var/folders/.../tmp.xxxxx
[INF][git-add-nonsub] Running git add on 'vendor/some-lib'...
[INF][git-add-nonsub] git add completed successfully
[INF][git-add-nonsub] Restored .git directory
Now vendor/some-lib is staged in your outer repo with all its files, not as a submodule pointer.
Stage a vendored library:
$ git-add-nonsub third_party/react
Stage a cloned project you want to absorb:
$ git-add-nonsub lib/parsing-lib
Verify the directory has a .git before running:
$ ls -la vendor/some-lib/.git
drwxr-xr-x 13 user staff 416 Apr 21 10:00 .git
$ git-add-nonsub vendor/some-lib
When you try to git add a directory that contains a .git subdirectory, git treats it as a submodule and only records a commit reference -- not the actual files. This script works around that by:
.git directory to a temporary locationgit add on the now-.git-free directory (so git sees regular files).git directory to its original locationThe inner repository remains intact and functional after the operation completes. You can continue working in it or remove the .git entirely if you no longer need it as a separate repo.
| Flag | Description |
|---|---|
directory |
Path to a directory containing its own .git subdirectory |
-h, --help |
Show help message |
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Runtime failure (backup, restore, or git add failed) |
| 2 | Usage / precondition error (no directory, directory doesn't exist, no .git found, not inside a git repo) |
| 130 | Interrupted (SIGINT/SIGTERM/SIGHUP); emergency restore attempted |
gitDo not interrupt the script while it's running. If the script is killed after moving .git but before restoring it, your nested repository will be in an inconsistent state (its .git directory will be in a temp location). The script prints the temp directory path when it creates the backup -- you can manually move it back if needed.
Do not run on uncommitted work in the nested repo. The script doesn't check the inner repo's status before proceeding. If you have uncommitted changes in the nested repository, consider committing them first or understanding that you're staging whatever state the working tree is currently in.
This doesn't remove the inner .git permanently. After running this script, the nested directory still contains a .git subdirectory -- it's just been added to the outer repo. If you commit and then run git status again, git will still see the nested repo as a potential submodule. If you want to fully absorb the nested repo, you'll need to remove its .git directory manually after running this script.