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