LinuxCommandLibrary

git-subtree

Manage subprojects within a git repository

TLDR

Add a Git repository as a subtree

$ git subtree add [[-P|--prefix]] [path/to/directory]/ --squash [repository_url] [branch_name]
copy

Update subtree repository to its latest commit
$ git subtree pull [[-P|--prefix]] [path/to/directory]/ [repository_url] [branch_name]
copy

Merge recent changes up to the latest subtree commit into the subtree
$ git subtree merge [[-P|--prefix]] [path/to/directory]/ --squash [repository_url] [branch_name]
copy

Push commits to a subtree repository
$ git subtree push [[-P|--prefix]] [path/to/directory]/ [repository_url] [branch_name]
copy

Extract a new project history from the history of a subtree
$ git subtree split [[-P|--prefix]] [path/to/directory]/ [repository_url] [[-b|--branch]] [branch_name]
copy

SYNOPSIS

git subtree <command> [<options>]

Common commands and their primary syntax:
  git subtree add --prefix=<prefix> <repository> <ref>
  git subtree pull --prefix=<prefix> [<repository> [<ref>]]
  git subtree push --prefix=<prefix> <repository> <ref>
  git subtree split --prefix=<prefix> [<commit>]
  git subtree merge --prefix=<prefix> <commit>

PARAMETERS

add
    Adds a new external repository as a subtree into a specified subdirectory.

pull
    Updates an existing subtree by pulling changes from its remote source.

push
    Pushes changes made within the subtree directory back to its original external repository.

split
    Extracts the history of a specific subdirectory into a new, independent branch or a new repository.

merge
    Merges a specific commit from an external project into the designated subtree directory.

--prefix=<prefix>
    The mandatory path within the main repository where the subtree is (or will be) located. E.g., src/mylibrary.

<repository>
    The URL or local path to the external Git repository. Used with add, pull, and push.

<ref>
    The branch or commit from the external repository to integrate or push to (e.g., main, HEAD). Used with add, pull, and push.

--squash
    When adding or pulling, squashes the entire history of the subtree into a single commit. This helps keep the main repository's history cleaner and smaller.

--annotate=<annotation>
    Used with split. Prepends a custom string to commit messages in the newly split history.

--branch=<branch>
    Used with split. Specifies the name of the new branch to create for the split history.

--rejoin
    Used with split. Creates a new commit in the original repository that records the split, allowing future merges to use the split history efficiently.

<commit>
    The specific commit hash to merge into the subtree. Used with merge.

DESCRIPTION

git-subtree is a Git command used to manage subprojects within a main Git repository. Unlike git-submodule, it doesn't store a separate repository reference; instead, it merges the history of the subproject directly into a subdirectory of the main repository. This approach simplifies workflows, as changes to the subproject appear as regular commits in the main repository's history, eliminating the need for separate .gitmodules files or specific git submodule commands.

It's particularly useful when you want to include a library or component from another project and treat it as an integral part of your own repository, allowing you to modify and push changes back to the original source or simply keep a unified history. git-subtree supports both integrating external repositories into a subdirectory and splitting a subdirectory's history out into its own repository.

CAVEATS

Caveats and limitations:
- Can create a very large history in the main repository if not squashed, as it merges the full history of the subtree.
- Pushing changes back to the original repository can be tricky, especially if the original repository has diverged significantly or if the history was squashed.
- Requires careful management of the --prefix to avoid mistakes, as operations are sensitive to this path.
- Not ideal for situations requiring strict version pinning or clear separation of development cycles between main and subprojects, where git-submodule might be a better fit.

SQUASHING HISTORY

The --squash option is a critical feature for add and pull operations. It condenses the entire history of the subtree being integrated into a single commit in the main repository. This significantly reduces the size and complexity of the main repository's log, making it easier to read and manage, especially when dealing with subprojects that have very long or irrelevant histories.

MERGE STRATEGY

git-subtree operations, particularly pull and merge, often implicitly utilize Git's specialized 'subtree' merge strategy. This strategy is designed to correctly handle merges where one tree is a subdirectory of another, by intelligently manipulating paths during the merge process to align the histories properly.

HISTORY

git-subtree originated as a contrib script (git-subtree.sh) bundled with Git, designed to offer an alternative to git-submodule. It was later rewritten in C and became a built-in command in Git 1.7.11, released in 2012. Its development aimed to provide a simpler way to integrate external projects by avoiding the complexities of detached HEADs and separate repository references inherent in submodules.

SEE ALSO

Copied to clipboard