diff --git a/git-clone.sh b/git-clone.sh
index a21f13af2ab..bfb8fd62853 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -144,8 +144,32 @@ yes,yes)
 *)
 	case "$repo" in
 	rsync://*)
-		rsync $quiet -avz --ignore-existing "$repo/objects/" "$D/.git/objects/" &&
-		rsync $quiet -avz --ignore-existing "$repo/refs/" "$D/.git/refs/"
+		rsync $quiet -av --ignore-existing  \
+			--exclude info "$repo/objects/" "$D/.git/objects/" &&
+		rsync $quiet -av --ignore-existing  \
+			--exclude info "$repo/refs/" "$D/.git/refs/" || exit
+
+		# Look at objects/info/alternates for rsync -- http will
+		# support it natively and git native ones will do it on the
+		# remote end.  Not having that file is not a crime.
+		rsync -q "$repo/objects/info/alternates" "$D/.git/TMP_ALT" ||
+			rm -f "$D/.git/TMP_ALT"
+		if test -f "$D/.git/TMP_ALT"
+		then
+		    ( cd $D &&
+		      . git-parse-remote &&
+		      resolve_alternates "$repo" <"./.git/TMP_ALT" ) |
+		    while read alt
+		    do
+			case "$alt" in 'bad alternate: '*) die "$alt";; esac
+			case "$quiet" in
+			'')	echo >&2 "Getting alternate: $alt" ;;
+			esac
+			rsync $quiet -av --ignore-existing  \
+			    --exclude info "$alt" "$D/.git/objects" || exit
+		    done
+		    rm -f "$D/.git/TMP_ALT"
+		fi
 		;;
 	http://*)
 		clone_dumb_http "$repo" "$D"
diff --git a/git-fetch.sh b/git-fetch.sh
index 22739440384..72f17ab6c9f 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -183,12 +183,30 @@ do
 	;;
     rsync://*)
 	TMP_HEAD="$GIT_DIR/TMP_HEAD"
-	rsync -L "$remote/$remote_name" "$TMP_HEAD" || exit 1
+	rsync -L -q "$remote/$remote_name" "$TMP_HEAD" || exit 1
 	head=$(git-rev-parse TMP_HEAD)
 	rm -f "$TMP_HEAD"
 	test "$rsync_slurped_objects" || {
-	    rsync -avz --ignore-existing "$remote/objects/" \
-		"$GIT_OBJECT_DIRECTORY/" || exit
+	    rsync -av --ignore-existing --exclude info \
+		"$remote/objects/" "$GIT_OBJECT_DIRECTORY/" || exit
+
+	    # Look at objects/info/alternates for rsync -- http will
+	    # support it natively and git native ones will do it on the remote
+	    # end.  Not having that file is not a crime.
+	    rsync -q "$remote/objects/info/alternates" "$GIT_DIR/TMP_ALT" ||
+		    rm -f "$GIT_DIR/TMP_ALT"
+	    if test -f "$GIT_DIR/TMP_ALT"
+	    then
+		resolve_alternates "$remote" <"$GIT_DIR/TMP_ALT" |
+		while read alt
+		do
+		    case "$alt" in 'bad alternate: '*) die "$alt";; esac
+		    echo >&2 "Getting alternate: $alt"
+		    rsync -av --ignore-existing --exclude info \
+		    "$alt" "$GIT_OBJECT_DIRECTORY/" || exit
+		done
+		rm -f "$GIT_DIR/TMP_ALT"
+	    fi
 	    rsync_slurped_objects=t
 	}
 	;;
diff --git a/git-parse-remote.sh b/git-parse-remote.sh
index 3c5d94b344b..a9db0cd8255 100755
--- a/git-parse-remote.sh
+++ b/git-parse-remote.sh
@@ -153,3 +153,24 @@ get_remote_refs_for_fetch () {
 	    ;;
 	esac
 }
+
+resolve_alternates () {
+	# original URL (xxx.git)
+	top_=`expr "$1" : '\([^:]*:/*[^/]*\)/'`
+	while read path
+	do
+		case "$path" in
+		\#* | '')
+			continue ;;
+		/*)
+			echo "$top_$path/" ;;
+		../*)
+			# relative -- ugly but seems to work.
+			echo "$1/objects/$path/" ;;
+		*)
+			# exit code may not be caught by the reader.
+			echo "bad alternate: $path"
+			exit 1 ;;
+		esac
+	done
+}