{"thread":{"id":"41660","subject":"[PATCH 0/3] subtree: add 'git-subtree-repo' and list command","startedAt":"2016-03-10T09:44:09Z","lastAt":"2016-05-21T22:59:36Z","messageCount":5,"participants":["Nicola Paolucci","David A. Greene"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"280539","messageId":"1457603052-53963-1-git-send-email-npaolucci@atlassian.com","threadId":"41660","inReplyTo":null,"subject":"[PATCH 0/3] subtree: add 'git-subtree-repo' and list command","fromName":"Nicola Paolucci","fromEmail":"npaolucci@atlassian.com","sentAt":"2016-03-10T09:44:09Z","receivedAt":"2016-03-10T09:44:09Z","isPatch":true,"sender":{"key":"npaolucci@atlassian.com","avatar":"https://gravatar.com/avatar/c809e71e52357fdefc4fc43b956992e6b5c7bba23c47c3cfedf451e8c4714b96?d=mp&s=160"},"body":"To my knowledge 'git subtree' currently lacks a way to\ntrack where injected repositories come from originally.\nAdding this information allows for useful extensions to \nthe command and makes it easier to use subtrees to track\nexternal dependencies.\n\nIn this patch series I propose to add a 'git-subtree-repo'\nline to the meta-data stored when injecting a tree\nin a repository with 'git subtree add'. The result looks \nlike this:\n\n    git-subtree-dir: .vim/bundle/fireplace\n    git-subtree-split: b999b09cd9d69f359fa5668e81b09dcfde455cca\n    git-subtree-repo: https://repo/user/vim-fireplace.git\n\nThanks a lot to Mathias Nyman who has cleaned up my idea to\nadd 'git-subtree-repo' and already submitted it for review.\nI added a test and a tiny fix to his patch and I resend it \nhere (hence the v3 in the first patch).\n\nUsing this extra value a simple 'git subtree list' command can \nbe implemented which scans the checked out branch for subtrees\ninjected:\n\n    $ git subtree list\n    .vim/bundle/fireplace https://github.com/tpope/vim-fireplace.git b999b0\n\nI also added an optional '--resolve' flag to retrieve symbolic\nremote refs associated with the commit ids of the remote repository:\n\n    $ git-subtree.sh list --resolve\n    \n    vim-airline  https://repo/bling/vim-airline.git 4fa37e5e[...]\n    vim-airline  https://repo/bling/vim-airline.git HEAD\n    vim-airline  https://repo/bling/vim-airline.git refs/heads/master\n\n\nNicola Paolucci (3):\n  contrib/subtree: 'add' stores 'git-subtree-repo'\n  contrib/subtree: new list command to list subtrees\n  contrib/subtree: list --resolve gets symbolic refs\n\n contrib/subtree/git-subtree.sh     | 140 +++++++++++++++++++++++++++++--------\n contrib/subtree/git-subtree.txt    |  31 ++++++++\n contrib/subtree/t/t7900-subtree.sh |  63 +++++++++++++++++\n 3 files changed, 205 insertions(+), 29 deletions(-)\n\n-- \n2.7.1\n"},{"id":"280540","messageId":"1457603052-53963-2-git-send-email-npaolucci@atlassian.com","threadId":"41660","inReplyTo":"1457603052-53963-1-git-send-email-npaolucci@atlassian.com","subject":"[PATCH v3 1/3] contrib/subtree: 'add' stores 'git-subtree-repo'","fromName":"Nicola Paolucci","fromEmail":"npaolucci@atlassian.com","sentAt":"2016-03-10T09:44:10Z","receivedAt":"2016-03-10T09:44:10Z","isPatch":true,"sender":{"key":"npaolucci@atlassian.com","avatar":"https://gravatar.com/avatar/c809e71e52357fdefc4fc43b956992e6b5c7bba23c47c3cfedf451e8c4714b96?d=mp&s=160"},"body":"Git-subtree operations 'add' and 'pull', when called with the <repository>\nparameter will add this to the commit message:\n    git-subtree-repo: <repo_url>\n\nOther operations that don't have the <repository> information, like\n'merge' or 'add' without <repository> are unchanged. Users with such a\nworkflow will be on their own with the --message parameter, if they'd\nlike to record where the subtree came from.\n\nFor example:\n\n$ git subtree add --prefix .vim/bundle/fireplace \\\n    https://repo/user/vim-fireplace.git master --squash\n\nWill result in a commit like:\n\n    commit ce87dab198fecdff6043d88a26c55d7cd95e8bf9\n    Author: Bob Marley <bob@mahrley.com>\n    Date:   Tue May 12 13:37:03 2015 +0200\n\n    Squashed '.vim/bundle/fireplace/' content from commit b999b09\n\n    git-subtree-dir: .vim/bundle/fireplace\n    git-subtree-split: b999b09cd9d69f359fa5668e81b09dcfde455cca\n    git-subtree-repo: https://repo/user/vim-fireplace.git\n\nThis allows new ways to interact with injected trees, for example\na new command 'git subtree list' becomes possible:\n\n$ git subtree list\n.vim/bundle/fireplace https://repo/user/vim-fireplace.git b999b0\n\nSigned-off-by: Mathias Nyman <mathias.nyman@iki.fi>\nSigned-off-by: Nicola Paolucci <npaolucci@atlassian.com>\nThanks-to: Aleksi Aalto <aga@iki.fi>\n---\n contrib/subtree/git-subtree.sh     | 73 +++++++++++++++++++++++++-------------\n contrib/subtree/t/t7900-subtree.sh | 19 ++++++++++\n 2 files changed, 68 insertions(+), 24 deletions(-)\n\ndiff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh\nindex 7a39b30..278699b 100755\n--- a/contrib/subtree/git-subtree.sh\n+++ b/contrib/subtree/git-subtree.sh\n@@ -335,18 +335,21 @@ add_msg()\n \tdir=\"$1\"\n \tlatest_old=\"$2\"\n \tlatest_new=\"$3\"\n+\trepo=\"$4\" # optional\n \tif [ -n \"$message\" ]; then\n \t\tcommit_message=\"$message\"\n \telse\n \t\tcommit_message=\"Add '$dir/' from commit '$latest_new'\"\n \tfi\n-\tcat <<-EOF\n-\t\t$commit_message\n-\t\t\n-\t\tgit-subtree-dir: $dir\n-\t\tgit-subtree-mainline: $latest_old\n-\t\tgit-subtree-split: $latest_new\n-\tEOF\n+\techo $commit_message\n+\techo\n+\techo git-subtree-dir: $dir\n+\techo git-subtree-mainline: $latest_old\n+\techo git-subtree-split: $latest_new\n+\tif [ -n \"$repo\" ]; then\n+\t\trepo_url=$(get_repository_url \"$repo\")\n+\t\techo \"git-subtree-repo: $repo_url\"\n+\tfi\n }\n \n add_squashed_msg()\n@@ -382,8 +385,9 @@ squash_msg()\n \tdir=\"$1\"\n \toldsub=\"$2\"\n \tnewsub=\"$3\"\n+\trepo=\"$4\" # optional\n \tnewsub_short=$(git rev-parse --short \"$newsub\")\n-\t\n+\n \tif [ -n \"$oldsub\" ]; then\n \t\toldsub_short=$(git rev-parse --short \"$oldsub\")\n \t\techo \"Squashed '$dir/' changes from $oldsub_short..$newsub_short\"\n@@ -397,6 +401,10 @@ squash_msg()\n \techo\n \techo \"git-subtree-dir: $dir\"\n \techo \"git-subtree-split: $newsub\"\n+\tif [ -n \"$repo\" ]; then\n+\t\trepo_url=$(get_repository_url \"$repo\")\n+\t\techo \"git-subtree-repo: $repo_url\"\n+\tfi\n }\n \n toptree_for_commit()\n@@ -440,12 +448,13 @@ new_squash_commit()\n \told=\"$1\"\n \toldsub=\"$2\"\n \tnewsub=\"$3\"\n+\trepo=\"$4\" # optional\n \ttree=$(toptree_for_commit $newsub) || exit $?\n \tif [ -n \"$old\" ]; then\n-\t\tsquash_msg \"$dir\" \"$oldsub\" \"$newsub\" | \n+\t\tsquash_msg \"$dir\" \"$oldsub\" \"$newsub\" \"$repo\" |\n \t\t\tgit commit-tree \"$tree\" -p \"$old\" || exit $?\n \telse\n-\t\tsquash_msg \"$dir\" \"\" \"$newsub\" |\n+\t\tsquash_msg \"$dir\" \"\" \"$newsub\" \"$repo\" |\n \t\t\tgit commit-tree \"$tree\" || exit $?\n \tfi\n }\n@@ -517,6 +526,16 @@ ensure_valid_ref_format()\n \t    die \"'$1' does not look like a ref\"\n }\n \n+get_repository_url()\n+{\n+\trepo=$1\n+\trepo_url=$(git config --get remote.$repo.url)\n+\tif [ -z \"$repo_url\" ]; then\n+\t\trepo_url=$repo\n+\tfi\n+\techo $repo_url\n+}\n+\n cmd_add()\n {\n \tif [ -e \"$dir\" ]; then\n@@ -548,19 +567,18 @@ cmd_add()\n cmd_add_repository()\n {\n \techo \"git fetch\" \"$@\"\n-\trepository=$1\n+\trepo=$1\n \trefspec=$2\n \tgit fetch \"$@\" || exit $?\n \trevs=FETCH_HEAD\n-\tset -- $revs\n+\tset -- $revs $repo\n \tcmd_add_commit \"$@\"\n }\n \n cmd_add_commit()\n {\n-\trevs=$(git rev-parse $default --revs-only \"$@\") || exit $?\n-\tset -- $revs\n-\trev=\"$1\"\n+\trev=$(git rev-parse $default --revs-only \"$1\") || exit $?\n+\trepo=\"${@:2}\" # optional\n \t\n \tdebug \"Adding $dir as '$rev'...\"\n \tgit read-tree --prefix=\"$dir\" $rev || exit $?\n@@ -575,12 +593,12 @@ cmd_add_commit()\n \tfi\n \t\n \tif [ -n \"$squash\" ]; then\n-\t\trev=$(new_squash_commit \"\" \"\" \"$rev\") || exit $?\n+\t\trev=$(new_squash_commit \"\" \"\" \"$rev\" \"$repo\") || exit $?\n \t\tcommit=$(add_squashed_msg \"$rev\" \"$dir\" |\n \t\t\t git commit-tree $tree $headp -p \"$rev\") || exit $?\n \telse\n \t\trevp=$(peel_committish \"$rev\") &&\n-\t\tcommit=$(add_msg \"$dir\" \"$headrev\" \"$rev\" |\n+\t\tcommit=$(add_msg \"$dir\" \"$headrev\" \"$rev\" \"$repo\" |\n \t\t\t git commit-tree $tree $headp -p \"$revp\") || exit $?\n \tfi\n \tgit reset \"$commit\" || exit $?\n@@ -609,7 +627,8 @@ cmd_split()\n \telse\n \t\tunrevs=\"$(find_existing_splits \"$dir\" \"$revs\")\"\n \tfi\n-\t\n+\n+\trev=\"$1\"\n \t# We can't restrict rev-list to only $dir here, because some of our\n \t# parents have the $dir contents the root, and those won't match.\n \t# (and rev-list --follow doesn't seem to solve this)\n@@ -683,15 +702,20 @@ cmd_split()\n \n cmd_merge()\n {\n-\trevs=$(git rev-parse $default --revs-only \"$@\") || exit $?\n+\trevs=$(git rev-parse $default --revs-only \"$1\") || exit $?\n \tensure_clean\n-\t\n \tset -- $revs\n \tif [ $# -ne 1 ]; then\n \t\tdie \"You must provide exactly one revision.  Got: '$revs'\"\n \tfi\n+\tdo_merge \"$@\"\n+}\n+\n+do_merge()\n+{\n \trev=\"$1\"\n-\t\n+\trepo=\"$2\" # optional\n+\n \tif [ -n \"$squash\" ]; then\n \t\tfirst_split=\"$(find_latest_squash \"$dir\")\"\n \t\tif [ -z \"$first_split\" ]; then\n@@ -704,7 +728,7 @@ cmd_merge()\n \t\t\tsay \"Subtree is already at commit $rev.\"\n \t\t\texit 0\n \t\tfi\n-\t\tnew=$(new_squash_commit \"$old\" \"$sub\" \"$rev\") || exit $?\n+\t\tnew=$(new_squash_commit \"$old\" \"$sub\" \"$rev\" \"$repo\") || exit $?\n \t\tdebug \"New squash commit: $new\"\n \t\trev=\"$new\"\n \tfi\n@@ -730,12 +754,13 @@ cmd_pull()\n \tif [ $# -ne 2 ]; then\n \t    die \"You must provide <repository> <ref>\"\n \tfi\n+\trepo=$1\n \tensure_clean\n \tensure_valid_ref_format \"$2\"\n \tgit fetch \"$@\" || exit $?\n \trevs=FETCH_HEAD\n-\tset -- $revs\n-\tcmd_merge \"$@\"\n+\tset -- $revs $repo\n+\tdo_merge \"$@\"\n }\n \n cmd_push()\ndiff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh\nindex 3bf96a9..ed40e73 100755\n--- a/contrib/subtree/t/t7900-subtree.sh\n+++ b/contrib/subtree/t/t7900-subtree.sh\n@@ -86,6 +86,11 @@ last_commit_message()\n \tgit log --pretty=format:%s -1\n }\n \n+squashed_commit_body()\n+{\n+\tgit log --pretty=format:%b --numstat HEAD^2\n+}\n+\n subtree_test_count=0\n next_test() {\n \tsubtree_test_count=$(($subtree_test_count+1))\n@@ -176,6 +181,20 @@ test_expect_success 'add subproj as subtree into sub dir/ with --squash and --pr\n \t)\n '\n \n+next_test\n+test_expect_success 'add --squash stores git-subtree-repo value' '\n+\tsubtree_test_create_repo \"$subtree_test_count\" &&\n+\tsubtree_test_create_repo \"$subtree_test_count/sub proj\" &&\n+\ttest_create_commit \"$subtree_test_count\" main1 &&\n+\ttest_create_commit \"$subtree_test_count/sub proj\" sub1 &&\n+\t(\n+\t\tcd \"$subtree_test_count\" &&\n+\t\tgit fetch ./\"sub proj\" master &&\n+\t\tgit subtree add --prefix=\"sub dir\" \"./sub proj\" HEAD --squash &&\n+\t\tcheck_equal \"$(squashed_commit_body | grep git-subtree-repo)\" \"git-subtree-repo: ./sub proj\"\n+\t)\n+'\n+\n #\n # Tests for 'git subtree merge'\n #\n-- \n2.7.1\n"},{"id":"280541","messageId":"1457603052-53963-3-git-send-email-npaolucci@atlassian.com","threadId":"41660","inReplyTo":"1457603052-53963-1-git-send-email-npaolucci@atlassian.com","subject":"[PATCH 2/3] contrib/subtree: new list command to list subtrees","fromName":"Nicola Paolucci","fromEmail":"npaolucci@atlassian.com","sentAt":"2016-03-10T09:44:11Z","receivedAt":"2016-03-10T09:44:11Z","isPatch":true,"sender":{"key":"npaolucci@atlassian.com","avatar":"https://gravatar.com/avatar/c809e71e52357fdefc4fc43b956992e6b5c7bba23c47c3cfedf451e8c4714b96?d=mp&s=160"},"body":"Example output:\n\n$ git subtree list\n.vim/bundle/fireplace https://github.com/tpope/vim-fireplace.git b999b0\n\nSigned-off-by: Nicola Paolucci <npaolucci@atlassian.com>\n---\n contrib/subtree/git-subtree.sh     | 54 ++++++++++++++++++++++++++++++++++----\n contrib/subtree/t/t7900-subtree.sh | 18 +++++++++++++\n 2 files changed, 67 insertions(+), 5 deletions(-)\n\ndiff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh\nindex 278699b..82f3fce 100755\n--- a/contrib/subtree/git-subtree.sh\n+++ b/contrib/subtree/git-subtree.sh\n@@ -14,6 +14,7 @@ git subtree merge --prefix=<prefix> <commit>\n git subtree pull  --prefix=<prefix> <repository> <ref>\n git subtree push  --prefix=<prefix> <repository> <ref>\n git subtree split --prefix=<prefix> <commit...>\n+git subtree list\n --\n h,help        show the help\n q             quiet\n@@ -109,19 +110,22 @@ done\n command=\"$1\"\n shift\n case \"$command\" in\n-\tadd|merge|pull) default= ;;\n+\tadd|merge|pull|list) default= ;;\n \tsplit|push) default=\"--default HEAD\" ;;\n \t*) die \"Unknown command '$command'\" ;;\n esac\n \n-if [ -z \"$prefix\" ]; then\n-\tdie \"You must provide the --prefix option.\"\n+if [ \"$command\" != \"list\" ]; then\n+\tif [ -z \"$prefix\" ]; then\n+\t\tdie \"You must provide the --prefix option.\"\n+\tfi\n fi\n \n case \"$command\" in\n-\tadd) [ -e \"$prefix\" ] && \n+\tadd) [ -e \"$prefix\" ] &&\n \t\tdie \"prefix '$prefix' already exists.\" ;;\n-\t*)   [ -e \"$prefix\" ] || \n+\tlist) ;;\n+\t*)   [ -e \"$prefix\" ] ||\n \t\tdie \"'$prefix' does not exist; use 'git subtree add'\" ;;\n esac\n \n@@ -230,6 +234,41 @@ try_remove_previous()\n \tfi\n }\n \n+find_subtree_repos()\n+{\n+\tdebug \"Looking for subtree repos...\"\n+\tsq=\n+\tmain=\n+\tsub=\n+\tgit log --grep=\"^git-subtree-dir:\" \\\n+\t\t--pretty=format:'START %H%n%s%n%n%b%nEND%n' HEAD |\n+\twhile read a b c; do\n+\t\tdebug \"$a $b $c\"\n+\t\tdebug \"{{$sq/$main/$sub}}\"\n+\t\tcase \"$a\" in\n+\t\t\tSTART) sq=\"$b\" ;;\n+\t\t\tgit-subtree-dir:) dir=\"$b $c\" ;;\n+\t\t\tgit-subtree-mainline:) main=\"$b\" ;;\n+\t\t\tgit-subtree-split:) sub=\"$b\" ;;\n+\t\t\tgit-subtree-repo:) repo=\"$b $c\" ;;\n+\t\t\tEND)\n+\t\t\t\tif [ -n \"$sub\" ]; then\n+\t\t\t\t\tif [ -n \"$main\" ]; then\n+\t\t\t\t\t\t# a rejoin commit?\n+\t\t\t\t\t\t# Pretend its sub was a squash.\n+\t\t\t\t\t\tsq=\"$sub\"\n+\t\t\t\t\tfi\n+\t\t\t\t\tdebug \"Subtree found: $dir $repo $sub\"\n+\t\t\t\t\techo \"$dir\" \"$repo\" \"$sub\"\n+\t\t\t\tfi\n+\t\t\t\tsq=\n+\t\t\t\tmain=\n+\t\t\t\tsub=\n+\t\t\t\t;;\n+\t\tesac\n+\tdone\n+}\n+\n find_latest_squash()\n {\n \tdebug \"Looking for latest squash ($dir)...\"\n@@ -536,6 +575,11 @@ get_repository_url()\n \techo $repo_url\n }\n \n+cmd_list()\n+{\n+\tfind_subtree_repos \"$@\"\n+}\n+\n cmd_add()\n {\n \tif [ -e \"$dir\" ]; then\ndiff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh\nindex ed40e73..ce97446 100755\n--- a/contrib/subtree/t/t7900-subtree.sh\n+++ b/contrib/subtree/t/t7900-subtree.sh\n@@ -196,6 +196,24 @@ test_expect_success 'add --squash stores git-subtree-repo value' '\n '\n \n #\n+# Tests for 'git subtree list'\n+#\n+\n+next_test\n+test_expect_success 'list outputs list of subtrees' '\n+\tsubtree_test_create_repo \"$subtree_test_count\" &&\n+\tsubtree_test_create_repo \"$subtree_test_count/sub proj\" &&\n+\ttest_create_commit \"$subtree_test_count\" main1 &&\n+\ttest_create_commit \"$subtree_test_count/sub proj\" sub1 &&\n+\t(\n+\t\tcd \"$subtree_test_count\" &&\n+\t\tgit fetch ./\"sub proj\" master &&\n+\t\tgit subtree add --prefix=\"sub dir\" \"./sub proj\" HEAD --squash &&\n+\t\tcheck_equal \"$(git subtree list | cut -c -19)\" \"sub dir ./sub proj \"\n+\t)\n+'\n+\n+#\n # Tests for 'git subtree merge'\n #\n \n-- \n2.7.1\n"},{"id":"280542","messageId":"1457603052-53963-4-git-send-email-npaolucci@atlassian.com","threadId":"41660","inReplyTo":"1457603052-53963-1-git-send-email-npaolucci@atlassian.com","subject":"[PATCH 3/3] contrib/subtree: list --resolve gets symbolic refs","fromName":"Nicola Paolucci","fromEmail":"npaolucci@atlassian.com","sentAt":"2016-03-10T09:44:12Z","receivedAt":"2016-03-10T09:44:12Z","isPatch":true,"sender":{"key":"npaolucci@atlassian.com","avatar":"https://gravatar.com/avatar/c809e71e52357fdefc4fc43b956992e6b5c7bba23c47c3cfedf451e8c4714b96?d=mp&s=160"},"body":"As the 'list' command finds commit ids for subtrees injected into the\nchecked out branch the --resolve flag tries to look up the repositories\nat 'git-subtree-repo' and retrive the symbolic refs associated with the\ncommit ids found.\n\nExample:\n\n    $ git-subtree.sh list --resolve\n\n    vim-airline  https://repo/bling/vim-airline.git 4fa37e5e[...]\n    vim-airline  https://repo/bling/vim-airline.git HEAD\n    vim-airline  https://repo/bling/vim-airline.git refs/heads/master\n\nSigned-off-by: Nicola Paolucci <npaolucci@atlassian.com>\n---\n contrib/subtree/git-subtree.sh     | 21 +++++++++++++++++----\n contrib/subtree/git-subtree.txt    | 31 +++++++++++++++++++++++++++++++\n contrib/subtree/t/t7900-subtree.sh | 26 ++++++++++++++++++++++++++\n 3 files changed, 74 insertions(+), 4 deletions(-)\n\ndiff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh\nindex 82f3fce..fe62151 100755\n--- a/contrib/subtree/git-subtree.sh\n+++ b/contrib/subtree/git-subtree.sh\n@@ -14,7 +14,7 @@ git subtree merge --prefix=<prefix> <commit>\n git subtree pull  --prefix=<prefix> <repository> <ref>\n git subtree push  --prefix=<prefix> <repository> <ref>\n git subtree split --prefix=<prefix> <commit...>\n-git subtree list\n+git subtree list  [--resolve]\n --\n h,help        show the help\n q             quiet\n@@ -29,6 +29,7 @@ onto=         try connecting new tree to an existing one\n rejoin        merge the new branch back into HEAD\n  options for 'add', 'merge', and 'pull'\n squash        merge subtree changes as a single commit\n+resolve       resolves ids to refs when possible\n \"\n eval \"$(echo \"$OPTS_SPEC\" | git rev-parse --parseopt -- \"$@\" || echo exit $?)\"\n \n@@ -48,6 +49,7 @@ annotate=\n squash=\n message=\n prefix=\n+resolve=\n \n debug()\n {\n@@ -102,6 +104,8 @@ while [ $# -gt 0 ]; do\n \t\t--no-ignore-joins) ignore_joins= ;;\n \t\t--squash) squash=1 ;;\n \t\t--no-squash) squash= ;;\n+\t\t--resolve) resolve=1 ;;\n+\t\t--no-resolve) resolve= ;;\n \t\t--) break ;;\n \t\t*) die \"Unexpected option: $opt\" ;;\n \tesac\n@@ -254,12 +258,21 @@ find_subtree_repos()\n \t\t\tEND)\n \t\t\t\tif [ -n \"$sub\" ]; then\n \t\t\t\t\tif [ -n \"$main\" ]; then\n-\t\t\t\t\t\t# a rejoin commit?\n-\t\t\t\t\t\t# Pretend its sub was a squash.\n \t\t\t\t\t\tsq=\"$sub\"\n \t\t\t\t\tfi\n \t\t\t\t\tdebug \"Subtree found: $dir $repo $sub\"\n-\t\t\t\t\techo \"$dir\" \"$repo\" \"$sub\"\n+\t\t\t\t\t# Strip potential space at the end in repo\n+\t\t\t\t\trepo=$(echo $repo)\n+\t\t\t\t\tif [ -n \"$resolve\" ] && [ -n \"$repo\" ]; then\n+\t\t\t\t\t\techo \"$dir\" \"$repo\" \"$sub\"\n+\t\t\t\t\t\t# Retrieve remote refs if repo is available\n+\t\t\t\t\t\tresolved_refs=$(git ls-remote \"$repo\" | grep \"$sub\" | cut -c 42- | xargs)\n+\t\t\t\t\t\tfor r in $resolved_refs; do\n+\t\t\t\t\t\t\techo \"$dir\" \"$repo\" \"$r\"\n+\t\t\t\t\t\tdone\n+\t\t\t\t\telse\n+\t\t\t\t\t\techo \"$dir\" \"$repo\" \"$sub\"\n+\t\t\t\t\tfi\n \t\t\t\tfi\n \t\t\t\tsq=\n \t\t\t\tmain=\ndiff --git a/contrib/subtree/git-subtree.txt b/contrib/subtree/git-subtree.txt\nindex 60d76cd..ab36951 100644\n--- a/contrib/subtree/git-subtree.txt\n+++ b/contrib/subtree/git-subtree.txt\n@@ -15,6 +15,7 @@ SYNOPSIS\n 'git subtree' push  -P <prefix> <repository> <ref>\n 'git subtree' merge -P <prefix> <commit>\n 'git subtree' split -P <prefix> [OPTIONS] [<commit>]\n+'git subtree' list [--resolve]\n \n \n DESCRIPTION\n@@ -106,6 +107,12 @@ split::\n \tcontents of <prefix> at the root of the project instead\n \tof in a subdirectory.  Thus, the newly created history\n \tis suitable for export as a separate git repository.\n+\n+list::\n+\tList all subtrees injected in checked out branch. Run\n+\twith optional `--resolve` to retrieve remote symbolic refs\n+\tassociated with the subtree. Address of subtree repo is stored\n+\tin commits as `git-subtree-repo` at the time of `git subtree add`.\n +\n After splitting successfully, a single commit id is printed to stdout.\n This corresponds to the HEAD of the newly created tree, which you can\n@@ -240,6 +247,13 @@ split, because you don't want the subproject's history to be part of\n your project anyway.\n \n \n+OPTIONS FOR list\n+----------------------------------\n+--resolve::\n+\tResolves 'git-subtree-split' refs by looking up symbolic refs at\n+\t'git-subtree-repo'.\n+\n+\n EXAMPLE 1. Add command\n ----------------------\n Let's assume that you have a local repository that you would like\n@@ -341,6 +355,23 @@ Then push the new branch onto the new empty repository:\n \t$ git push <new-repo> split:master\n \n \n+EXAMPLE 3. List subtrees\n+-----------------------------------------\n+Suppose you add a subtree with:\n+\n+\t$ git subtree add --prefix dependency https://host/repo.git master --squash\n+\n+You can list all subtrees in the current branch resolving the refs with:\n+\n+\t$ git subtree list --resolve\n+\n+Which would output something like:\n+\n+\tdepenency  https://host/repo.git 4fa37e5e20b5ae9b[...]\n+\tdepenency  https://host/repo.git HEAD\n+\tdepenency  https://host/repo.git refs/heads/master\n+\n+\n AUTHOR\n ------\n Written by Avery Pennarun <apenwarr@gmail.com>\ndiff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh\nindex ce97446..d100001 100755\n--- a/contrib/subtree/t/t7900-subtree.sh\n+++ b/contrib/subtree/t/t7900-subtree.sh\n@@ -81,6 +81,19 @@ test_create_commit() (\n \tgit commit -m \"$commit\" || error \"Could not commit\"\n )\n \n+test_create_commit_and_tag() (\n+\trepo=$1\n+\tcommit=$2\n+    tag=$3\n+\tcd \"$repo\"\n+\tmkdir -p $(dirname \"$commit\") \\\n+\t|| error \"Could not create directory for commit\"\n+\techo \"$commit\" >\"$commit\"\n+\tgit add \"$commit\" || error \"Could not add commit\"\n+\tgit commit -m \"$commit\" || error \"Could not commit\"\n+\tgit tag -m \"$commit tag: $tag\" $tag || error \"Could not tag\"\n+)\n+\n last_commit_message()\n {\n \tgit log --pretty=format:%s -1\n@@ -212,6 +225,19 @@ test_expect_success 'list outputs list of subtrees' '\n \t\tcheck_equal \"$(git subtree list | cut -c -19)\" \"sub dir ./sub proj \"\n \t)\n '\n+next_test\n+test_expect_success 'list --resolve resolves refs' '\n+\tsubtree_test_create_repo \"$subtree_test_count\" &&\n+\tsubtree_test_create_repo \"$subtree_test_count/sub proj\" &&\n+\ttest_create_commit_and_tag \"$subtree_test_count/sub proj\" sub1 test-v0.1 &&\n+\ttest_create_commit \"$subtree_test_count\" main1 &&\n+\t(\n+\t\tcd \"$subtree_test_count\" &&\n+\t\tgit fetch ./\"sub proj\" master &&\n+\t\tgit subtree add --prefix=\"sub dir\" \"./sub proj\" HEAD --squash &&\n+\t\tcheck_equal \"$(git subtree -d list --resolve | grep HEAD)\" \"sub dir ./sub proj HEAD\"\n+\t)\n+'\n \n #\n # Tests for 'git subtree merge'\n-- \n2.7.1\n"},{"id":"287159","messageId":"87mvnjow4n.fsf@waller.obbligato.org","threadId":"41660","inReplyTo":"1457603052-53963-1-git-send-email-npaolucci@atlassian.com","subject":"Re: [PATCH 0/3] subtree: add 'git-subtree-repo' and list command","fromName":"David A. Greene","fromEmail":"greened@obbligato.org","sentAt":"2016-05-21T22:59:36Z","receivedAt":"2016-05-21T22:59:36Z","isPatch":true,"sender":{"key":"greened@obbligato.org","avatar":"https://avatars.githubusercontent.com/u/5291869?v=4"},"body":"Nicola Paolucci <npaolucci@atlassian.com> writes:\n\n> To my knowledge 'git subtree' currently lacks a way to\n> track where injected repositories come from originally.\n> Adding this information allows for useful extensions to \n> the command and makes it easier to use subtrees to track\n> external dependencies.\n\nThanks for working on this.  I just sent a reply to your earlier\niteration.\n\nWhat is the intent for use of this?  Is it simply to record from where\ncommits were pulled or do you intend to use this information later on to\nhave git-subtree guess from where to fetch future commits?\n\nI would be opposed to the latter because I think it potentially limits\nthe utility of git-subtree and may be misleading.  I frequently pull\ncommits for a subtree from several different clones of the same\nreposiory.  How does git-subtree list handle that situation?\n\nDoes git-subtree list really print out repository information for every\ncommit added by git-subtree?  That's potentially a lot of commits.  It\nmight be more useful to aggregate repository information and only dump\nout unique URLs.  In any case, processing all commits seems like a ton\nof work for such a simple operation.  Maybe this information should be\ncached in .gitconfig.\n\nI'm actually in the middle of cleaing up metadata but I'm not going to\nblock these commits due to that.  Just be aware that it may change a\nbit.\n\n                  -David\n"}]}