tests: add a special setup where rebase.useBuiltin is off
[gitweb.git] / contrib / subtree / git-subtree.sh
index de49eb08f3d5b243ad8f84c06806e1c1a240ce4d..147201dc6c5786c9cfedbbbff0df0b1dced171db 100755 (executable)
@@ -49,32 +49,28 @@ squash=
 message=
 prefix=
 
-debug()
-{
+debug () {
        if test -n "$debug"
        then
                printf "%s\n" "$*" >&2
        fi
 }
 
-say()
-{
+say () {
        if test -z "$quiet"
        then
                printf "%s\n" "$*" >&2
        fi
 }
 
-progress()
-{
+progress () {
        if test -z "$quiet"
        then
                printf "%s\r" "$*" >&2
        fi
 }
 
-assert()
-{
+assert () {
        if ! "$@"
        then
                die "assertion failed: " "$@"
@@ -202,8 +198,7 @@ debug "dir: {$dir}"
 debug "opts: {$*}"
 debug
 
-cache_setup()
-{
+cache_setup () {
        cachedir="$GIT_DIR/subtree-cache/$$"
        rm -rf "$cachedir" ||
                die "Can't delete old cachedir: $cachedir"
@@ -214,8 +209,7 @@ cache_setup()
        debug "Using cachedir: $cachedir" >&2
 }
 
-cache_get()
-{
+cache_get () {
        for oldrev in "$@"
        do
                if test -r "$cachedir/$oldrev"
@@ -226,8 +220,7 @@ cache_get()
        done
 }
 
-cache_miss()
-{
+cache_miss () {
        for oldrev in "$@"
        do
                if ! test -r "$cachedir/$oldrev"
@@ -237,25 +230,24 @@ cache_miss()
        done
 }
 
-check_parents()
-{
-       missed=$(cache_miss "$@")
+check_parents () {
+       missed=$(cache_miss "$1")
+       local indent=$(($2 + 1))
        for miss in $missed
        do
                if ! test -r "$cachedir/notree/$miss"
                then
                        debug "  incorrect order: $miss"
+                       process_split_commit "$miss" "" "$indent"
                fi
        done
 }
 
-set_notree()
-{
+set_notree () {
        echo "1" > "$cachedir/notree/$1"
 }
 
-cache_set()
-{
+cache_set () {
        oldrev="$1"
        newrev="$2"
        if test "$oldrev" != "latest_old" &&
@@ -267,8 +259,7 @@ cache_set()
        echo "$newrev" >"$cachedir/$oldrev"
 }
 
-rev_exists()
-{
+rev_exists () {
        if git rev-parse "$1" >/dev/null 2>&1
        then
                return 0
@@ -277,8 +268,7 @@ rev_exists()
        fi
 }
 
-rev_is_descendant_of_branch()
-{
+rev_is_descendant_of_branch () {
        newrev="$1"
        branch="$2"
        branch_hash=$(git rev-parse "$branch")
@@ -295,23 +285,21 @@ rev_is_descendant_of_branch()
 # 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()
-{
+try_remove_previous () {
        if rev_exists "$1^"
        then
                echo "^$1^"
        fi
 }
 
-find_latest_squash()
-{
+find_latest_squash () {
        debug "Looking for latest squash ($dir)..."
        dir="$1"
        sq=
        main=
        sub=
        git log --grep="^git-subtree-dir: $dir/*\$" \
-               --pretty=format:'START %H%n%s%n%n%b%nEND%n' HEAD |
+               --no-show-signature --pretty=format:'START %H%n%s%n%n%b%nEND%n' HEAD |
        while read a b junk
        do
                debug "$a $b $junk"
@@ -348,15 +336,19 @@ find_latest_squash()
        done
 }
 
-find_existing_splits()
-{
+find_existing_splits () {
        debug "Looking for prior splits..."
        dir="$1"
        revs="$2"
        main=
        sub=
-       git log --grep="^git-subtree-dir: $dir/*\$" \
-               --pretty=format:'START %H%n%s%n%n%b%nEND%n' $revs |
+       local grep_format="^git-subtree-dir: $dir/*\$"
+       if test -n "$ignore_joins"
+       then
+               grep_format="^Add '$dir/' from commit '"
+       fi
+       git log --grep="$grep_format" \
+               --no-show-signature --pretty=format:'START %H%n%s%n%n%b%nEND%n' $revs |
        while read a b junk
        do
                case "$a" in
@@ -393,12 +385,11 @@ find_existing_splits()
        done
 }
 
-copy_commit()
-{
+copy_commit () {
        # We're going to set some environment vars here, so
        # do it in a subshell to get rid of them safely later
        debug copy_commit "{$1}" "{$2}" "{$3}"
-       git log -1 --pretty=format:'%an%n%ae%n%aD%n%cn%n%ce%n%cD%n%B' "$1" |
+       git log -1 --no-show-signature --pretty=format:'%an%n%ae%n%aD%n%cn%n%ce%n%cD%n%B' "$1" |
        (
                read GIT_AUTHOR_NAME
                read GIT_AUTHOR_EMAIL
@@ -420,8 +411,7 @@ copy_commit()
        ) || die "Can't copy commit $1"
 }
 
-add_msg()
-{
+add_msg () {
        dir="$1"
        latest_old="$2"
        latest_new="$3"
@@ -440,8 +430,7 @@ add_msg()
        EOF
 }
 
-add_squashed_msg()
-{
+add_squashed_msg () {
        if test -n "$message"
        then
                echo "$message"
@@ -450,8 +439,7 @@ add_squashed_msg()
        fi
 }
 
-rejoin_msg()
-{
+rejoin_msg () {
        dir="$1"
        latest_old="$2"
        latest_new="$3"
@@ -470,8 +458,7 @@ rejoin_msg()
        EOF
 }
 
-squash_msg()
-{
+squash_msg () {
        dir="$1"
        oldsub="$2"
        newsub="$3"
@@ -482,8 +469,8 @@ squash_msg()
                oldsub_short=$(git rev-parse --short "$oldsub")
                echo "Squashed '$dir/' changes from $oldsub_short..$newsub_short"
                echo
-               git log --pretty=tformat:'%h %s' "$oldsub..$newsub"
-               git log --pretty=tformat:'REVERT: %h %s' "$newsub..$oldsub"
+               git log --no-show-signature --pretty=tformat:'%h %s' "$oldsub..$newsub"
+               git log --no-show-signature --pretty=tformat:'REVERT: %h %s' "$newsub..$oldsub"
        else
                echo "Squashed '$dir/' content from commit $newsub_short"
        fi
@@ -493,14 +480,12 @@ squash_msg()
        echo "git-subtree-split: $newsub"
 }
 
-toptree_for_commit()
-{
+toptree_for_commit () {
        commit="$1"
-       git log -1 --pretty=format:'%T' "$commit" -- || exit $?
+       git rev-parse --verify "$commit^{tree}" || exit $?
 }
 
-subtree_for_commit()
-{
+subtree_for_commit () {
        commit="$1"
        dir="$2"
        git ls-tree "$commit" -- "$dir" |
@@ -514,8 +499,7 @@ subtree_for_commit()
        done
 }
 
-tree_changed()
-{
+tree_changed () {
        tree=$1
        shift
        if test $# -ne 1
@@ -532,8 +516,7 @@ tree_changed()
        fi
 }
 
-new_squash_commit()
-{
+new_squash_commit () {
        old="$1"
        oldsub="$2"
        newsub="$3"
@@ -548,8 +531,7 @@ new_squash_commit()
        fi
 }
 
-copy_or_skip()
-{
+copy_or_skip () {
        rev="$1"
        tree="$2"
        newparents="$3"
@@ -559,6 +541,7 @@ copy_or_skip()
        nonidentical=
        p=
        gotparents=
+       copycommit=
        for parent in $newparents
        do
                ptree=$(toptree_for_commit $parent) || exit $?
@@ -566,7 +549,24 @@ copy_or_skip()
                if test "$ptree" = "$tree"
                then
                        # an identical parent could be used in place of this rev.
-                       identical="$parent"
+                       if test -n "$identical"
+                       then
+                               # if a previous identical parent was found, check whether
+                               # one is already an ancestor of the other
+                               mergebase=$(git merge-base $identical $parent)
+                               if test "$identical" = "$mergebase"
+                               then
+                                       # current identical commit is an ancestor of parent
+                                       identical="$parent"
+                               elif test "$parent" != "$mergebase"
+                               then
+                                       # no common history; commit must be copied
+                                       copycommit=1
+                               fi
+                       else
+                               # first identical parent detected
+                               identical="$parent"
+                       fi
                else
                        nonidentical="$parent"
                fi
@@ -589,7 +589,6 @@ copy_or_skip()
                fi
        done
 
-       copycommit=
        if test -n "$identical" && test -n "$nonidentical"
        then
                extras=$(git rev-list --count $identical..$nonidentical)
@@ -607,8 +606,7 @@ copy_or_skip()
        fi
 }
 
-ensure_clean()
-{
+ensure_clean () {
        if ! git diff-index HEAD --exit-code --quiet 2>&1
        then
                die "Working tree has modifications.  Cannot add."
@@ -619,14 +617,64 @@ ensure_clean()
        fi
 }
 
-ensure_valid_ref_format()
-{
+ensure_valid_ref_format () {
        git check-ref-format "refs/heads/$1" ||
                die "'$1' does not look like a ref"
 }
 
-cmd_add()
-{
+process_split_commit () {
+       local rev="$1"
+       local parents="$2"
+       local indent=$3
+
+       if test $indent -eq 0
+       then
+               revcount=$(($revcount + 1))
+       else
+               # processing commit without normal parent information;
+               # fetch from repo
+               parents=$(git rev-parse "$rev^@")
+               extracount=$(($extracount + 1))
+       fi
+
+       progress "$revcount/$revmax ($createcount) [$extracount]"
+
+       debug "Processing commit: $rev"
+       exists=$(cache_get "$rev")
+       if test -n "$exists"
+       then
+               debug "  prior: $exists"
+               return
+       fi
+       createcount=$(($createcount + 1))
+       debug "  parents: $parents"
+       check_parents "$parents" "$indent"
+       newparents=$(cache_get $parents)
+       debug "  newparents: $newparents"
+
+       tree=$(subtree_for_commit "$rev" "$dir")
+       debug "  tree is: $tree"
+
+       # ugly.  is there no better way to tell if this is a subtree
+       # vs. a mainline commit?  Does it matter?
+       if test -z "$tree"
+       then
+               set_notree "$rev"
+               if test -n "$newparents"
+               then
+                       cache_set "$rev" "$rev"
+               fi
+               return
+       fi
+
+       newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $?
+       debug "  newrev is: $newrev"
+       cache_set "$rev" "$newrev"
+       cache_set latest_new "$newrev"
+       cache_set latest_old "$rev"
+}
+
+cmd_add () {
        if test -e "$dir"
        then
                die "'$dir' already exists.  Cannot add."
@@ -657,8 +705,7 @@ cmd_add()
        fi
 }
 
-cmd_add_repository()
-{
+cmd_add_repository () {
        echo "git fetch" "$@"
        repository=$1
        refspec=$2
@@ -668,8 +715,7 @@ cmd_add_repository()
        cmd_add_commit "$@"
 }
 
-cmd_add_commit()
-{
+cmd_add_commit () {
        revs=$(git rev-parse $default --revs-only "$@") || exit $?
        set -- $revs
        rev="$1"
@@ -702,8 +748,7 @@ cmd_add_commit()
        say "Added dir '$dir'"
 }
 
-cmd_split()
-{
+cmd_split () {
        debug "Splitting $dir..."
        cache_setup || exit $?
 
@@ -720,12 +765,7 @@ cmd_split()
                done
        fi
 
-       if test -n "$ignore_joins"
-       then
-               unrevs=
-       else
-               unrevs="$(find_existing_splits "$dir" "$revs")"
-       fi
+       unrevs="$(find_existing_splits "$dir" "$revs")"
 
        # We can't restrict rev-list to only $dir here, because some of our
        # parents have the $dir contents the root, and those won't match.
@@ -734,45 +774,11 @@ cmd_split()
        revmax=$(eval "$grl" | wc -l)
        revcount=0
        createcount=0
+       extracount=0
        eval "$grl" |
        while read rev parents
        do
-               revcount=$(($revcount + 1))
-               progress "$revcount/$revmax ($createcount)"
-               debug "Processing commit: $rev"
-               exists=$(cache_get "$rev")
-               if test -n "$exists"
-               then
-                       debug "  prior: $exists"
-                       continue
-               fi
-               createcount=$(($createcount + 1))
-               debug "  parents: $parents"
-               newparents=$(cache_get $parents)
-               debug "  newparents: $newparents"
-
-               tree=$(subtree_for_commit "$rev" "$dir")
-               debug "  tree is: $tree"
-
-               check_parents $parents
-
-               # ugly.  is there no better way to tell if this is a subtree
-               # vs. a mainline commit?  Does it matter?
-               if test -z "$tree"
-               then
-                       set_notree "$rev"
-                       if test -n "$newparents"
-                       then
-                               cache_set "$rev" "$rev"
-                       fi
-                       continue
-               fi
-
-               newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $?
-               debug "  newrev is: $newrev"
-               cache_set "$rev" "$newrev"
-               cache_set latest_new "$newrev"
-               cache_set latest_old "$rev"
+               process_split_commit "$rev" "$parents" 0
        done || exit $?
 
        latest_new=$(cache_get latest_new)
@@ -810,8 +816,7 @@ cmd_split()
        exit 0
 }
 
-cmd_merge()
-{
+cmd_merge () {
        revs=$(git rev-parse $default --revs-only "$@") || exit $?
        ensure_clean
 
@@ -862,8 +867,7 @@ cmd_merge()
        fi
 }
 
-cmd_pull()
-{
+cmd_pull () {
        if test $# -ne 2
        then
                die "You must provide <repository> <ref>"
@@ -876,8 +880,7 @@ cmd_pull()
        cmd_merge "$@"
 }
 
-cmd_push()
-{
+cmd_push () {
        if test $# -ne 2
        then
                die "You must provide <repository> <ref>"