From f81d2de1384f8125d992c2ec445c0a7d39857f46 Mon Sep 17 00:00:00 2001 From: Herman van Rink Date: Mon, 7 May 2012 21:18:58 +0200 Subject: [PATCH 1/2] Collected subtree updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These include: * a .gittrees file with meta data * new sub commands (push-all, pull-all, from-submodule, prune, diff, list) * Documentation updates Contributers from the git log on https://github.com/helmo/git-subtree bibendi Carl Fürstenberg Francesco Delfino helmo Herman van Rink James Roper John Yani Jonathan 'Wolf' Rentzsch mhart mhoffman Nate Jones Paul Cartwright Peter Jaros rentzsch sun Thomas Van Doren Vanya at notebook --- contrib/subtree/.gitignore | 3 + contrib/subtree/README | 8 - contrib/subtree/README.md | 39 ++++ contrib/subtree/git-subtree.sh | 207 ++++++++++++++-- contrib/subtree/git-subtree.txt | 47 +++- contrib/subtree/test.sh | 402 ++++++++++++++++++++++++++++++++ 6 files changed, 670 insertions(+), 36 deletions(-) delete mode 100644 contrib/subtree/README create mode 100644 contrib/subtree/README.md create mode 100755 contrib/subtree/test.sh diff --git a/contrib/subtree/.gitignore b/contrib/subtree/.gitignore index 7e77c9d0229e94..30adcd16e71c65 100644 --- a/contrib/subtree/.gitignore +++ b/contrib/subtree/.gitignore @@ -3,3 +3,6 @@ git-subtree.xml git-subtree.1 mainline subproj +submodule* +launchpad +.gittrees diff --git a/contrib/subtree/README b/contrib/subtree/README deleted file mode 100644 index c686b4a69b1257..00000000000000 --- a/contrib/subtree/README +++ /dev/null @@ -1,8 +0,0 @@ - -Please read git-subtree.txt for documentation. - -Please don't contact me using github mail; it's slow, ugly, and worst of -all, redundant. Email me instead at apenwarr@gmail.com and I'll be happy to -help. - -Avery diff --git a/contrib/subtree/README.md b/contrib/subtree/README.md new file mode 100644 index 00000000000000..21f57bbe2e8cce --- /dev/null +++ b/contrib/subtree/README.md @@ -0,0 +1,39 @@ +# git-subtree + +git-subtree allows subprojects to be included within a sub-directory of a main project, optionally including the sub-project's entire history. + +## Installation + +Choose **one** of the following ways to install git-subtree: + +1. Copy the file `git-subtree.sh` to where all other git scripts are stored (`git --exec-path` will tell you this). +1. Run `install.sh` in a Git-enabled shell (that's "Git Bash" on Windows). +1. Run `make install` in a Cygwin-enabled shell. + +Any *one* of these actions makes the `git subtree` command available (note: space instead of dash). + +To additionally install the man page: + + make doc + cp git-subtree.1 /usr/share/man/man1/ + + +## Usage + +See `git-subtree.txt` for details. + +## Known issues + +See `todo`. + +## License + +You may use this software under the terms of the GNU General Public License (GPL), Version 2. + +See `COPYING`. + +## Credits + +Originally authored by Avery Pennarun, + +Please do not contact the author using github mail. Instead, diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh index 920c664bb7c7a4..3108fcce299432 100755 --- a/contrib/subtree/git-subtree.sh +++ b/contrib/subtree/git-subtree.sh @@ -8,11 +8,17 @@ if [ $# -eq 0 ]; then set -- -h fi OPTS_SPEC="\ -git subtree add --prefix= +git subtree add --prefix= git subtree merge --prefix= -git subtree pull --prefix= -git subtree push --prefix= +git subtree pull --prefix= [ [...]] +git subtree pull-all +git subtree push-all +git subtree push --prefix= [ [...]] +git subtree list git subtree split --prefix= +git subtree from-submodule --prefix= +git subtree prune +git subtree diff --prefix= [ [...]] -- h,help show the help q quiet @@ -25,6 +31,8 @@ b,branch= create a new branch from the split subtree ignore-joins ignore prior --rejoin commits onto= try connecting new tree to an existing one rejoin merge the new branch back into HEAD + options for 'push' +f,force use force push options for 'add', 'merge', 'pull' and 'push' squash merge subtree changes as a single commit " @@ -81,8 +89,9 @@ while [ $# -gt 0 ]; do --annotate) annotate="$1"; shift ;; --no-annotate) annotate= ;; -b) branch="$1"; shift ;; - -P) prefix="$1"; shift ;; + -P|--prefix) prefix="$1"; shift ;; -m) message="$1"; shift ;; + -f|--force) force=1 ;; --no-prefix) prefix= ;; --onto) onto="$1"; shift ;; --no-onto) onto= ;; @@ -97,19 +106,26 @@ while [ $# -gt 0 ]; do esac done +# Remove trailing slash +prefix="${prefix%/}"; + command="$1" shift case "$command" in - add|merge|pull) default= ;; - split|push) default="--default HEAD" ;; + add|merge|pull|pull-all|push-all|from-submodule|prune) default= ;; + split|push|diff|list) default="--default HEAD" ;; *) die "Unknown command '$command'" ;; esac -if [ -z "$prefix" ]; then +if [ -z "$prefix" -a "$command" != "pull-all" -a "$command" != "push-all" -a "$command" != "list" -a "$command" != "prune" ]; then die "You must provide the --prefix option." fi case "$command" in + pull-all);; + push-all);; + list);; + prune);; add) [ -e "$prefix" ] && die "prefix '$prefix' already exists." ;; *) [ -e "$prefix" ] || @@ -118,7 +134,7 @@ esac dir="$(dirname "$prefix/.")" -if [ "$command" != "pull" -a "$command" != "add" -a "$command" != "push" ]; then +if [ "$command" != "pull" -a "$command" != "add" -a "$command" != "push" -a "$command" != "pull-all" -a "$command" != "diff" ]; then revs=$(git rev-parse $default --revs-only "$@") || exit $? dirs="$(git rev-parse --no-revs --no-flags "$@")" || exit $? if [ -n "$dirs" ]; then @@ -515,6 +531,14 @@ cmd_add_repository() revs=FETCH_HEAD set -- $revs cmd_add_commit "$@" + + # now add it to our list of repos + git config -f .gittrees --unset subtree.$dir.url + git config -f .gittrees --add subtree.$dir.url $repository + git config -f .gittrees --unset subtree.$dir.path + git config -f .gittrees --add subtree.$dir.path $dir + git config -f .gittrees --unset subtree.$dir.branch + git config -f .gittrees --add subtree.$dir.branch $refspec } cmd_add_commit() @@ -580,7 +604,8 @@ cmd_split() eval "$grl" | while read rev parents; do revcount=$(($revcount + 1)) - say -n "$revcount/$revmax ($createcount) " + say -n "$revcount/$revmax ($createcount) +" debug "Processing commit: $rev" exists=$(cache_get $rev) if [ -n "$exists" ]; then @@ -687,26 +712,168 @@ cmd_merge() cmd_pull() { - ensure_clean - git fetch "$@" || exit $? - revs=FETCH_HEAD - set -- $revs - cmd_merge "$@" + if [ $# -gt 2 ]; then + die "You should provide either or " + fi + if [ -e "$dir" ]; then + ensure_clean + if [ $# -eq 1 ]; then + repository=$(git config -f .gittrees subtree.$prefix.url) + refspec=$1 + elif [ $# -eq 2 ]; then + repository=$1 + refspec=$2 + else + repository=$(git config -f .gittrees subtree.$prefix.url) + refspec=$(git config -f .gittrees subtree.$prefix.branch) + fi + git fetch $repository $refspec || exit $? + echo "git fetch using: " $repository $refspec + revs=FETCH_HEAD + set -- $revs + cmd_merge "$@" + else + die "'$dir' must already exist. Try 'git subtree add'." + fi } +cmd_diff() +{ + if [ -e "$dir" ]; then + if [ $# -eq 1 ]; then + repository=$(git config -f .gittrees subtree.$prefix.url) + refspec=$1 + elif [ $# -eq 2 ]; then + repository=$1 + refspec=$2 + else + repository=$(git config -f .gittrees subtree.$prefix.url) + refspec=$(git config -f .gittrees subtree.$prefix.branch) + fi + # this is ugly, but I don't know of a better way to do it. My git-fu is weak. + # git diff-tree expects a treeish, but I have only a repository and branch name. + # I don't know how to turn that into a treeish without creating a remote. + # Please change this if you know a better way! + tmp_remote=__diff-tmp + git remote rm $tmp_remote > /dev/null 2>&1 + git remote add -t $refspec $tmp_remote $repository > /dev/null + # we fetch as a separate step so we can pass -q (quiet), which isn't an option for "git remote" + # could this instead be "git fetch -q $repository $refspec" and leave aside creating the remote? + # Still need a treeish for the diff-tree command... + git fetch -q $tmp_remote + git diff-tree -p refs/remotes/$tmp_remote/$refspec + git remote rm $tmp_remote > /dev/null 2>&1 + else + die "Cannot resolve directory '$dir'. Please point to an existing subtree directory to diff. Try 'git subtree add' to add a subtree." + fi +} cmd_push() { - if [ $# -ne 2 ]; then - die "You must provide " + if [ $# -gt 2 ]; then + die "You shold provide either or " fi if [ -e "$dir" ]; then - repository=$1 - refspec=$2 - echo "git push using: " $repository $refspec - git push $repository $(git subtree split --prefix=$prefix):refs/heads/$refspec + if [ $# -eq 1 ]; then + repository=$(git config -f .gittrees subtree.$prefix.url) + refspec=$1 + elif [ $# -eq 2 ]; then + repository=$1 + refspec=$2 + else + repository=$(git config -f .gittrees subtree.$prefix.url) + refspec=$(git config -f .gittrees subtree.$prefix.branch) + fi + + push_opts= + if [ "$force" == "1" ]; then + push_opts="$push_opts --force" + fi + + echo "git push using: " $repository $refspec + rev=$(git subtree split --prefix=$prefix) + if [ -n "$rev" ]; then + git push $push_opts $repository $rev:refs/heads/$refspec + else + die "Couldn't push, 'git subtree split' failed." + fi else die "'$dir' must already exist. Try 'git subtree add'." fi } +subtree_list() +{ + git config -f .gittrees -l | grep subtree | grep path | grep -o '=.*' | grep -o '[^=].*' | + while read path; do + repository=$(git config -f .gittrees subtree.$path.url) + refspec=$(git config -f .gittrees subtree.$path.branch) + echo " $path (merged from $repository branch $refspec) " + done +} + +cmd_list() +{ + subtree_list +} + +cmd_from-submodule() +{ + ensure_clean + + local submodule_sha=$(git submodule status $prefix | cut -d ' ' -f 2) + local submodule_orig_repo=$(git config --file .gitmodules submodule.$prefix.url) + + # Remove references to submodule. + git config --remove-section submodule.$prefix + git config --file .gitmodules --remove-section submodule.$prefix + git add .gitmodules + + # Move submodule aside. + local tmp_repo="$(mktemp -d /tmp/git-subtree.XXXXX)" + rm -r $tmp_repo + mv $prefix $tmp_repo + git rm $prefix + + # Commit changes. + git commit -m "Remove '$prefix/' submodule" + + # subtree add from submodule repo. + # TODO: Could be determin HEAD to be a specific branch + cmd_add_repository $tmp_repo HEAD + + # Update .gittrees with the original repo url + git config --file .gittrees --unset subtree.$prefix.url + git config --file .gittrees subtree.$prefix.url $submodule_orig_repo + + # Remove submodule repo. + rm -rf $tmp_repo +} + +cmd_prune() +{ + git config -f .gittrees -l | grep subtree | grep path | grep -o '=.*' | grep -o '[^=].*' | + while read path; do + if [ ! -e "$path" ]; then + echo "pruning $path" + git config -f .gittrees --remove-section subtree.$path + fi + done +} + +cmd_pull-all() +{ + git config -f .gittrees -l | grep subtree | grep path | grep -o '=.*' | grep -o '[^=].*' | + while read path; do + git subtree pull -P $path $(git config -f .gittrees subtree.$path.url) $(git config -f .gittrees subtree.$path.branch) || exit $? + done +} + +cmd_push-all() +{ + git config -f .gittrees -l | grep subtree | grep path | grep -o '=.*' | grep -o '[^=].*' | + while read path; do + git subtree push -P $path $(git config -f .gittrees subtree.$path.url) $(git config -f .gittrees subtree.$path.branch) || exit $? + done +} + "cmd_$command" "$@" diff --git a/contrib/subtree/git-subtree.txt b/contrib/subtree/git-subtree.txt index 0c44fda011bcfd..e23973278f746a 100644 --- a/contrib/subtree/git-subtree.txt +++ b/contrib/subtree/git-subtree.txt @@ -9,12 +9,17 @@ git-subtree - Merge subtrees together and split repository into subtrees SYNOPSIS -------- [verse] -'git subtree' add -P -'git subtree' pull -P -'git subtree' push -P -'git subtree' merge -P -'git subtree' split -P [OPTIONS] [] - +'git subtree' add --prefix= +'git subtree' merge --prefix= +'git subtree' pull --prefix= [ [...]] +'git subtree' pull-all +'git subtree' push-all +'git subtree' push --prefix= [ [...]] +'git subtree' list +'git subtree' split --prefix= +'git subtree' from-submodule --prefix= +'git subtree' prune +'git subtree' diff --prefix= [ [...]] DESCRIPTION ----------- @@ -91,13 +96,22 @@ pull:: Exactly like 'merge', but parallels 'git pull' in that it fetches the given commit from the specified remote repository. - + +pull-all:: + Perform a pull operation on all in .gittrees registered subtrees. + push:: Does a 'split' (see above) using the supplied and then does a 'git push' to push the result to the repository and refspec. This can be used to push your subtree to different branches of the remote repository. +push-all:: + Perform a pull operation on all in .gittrees registered subtrees. + +list:: + Show a list of the in .gittrees registered subtrees + split:: Extract a new, synthetic project history from the history of the subtree. The new history @@ -122,6 +136,16 @@ split:: Note that if you use '--squash' when you merge, you should usually not just '--rejoin' when you split. +from-submodule:: + Convert a git submodule to a subtree. + The module is removed from the .gitmodules file and + the repo contents are integrated as a subtree. + +prune:: + Cleanup .gittrees entries for which the subtree nolonger exists. + +diff:: + TO BE DOCUMENTED OPTIONS ------- @@ -254,6 +278,13 @@ OPTIONS FOR split '--rejoin' when you split, because you don't want the subproject's history to be part of your project anyway. +OPTIONS FOR push +---------------- +-f:: +--force:: + Uses 'git push --force'. + + EXAMPLE 1. Add command ---------------------- @@ -269,7 +300,7 @@ git-extensions repository in ~/git-extensions/: name You can omit the --squash flag, but doing so will increase the number -of commits that are incldued in your local repository. +of commits that are included in your local repository. We now have a ~/git-extensions/git-subtree directory containing code from the master branch of git://github.com/apenwarr/git-subtree.git diff --git a/contrib/subtree/test.sh b/contrib/subtree/test.sh new file mode 100755 index 00000000000000..3dc4b38ebc2e92 --- /dev/null +++ b/contrib/subtree/test.sh @@ -0,0 +1,402 @@ +#!/bin/bash +. shellopts.sh +set -e + +create() +{ + echo "$1" >"$1" + git add "$1" +} + +check() +{ + echo + echo "check:" "$@" + if "$@"; then + echo ok + return 0 + else + echo FAILED + exit 1 + fi +} + +check_not() +{ + echo + echo "check: NOT " "$@" + if "$@"; then + echo FAILED + exit 1 + else + echo ok + return 0 + fi +} + +check_equal() +{ + echo + echo "check a:" "{$1}" + echo " b:" "{$2}" + if [ "$1" = "$2" ]; then + return 0 + else + echo FAILED + exit 1 + fi +} + +fixnl() +{ + t="" + while read x; do + t="$t$x " + done + echo $t +} + +multiline() +{ + while read x; do + set -- $x + for d in "$@"; do + echo "$d" + done + done +} + +undo() +{ + git reset --hard HEAD~ +} + +last_commit_message() +{ + git log --pretty=format:%s -1 +} + +rm -rf mainline subproj +mkdir mainline subproj + +cd subproj +git init + +create sub1 +git commit -m 'sub1' +git branch sub1 +git branch -m master subproj +check true + +create sub2 +git commit -m 'sub2' +git branch sub2 + +create sub3 +git commit -m 'sub3' +git branch sub3 + +cd ../mainline +git init +create main4 +git commit -m 'main4' +git branch -m master mainline +git branch subdir + +git fetch ../subproj sub1 +git branch sub1 FETCH_HEAD + +# check if --message works for add +check_not git subtree merge --prefix=subdir sub1 +check_not git subtree pull --prefix=subdir ../subproj sub1 +git subtree add --prefix=subdir --message="Added subproject" sub1 +check_equal "$(last_commit_message)" "Added subproject" +undo + +# check if --message works as -m and --prefix as -P +git subtree add -P subdir -m "Added subproject using git subtree" sub1 +check_equal "$(last_commit_message)" "Added subproject using git subtree" +undo + +# check if --message works with squash too +git subtree add -P subdir -m "Added subproject with squash" --squash sub1 +check_equal "$(last_commit_message)" "Added subproject with squash" +undo + +git subtree add --prefix=subdir/ FETCH_HEAD +check_equal "$(last_commit_message)" "Add 'subdir/' from commit '$(git rev-parse sub1)'" + +# this shouldn't actually do anything, since FETCH_HEAD is already a parent +git merge -m 'merge -s -ours' -s ours FETCH_HEAD + +create subdir/main-sub5 +git commit -m 'main-sub5' + +create main6 +git commit -m 'main6 boring' + +create subdir/main-sub7 +git commit -m 'main-sub7' + +git fetch ../subproj sub2 +git branch sub2 FETCH_HEAD + +# check if --message works for merge +git subtree merge --prefix=subdir -m "Merged changes from subproject" sub2 +check_equal "$(last_commit_message)" "Merged changes from subproject" +undo + +# check if --message for merge works with squash too +git subtree merge --prefix subdir -m "Merged changes from subproject using squash" --squash sub2 +check_equal "$(last_commit_message)" "Merged changes from subproject using squash" +undo + +git subtree merge --prefix=subdir FETCH_HEAD +git branch pre-split +check_equal "$(last_commit_message)" "Merge commit '$(git rev-parse sub2)' into mainline" + +# Check that prefix argument is required for split (exits with warning and exit status = 1) +! result=$(git subtree split 2>&1) +check_equal "You must provide the --prefix option." "$result" + +# Check that the exists for a split. +! result=$(git subtree split --prefix=non-existent-directory 2>&1) +check_equal "'non-existent-directory' does not exist; use 'git subtree add'" \ + "$result" + +# check if --message works for split+rejoin +spl1=$(git subtree split --annotate='*' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin) +echo "spl1={$spl1}" +git branch spl1 "$spl1" +check_equal "$(last_commit_message)" "Split & rejoin" +undo + +# check split with --branch +git subtree split --annotate='*' --prefix subdir --onto FETCH_HEAD --branch splitbr1 +check_equal "$(git rev-parse splitbr1)" "$spl1" + +# check split with --branch for an existing branch +git branch splitbr2 sub1 +git subtree split --annotate='*' --prefix subdir --onto FETCH_HEAD --branch splitbr2 +check_equal "$(git rev-parse splitbr2)" "$spl1" + +# check split with --branch for an incompatible branch +result=$(git subtree split --prefix subdir --onto FETCH_HEAD --branch subdir || echo "caught error") +check_equal "$result" "caught error" + + +git subtree split --annotate='*' --prefix subdir --onto FETCH_HEAD --rejoin +check_equal "$(last_commit_message)" "Split 'subdir/' into commit '$spl1'" + +create subdir/main-sub8 +git commit -m 'main-sub8' + +cd ../subproj +git fetch ../mainline spl1 +git branch spl1 FETCH_HEAD +git merge FETCH_HEAD + +create sub9 +git commit -m 'sub9' + +cd ../mainline +split2=$(git subtree split --annotate='*' --prefix subdir/ --rejoin) +git branch split2 "$split2" + +create subdir/main-sub10 +git commit -m 'main-sub10' + +spl3=$(git subtree split --annotate='*' --prefix subdir --rejoin) +git branch spl3 "$spl3" + +cd ../subproj +git fetch ../mainline spl3 +git branch spl3 FETCH_HEAD +git merge FETCH_HEAD +git branch subproj-merge-spl3 + +chkm="main4 main6" +chkms="main-sub10 main-sub5 main-sub7 main-sub8" +chkms_sub=$(echo $chkms | multiline | sed 's,^,subdir/,' | fixnl) +chks="sub1 sub2 sub3 sub9" +chks_sub=$(echo $chks | multiline | sed 's,^,subdir/,' | fixnl) + +# make sure exactly the right set of files ends up in the subproj +subfiles=$(git ls-files | fixnl) +check_equal "$subfiles" "$chkms $chks" + +# make sure the subproj history *only* contains commits that affect the subdir. +allchanges=$(git log --name-only --pretty=format:'' | sort | fixnl) +check_equal "$allchanges" "$chkms $chks" + +cd ../mainline +git fetch ../subproj subproj-merge-spl3 +git branch subproj-merge-spl3 FETCH_HEAD +git subtree pull --prefix=subdir ../subproj subproj-merge-spl3 + +# make sure exactly the right set of files ends up in the mainline +mainfiles=$(git ls-files | fixnl) +check_equal "$mainfiles" "$chkm $chkms_sub $chks_sub" + +# make sure each filename changed exactly once in the entire history. +# 'main-sub??' and '/subdir/main-sub??' both change, because those are the +# changes that were split into their own history. And 'subdir/sub??' never +# change, since they were *only* changed in the subtree branch. +allchanges=$(git log --name-only --pretty=format:'' | sort | fixnl) +check_equal "$allchanges" "$(echo $chkms $chkm $chks $chkms_sub | multiline | sort | fixnl)" + +# make sure the --rejoin commits never make it into subproj +check_equal "$(git log --pretty=format:'%s' HEAD^2 | grep -i split)" "" + +# make sure no 'git subtree' tagged commits make it into subproj. (They're +# meaningless to subproj since one side of the merge refers to the mainline) +check_equal "$(git log --pretty=format:'%s%n%b' HEAD^2 | grep 'git-subtree.*:')" "" + + +# check if split can find proper base without --onto +# prepare second pair of repositories +mkdir test2 +cd test2 + +mkdir main +cd main +git init +create main1 +git commit -m "main1" + +cd .. +mkdir sub +cd sub +git init +create sub2 +git commit -m "sub2" + +cd ../main +git fetch ../sub master +git branch sub2 FETCH_HEAD +git subtree add --prefix subdir sub2 + +cd ../sub +create sub3 +git commit -m "sub3" + +cd ../main +git fetch ../sub master +git branch sub3 FETCH_HEAD +git subtree merge --prefix subdir sub3 + +create subdir/main-sub4 +git commit -m "main-sub4" +git subtree split --prefix subdir --branch mainsub4 + +# at this point, the new commit's parent should be sub3 +# if it's not, something went wrong (the "newparent" of "master~" commit should have been sub3, +# but it wasn't, because it's cache was not set to itself) +check_equal "$(git log --pretty=format:%P -1 mainsub4)" "$(git rev-parse sub3)" + +mkdir subdir2 +create subdir2/main-sub5 +git commit -m "main-sub5" +git subtree split --prefix subdir2 --branch mainsub5 + +# also test that we still can split out an entirely new subtree +# if the parent of the first commit in the tree isn't empty, +# then the new subtree has accidently been attached to something +check_equal "$(git log --pretty=format:%P -1 mainsub5)" "" + + +# make sure no patch changes more than one file. The original set of commits +# changed only one file each. A multi-file change would imply that we pruned +# commits too aggressively. +joincommits() +{ + commit= + all= + while read x y; do + echo "{$x}" >&2 + if [ -z "$x" ]; then + continue + elif [ "$x" = "commit:" ]; then + if [ -n "$commit" ]; then + echo "$commit $all" + all= + fi + commit="$y" + else + all="$all $y" + fi + done + echo "$commit $all" +} +x= +git log --pretty=format:'commit: %H' | joincommits | +( while read commit a b; do + echo "Verifying commit $commit" + check_equal "$b" "" + x=1 + done + check_equal "$x" 1 +) || exit 1 + +# Return to mainline +cd ../.. + + +# from-submodule + +make_submodule() +{ + cd .. + local prefix=$1 + + local submodule_path=$(mktemp -d submodule.XXX) + cd $submodule_path + local submodule_abs_path=$(pwd) + + git init > /dev/null + create "sub-file" + git commit -m "sub-commit" > /dev/null + local submodule_sha=$(git rev-parse HEAD) + cd ../mainline + + git submodule add $submodule_abs_path $prefix > /dev/null + git submodule update --init > /dev/null + git commit -m "Added $prefix." > /dev/null + + echo $submodule_sha +} + +# Unfortunately the following submodule tests were broken when I found them: +# +# subA_sha=$(make_submodule submodules/subA) +# +# git subtree from-submodule --prefix submodules/subA +# +# check_equal "$(last_commit_message)" "Add 'submodules/subA/' from commit '${subA_sha}'" +# # Submodule should be gone. +# check_equal "$(git submodule status)" "" +# +# rm -rf ../submodule.* + + +# Simple subtree add-and-immediately-push test: +echo +echo '*** TESTING add subtree and immediately push' +echo + +cd .. # return to test dir top level +rm -rf mainline2 +git init mainline2 +cd mainline2 +date > mainline2.txt && git add . && git commit -m "initial mainline2 commit" +git subtree add --prefix=subproj ../subproj subproj +git subtree push --prefix=subproj ../subproj subproj +result=$? +cd .. +check_equal 0 "$result" + +echo +echo '*** PASSED add subtree and immediately push' +echo + +echo '*** ALL TESTS PASSED ***' From 40295daf1f53bb3284f79367e92ecaedc0b87145 Mon Sep 17 00:00:00 2001 From: Stephan Leicht Vogt Date: Thu, 23 Aug 2012 18:51:20 +0200 Subject: [PATCH 2/2] pull-all and push-all do squash if option is set --- contrib/subtree/git-subtree.sh | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh index 3108fcce299432..d46e19b0742d91 100755 --- a/contrib/subtree/git-subtree.sh +++ b/contrib/subtree/git-subtree.sh @@ -864,7 +864,11 @@ cmd_pull-all() { git config -f .gittrees -l | grep subtree | grep path | grep -o '=.*' | grep -o '[^=].*' | while read path; do - git subtree pull -P $path $(git config -f .gittrees subtree.$path.url) $(git config -f .gittrees subtree.$path.branch) || exit $? + if [ -n "$squash" ]; then + git subtree pull -P $path --squash $(git config -f .gittrees subtree.$path.url) $(git config -f .gittrees subtree.$path.branch) || exit $? + else + git subtree pull -P $path $(git config -f .gittrees subtree.$path.url) $(git config -f .gittrees subtree.$path.branch) || exit $? + fi done } @@ -872,7 +876,11 @@ cmd_push-all() { git config -f .gittrees -l | grep subtree | grep path | grep -o '=.*' | grep -o '[^=].*' | while read path; do - git subtree push -P $path $(git config -f .gittrees subtree.$path.url) $(git config -f .gittrees subtree.$path.branch) || exit $? + if [ -n "$squash" ]; then + git subtree push -P $path --squash $(git config -f .gittrees subtree.$path.url) $(git config -f .gittrees subtree.$path.branch) || exit $? + else + git subtree push -P $path $(git config -f .gittrees subtree.$path.url) $(git config -f .gittrees subtree.$path.branch) || exit $? + fi done }