Skip to content

Commit e7b0737

Browse files
committed
Merge branch 'rs/subtree-fixes'
Various subtree fixes. * rs/subtree-fixes: subtree: performance improvement for finding unexpected parent commits subtree: improve decision on merges kept in split subtree: use commits before rejoins for splits subtree: make --ignore-joins pay attention to adds subtree: refactor split of a commit into standalone method
2 parents c4df23f + 19ad68d commit e7b0737

File tree

1 file changed

+83
-46
lines changed

1 file changed

+83
-46
lines changed

contrib/subtree/git-subtree.sh

Lines changed: 83 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -231,12 +231,14 @@ cache_miss () {
231231
}
232232

233233
check_parents () {
234-
missed=$(cache_miss "$@")
234+
missed=$(cache_miss "$1")
235+
local indent=$(($2 + 1))
235236
for miss in $missed
236237
do
237238
if ! test -r "$cachedir/notree/$miss"
238239
then
239240
debug " incorrect order: $miss"
241+
process_split_commit "$miss" "" "$indent"
240242
fi
241243
done
242244
}
@@ -340,7 +342,12 @@ find_existing_splits () {
340342
revs="$2"
341343
main=
342344
sub=
343-
git log --grep="^git-subtree-dir: $dir/*\$" \
345+
local grep_format="^git-subtree-dir: $dir/*\$"
346+
if test -n "$ignore_joins"
347+
then
348+
grep_format="^Add '$dir/' from commit '"
349+
fi
350+
git log --grep="$grep_format" \
344351
--no-show-signature --pretty=format:'START %H%n%s%n%n%b%nEND%n' $revs |
345352
while read a b junk
346353
do
@@ -534,14 +541,32 @@ copy_or_skip () {
534541
nonidentical=
535542
p=
536543
gotparents=
544+
copycommit=
537545
for parent in $newparents
538546
do
539547
ptree=$(toptree_for_commit $parent) || exit $?
540548
test -z "$ptree" && continue
541549
if test "$ptree" = "$tree"
542550
then
543551
# an identical parent could be used in place of this rev.
544-
identical="$parent"
552+
if test -n "$identical"
553+
then
554+
# if a previous identical parent was found, check whether
555+
# one is already an ancestor of the other
556+
mergebase=$(git merge-base $identical $parent)
557+
if test "$identical" = "$mergebase"
558+
then
559+
# current identical commit is an ancestor of parent
560+
identical="$parent"
561+
elif test "$parent" != "$mergebase"
562+
then
563+
# no common history; commit must be copied
564+
copycommit=1
565+
fi
566+
else
567+
# first identical parent detected
568+
identical="$parent"
569+
fi
545570
else
546571
nonidentical="$parent"
547572
fi
@@ -564,7 +589,6 @@ copy_or_skip () {
564589
fi
565590
done
566591

567-
copycommit=
568592
if test -n "$identical" && test -n "$nonidentical"
569593
then
570594
extras=$(git rev-list --count $identical..$nonidentical)
@@ -598,6 +622,58 @@ ensure_valid_ref_format () {
598622
die "'$1' does not look like a ref"
599623
}
600624

625+
process_split_commit () {
626+
local rev="$1"
627+
local parents="$2"
628+
local indent=$3
629+
630+
if test $indent -eq 0
631+
then
632+
revcount=$(($revcount + 1))
633+
else
634+
# processing commit without normal parent information;
635+
# fetch from repo
636+
parents=$(git rev-parse "$rev^@")
637+
extracount=$(($extracount + 1))
638+
fi
639+
640+
progress "$revcount/$revmax ($createcount) [$extracount]"
641+
642+
debug "Processing commit: $rev"
643+
exists=$(cache_get "$rev")
644+
if test -n "$exists"
645+
then
646+
debug " prior: $exists"
647+
return
648+
fi
649+
createcount=$(($createcount + 1))
650+
debug " parents: $parents"
651+
check_parents "$parents" "$indent"
652+
newparents=$(cache_get $parents)
653+
debug " newparents: $newparents"
654+
655+
tree=$(subtree_for_commit "$rev" "$dir")
656+
debug " tree is: $tree"
657+
658+
# ugly. is there no better way to tell if this is a subtree
659+
# vs. a mainline commit? Does it matter?
660+
if test -z "$tree"
661+
then
662+
set_notree "$rev"
663+
if test -n "$newparents"
664+
then
665+
cache_set "$rev" "$rev"
666+
fi
667+
return
668+
fi
669+
670+
newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $?
671+
debug " newrev is: $newrev"
672+
cache_set "$rev" "$newrev"
673+
cache_set latest_new "$newrev"
674+
cache_set latest_old "$rev"
675+
}
676+
601677
cmd_add () {
602678
if test -e "$dir"
603679
then
@@ -689,12 +765,7 @@ cmd_split () {
689765
done
690766
fi
691767

692-
if test -n "$ignore_joins"
693-
then
694-
unrevs=
695-
else
696-
unrevs="$(find_existing_splits "$dir" "$revs")"
697-
fi
768+
unrevs="$(find_existing_splits "$dir" "$revs")"
698769

699770
# We can't restrict rev-list to only $dir here, because some of our
700771
# parents have the $dir contents the root, and those won't match.
@@ -703,45 +774,11 @@ cmd_split () {
703774
revmax=$(eval "$grl" | wc -l)
704775
revcount=0
705776
createcount=0
777+
extracount=0
706778
eval "$grl" |
707779
while read rev parents
708780
do
709-
revcount=$(($revcount + 1))
710-
progress "$revcount/$revmax ($createcount)"
711-
debug "Processing commit: $rev"
712-
exists=$(cache_get "$rev")
713-
if test -n "$exists"
714-
then
715-
debug " prior: $exists"
716-
continue
717-
fi
718-
createcount=$(($createcount + 1))
719-
debug " parents: $parents"
720-
newparents=$(cache_get $parents)
721-
debug " newparents: $newparents"
722-
723-
tree=$(subtree_for_commit "$rev" "$dir")
724-
debug " tree is: $tree"
725-
726-
check_parents $parents
727-
728-
# ugly. is there no better way to tell if this is a subtree
729-
# vs. a mainline commit? Does it matter?
730-
if test -z "$tree"
731-
then
732-
set_notree "$rev"
733-
if test -n "$newparents"
734-
then
735-
cache_set "$rev" "$rev"
736-
fi
737-
continue
738-
fi
739-
740-
newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $?
741-
debug " newrev is: $newrev"
742-
cache_set "$rev" "$newrev"
743-
cache_set latest_new "$newrev"
744-
cache_set latest_old "$rev"
781+
process_split_commit "$rev" "$parents" 0
745782
done || exit $?
746783

747784
latest_new=$(cache_get latest_new)

0 commit comments

Comments
 (0)