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