Urgent.News

What's breaking now, across thousands of outlets.

Tech

Git Submodules as a Package Manager

Git submodules have been viewed as an alternative to a package manager, but there are significant issues with their implementation. When using a worktree within a repository that contains submodules, Git refuses to remove the worktree unless the --force flag is used. Similarly, Git's git worktree move feature refuses to move a worktree that contains submodules. This contradictory behavior is due to Git's handling of unclean worktrees and submodules separately.

Another issue arises when attempting to remove a worktree containing submodules, as Git requires the --force flag. This requirement persists even though the module's objects still exist in every clone that has them. Furthermore, Git's gitlink in the superproject's tree acts as a lockfile entry, recording the commit SHA for each submodule. If the upstream repository is renamed, transferred to a different host, or taken private, the downstream pin breaks, even though the SHA remains unchanged.

The manifest file, .gitmodules, maps submodule paths to fetch URLs, and Git reads these values during the git submodule update process. However, the gitlink record in the superproject's .git/config overrides the .gitmodules file, which can lead to inconsistencies. Worktree add has been patched to ignore submodule.recurse, but this workaround leaves the submodule directory empty until git submodule update --init is run.

The gitlink commit, which git submodule update checks out, is detached, leading to confusion when switching branches in the superproject. Git status immediately shows the submodule as modified, and passing the --recurse-submodules flag or setting submodule.recurse defaults to off. The Rust project experienced issues with checkouts being empty or on the wrong commit after clone, unrelated submodule bumps landing in pull requests, and custom logic in bootstrap build tools.

Git stores a submodule's git directory under the superproject's $GIT_DIR/modules/ name /, with a .git file containing a gitdir: pointer and core.worktree setting. Removing a submodule involves multiple steps, including dropping the gitlink, clearing the working tree and .git/config entry, and manually removing the absorbed $GIT_DIR/modules/ directory.

Worktrees and submodules can collide due to their shared $GIT_DIR, requiring separate checkouts and indices for each worktree referencing the same submodule at different commits.

Written by urgent.news from Lobsters's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.

Read the original at nesbitt.io →

More in Tech

More from Saturday 5 September →