[PATCH 0/3] subtree: add 'git-subtree-repo' and list command
To my knowledge 'git subtree' currently lacks a way to
track where injected repositories come from originally.
Adding this information allows for useful extensions to
the command and makes it easier to use subtrees to track
In this patch series I propose to add a 'git-subtree-repo'
line to the meta-data stored when injecting a tree
in a repository with 'git subtree add'. The result looks
Thanks a lot to Mathias Nyman who has cleaned up my idea to
add 'git-subtree-repo' and already submitted it for review.
I added a test and a tiny fix to his patch and I resend it
here (hence the v3 in the first patch).
Using this extra value a simple 'git subtree list' command can
be implemented which scans the checked out branch for subtrees
Git-subtree operations 'add' and 'pull', when called with the <repository>
parameter will add this to the commit message:
Other operations that don't have the <repository> information, like
'merge' or 'add' without <repository> are unchanged. Users with such a
workflow will be on their own with the --message parameter, if they'd
like to record where the subtree came from.
As the 'list' command finds commit ids for subtrees injected into the
checked out branch the --resolve flag tries to look up the repositories
at 'git-subtree-repo' and retrive the symbolic refs associated with the
commit ids found.
diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh
index 82f3fce..fe62151 100755
@@ -14,7 +14,7 @@ git subtree merge --prefix=<prefix> <commit>
git subtree pull --prefix=<prefix> <repository> <ref>
git subtree push --prefix=<prefix> <repository> <ref>
git subtree split --prefix=<prefix> <commit...>
-git subtree list
+git subtree list [--resolve]
h,help show the help
@@ -29,6 +29,7 @@ onto= try connecting new tree to an existing one
rejoin merge the new branch back into HEAD
options for 'add', 'merge', and 'pull'
squash merge subtree changes as a single commit
+resolve resolves ids to refs when possible
eval "$(echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?)"
@@ -102,6 +104,8 @@ while [ $# -gt 0 ]; do
--no-ignore-joins) ignore_joins= ;;
--squash) squash=1 ;;
--no-squash) squash= ;;
+ --resolve) resolve=1 ;;
+ --no-resolve) resolve= ;;
--) break ;;
*) die "Unexpected option: $opt" ;;
@@ -254,12 +258,21 @@ find_subtree_repos()
if [ -n "$sub" ]; then
if [ -n "$main" ]; then
- # a rejoin commit?
- # Pretend its sub was a squash.
debug "Subtree found: $dir $repo $sub"
- echo "$dir" "$repo" "$sub"
+ # Strip potential space at the end in repo
+ repo=$(echo $repo)
+ if [ -n "$resolve" ] && [ -n "$repo" ]; then
+ echo "$dir" "$repo" "$sub"
+ # Retrieve remote refs if repo is available
+ resolved_refs=$(git ls-remote "$repo" | grep "$sub" | cut -c 42- | xargs)
+ for r in $resolved_refs; do
+ echo "$dir" "$repo" "$r"
+ echo "$dir" "$repo" "$sub"
diff --git a/contrib/subtree/git-subtree.txt b/contrib/subtree/git-subtree.txt
index 60d76cd..ab36951 100644
@@ -15,6 +15,7 @@ SYNOPSIS
'git subtree' push -P <prefix> <repository> <ref>
'git subtree' merge -P <prefix> <commit>
'git subtree' split -P <prefix> [OPTIONS] [<commit>]
+'git subtree' list [--resolve]
@@ -106,6 +107,12 @@ split::
contents of <prefix> at the root of the project instead
of in a subdirectory. Thus, the newly created history
is suitable for export as a separate git repository.
+ List all subtrees injected in checked out branch. Run
+ with optional `--resolve` to retrieve remote symbolic refs
+ associated with the subtree. Address of subtree repo is stored
+ in commits as `git-subtree-repo` at the time of `git subtree add`.
After splitting successfully, a single commit id is printed to stdout.
This corresponds to the HEAD of the newly created tree, which you can
@@ -240,6 +247,13 @@ split, because you don't want the subproject's history to be part of
your project anyway.
+OPTIONS FOR list
+ Resolves 'git-subtree-split' refs by looking up symbolic refs at
EXAMPLE 1. Add command
Let's assume that you have a local repository that you would like
@@ -341,6 +355,23 @@ Then push the new branch onto the new empty repository:
$ git push <new-repo> split:master
+EXAMPLE 3. List subtrees
+Suppose you add a subtree with:
+ $ git subtree add --prefix dependency https://host/repo.git master --squash
+You can list all subtrees in the current branch resolving the refs with:
+ $ git subtree list --resolve
+Which would output something like:
+ depenency https://host/repo.git 4fa37e5e20b5ae9b[...]
+ depenency https://host/repo.git HEAD
+ depenency https://host/repo.git refs/heads/master
Written by Avery Pennarun <[hidden email]>
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index ce97446..d100001 100755
@@ -81,6 +81,19 @@ test_create_commit() (
git commit -m "$commit" || error "Could not commit"
> To my knowledge 'git subtree' currently lacks a way to
> track where injected repositories come from originally.
> Adding this information allows for useful extensions to
> the command and makes it easier to use subtrees to track
> external dependencies.
Thanks for working on this. I just sent a reply to your earlier
What is the intent for use of this? Is it simply to record from where
commits were pulled or do you intend to use this information later on to
have git-subtree guess from where to fetch future commits?
I would be opposed to the latter because I think it potentially limits
the utility of git-subtree and may be misleading. I frequently pull
commits for a subtree from several different clones of the same
reposiory. How does git-subtree list handle that situation?
Does git-subtree list really print out repository information for every
commit added by git-subtree? That's potentially a lot of commits. It
might be more useful to aggregate repository information and only dump
out unique URLs. In any case, processing all commits seems like a ton
of work for such a simple operation. Maybe this information should be
cached in .gitconfig.
I'm actually in the middle of cleaing up metadata but I'm not going to
block these commits due to that. Just be aware that it may change a