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: " "$@"
debug "opts: {$*}"
debug
-cache_setup()
-{
+cache_setup () {
cachedir="$GIT_DIR/subtree-cache/$$"
rm -rf "$cachedir" ||
die "Can't delete old cachedir: $cachedir"
debug "Using cachedir: $cachedir" >&2
}
-cache_get()
-{
+cache_get () {
for oldrev in "$@"
do
if test -r "$cachedir/$oldrev"
done
}
-cache_miss()
-{
+cache_miss () {
for oldrev in "$@"
do
if ! test -r "$cachedir/$oldrev"
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" &&
echo "$newrev" >"$cachedir/$oldrev"
}
-rev_exists()
-{
+rev_exists () {
if git rev-parse "$1" >/dev/null 2>&1
then
return 0
fi
}
-rev_is_descendant_of_branch()
-{
+rev_is_descendant_of_branch () {
newrev="$1"
branch="$2"
branch_hash=$(git rev-parse "$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"
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
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
) || die "Can't copy commit $1"
}
-add_msg()
-{
+add_msg () {
dir="$1"
latest_old="$2"
latest_new="$3"
EOF
}
-add_squashed_msg()
-{
+add_squashed_msg () {
if test -n "$message"
then
echo "$message"
fi
}
-rejoin_msg()
-{
+rejoin_msg () {
dir="$1"
latest_old="$2"
latest_new="$3"
EOF
}
-squash_msg()
-{
+squash_msg () {
dir="$1"
oldsub="$2"
newsub="$3"
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
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" |
done
}
-tree_changed()
-{
+tree_changed () {
tree=$1
shift
if test $# -ne 1
fi
}
-new_squash_commit()
-{
+new_squash_commit () {
old="$1"
oldsub="$2"
newsub="$3"
fi
}
-copy_or_skip()
-{
+copy_or_skip () {
rev="$1"
tree="$2"
newparents="$3"
nonidentical=
p=
gotparents=
+ copycommit=
for parent in $newparents
do
ptree=$(toptree_for_commit $parent) || exit $?
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
fi
done
- copycommit=
if test -n "$identical" && test -n "$nonidentical"
then
extras=$(git rev-list --count $identical..$nonidentical)
fi
}
-ensure_clean()
-{
+ensure_clean () {
if ! git diff-index HEAD --exit-code --quiet 2>&1
then
die "Working tree has modifications. Cannot add."
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."
fi
}
-cmd_add_repository()
-{
+cmd_add_repository () {
echo "git fetch" "$@"
repository=$1
refspec=$2
cmd_add_commit "$@"
}
-cmd_add_commit()
-{
+cmd_add_commit () {
revs=$(git rev-parse $default --revs-only "$@") || exit $?
set -- $revs
rev="$1"
say "Added dir '$dir'"
}
-cmd_split()
-{
+cmd_split () {
debug "Splitting $dir..."
cache_setup || exit $?
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.
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)
exit 0
}
-cmd_merge()
-{
+cmd_merge () {
revs=$(git rev-parse $default --revs-only "$@") || exit $?
ensure_clean
fi
}
-cmd_pull()
-{
+cmd_pull () {
if test $# -ne 2
then
die "You must provide <repository> <ref>"
cmd_merge "$@"
}
-cmd_push()
-{
+cmd_push () {
if test $# -ne 2
then
die "You must provide <repository> <ref>"