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