]> git.ipfire.org Git - thirdparty/git.git/blob - contrib/subtree/git-subtree.sh
The fifth 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> [-S[=<key-id>]] <commit>
30 git subtree add --prefix=<prefix> [-S[=<key-id>]] <repository> <ref>
31 git subtree merge --prefix=<prefix> [-S[=<key-id>]] <commit>
32 git subtree split --prefix=<prefix> [-S[=<key-id>]] [<commit>]
33 git subtree pull --prefix=<prefix> [-S[=<key-id>]] <repository> <ref>
34 git subtree push --prefix=<prefix> [-S[=<key-id>]] <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 S,gpg-sign?key-id GPG-sign commits. The keyid argument is optional and defaults to the committer identity
50 "
51
52 indent=0
53
54 # Usage: say [MSG...]
55 say () {
56 if test -z "$arg_quiet"
57 then
58 printf '%s\n' "$*"
59 fi
60 }
61
62 # Usage: debug [MSG...]
63 debug () {
64 if test -n "$arg_debug"
65 then
66 printf "%$(($indent * 2))s%s\n" '' "$*" >&2
67 fi
68 }
69
70 # Usage: progress [MSG...]
71 progress () {
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...
99 assert () {
100 if ! "$@"
101 then
102 die "fatal: assertion failed: $*"
103 fi
104 }
105
106 # Usage: die_incompatible_opt OPTION COMMAND
107 die_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
114 main () {
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
279 cache_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...]
292 cache_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...]
304 cache_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...]
315 check_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
329 set_notree () {
330 assert test $# = 1
331 echo "1" > "$cachedir/notree/$1"
332 }
333
334 # Usage: cache_set OLDREV NEWREV
335 cache_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
349 rev_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.
364 try_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]
373 process_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]
403 find_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]
457 find_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
515 copy_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
543 add_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
571 add_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
582 rejoin_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
603 squash_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
627 toptree_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
634 subtree_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...]
656 tree_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
675 new_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
692 copy_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
769 ensure_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
782 ensure_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: check if a commit from another subtree should be
789 # ignored from processing for splits
790 should_ignore_subtree_split_commit () {
791 assert test $# = 1
792 local rev="$1"
793 if test -n "$(git log -1 --grep="git-subtree-dir:" $rev)"
794 then
795 if test -z "$(git log -1 --grep="git-subtree-mainline:" $rev)" &&
796 test -z "$(git log -1 --grep="git-subtree-dir: $arg_prefix$" $rev)"
797 then
798 return 0
799 fi
800 fi
801 return 1
802 }
803
804 # Usage: process_split_commit REV PARENTS
805 process_split_commit () {
806 assert test $# = 2
807 local rev="$1"
808 local parents="$2"
809
810 if test $indent -eq 0
811 then
812 revcount=$(($revcount + 1))
813 else
814 # processing commit without normal parent information;
815 # fetch from repo
816 parents=$(git rev-parse "$rev^@")
817 extracount=$(($extracount + 1))
818 fi
819
820 progress "$revcount/$revmax ($createcount) [$extracount]"
821
822 debug "Processing commit: $rev"
823 local indent=$(($indent + 1))
824 exists=$(cache_get "$rev") || exit $?
825 if test -n "$exists"
826 then
827 debug "prior: $exists"
828 return
829 fi
830 createcount=$(($createcount + 1))
831 debug "parents: $parents"
832 check_parents $parents
833 newparents=$(cache_get $parents) || exit $?
834 debug "newparents: $newparents"
835
836 tree=$(subtree_for_commit "$rev" "$dir") || exit $?
837 debug "tree is: $tree"
838
839 # ugly. is there no better way to tell if this is a subtree
840 # vs. a mainline commit? Does it matter?
841 if test -z "$tree"
842 then
843 set_notree "$rev"
844 if test -n "$newparents"
845 then
846 cache_set "$rev" "$rev"
847 fi
848 return
849 fi
850
851 newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $?
852 debug "newrev is: $newrev"
853 cache_set "$rev" "$newrev"
854 cache_set latest_new "$newrev"
855 cache_set latest_old "$rev"
856 }
857
858 # Usage: cmd_add REV
859 # Or: cmd_add REPOSITORY REF
860 cmd_add () {
861
862 ensure_clean
863
864 if test $# -eq 1
865 then
866 git rev-parse -q --verify "$1^{commit}" >/dev/null ||
867 die "fatal: '$1' does not refer to a commit"
868
869 cmd_add_commit "$@"
870
871 elif test $# -eq 2
872 then
873 # Technically we could accept a refspec here but we're
874 # just going to turn around and add FETCH_HEAD under the
875 # specified directory. Allowing a refspec might be
876 # misleading because we won't do anything with any other
877 # branches fetched via the refspec.
878 ensure_valid_ref_format "$2"
879
880 cmd_add_repository "$@"
881 else
882 say >&2 "fatal: parameters were '$*'"
883 die "Provide either a commit or a repository and commit."
884 fi
885 }
886
887 # Usage: cmd_add_repository REPOSITORY REFSPEC
888 cmd_add_repository () {
889 assert test $# = 2
890 echo "git fetch" "$@"
891 repository=$1
892 refspec=$2
893 git fetch "$@" || exit $?
894 cmd_add_commit FETCH_HEAD
895 }
896
897 # Usage: cmd_add_commit REV
898 cmd_add_commit () {
899 # The rev has already been validated by cmd_add(), we just
900 # need to normalize it.
901 assert test $# = 1
902 rev=$(git rev-parse --verify "$1^{commit}") || exit $?
903
904 debug "Adding $dir as '$rev'..."
905 if test -z "$arg_split_rejoin"
906 then
907 # Only bother doing this if this is a genuine 'add',
908 # not a synthetic 'add' from '--rejoin'.
909 git read-tree --prefix="$dir" $rev || exit $?
910 fi
911 git checkout -- "$dir" || exit $?
912 tree=$(git write-tree) || exit $?
913
914 headrev=$(git rev-parse --verify HEAD) || exit $?
915 if test -n "$headrev" && test "$headrev" != "$rev"
916 then
917 headp="-p $headrev"
918 else
919 headp=
920 fi
921
922 if test -n "$arg_addmerge_squash"
923 then
924 rev=$(new_squash_commit "" "" "$rev") || exit $?
925 commit=$(add_squashed_msg "$rev" "$dir" |
926 git commit-tree $arg_gpg_sign "$tree" $headp -p "$rev") || exit $?
927 else
928 revp=$(peel_committish "$rev") || exit $?
929 commit=$(add_msg "$dir" $headrev "$rev" |
930 git commit-tree $arg_gpg_sign "$tree" $headp -p "$revp") || exit $?
931 fi
932 git reset "$commit" || exit $?
933
934 say >&2 "Added dir '$dir'"
935 }
936
937 # Usage: cmd_split [REV] [REPOSITORY]
938 cmd_split () {
939 if test $# -eq 0
940 then
941 rev=$(git rev-parse HEAD)
942 elif test $# -eq 1 || test $# -eq 2
943 then
944 rev=$(git rev-parse -q --verify "$1^{commit}") ||
945 die "fatal: '$1' does not refer to a commit"
946 else
947 die "fatal: you must provide exactly one revision, and optionally a repository. Got: '$*'"
948 fi
949 repository=""
950 if test "$#" = 2
951 then
952 repository="$2"
953 fi
954
955 if test -n "$arg_split_rejoin"
956 then
957 ensure_clean
958 fi
959
960 debug "Splitting $dir..."
961 cache_setup || exit $?
962
963 if test -n "$arg_split_onto"
964 then
965 debug "Reading history for --onto=$arg_split_onto..."
966 git rev-list $arg_split_onto |
967 while read rev
968 do
969 # the 'onto' history is already just the subdir, so
970 # any parent we find there can be used verbatim
971 debug "cache: $rev"
972 cache_set "$rev" "$rev"
973 done || exit $?
974 fi
975
976 unrevs="$(find_existing_splits "$dir" "$rev" "$repository")" || exit $?
977
978 # We can't restrict rev-list to only $dir here, because some of our
979 # parents have the $dir contents the root, and those won't match.
980 # (and rev-list --follow doesn't seem to solve this)
981 grl='git rev-list --topo-order --reverse --parents $rev $unrevs'
982 revmax=$(eval "$grl" | wc -l)
983 revcount=0
984 createcount=0
985 extracount=0
986 eval "$grl" |
987 while read rev parents
988 do
989 if should_ignore_subtree_split_commit "$rev"
990 then
991 continue
992 fi
993 parsedparents=''
994 for parent in $parents
995 do
996 if ! should_ignore_subtree_split_commit "$parent"
997 then
998 parsedparents="$parsedparents$parent "
999 fi
1000 done
1001 process_split_commit "$rev" "$parsedparents"
1002 done || exit $?
1003
1004 latest_new=$(cache_get latest_new) || exit $?
1005 if test -z "$latest_new"
1006 then
1007 die "fatal: no new revisions were found"
1008 fi
1009
1010 if test -n "$arg_split_rejoin"
1011 then
1012 debug "Merging split branch into HEAD..."
1013 latest_old=$(cache_get latest_old) || exit $?
1014 arg_addmerge_message="$(rejoin_msg "$dir" "$latest_old" "$latest_new")" || exit $?
1015 if test -z "$(find_latest_squash "$dir")"
1016 then
1017 cmd_add "$latest_new" >&2 || exit $?
1018 else
1019 cmd_merge "$latest_new" >&2 || exit $?
1020 fi
1021 fi
1022 if test -n "$arg_split_branch"
1023 then
1024 if rev_exists "refs/heads/$arg_split_branch"
1025 then
1026 if ! git merge-base --is-ancestor "$arg_split_branch" "$latest_new"
1027 then
1028 die "fatal: branch '$arg_split_branch' is not an ancestor of commit '$latest_new'."
1029 fi
1030 action='Updated'
1031 else
1032 action='Created'
1033 fi
1034 git update-ref -m 'subtree split' \
1035 "refs/heads/$arg_split_branch" "$latest_new" || exit $?
1036 say >&2 "$action branch '$arg_split_branch'"
1037 fi
1038 echo "$latest_new"
1039 exit 0
1040 }
1041
1042 # Usage: cmd_merge REV [REPOSITORY]
1043 cmd_merge () {
1044 if test $# -lt 1 || test $# -gt 2
1045 then
1046 die "fatal: you must provide exactly one revision, and optionally a repository. Got: '$*'"
1047 fi
1048
1049 rev=$(git rev-parse -q --verify "$1^{commit}") ||
1050 die "fatal: '$1' does not refer to a commit"
1051 repository=""
1052 if test "$#" = 2
1053 then
1054 repository="$2"
1055 fi
1056 ensure_clean
1057
1058 if test -n "$arg_addmerge_squash"
1059 then
1060 first_split="$(find_latest_squash "$dir" "$repository")" || exit $?
1061 if test -z "$first_split"
1062 then
1063 die "fatal: can't squash-merge: '$dir' was never added."
1064 fi
1065 set $first_split
1066 old=$1
1067 sub=$2
1068 if test "$sub" = "$rev"
1069 then
1070 say >&2 "Subtree is already at commit $rev."
1071 exit 0
1072 fi
1073 new=$(new_squash_commit "$old" "$sub" "$rev") || exit $?
1074 debug "New squash commit: $new"
1075 rev="$new"
1076 fi
1077
1078 if test -n "$arg_addmerge_message"
1079 then
1080 git merge --no-ff -Xsubtree="$arg_prefix" \
1081 --message="$arg_addmerge_message" $arg_gpg_sign "$rev"
1082 else
1083 git merge --no-ff -Xsubtree="$arg_prefix" $arg_gpg_sign $rev
1084 fi
1085 }
1086
1087 # Usage: cmd_pull REPOSITORY REMOTEREF
1088 cmd_pull () {
1089 if test $# -ne 2
1090 then
1091 die "fatal: you must provide <repository> <ref>"
1092 fi
1093 repository="$1"
1094 ref="$2"
1095 ensure_clean
1096 ensure_valid_ref_format "$ref"
1097 git fetch "$repository" "$ref" || exit $?
1098 cmd_merge FETCH_HEAD "$repository"
1099 }
1100
1101 # Usage: cmd_push REPOSITORY [+][LOCALREV:]REMOTEREF
1102 cmd_push () {
1103 if test $# -ne 2
1104 then
1105 die "fatal: you must provide <repository> <refspec>"
1106 fi
1107 if test -e "$dir"
1108 then
1109 repository=$1
1110 refspec=${2#+}
1111 remoteref=${refspec#*:}
1112 if test "$remoteref" = "$refspec"
1113 then
1114 localrevname_presplit=HEAD
1115 else
1116 localrevname_presplit=${refspec%%:*}
1117 fi
1118 ensure_valid_ref_format "$remoteref"
1119 localrev_presplit=$(git rev-parse -q --verify "$localrevname_presplit^{commit}") ||
1120 die "fatal: '$localrevname_presplit' does not refer to a commit"
1121
1122 echo "git push using: " "$repository" "$refspec"
1123 localrev=$(cmd_split "$localrev_presplit" "$repository") || die
1124 git push "$repository" "$localrev":"refs/heads/$remoteref"
1125 else
1126 die "fatal: '$dir' must already exist. Try 'git subtree add'."
1127 fi
1128 }
1129
1130 main "$@"