{"thread":{"id":"36792","subject":"[PATCH 1/2] contrib/subtree: Store subtree metadata in .gittrees file","startedAt":"2014-05-30T17:45:14Z","lastAt":"2014-05-30T17:45:15Z","messageCount":2,"participants":["Keval Patel"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"243024","messageId":"1401471915-47195-1-git-send-email-kapatel@lutron.com","threadId":"36792","inReplyTo":null,"subject":"[PATCH 1/2] contrib/subtree: Store subtree metadata in .gittrees file","fromName":"Keval Patel","fromEmail":"patel.keval88@gmail.com","sentAt":"2014-05-30T17:45:14Z","receivedAt":"2014-05-30T17:45:14Z","isPatch":true,"sender":{"key":"patel.keval88@gmail.com","avatar":null},"body":"- Most of the changes are taken from helmo’s repository from following\nURL:\nhttps://github.com/helmo/git-subtree/blob/master/git-subtree.sh\n- Add code to create a file named .gittrees which can store information\non the subtree that is getting merged to the super-repository. The\nadvantage is ‘subtree push’ and ‘subtree pull’ subcommands can be\ncalled with only the —prefix option and no need to provide remote url\n- Add tests for new feature additions. All existing and new tests pass\nsuccessfully\nFiles changed in this commit:\n1. git/contrib/subtree/git-subtree.sh\n2. git/contrib/subtree/t/t7900-subtree.sh\n\nSigned-off-by: Keval Patel <kapatel@lutron.com>\n---\nA selection of updates to git-subtree were offered to the list by couple of people\n($gmane/196667) by Herman van Rink and ($gmane/217820) by Paul Campbell\nFor various reasons the work stalled and I would like to pick it up from there\n\nThe following patches take a selection of these commits and rebase them \nagainst the tip of master.\nThe make test works and I have added more tests to cover the new commands and \nuse of .gittrees file for storing the subtree metadata\n\nThanks-to and Based-on-patch-by:\n- Herman van Rink\n- Matt Hoffman\n- Michael Hart\n- Nate Jones\n- Paul Campbell\n- Paul Cartwright\n- Peter Jaros\n- bibendi\n\n contrib/subtree/git-subtree.sh     |   98 +++++++++++++++++++++------------\n contrib/subtree/t/t7900-subtree.sh |  106 ++++++++++++++++++++++++++++++++++++\n 2 files changed, 168 insertions(+), 36 deletions(-)\n\ndiff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh\nindex db925ca..7d01b4b 100755\n--- a/contrib/subtree/git-subtree.sh\n+++ b/contrib/subtree/git-subtree.sh\n@@ -8,8 +8,7 @@ if [ $# -eq 0 ]; then\n     set -- -h\n fi\n OPTS_SPEC=\"\\\n-git subtree add   --prefix=<prefix> <commit>\n-git subtree add   --prefix=<prefix> <repository> <ref>\n+git subtree add   --prefix=<prefix> <repository> <refspec>\n git subtree merge --prefix=<prefix> <commit>\n git subtree pull  --prefix=<prefix> <repository> <ref>\n git subtree push  --prefix=<prefix> <repository> <ref>\n@@ -490,12 +489,6 @@ ensure_clean()\n \tfi\n }\n \n-ensure_valid_ref_format()\n-{\n-\tgit check-ref-format \"refs/heads/$1\" ||\n-\t    die \"'$1' does not look like a ref\"\n-}\n-\n cmd_add()\n {\n \tif [ -e \"$dir\" ]; then\n@@ -505,22 +498,12 @@ cmd_add()\n \tensure_clean\n \t\n \tif [ $# -eq 1 ]; then\n-\t    git rev-parse -q --verify \"$1^{commit}\" >/dev/null ||\n-\t    die \"'$1' does not refer to a commit\"\n-\n-\t    \"cmd_add_commit\" \"$@\"\n+\t\t\"cmd_add_commit\" \"$@\"\n \telif [ $# -eq 2 ]; then\n-\t    # Technically we could accept a refspec here but we're\n-\t    # just going to turn around and add FETCH_HEAD under the\n-\t    # specified directory.  Allowing a refspec might be\n-\t    # misleading because we won't do anything with any other\n-\t    # branches fetched via the refspec.\n-\t    ensure_valid_ref_format \"$2\"\n-\n-\t    \"cmd_add_repository\" \"$@\"\n+\t\t\"cmd_add_repository\" \"$@\"\n \telse\n-\t    say \"error: parameters were '$@'\"\n-\t    die \"Provide either a commit or a repository and commit.\"\n+\t\tsay \"error: parameters were '$@'\"\n+\t\tdie \"Provide either a refspec or a repository and refspec.\"\n \tfi\n }\n \n@@ -533,6 +516,24 @@ cmd_add_repository()\n \trevs=FETCH_HEAD\n \tset -- $revs\n \tcmd_add_commit \"$@\"\n+\t\n+\trevs=$(git rev-parse $default --revs-only \"$@\") || exit $?\n+\tset -- $revs\n+\trev=\"$1\"\n+\t\n+\tsubtree_mainline_merge=$(git rev-parse HEAD) || exit $?\n+\t\n+\t# now add it to our list of repos \n+\tgit config -f .gittrees --unset subtree.$dir.url\n+\tgit config -f .gittrees --add subtree.$dir.url $repository\n+\tgit config -f .gittrees --unset subtree.$dir.path\n+\tgit config -f .gittrees --add subtree.$dir.path $dir\n+\tgit config -f .gittrees --unset subtree.$dir.branch\n+\tgit config -f .gittrees --add subtree.$dir.branch $refspec\n+\tgit config -f .gittrees --unset subtree.$dir.subtreeCommit\n+\tgit config -f .gittrees --add subtree.$dir.subtreeCommit $rev\n+\tgit config -f .gittrees --unset subtree.$dir.subtreeMergeCommit\n+\tgit config -f .gittrees --add subtree.$dir.subtreeMergeCommit $subtree_mainline_merge\n }\n \n cmd_add_commit()\n@@ -598,7 +599,7 @@ cmd_split()\n \teval \"$grl\" |\n \twhile read rev parents; do\n \t\trevcount=$(($revcount + 1))\n-\t\tsay -n \"$revcount/$revmax ($createcount)\n\"\n+\t\tsay -n \"$revcount/$revmax ($createcount)\"\n \t\tdebug \"Processing commit: $rev\"\n \t\texists=$(cache_get $rev)\n \t\tif [ -n \"$exists\" ]; then\n@@ -705,31 +706,56 @@ cmd_merge()\n \n cmd_pull()\n {\n-\tif [ $# -ne 2 ]; then\n-\t    die \"You must provide <repository> <ref>\"\n+\tif [ $# -gt 2 ]; then\n+\t\tdie \"You should provide either <refspec> or <repository> <refspec>\"\n \tfi\n+\tif [ -e \"$dir\" ]; then\n \tensure_clean\n-\tensure_valid_ref_format \"$2\"\n-\tgit fetch \"$@\" || exit $?\n+\t\tif [ $# -eq 1 ]; then \n+\t\t\trepository=$(git config -f .gittrees subtree.$prefix.url)\n+\t\t\trefspec=$1\n+\t\telif [ $# -eq 2 ]; then \n+\t\t\trepository=$1\n+\t\t\trefspec=$2\n+\t\telse \n+\t\t\trepository=$(git config -f .gittrees subtree.$prefix.url)\n+\t\t\trefspec=$(git config -f .gittrees subtree.$prefix.branch)\n+\t\tfi\n+\t\tgit fetch $repository $refspec || exit $?\n+\t\techo \"git fetch using: \" $repository $refspec\n \trevs=FETCH_HEAD\n \tset -- $revs\n \tcmd_merge \"$@\"\n+\telse\n+\t\tdie \"'$dir' must already exist. Try 'git subtree add'.\"\n+\tfi\n }\n \n cmd_push()\n {\n-\tif [ $# -ne 2 ]; then\n-\t    die \"You must provide <repository> <ref>\"\n+\tif [ $# -gt 2 ]; then\n+\t\tdie \"You should provide either <refspec> or <repository> <refspec>\"\n \tfi\n-\tensure_valid_ref_format \"$2\"\n \tif [ -e \"$dir\" ]; then\n-\t    repository=$1\n-\t    refspec=$2\n-\t    echo \"git push using: \" $repository $refspec\n-\t    localrev=$(git subtree split --prefix=\"$prefix\") || die\n-\t    git push $repository $localrev:refs/heads/$refspec\n+\t\tif [ $# -eq 1 ]; then \n+\t\t\trepository=$(git config -f .gittrees subtree.$prefix.url)\n+\t\t\trefspec=$1\n+\t\telif [ $# -eq 2 ]; then \n+\t\trepository=$1\n+\t\trefspec=$2\n+\t\telse\n+\t\t\trepository=$(git config -f .gittrees subtree.$prefix.url)\n+\t\t\trefspec=$(git config -f .gittrees subtree.$prefix.branch)\n+\t\tfi\n+\t\techo \"git push using: \" $repository $refspec\n+\t\trev=$(git subtree split --prefix=$prefix)\n+\t\tif [ -n \"$rev\" ]; then\n+\t\t\tgit push $repository $rev:refs/heads/$refspec\n+\t\telse\n+\t\t\tdie \"Couldn't push, 'git subtree split' failed.\"\n+\t\tfi\n \telse\n-\t    die \"'$dir' must already exist. Try 'git subtree add'.\"\n+\t\tdie \"'$dir' must already exist. Try 'git subtree add'.\"\n \tfi\n }\n \ndiff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh\nindex 66ce4b0..05110f7 100755\n--- a/contrib/subtree/t/t7900-subtree.sh\n+++ b/contrib/subtree/t/t7900-subtree.sh\n@@ -60,6 +60,11 @@ last_commit_message()\n \tgit log --pretty=format:%s -1\n }\n \n+last_commit_id()\n+{\n+    git log --format=\"%H\" -n 1\n+}\n+\n test_expect_success 'init subproj' '\n         test_create_repo subproj\n '\n@@ -464,5 +469,106 @@ test_expect_success 'verify one file change per commit' '\n \t        check_equal \"$x\" 1\n         ))\n '\n+# Tests for subtree add which creates .gittrees for storing metadata\n+\n+# Back to mainline and create new directory for testing\n+cd ../..\n+\n+mkdir test_sub\n+cd test_sub\n+\n+mkdir shared_projects\n+# To shared_projects!\n+cd shared_projects\n+\n+# Create couple of Git repos in shared_projects folder which can be\n+# added as subtrees to our parent projects\n+test_expect_success 'add subtree1' '\n+        test_create_repo subtree1 &&\n+        cd subtree1 &&\n+        create sub1_file1 &&\n+        git commit -m \"Initial subtree1 commit\"\n+'\n+\n+# Store the latest commit value for future use\n+expected_subtreeCommit=`echo $(last_commit_id)`\n+expected_branch=`echo $(git rev-parse --abbrev-ref HEAD)`\n+\n+# Back to shared_projects\n+cd ..\n+\n+test_expect_success 'add subtree2' '\n+        test_create_repo subtree2 &&\n+        cd subtree2 &&\n+        create sub2_file1 &&\n+        git commit -m \"Initial subtree2 commit\"\n+'\n+\n+# Back to test_sub\n+cd ../..\n+\n+# Create test parent repos that will add subtrees to itself\n+test_expect_success 'add parent1' '\n+        test_create_repo parent1 &&\n+        cd parent1 &&\n+        create parent1_file1 &&\n+        git commit -m \"Initial parent1 commit\"\n+'\n+\n+# Back to test_sub from parent1\n+cd ..\n+\n+test_expect_success 'add parent2' '\n+        test_create_repo parent2 &&\n+        cd parent2 &&\n+        create parent2_file1 &&\n+        git commit -m \"Initial parent2 commit\"\n+'\n+\n+\n+# To parent1 now. Start the tests\n+cd ../parent1\n+\n+# .gittrees file creation tests\n+test_expect_success 'check add for subtree with master branch' '\n+        git subtree add -m \"Add sub1 subtree\" -P sub1 ../shared_projects/subtree1 master &&\n+        check_equal ''\"$(last_commit_message)\"'' \"Add sub1 subtree\"\n+'\n+\n+# Store latest commit id for future use\n+expected_subtreeMergeCommit=$(last_commit_id)\n+\n+test_expect_success 'check if .gittrees file was created' '\n+        test -a '.gittrees'\n+'\n+# Now lets test if the .gittrees file has the correct information\n+# Hardcoded some expected results for checking data inside .gittrees file\n+expected_url='../shared_projects/subtree1'\n+expected_path='sub1'\n+\n+echo $expected_url>>expected_gittrees\n+echo $expected_path>>expected_gittrees\n+echo $expected_branch>>expected_gittrees\n+echo $expected_subtreeCommit>>expected_gittrees\n+echo $expected_subtreeMergeCommit>>expected_gittrees\n+\n+grep = .gittrees | cut -f2 -d\"=\" | cut -f2 -d\" \" > actual_gittrees\n+\n+test_expect_success 'check .gittrees file has the necessary changes' '\n+        test_cmp actual_gittrees expected_gittrees\n+'\n+\n+test_expect_success 'check subtree does not get created with incorrect remote url' '\n+        test_must_fail git subtree add -P s2 ../shared_projects/subbtree1 master\n+'\n+\n+test_expect_success 'check that subtree does not get created with incorrect branch' '\n+        test_must_fail git subtree add -P s2 ../shared_projects/subtree1 development\n+'\n+\n+test_expect_success 'add another subtree with master branch' '\n+        git subtree add -m \"Add sub2 subtree\" -P sub2 ../shared_projects/subtree2 master &&\n+        check_equal ''\"$(last_commit_message)\"'' \"Add sub2 subtree\"\n+'\n \n test_done\n-- \n1.7.9\n"},{"id":"243025","messageId":"1401471915-47195-2-git-send-email-kapatel@lutron.com","threadId":"36792","inReplyTo":"1401471915-47195-1-git-send-email-kapatel@lutron.com","subject":"[PATCH 2/2] contrib/subtree: List subcmd and modify push/pull to use .gittrees","fromName":"Keval Patel","fromEmail":"patel.keval88@gmail.com","sentAt":"2014-05-30T17:45:15Z","receivedAt":"2014-05-30T17:45:15Z","isPatch":true,"sender":{"key":"patel.keval88@gmail.com","avatar":null},"body":"- Add subtree list sub-command\n- git subtree list - Lists the subtrees in current project\n- Changes taken from helmo’s repository from following URL:\nhttps://github.com/helmo/git-subtree/blob/master/git-subtree.sh\n- Add tests for subtree list and subtree push/pull using .gittrees\n- Files changed in this commit:\n1. git/contrib/subtree/git-subtree.sh\n2. git/contrib/subtree/t/t7900-subtree.sh\n\nSigned-off-by: Keval Patel <kapatel@lutron.com>\n---\nA selection of updates to git-subtree were offered to the list by couple of people\n($gmane/196667) by Herman van Rink and ($gmane/217820) by Paul Campbell\nFor various reasons the work stalled and I would like to pick it up from there\n\nThe following patches take a selection of these commits and rebase them \nagainst the tip of master.\nThe make test works and I have added more tests to cover the new commands and \nuse of .gittrees file for storing the subtree metadata\n\nThanks-to and Based-on-patch-by:\n- Herman van Rink\n- Matt Hoffman\n- Michael Hart\n- Nate Jones\n- Paul Campbell\n- Paul Cartwright\n- Peter Jaros\n- bibendi\n\n contrib/subtree/git-subtree.sh     |   25 ++++++++++--\n contrib/subtree/t/t7900-subtree.sh |   72 ++++++++++++++++++++++++++++++++++++\n 2 files changed, 93 insertions(+), 4 deletions(-)\n\ndiff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh\nindex 7d01b4b..1151884 100755\n--- a/contrib/subtree/git-subtree.sh\n+++ b/contrib/subtree/git-subtree.sh\n@@ -10,9 +10,10 @@ fi\n OPTS_SPEC=\"\\\n git subtree add   --prefix=<prefix> <repository> <refspec>\n 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 pull  --prefix=<prefix> [<repository> [<refspec>...]]\n+git subtree push  --prefix=<prefix> [<repository> [<refspec>...]]\n git subtree split --prefix=<prefix> <commit...>\n+git subtree list\n --\n h,help        show the help\n q             quiet\n@@ -102,15 +103,16 @@ command=\"$1\"\n shift\n case \"$command\" in\n \tadd|merge|pull) default= ;;\n-\tsplit|push) default=\"--default HEAD\" ;;\n+\tsplit|push|list) default=\"--default HEAD\" ;;\n \t*) die \"Unknown command '$command'\" ;;\n esac\n \n-if [ -z \"$prefix\" ]; then\n+if [ -z \"$prefix\" -a \"$command\" != \"list\" ]; then\n \tdie \"You must provide the --prefix option.\"\n fi\n \n case \"$command\" in\n+\tlist);;\n \tadd) [ -e \"$prefix\" ] && \n \t\tdie \"prefix '$prefix' already exists.\" ;;\n \t*)   [ -e \"$prefix\" ] || \n@@ -759,4 +761,19 @@ cmd_push()\n \tfi\n }\n \n+subtree_list() \n+{\n+\tgit config -f .gittrees -l | grep subtree | grep path | grep -o '=.*' | grep -o '[^=].*' |\n+\twhile read path; do \n+\t\trepository=$(git config -f .gittrees subtree.$path.url)\n+\t\trefspec=$(git config -f .gittrees subtree.$path.branch)\n+\t\techo \"    $path        (merged from $repository branch $refspec) \"\n+\tdone\n+}\n+\n+cmd_list()\n+{\n+\tsubtree_list\n+}\n+\n \"cmd_$command\" \"$@\"\ndiff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh\nindex 05110f7..c29993e 100755\n--- a/contrib/subtree/t/t7900-subtree.sh\n+++ b/contrib/subtree/t/t7900-subtree.sh\n@@ -571,4 +571,76 @@ test_expect_success 'add another subtree with master branch' '\n         check_equal ''\"$(last_commit_message)\"'' \"Add sub2 subtree\"\n '\n \n+# Lets commit the changes we made to .gittrees file\n+test_expect_success 'Commit chages to .gittrees for sub1 and sub2 in repo' '\n+        git add .gittrees &&\n+        git commit -m \"Add .gittrees file\"\n+'\n+# Tests for subtree list\n+# Hardcode expected output to a file\n+cat >expect <<-\\EOF\n+    sub1        (merged from ../shared_projects/subtree1 branch master) \n+    sub2        (merged from ../shared_projects/subtree2 branch master) \n+EOF\n+\n+test_expect_success 'check subtree list gives correct output' '\n+        git subtree list>output &&\n+        test_cmp expect output\n+'\n+# Lets commit the changes to parent1 before proceeding\n+test_expect_success 'Commit changes to the repository' '\n+        git add --all &&\n+        git commit -m \"Commit expect and output file additions\"\n+'\n+\n+# Tests for individual subtree pull using information in .gittrees\n+# Go to subtree1 and make a change\n+cd ../shared_projects/subtree1\n+\n+subtree1_change1=\"Add_line_to_Sub1_File2\"\n+\n+echo $subtree1_change1>>sub1_file2\n+\n+# Lets commit the changes to subtree1 before proceeding\n+test_expect_success 'Commit changes to the subtree1' '\n+        git add --all &&\n+        git commit -m \"Commit change to sub1_file2\"\n+'\n+\n+# Switch to develop branch for a future test to push changes to master\n+test_expect_success 'Switch to branch develop' '\n+        git checkout -b develop\n+'\n+\n+# Back to parent1\n+cd ../../parent1\n+\n+test_expect_success 'check  git subtree pull <prefix> works' '\n+        git subtree pull -P sub1 master &&\n+        test_cmp sub1/sub1_file1 ../shared_projects/subtree1/sub1_file1 &&\n+        test_cmp sub1/sub1_file2 ../shared_projects/subtree1/sub1_file2\n+'\n+\n+# Now lets make local change on subtree and push it to subtree remote\n+cd sub1\n+\n+local_change=\"Local addition of line to sub1 file 2\"\n+echo $local_change1>>sub1_file2\n+\n+# Back to parent1\n+cd ..\n+\n+# Lets commit the changes to parent1 before proceeding\n+test_expect_success 'Commit changes to parent repository' '\n+        git add --all &&\n+        git commit -m \"Commit local changes to sub1/sub1 file2\"\n+'\n+\n+test_expect_success 'check git subtree push <prefix> works' '\n+        git subtree push -P sub1 &&\n+        cd ../shared_projects/subtree1 &&\n+        git checkout master &&\n+        test_cmp ../../parent1/sub1/sub1_file1 sub1_file1 &&\n+        test_cmp ../../parent1/sub1/sub1_file2 sub1_file2\n+'\n test_done\n-- \n1.7.9\n"}]}