subtree: use commits before rejoins for splits

Adds recursive evaluation of parent commits which were not part of the initial commit list when performing a split. Split expects all relevant commits to be reachable from the target commit but not reachable from any previous rejoins. However, a branch could be based on a commit prior to a rejoin, then later merged back into the current code. In this case, a parent to the commit will not be present in the initial list of commits, trigging an "incorrect order" warning. Previous behavior was to consider that commit to have no parent, creating an original commit containing all subtree content. This commit is not present in an existing subtree commit graph, changing commit hashes and making pushing to a subtree repo impossible. New behavior will recursively check these unexpected parent commits to track them back to either an earlier rejoin, or a true original commit. The generated synthetic commits will properly match previously-generated commits, allowing successful pushing to a prior subtree repo. Signed-off-by: Strain, Roger L <roger.strain@swri.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Strain, Roger L committed Sep 28, 2018 at 13:35 UTC 315a84f9aa0e2e629b0680068646b0032518ebed
1 file changed +20 -6
contrib/subtree/git-subtree.sh
+20 -6
@@ -231,12 +231,14 @@ cache_miss () {
231 }
232
233 check_parents () {
234 - missed=$(cache_miss "$@")
234 + missed=$(cache_miss "$1")
235 + local indent=$(($2 + 1))
236 for miss in $missed
237 do
238 if ! test -r "$cachedir/notree/$miss"
239 then
240 debug " incorrect order: $miss"
241 + process_split_commit "$miss" "" "$indent"
242 fi
243 done
244 }
@@ -606,8 +608,20 @@ ensure_valid_ref_format () {
608 process_split_commit () {
609 local rev="$1"
610 local parents="$2"
609 - revcount=$(($revcount + 1))
610 - progress "$revcount/$revmax ($createcount)"
611 + local indent=$3
612 +
613 + if test $indent -eq 0
614 + then
615 + revcount=$(($revcount + 1))
616 + else
617 + # processing commit without normal parent information;
618 + # fetch from repo
619 + parents=$(git show -s --pretty=%P "$rev")
620 + extracount=$(($extracount + 1))
621 + fi
622 +
623 + progress "$revcount/$revmax ($createcount) [$extracount]"
624 +
625 debug "Processing commit: $rev"
626 exists=$(cache_get "$rev")
627 if test -n "$exists"
@@ -617,14 +631,13 @@ process_split_commit () {
631 fi
632 createcount=$(($createcount + 1))
633 debug " parents: $parents"
634 + check_parents "$parents" "$indent"
635 newparents=$(cache_get $parents)
636 debug " newparents: $newparents"
637
638 tree=$(subtree_for_commit "$rev" "$dir")
639 debug " tree is: $tree"
640
626 - check_parents $parents
627 -
641 # ugly. is there no better way to tell if this is a subtree
642 # vs. a mainline commit? Does it matter?
643 if test -z "$tree"
@@ -744,10 +757,11 @@ cmd_split () {
757 revmax=$(eval "$grl" | wc -l)
758 revcount=0
759 createcount=0
760 + extracount=0
761 eval "$grl" |
762 while read rev parents
763 do
750 - process_split_commit "$rev" "$parents"
764 + process_split_commit "$rev" "$parents" 0
765 done || exit $?
766
767 latest_new=$(cache_get latest_new)