Git fork
at reftables-rust 1150 lines 26 kB view raw
1#!/bin/sh 2# 3# git-subtree.sh: split/join git repositories in subdirectories of this one 4# 5# Copyright (C) 2009 Avery Pennarun <apenwarr@gmail.com> 6# 7 8if test -z "$GIT_EXEC_PATH" || ! test -f "$GIT_EXEC_PATH/git-sh-setup" || { 9 test "${PATH#"${GIT_EXEC_PATH}:"}" = "$PATH" && 10 test ! "$GIT_EXEC_PATH" -ef "${PATH%%:*}" 2>/dev/null 11} 12then 13 basename=${0##*[/\\]} 14 echo >&2 'It looks like either your git installation or your' 15 echo >&2 'git-subtree installation is broken.' 16 echo >&2 17 echo >&2 "Tips:" 18 echo >&2 " - If \`git --exec-path\` does not print the correct path to" 19 echo >&2 " your git install directory, then set the GIT_EXEC_PATH" 20 echo >&2 " environment variable to the correct directory." 21 echo >&2 " - Make sure that your \`$basename\` file is either in your" 22 echo >&2 " PATH or in your git exec path (\`$(git --exec-path)\`)." 23 echo >&2 " - You should run git-subtree as \`git ${basename#git-}\`," 24 echo >&2 " not as \`$basename\`." >&2 25 exit 126 26fi 27 28OPTS_SPEC="\ 29git subtree add --prefix=<prefix> [-S[=<key-id>]] <commit> 30git subtree add --prefix=<prefix> [-S[=<key-id>]] <repository> <ref> 31git subtree merge --prefix=<prefix> [-S[=<key-id>]] <commit> 32git subtree split --prefix=<prefix> [-S[=<key-id>]] [<commit>] 33git subtree pull --prefix=<prefix> [-S[=<key-id>]] <repository> <ref> 34git subtree push --prefix=<prefix> [-S[=<key-id>]] <repository> <refspec> 35-- 36h,help! show the help 37q,quiet! quiet 38d,debug! show debug messages 39P,prefix= the name of the subdir to split out 40 options for 'split' (also: 'push') 41annotate= add a prefix to commit message of new commits 42b,branch!= create a new branch from the split subtree 43ignore-joins ignore prior --rejoin commits 44onto= try connecting new tree to an existing one 45rejoin merge the new branch back into HEAD 46 options for 'add' and 'merge' (also: 'pull', 'split --rejoin', and 'push --rejoin') 47squash merge subtree changes as a single commit 48m,message!= use the given message as the commit message for the merge commit 49S,gpg-sign?key-id GPG-sign commits. The keyid argument is optional and defaults to the committer identity 50" 51 52indent=0 53 54# Usage: say [MSG...] 55say () { 56 if test -z "$arg_quiet" 57 then 58 printf '%s\n' "$*" 59 fi 60} 61 62# Usage: debug [MSG...] 63debug () { 64 if test -n "$arg_debug" 65 then 66 printf "%$(($indent * 2))s%s\n" '' "$*" >&2 67 fi 68} 69 70# Usage: progress [MSG...] 71progress () { 72 if test -z "$arg_quiet" 73 then 74 if test -z "$arg_debug" 75 then 76 # Debug mode is off. 77 # 78 # Print one progress line that we keep updating (use 79 # "\r" to return to the beginning of the line, rather 80 # than "\n" to start a new line). This only really 81 # works when stderr is a terminal. 82 printf "%s\r" "$*" >&2 83 else 84 # Debug mode is on. The `debug` function is regularly 85 # printing to stderr. 86 # 87 # Don't do the one-line-with-"\r" thing, because on a 88 # terminal the debug output would overwrite and hide the 89 # progress output. Add a "progress:" prefix to make the 90 # progress output and the debug output easy to 91 # distinguish. This ensures maximum readability whether 92 # stderr is a terminal or a file. 93 printf "progress: %s\n" "$*" >&2 94 fi 95 fi 96} 97 98# Usage: assert CMD... 99assert () { 100 if ! "$@" 101 then 102 die "fatal: assertion failed: $*" 103 fi 104} 105 106# Usage: die_incompatible_opt OPTION COMMAND 107die_incompatible_opt () { 108 assert test "$#" = 2 109 opt="$1" 110 arg_command="$2" 111 die "fatal: the '$opt' flag does not make sense with 'git subtree $arg_command'." 112} 113 114main () { 115 if test $# -eq 0 116 then 117 set -- -h 118 fi 119 set_args="$(echo "$OPTS_SPEC" | git rev-parse --parseopt --stuck-long -- "$@" || echo exit $?)" 120 eval "$set_args" 121 . git-sh-setup 122 require_work_tree 123 124 # First figure out the command and whether we use --rejoin, so 125 # that we can provide more helpful validation when we do the 126 # "real" flag parsing. 127 arg_split_rejoin= 128 allow_split= 129 allow_addmerge= 130 while test $# -gt 0 131 do 132 opt="$1" 133 shift 134 case "$opt" in 135 --rejoin) 136 arg_split_rejoin=1 137 ;; 138 --no-rejoin) 139 arg_split_rejoin= 140 ;; 141 --) 142 break 143 ;; 144 esac 145 done 146 arg_command=$1 147 case "$arg_command" in 148 add|merge|pull) 149 allow_addmerge=1 150 ;; 151 split|push) 152 allow_split=1 153 allow_addmerge=$arg_split_rejoin 154 ;; 155 *) 156 die "fatal: unknown command '$arg_command'" 157 ;; 158 esac 159 # Reset the arguments array for "real" flag parsing. 160 eval "$set_args" 161 162 # Begin "real" flag parsing. 163 arg_quiet= 164 arg_debug= 165 arg_prefix= 166 arg_split_branch= 167 arg_split_onto= 168 arg_split_ignore_joins= 169 arg_split_annotate= 170 arg_addmerge_squash= 171 arg_addmerge_message= 172 arg_gpg_sign= 173 while test $# -gt 0 174 do 175 opt="$1" 176 shift 177 178 case "$opt" in 179 --quiet) 180 arg_quiet=1 181 ;; 182 --debug) 183 arg_debug=1 184 ;; 185 --annotate=*) 186 test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command" 187 arg_split_annotate="${opt#*=}" 188 ;; 189 --no-annotate) 190 test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command" 191 arg_split_annotate= 192 ;; 193 --branch=*) 194 test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command" 195 arg_split_branch="${opt#*=}" 196 ;; 197 --prefix=*) 198 arg_prefix="${opt#*=}" 199 ;; 200 --message=*) 201 test -n "$allow_addmerge" || die_incompatible_opt "$opt" "$arg_command" 202 arg_addmerge_message="${opt#*=}" 203 ;; 204 --no-prefix) 205 arg_prefix= 206 ;; 207 --onto=*) 208 test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command" 209 arg_split_onto="${opt#*=}" 210 ;; 211 --no-onto) 212 test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command" 213 arg_split_onto= 214 ;; 215 --rejoin) 216 test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command" 217 ;; 218 --no-rejoin) 219 test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command" 220 ;; 221 --ignore-joins) 222 test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command" 223 arg_split_ignore_joins=1 224 ;; 225 --no-ignore-joins) 226 test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command" 227 arg_split_ignore_joins= 228 ;; 229 --squash) 230 test -n "$allow_addmerge" || die_incompatible_opt "$opt" "$arg_command" 231 arg_addmerge_squash=1 232 ;; 233 --no-squash) 234 test -n "$allow_addmerge" || die_incompatible_opt "$opt" "$arg_command" 235 arg_addmerge_squash= 236 ;; 237 --gpg-sign=* | --gpg-sign | --no-gpg-sign) 238 arg_gpg_sign="$opt" 239 ;; 240 --) 241 break 242 ;; 243 *) 244 die "fatal: unexpected option: $opt" 245 ;; 246 esac 247 done 248 shift 249 250 if test -z "$arg_prefix" 251 then 252 die "fatal: you must provide the --prefix option." 253 fi 254 255 case "$arg_command" in 256 add) 257 test -e "$arg_prefix" && 258 die "fatal: prefix '$arg_prefix' already exists." 259 ;; 260 *) 261 test -e "$arg_prefix" || 262 die "fatal: '$arg_prefix' does not exist; use 'git subtree add'" 263 ;; 264 esac 265 266 dir="$(dirname "$arg_prefix/.")" 267 268 debug "command: {$arg_command}" 269 debug "quiet: {$arg_quiet}" 270 debug "dir: {$dir}" 271 debug "opts: {$*}" 272 debug "gpg-sign: {$arg_gpg_sign}" 273 debug 274 275 "cmd_$arg_command" "$@" 276} 277 278# Usage: cache_setup 279cache_setup () { 280 assert test $# = 0 281 cachedir="$GIT_DIR/subtree-cache/$$" 282 rm -rf "$cachedir" || 283 die "fatal: can't delete old cachedir: $cachedir" 284 mkdir -p "$cachedir" || 285 die "fatal: can't create new cachedir: $cachedir" 286 mkdir -p "$cachedir/notree" || 287 die "fatal: can't create new cachedir: $cachedir/notree" 288 debug "Using cachedir: $cachedir" >&2 289} 290 291# Usage: cache_get [REVS...] 292cache_get () { 293 for oldrev in "$@" 294 do 295 if test -r "$cachedir/$oldrev" 296 then 297 read newrev <"$cachedir/$oldrev" 298 echo $newrev 299 fi 300 done 301} 302 303# Usage: cache_miss [REVS...] 304cache_miss () { 305 for oldrev in "$@" 306 do 307 if ! test -r "$cachedir/$oldrev" 308 then 309 echo $oldrev 310 fi 311 done 312} 313 314# Usage: check_parents [REVS...] 315check_parents () { 316 missed=$(cache_miss "$@") || exit $? 317 local indent=$(($indent + 1)) 318 for miss in $missed 319 do 320 if ! test -r "$cachedir/notree/$miss" 321 then 322 debug "incorrect order: $miss" 323 process_split_commit "$miss" "" 324 fi 325 done 326} 327 328# Usage: set_notree REV 329set_notree () { 330 assert test $# = 1 331 echo "1" > "$cachedir/notree/$1" 332} 333 334# Usage: cache_set OLDREV NEWREV 335cache_set () { 336 assert test $# = 2 337 oldrev="$1" 338 newrev="$2" 339 if test "$oldrev" != "latest_old" && 340 test "$oldrev" != "latest_new" && 341 test -e "$cachedir/$oldrev" 342 then 343 die "fatal: cache for $oldrev already exists!" 344 fi 345 echo "$newrev" >"$cachedir/$oldrev" 346} 347 348# Usage: rev_exists REV 349rev_exists () { 350 assert test $# = 1 351 if git rev-parse "$1" >/dev/null 2>&1 352 then 353 return 0 354 else 355 return 1 356 fi 357} 358 359# Usage: try_remove_previous REV 360# 361# If a commit doesn't have a parent, this might not work. But we only want 362# to remove the parent from the rev-list, and since it doesn't exist, it won't 363# be there anyway, so do nothing in that case. 364try_remove_previous () { 365 assert test $# = 1 366 if rev_exists "$1^" 367 then 368 echo "^$1^" 369 fi 370} 371 372# Usage: process_subtree_split_trailer SPLIT_HASH MAIN_HASH [REPOSITORY] 373process_subtree_split_trailer () { 374 assert test $# -ge 2 375 assert test $# -le 3 376 b="$1" 377 sq="$2" 378 repository="" 379 if test "$#" = 3 380 then 381 repository="$3" 382 fi 383 fail_msg="fatal: could not rev-parse split hash $b from commit $sq" 384 if ! sub="$(git rev-parse --verify --quiet "$b^{commit}")" 385 then 386 # if 'repository' was given, try to fetch the 'git-subtree-split' hash 387 # before 'rev-parse'-ing it again, as it might be a tag that we do not have locally 388 if test -n "${repository}" 389 then 390 git fetch "$repository" "$b" 391 sub="$(git rev-parse --verify --quiet "$b^{commit}")" || 392 die "$fail_msg" 393 else 394 hint1=$(printf "hint: hash might be a tag, try fetching it from the subtree repository:") 395 hint2=$(printf "hint: git fetch <subtree-repository> $b") 396 fail_msg=$(printf "$fail_msg\n$hint1\n$hint2") 397 die "$fail_msg" 398 fi 399 fi 400} 401 402# Usage: find_latest_squash DIR [REPOSITORY] 403find_latest_squash () { 404 assert test $# -ge 1 405 assert test $# -le 2 406 dir="$1" 407 repository="" 408 if test "$#" = 2 409 then 410 repository="$2" 411 fi 412 debug "Looking for latest squash (dir=$dir, repository=$repository)..." 413 local indent=$(($indent + 1)) 414 415 sq= 416 main= 417 sub= 418 git log --grep="^git-subtree-dir: $dir/*\$" \ 419 --no-show-signature --pretty=format:'START %H%n%s%n%n%b%nEND%n' HEAD | 420 while read a b junk 421 do 422 debug "$a $b $junk" 423 debug "{{$sq/$main/$sub}}" 424 case "$a" in 425 START) 426 sq="$b" 427 ;; 428 git-subtree-mainline:) 429 main="$b" 430 ;; 431 git-subtree-split:) 432 process_subtree_split_trailer "$b" "$sq" "$repository" 433 ;; 434 END) 435 if test -n "$sub" 436 then 437 if test -n "$main" 438 then 439 # a rejoin commit? 440 # Pretend its sub was a squash. 441 sq=$(git rev-parse --verify "$sq^2") || 442 die 443 fi 444 debug "Squash found: $sq $sub" 445 echo "$sq" "$sub" 446 break 447 fi 448 sq= 449 main= 450 sub= 451 ;; 452 esac 453 done || exit $? 454} 455 456# Usage: find_existing_splits DIR REV [REPOSITORY] 457find_existing_splits () { 458 assert test $# -ge 2 459 assert test $# -le 3 460 debug "Looking for prior splits..." 461 local indent=$(($indent + 1)) 462 463 dir="$1" 464 rev="$2" 465 repository="" 466 if test "$#" = 3 467 then 468 repository="$3" 469 fi 470 main= 471 sub= 472 local grep_format="^git-subtree-dir: $dir/*\$" 473 if test -n "$arg_split_ignore_joins" 474 then 475 grep_format="^Add '$dir/' from commit '" 476 fi 477 git log --grep="$grep_format" \ 478 --no-show-signature --pretty=format:'START %H%n%s%n%n%b%nEND%n' "$rev" | 479 while read a b junk 480 do 481 case "$a" in 482 START) 483 sq="$b" 484 ;; 485 git-subtree-mainline:) 486 main="$b" 487 ;; 488 git-subtree-split:) 489 process_subtree_split_trailer "$b" "$sq" "$repository" 490 ;; 491 END) 492 debug "Main is: '$main'" 493 if test -z "$main" && test -n "$sub" 494 then 495 # squash commits refer to a subtree 496 debug " Squash: $sq from $sub" 497 cache_set "$sq" "$sub" 498 fi 499 if test -n "$main" && test -n "$sub" 500 then 501 debug " Prior: $main -> $sub" 502 cache_set $main $sub 503 cache_set $sub $sub 504 try_remove_previous "$main" 505 try_remove_previous "$sub" 506 fi 507 main= 508 sub= 509 ;; 510 esac 511 done || exit $? 512} 513 514# Usage: copy_commit REV TREE FLAGS_STR 515copy_commit () { 516 assert test $# = 3 517 # We're going to set some environment vars here, so 518 # do it in a subshell to get rid of them safely later 519 debug copy_commit "{$1}" "{$2}" "{$3}" 520 git log -1 --no-show-signature --pretty=format:'%an%n%ae%n%aD%n%cn%n%ce%n%cD%n%B' "$1" | 521 ( 522 read GIT_AUTHOR_NAME 523 read GIT_AUTHOR_EMAIL 524 read GIT_AUTHOR_DATE 525 read GIT_COMMITTER_NAME 526 read GIT_COMMITTER_EMAIL 527 read GIT_COMMITTER_DATE 528 export GIT_AUTHOR_NAME \ 529 GIT_AUTHOR_EMAIL \ 530 GIT_AUTHOR_DATE \ 531 GIT_COMMITTER_NAME \ 532 GIT_COMMITTER_EMAIL \ 533 GIT_COMMITTER_DATE 534 ( 535 printf "%s" "$arg_split_annotate" 536 cat 537 ) | 538 git commit-tree $arg_gpg_sign "$2" $3 # reads the rest of stdin 539 ) || die "fatal: can't copy commit $1" 540} 541 542# Usage: add_msg DIR LATEST_OLD LATEST_NEW 543add_msg () { 544 assert test $# = 3 545 dir="$1" 546 latest_old="$2" 547 latest_new="$3" 548 if test -n "$arg_addmerge_message" 549 then 550 commit_message="$arg_addmerge_message" 551 else 552 commit_message="Add '$dir/' from commit '$latest_new'" 553 fi 554 if test -n "$arg_split_rejoin" 555 then 556 # If this is from a --rejoin, then rejoin_msg has 557 # already inserted the `git-subtree-xxx:` tags 558 echo "$commit_message" 559 return 560 fi 561 cat <<-EOF 562 $commit_message 563 564 git-subtree-dir: $dir 565 git-subtree-mainline: $latest_old 566 git-subtree-split: $latest_new 567 EOF 568} 569 570# Usage: add_squashed_msg REV DIR 571add_squashed_msg () { 572 assert test $# = 2 573 if test -n "$arg_addmerge_message" 574 then 575 echo "$arg_addmerge_message" 576 else 577 echo "Merge commit '$1' as '$2'" 578 fi 579} 580 581# Usage: rejoin_msg DIR LATEST_OLD LATEST_NEW 582rejoin_msg () { 583 assert test $# = 3 584 dir="$1" 585 latest_old="$2" 586 latest_new="$3" 587 if test -n "$arg_addmerge_message" 588 then 589 commit_message="$arg_addmerge_message" 590 else 591 commit_message="Split '$dir/' into commit '$latest_new'" 592 fi 593 cat <<-EOF 594 $commit_message 595 596 git-subtree-dir: $dir 597 git-subtree-mainline: $latest_old 598 git-subtree-split: $latest_new 599 EOF 600} 601 602# Usage: squash_msg DIR OLD_SUBTREE_COMMIT NEW_SUBTREE_COMMIT 603squash_msg () { 604 assert test $# = 3 605 dir="$1" 606 oldsub="$2" 607 newsub="$3" 608 newsub_short=$(git rev-parse --short "$newsub") 609 610 if test -n "$oldsub" 611 then 612 oldsub_short=$(git rev-parse --short "$oldsub") 613 echo "Squashed '$dir/' changes from $oldsub_short..$newsub_short" 614 echo 615 git log --no-show-signature --pretty=tformat:'%h %s' "$oldsub..$newsub" 616 git log --no-show-signature --pretty=tformat:'REVERT: %h %s' "$newsub..$oldsub" 617 else 618 echo "Squashed '$dir/' content from commit $newsub_short" 619 fi 620 621 echo 622 echo "git-subtree-dir: $dir" 623 echo "git-subtree-split: $newsub" 624} 625 626# Usage: toptree_for_commit COMMIT 627toptree_for_commit () { 628 assert test $# = 1 629 commit="$1" 630 git rev-parse --verify "$commit^{tree}" || exit $? 631} 632 633# Usage: subtree_for_commit COMMIT DIR 634subtree_for_commit () { 635 assert test $# = 2 636 commit="$1" 637 dir="$2" 638 git ls-tree "$commit" -- "$dir" | 639 while read mode type tree name 640 do 641 assert test "$name" = "$dir" 642 643 case "$type" in 644 commit) 645 continue;; # ignore submodules 646 tree) 647 echo $tree 648 break;; 649 *) 650 die "fatal: tree entry is of type ${type}, expected tree or commit";; 651 esac 652 done || exit $? 653} 654 655# Usage: tree_changed TREE [PARENTS...] 656tree_changed () { 657 assert test $# -gt 0 658 tree=$1 659 shift 660 if test $# -ne 1 661 then 662 return 0 # weird parents, consider it changed 663 else 664 ptree=$(toptree_for_commit $1) || exit $? 665 if test "$ptree" != "$tree" 666 then 667 return 0 # changed 668 else 669 return 1 # not changed 670 fi 671 fi 672} 673 674# Usage: new_squash_commit OLD_SQUASHED_COMMIT OLD_NONSQUASHED_COMMIT NEW_NONSQUASHED_COMMIT 675new_squash_commit () { 676 assert test $# = 3 677 old="$1" 678 oldsub="$2" 679 newsub="$3" 680 tree=$(toptree_for_commit $newsub) || exit $? 681 if test -n "$old" 682 then 683 squash_msg "$dir" "$oldsub" "$newsub" | 684 git commit-tree $arg_gpg_sign "$tree" -p "$old" || exit $? 685 else 686 squash_msg "$dir" "" "$newsub" | 687 git commit-tree $arg_gpg_sign "$tree" || exit $? 688 fi 689} 690 691# Usage: copy_or_skip REV TREE NEWPARENTS 692copy_or_skip () { 693 assert test $# = 3 694 rev="$1" 695 tree="$2" 696 newparents="$3" 697 assert test -n "$tree" 698 699 identical= 700 nonidentical= 701 p= 702 gotparents= 703 copycommit= 704 for parent in $newparents 705 do 706 ptree=$(toptree_for_commit $parent) || exit $? 707 test -z "$ptree" && continue 708 if test "$ptree" = "$tree" 709 then 710 # an identical parent could be used in place of this rev. 711 if test -n "$identical" 712 then 713 # if a previous identical parent was found, check whether 714 # one is already an ancestor of the other 715 mergebase=$(git merge-base $identical $parent) 716 if test "$identical" = "$mergebase" 717 then 718 # current identical commit is an ancestor of parent 719 identical="$parent" 720 elif test "$parent" != "$mergebase" 721 then 722 # no common history; commit must be copied 723 copycommit=1 724 fi 725 else 726 # first identical parent detected 727 identical="$parent" 728 fi 729 else 730 nonidentical="$parent" 731 fi 732 733 # sometimes both old parents map to the same newparent; 734 # eliminate duplicates 735 is_new=1 736 for gp in $gotparents 737 do 738 if test "$gp" = "$parent" 739 then 740 is_new= 741 break 742 fi 743 done 744 if test -n "$is_new" 745 then 746 gotparents="$gotparents $parent" 747 p="$p -p $parent" 748 fi 749 done 750 751 if test -n "$identical" && test -n "$nonidentical" 752 then 753 extras=$(git rev-list --count $identical..$nonidentical) 754 if test "$extras" -ne 0 755 then 756 # we need to preserve history along the other branch 757 copycommit=1 758 fi 759 fi 760 if test -n "$identical" && test -z "$copycommit" 761 then 762 echo $identical 763 else 764 copy_commit "$rev" "$tree" "$p" || exit $? 765 fi 766} 767 768# Usage: ensure_clean 769ensure_clean () { 770 assert test $# = 0 771 if ! git diff-index HEAD --exit-code --quiet 2>&1 772 then 773 die "fatal: working tree has modifications. Cannot add." 774 fi 775 if ! git diff-index --cached HEAD --exit-code --quiet 2>&1 776 then 777 die "fatal: index has modifications. Cannot add." 778 fi 779} 780 781# Usage: ensure_valid_ref_format REF 782ensure_valid_ref_format () { 783 assert test $# = 1 784 git check-ref-format "refs/heads/$1" || 785 die "fatal: '$1' does not look like a ref" 786} 787 788# Usage: should_ignore_subtree_split_commit REV 789# 790# Check if REV is a commit from another subtree and should be 791# ignored from processing for splits 792should_ignore_subtree_split_commit () { 793 assert test $# = 1 794 795 git show \ 796 --no-patch \ 797 --no-show-signature \ 798 --format='%(trailers:key=git-subtree-dir,key=git-subtree-mainline)' \ 799 "$1" | 800 ( 801 have_mainline= 802 subtree_dir= 803 804 while read -r trailer val 805 do 806 case "$trailer" in 807 git-subtree-dir:) 808 subtree_dir="${val%/}" ;; 809 git-subtree-mainline:) 810 have_mainline=y ;; 811 esac 812 done 813 814 if test -n "${subtree_dir}" && 815 test -z "${have_mainline}" && 816 test "${subtree_dir}" != "$arg_prefix" 817 then 818 return 0 819 fi 820 return 1 821 ) 822} 823 824# Usage: process_split_commit REV PARENTS 825process_split_commit () { 826 assert test $# = 2 827 local rev="$1" 828 local parents="$2" 829 830 if test $indent -eq 0 831 then 832 revcount=$(($revcount + 1)) 833 else 834 # processing commit without normal parent information; 835 # fetch from repo 836 parents=$(git rev-parse "$rev^@") 837 extracount=$(($extracount + 1)) 838 fi 839 840 progress "$revcount/$revmax ($createcount) [$extracount]" 841 842 debug "Processing commit: $rev" 843 local indent=$(($indent + 1)) 844 exists=$(cache_get "$rev") || exit $? 845 if test -n "$exists" 846 then 847 debug "prior: $exists" 848 return 849 fi 850 createcount=$(($createcount + 1)) 851 debug "parents: $parents" 852 check_parents $parents 853 newparents=$(cache_get $parents) || exit $? 854 debug "newparents: $newparents" 855 856 tree=$(subtree_for_commit "$rev" "$dir") || exit $? 857 debug "tree is: $tree" 858 859 # ugly. is there no better way to tell if this is a subtree 860 # vs. a mainline commit? Does it matter? 861 if test -z "$tree" 862 then 863 set_notree "$rev" 864 if test -n "$newparents" 865 then 866 cache_set "$rev" "$rev" 867 fi 868 return 869 fi 870 871 newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $? 872 debug "newrev is: $newrev" 873 cache_set "$rev" "$newrev" 874 cache_set latest_new "$newrev" 875 cache_set latest_old "$rev" 876} 877 878# Usage: cmd_add REV 879# Or: cmd_add REPOSITORY REF 880cmd_add () { 881 882 ensure_clean 883 884 if test $# -eq 1 885 then 886 git rev-parse -q --verify "$1^{commit}" >/dev/null || 887 die "fatal: '$1' does not refer to a commit" 888 889 cmd_add_commit "$@" 890 891 elif test $# -eq 2 892 then 893 # Technically we could accept a refspec here but we're 894 # just going to turn around and add FETCH_HEAD under the 895 # specified directory. Allowing a refspec might be 896 # misleading because we won't do anything with any other 897 # branches fetched via the refspec. 898 ensure_valid_ref_format "$2" 899 900 cmd_add_repository "$@" 901 else 902 say >&2 "fatal: parameters were '$*'" 903 die "Provide either a commit or a repository and commit." 904 fi 905} 906 907# Usage: cmd_add_repository REPOSITORY REFSPEC 908cmd_add_repository () { 909 assert test $# = 2 910 echo "git fetch" "$@" 911 repository=$1 912 refspec=$2 913 git fetch "$@" || exit $? 914 cmd_add_commit FETCH_HEAD 915} 916 917# Usage: cmd_add_commit REV 918cmd_add_commit () { 919 # The rev has already been validated by cmd_add(), we just 920 # need to normalize it. 921 assert test $# = 1 922 rev=$(git rev-parse --verify "$1^{commit}") || exit $? 923 924 debug "Adding $dir as '$rev'..." 925 if test -z "$arg_split_rejoin" 926 then 927 # Only bother doing this if this is a genuine 'add', 928 # not a synthetic 'add' from '--rejoin'. 929 git read-tree --prefix="$dir" $rev || exit $? 930 fi 931 git checkout -- "$dir" || exit $? 932 tree=$(git write-tree) || exit $? 933 934 headrev=$(git rev-parse --verify HEAD) || exit $? 935 if test -n "$headrev" && test "$headrev" != "$rev" 936 then 937 headp="-p $headrev" 938 else 939 headp= 940 fi 941 942 if test -n "$arg_addmerge_squash" 943 then 944 rev=$(new_squash_commit "" "" "$rev") || exit $? 945 commit=$(add_squashed_msg "$rev" "$dir" | 946 git commit-tree $arg_gpg_sign "$tree" $headp -p "$rev") || exit $? 947 else 948 revp=$(peel_committish "$rev") || exit $? 949 commit=$(add_msg "$dir" $headrev "$rev" | 950 git commit-tree $arg_gpg_sign "$tree" $headp -p "$revp") || exit $? 951 fi 952 git reset "$commit" || exit $? 953 954 say >&2 "Added dir '$dir'" 955} 956 957# Usage: cmd_split [REV] [REPOSITORY] 958cmd_split () { 959 if test $# -eq 0 960 then 961 rev=$(git rev-parse HEAD) 962 elif test $# -eq 1 || test $# -eq 2 963 then 964 rev=$(git rev-parse -q --verify "$1^{commit}") || 965 die "fatal: '$1' does not refer to a commit" 966 else 967 die "fatal: you must provide exactly one revision, and optionally a repository. Got: '$*'" 968 fi 969 repository="" 970 if test "$#" = 2 971 then 972 repository="$2" 973 fi 974 975 if test -n "$arg_split_rejoin" 976 then 977 ensure_clean 978 fi 979 980 debug "Splitting $dir..." 981 cache_setup || exit $? 982 983 if test -n "$arg_split_onto" 984 then 985 debug "Reading history for --onto=$arg_split_onto..." 986 git rev-list $arg_split_onto | 987 while read rev 988 do 989 # the 'onto' history is already just the subdir, so 990 # any parent we find there can be used verbatim 991 debug "cache: $rev" 992 cache_set "$rev" "$rev" 993 done || exit $? 994 fi 995 996 unrevs="$(find_existing_splits "$dir" "$rev" "$repository")" || exit $? 997 998 # We can't restrict rev-list to only $dir here, because some of our 999 # parents have the $dir contents the root, and those won't match. 1000 # (and rev-list --follow doesn't seem to solve this) 1001 grl='git rev-list --topo-order --reverse --parents $rev $unrevs' 1002 revmax=$(eval "$grl" | wc -l) 1003 revcount=0 1004 createcount=0 1005 extracount=0 1006 eval "$grl" | 1007 while read rev parents 1008 do 1009 if should_ignore_subtree_split_commit "$rev" 1010 then 1011 continue 1012 fi 1013 parsedparents='' 1014 for parent in $parents 1015 do 1016 if ! should_ignore_subtree_split_commit "$parent" 1017 then 1018 parsedparents="$parsedparents$parent " 1019 fi 1020 done 1021 process_split_commit "$rev" "$parsedparents" 1022 done || exit $? 1023 1024 latest_new=$(cache_get latest_new) || exit $? 1025 if test -z "$latest_new" 1026 then 1027 die "fatal: no new revisions were found" 1028 fi 1029 1030 if test -n "$arg_split_rejoin" 1031 then 1032 debug "Merging split branch into HEAD..." 1033 latest_old=$(cache_get latest_old) || exit $? 1034 arg_addmerge_message="$(rejoin_msg "$dir" "$latest_old" "$latest_new")" || exit $? 1035 if test -z "$(find_latest_squash "$dir")" 1036 then 1037 cmd_add "$latest_new" >&2 || exit $? 1038 else 1039 cmd_merge "$latest_new" >&2 || exit $? 1040 fi 1041 fi 1042 if test -n "$arg_split_branch" 1043 then 1044 if rev_exists "refs/heads/$arg_split_branch" 1045 then 1046 if ! git merge-base --is-ancestor "$arg_split_branch" "$latest_new" 1047 then 1048 die "fatal: branch '$arg_split_branch' is not an ancestor of commit '$latest_new'." 1049 fi 1050 action='Updated' 1051 else 1052 action='Created' 1053 fi 1054 git update-ref -m 'subtree split' \ 1055 "refs/heads/$arg_split_branch" "$latest_new" || exit $? 1056 say >&2 "$action branch '$arg_split_branch'" 1057 fi 1058 echo "$latest_new" 1059 exit 0 1060} 1061 1062# Usage: cmd_merge REV [REPOSITORY] 1063cmd_merge () { 1064 if test $# -lt 1 || test $# -gt 2 1065 then 1066 die "fatal: you must provide exactly one revision, and optionally a repository. Got: '$*'" 1067 fi 1068 1069 rev=$(git rev-parse -q --verify "$1^{commit}") || 1070 die "fatal: '$1' does not refer to a commit" 1071 repository="" 1072 if test "$#" = 2 1073 then 1074 repository="$2" 1075 fi 1076 ensure_clean 1077 1078 if test -n "$arg_addmerge_squash" 1079 then 1080 first_split="$(find_latest_squash "$dir" "$repository")" || exit $? 1081 if test -z "$first_split" 1082 then 1083 die "fatal: can't squash-merge: '$dir' was never added." 1084 fi 1085 set $first_split 1086 old=$1 1087 sub=$2 1088 if test "$sub" = "$rev" 1089 then 1090 say >&2 "Subtree is already at commit $rev." 1091 exit 0 1092 fi 1093 new=$(new_squash_commit "$old" "$sub" "$rev") || exit $? 1094 debug "New squash commit: $new" 1095 rev="$new" 1096 fi 1097 1098 if test -n "$arg_addmerge_message" 1099 then 1100 git merge --no-ff -Xsubtree="$arg_prefix" \ 1101 --message="$arg_addmerge_message" $arg_gpg_sign "$rev" 1102 else 1103 git merge --no-ff -Xsubtree="$arg_prefix" $arg_gpg_sign $rev 1104 fi 1105} 1106 1107# Usage: cmd_pull REPOSITORY REMOTEREF 1108cmd_pull () { 1109 if test $# -ne 2 1110 then 1111 die "fatal: you must provide <repository> <ref>" 1112 fi 1113 repository="$1" 1114 ref="$2" 1115 ensure_clean 1116 ensure_valid_ref_format "$ref" 1117 git fetch "$repository" "$ref" || exit $? 1118 cmd_merge FETCH_HEAD "$repository" 1119} 1120 1121# Usage: cmd_push REPOSITORY [+][LOCALREV:]REMOTEREF 1122cmd_push () { 1123 if test $# -ne 2 1124 then 1125 die "fatal: you must provide <repository> <refspec>" 1126 fi 1127 if test -e "$dir" 1128 then 1129 repository=$1 1130 refspec=${2#+} 1131 remoteref=${refspec#*:} 1132 if test "$remoteref" = "$refspec" 1133 then 1134 localrevname_presplit=HEAD 1135 else 1136 localrevname_presplit=${refspec%%:*} 1137 fi 1138 ensure_valid_ref_format "$remoteref" 1139 localrev_presplit=$(git rev-parse -q --verify "$localrevname_presplit^{commit}") || 1140 die "fatal: '$localrevname_presplit' does not refer to a commit" 1141 1142 echo "git push using: " "$repository" "$refspec" 1143 localrev=$(cmd_split "$localrev_presplit" "$repository") || die 1144 git push "$repository" "$localrev":"refs/heads/$remoteref" 1145 else 1146 die "fatal: '$dir' must already exist. Try 'git subtree add'." 1147 fi 1148} 1149 1150main "$@"