Typo when searching for existing splits.
[gitweb.git] / git-subtree.sh
index 0c7b755cf1b779a040dde44c1ec850531ba813f4..8b797dfc23c8cfe7c5f7f4dea877be0dda45c87e 100755 (executable)
@@ -8,12 +8,16 @@ if [ $# -eq 0 ]; then
     set -- -h
 fi
 OPTS_SPEC="\
-git subtree split [options...] <--prefix=prefix> <commit...> -- <path>
-git subtree merge 
+git subtree add --prefix=<prefix> <commit>
+git subtree split [options...] --prefix=<prefix> <commit...>
+git subtree merge --prefix=<prefix> <commit>
+git subtree pull  --prefix=<prefix> <repository> <refspec...>
 --
 h,help        show the help
 q             quiet
 prefix=       the name of the subdir to split out
+ options for 'split'
+annotate=     add a prefix to commit message of new commits
 onto=         try connecting new tree to an existing one
 rejoin        merge the new branch back into HEAD
 ignore-joins  ignore prior --rejoin commits
@@ -27,6 +31,7 @@ command=
 onto=
 rejoin=
 ignore_joins=
+annotate=
 
 debug()
 {
@@ -52,6 +57,8 @@ while [ $# -gt 0 ]; do
        shift
        case "$opt" in
                -q) quiet=1 ;;
+               --annotate) annotate="$1"; shift ;;
+               --no-annotate) annotate= ;;
                --prefix) prefix="$1"; shift ;;
                --no-prefix) prefix= ;;
                --onto) onto="$1"; shift ;;
@@ -67,26 +74,30 @@ done
 command="$1"
 shift
 case "$command" in
-       split|merge) ;;
+       add|merge|pull) default= ;;
+       split) default="--default HEAD" ;;
        *) die "Unknown command '$command'" ;;
 esac
 
-revs=$(git rev-parse --default HEAD --revs-only "$@") || exit $?
-
 if [ -z "$prefix" ]; then
        die "You must provide the --prefix option."
 fi
 dir="$prefix"
 
-dirs="$(git rev-parse --no-revs --no-flags "$@")" || exit $?
-if [ -n "$dirs" ]; then
-       die "Error: Use --prefix instead of bare filenames."
+if [ "$command" != "pull" ]; then
+       revs=$(git rev-parse $default --revs-only "$@") || exit $?
+       dirs="$(git rev-parse --no-revs --no-flags "$@")" || exit $?
+       if [ -n "$dirs" ]; then
+               die "Error: Use --prefix instead of bare filenames."
+       fi
 fi
 
 debug "command: {$command}"
 debug "quiet: {$quiet}"
 debug "revs: {$revs}"
 debug "dir: {$dir}"
+debug "opts: {$*}"
+debug
 
 cache_setup()
 {
@@ -118,13 +129,23 @@ cache_set()
        echo "$newrev" >"$cachedir/$oldrev"
 }
 
+# if a commit doesn't have a parent, this might not work.  But we only want
+# to remove the parent from the rev-list, and since it doesn't exist, it won't
+# be there anyway, so do nothing in that case.
+try_remove_previous()
+{
+       if git rev-parse "$1^" >/dev/null 2>&1; then
+               echo "^$1^"
+       fi
+}
+
 find_existing_splits()
 {
        debug "Looking for prior splits..."
        dir="$1"
        revs="$2"
        git log --grep="^git-subtree-dir: $dir\$" \
-               --pretty=format:'%s%n%n%b%nEND' "$revs" |
+               --pretty=format:'%s%n%n%b%nEND' $revs |
        while read a b junk; do
                case "$a" in
                        git-subtree-mainline:) main="$b" ;;
@@ -133,7 +154,8 @@ find_existing_splits()
                                if [ -n "$main" -a -n "$sub" ]; then
                                        debug "  Prior: $main -> $sub"
                                        cache_set $main $sub
-                                       echo "^$main^ ^$sub^"
+                                       try_remove_previous "$main"
+                                       try_remove_previous "$sub"
                                        main=
                                        sub=
                                fi
@@ -160,11 +182,25 @@ copy_commit()
                        GIT_COMMITTER_NAME \
                        GIT_COMMITTER_EMAIL \
                        GIT_COMMITTER_DATE
-               (echo -n '*'; cat ) |  # FIXME
+               (echo -n "$annotate"; cat ) |
                git commit-tree "$2" $3  # reads the rest of stdin
        ) || die "Can't copy commit $1"
 }
 
+add_msg()
+{
+       dir="$1"
+       latest_old="$2"
+       latest_new="$3"
+       cat <<-EOF
+               Add '$dir/' from commit '$latest_new'
+               
+               git-subtree-dir: $dir
+               git-subtree-mainline: $latest_old
+               git-subtree-split: $latest_new
+       EOF
+}
+
 merge_msg()
 {
        dir="$1"
@@ -241,6 +277,45 @@ copy_or_skip()
        fi
 }
 
+ensure_clean()
+{
+       if ! git diff-index HEAD --exit-code --quiet; then
+               die "Working tree has modifications.  Cannot add."
+       fi
+       if ! git diff-index --cached HEAD --exit-code --quiet; then
+               die "Index has modifications.  Cannot add."
+       fi
+}
+
+cmd_add()
+{
+       if [ -e "$dir" ]; then
+               die "'$dir' already exists.  Cannot add."
+       fi
+       ensure_clean
+       
+       set -- $revs
+       if [ $# -ne 1 ]; then
+               die "You must provide exactly one revision.  Got: '$revs'"
+       fi
+       rev="$1"
+       
+       debug "Adding $dir as '$rev'..."
+       git read-tree --prefix="$dir" $rev || exit $?
+       git checkout "$dir" || exit $?
+       tree=$(git write-tree) || exit $?
+       
+       headrev=$(git rev-parse HEAD) || exit $?
+       if [ -n "$headrev" -a "$headrev" != "$rev" ]; then
+               headp="-p $headrev"
+       else
+               headp=
+       fi
+       commit=$(add_msg "$dir" "$headrev" "$rev" |
+                git commit-tree $tree $headp -p "$rev") || exit $?
+       git reset "$commit" || exit $?
+}
+
 cmd_split()
 {
        debug "Splitting $dir..."
@@ -307,7 +382,22 @@ cmd_split()
 
 cmd_merge()
 {
-       die "merge command not implemented yet"
+       ensure_clean
+       
+       set -- $revs
+       if [ $# -ne 1 ]; then
+               die "You must provide exactly one revision.  Got: '$revs'"
+       fi
+       rev="$1"
+       
+       git merge -s subtree $rev
+}
+
+cmd_pull()
+{
+       ensure_clean
+       set -x
+       git pull -s subtree "$@"
 }
 
-"cmd_$command"
+"cmd_$command" "$@"