#!/bin/sh

# git-ls-remote could be called from outside a git managed repository;
# this would fail in that case and would issue an error message.
GIT_DIR=$(git rev-parse --git-dir 2>/dev/null) || :;

get_data_source () {
	case "$1" in
	*/*)
		echo ''
		;;
	.)
		echo self
		;;
	*)
		if test "$(git config --get "remote.$1.url")"
		then
			echo config
		elif test -f "$GIT_DIR/remotes/$1"
		then
			echo remotes
		elif test -f "$GIT_DIR/branches/$1"
		then
			echo branches
		else
			echo ''
		fi ;;
	esac
}

get_remote_url () {
	data_source=$(get_data_source "$1")
	case "$data_source" in
	'')
		echo "$1"
		;;
	self)
		echo "$1"
		;;
	config)
		git config --get "remote.$1.url"
		;;
	remotes)
		sed -ne '/^URL: */{
			s///p
			q
		}' "$GIT_DIR/remotes/$1"
		;;
	branches)
		sed -e 's/#.*//' "$GIT_DIR/branches/$1"
		;;
	*)
		die "internal error: get-remote-url $1" ;;
	esac
}

get_default_remote () {
	curr_branch=$(git symbolic-ref -q HEAD | sed -e 's|^refs/heads/||')
	origin=$(git config --get "branch.$curr_branch.remote")
	echo ${origin:-origin}
}

get_remote_default_refs_for_push () {
	data_source=$(get_data_source "$1")
	case "$data_source" in
	'' | branches | self)
		;; # no default push mapping, just send matching refs.
	config)
		git config --get-all "remote.$1.push" ;;
	remotes)
		sed -ne '/^Push: */{
			s///p
		}' "$GIT_DIR/remotes/$1" ;;
	*)
		die "internal error: get-remote-default-ref-for-push $1" ;;
	esac
}

# Called from canon_refs_list_for_fetch -d "$remote", which
# is called from get_remote_default_refs_for_fetch to grok
# refspecs that are retrieved from the configuration, but not
# from get_remote_refs_for_fetch when it deals with refspecs
# supplied on the command line.  $ls_remote_result has the list
# of refs available at remote.
#
# The first token returned is either "explicit" or "glob"; this
# is to help prevent randomly "globbed" ref from being chosen as
# a merge candidate
expand_refs_wildcard () {
	echo "$ls_remote_result" |
	git fetch--tool expand-refs-wildcard "-" "$@"
}

# Subroutine to canonicalize remote:local notation.
canon_refs_list_for_fetch () {
	# If called from get_remote_default_refs_for_fetch
	# leave the branches in branch.${curr_branch}.merge alone,
	# or the first one otherwise; add prefix . to the rest
	# to prevent the secondary branches to be merged by default.
	merge_branches=
	curr_branch=
	if test "$1" = "-d"
	then
		shift ; remote="$1" ; shift
		set $(expand_refs_wildcard "$remote" "$@")
		is_explicit="$1"
		shift
		if test "$remote" = "$(get_default_remote)"
		then
			curr_branch=$(git symbolic-ref -q HEAD | \
			    sed -e 's|^refs/heads/||')
			merge_branches=$(git config \
			    --get-all "branch.${curr_branch}.merge")
		fi
		if test -z "$merge_branches" && test $is_explicit != explicit
		then
			merge_branches=..this.will.never.match.any.ref..
		fi
	fi
	for ref
	do
		force=
		case "$ref" in
		+*)
			ref=$(expr "z$ref" : 'z+\(.*\)')
			force=+
			;;
		esac
		expr "z$ref" : 'z.*:' >/dev/null || ref="${ref}:"
		remote=$(expr "z$ref" : 'z\([^:]*\):')
		local=$(expr "z$ref" : 'z[^:]*:\(.*\)')
		dot_prefix=.
		if test -z "$merge_branches"
		then
			merge_branches=$remote
			dot_prefix=
		else
			for merge_branch in $merge_branches
			do
			    [ "$remote" = "$merge_branch" ] &&
			    dot_prefix= && break
			done
		fi
		case "$remote" in
		'' | HEAD ) remote=HEAD ;;
		refs/*) ;;
		heads/* | tags/* | remotes/* ) remote="refs/$remote" ;;
		*) remote="refs/heads/$remote" ;;
		esac
		case "$local" in
		'') local= ;;
		refs/*) ;;
		heads/* | tags/* | remotes/* ) local="refs/$local" ;;
		*) local="refs/heads/$local" ;;
		esac

		if local_ref_name=$(expr "z$local" : 'zrefs/\(.*\)')
		then
		   git check-ref-format "$local_ref_name" ||
		   die "* refusing to create funny ref '$local_ref_name' locally"
		fi
		echo "${dot_prefix}${force}${remote}:${local}"
	done
}

# Returns list of src: (no store), or src:dst (store)
get_remote_default_refs_for_fetch () {
	data_source=$(get_data_source "$1")
	case "$data_source" in
	'')
		echo "HEAD:" ;;
	self)
	        canon_refs_list_for_fetch -d "$1" \
			$(git for-each-ref --format='%(refname):')
		;;
	config)
		canon_refs_list_for_fetch -d "$1" \
			$(git config --get-all "remote.$1.fetch") ;;
	branches)
		remote_branch=$(sed -ne '/#/s/.*#//p' "$GIT_DIR/branches/$1")
		case "$remote_branch" in '') remote_branch=master ;; esac
		echo "refs/heads/${remote_branch}:refs/heads/$1"
		;;
	remotes)
		canon_refs_list_for_fetch -d "$1" $(sed -ne '/^Pull: */{
						s///p
					}' "$GIT_DIR/remotes/$1")
		;;
	*)
		die "internal error: get-remote-default-ref-for-fetch $1" ;;
	esac
}

get_remote_refs_for_push () {
	case "$#" in
	0) die "internal error: get-remote-refs-for-push." ;;
	1) get_remote_default_refs_for_push "$@" ;;
	*) shift; echo "$@" ;;
	esac
}

get_remote_refs_for_fetch () {
	case "$#" in
	0)
	    die "internal error: get-remote-refs-for-fetch." ;;
	1)
	    get_remote_default_refs_for_fetch "$@" ;;
	*)
	    shift
	    tag_just_seen=
	    for ref
	    do
		if test "$tag_just_seen"
		then
		    echo "refs/tags/${ref}:refs/tags/${ref}"
		    tag_just_seen=
		    continue
		else
		    case "$ref" in
		    tag)
			tag_just_seen=yes
			continue
			;;
		    esac
		fi
		canon_refs_list_for_fetch "$ref"
	    done
	    ;;
	esac
}

resolve_alternates () {
	# original URL (xxx.git)
	top_=`expr "z$1" : 'z\([^:]*:/*[^/]*\)/'`
	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
}

get_uploadpack () {
	data_source=$(get_data_source "$1")
	case "$data_source" in
	config)
		uplp=$(git config --get "remote.$1.uploadpack")
		echo ${uplp:-git-upload-pack}
		;;
	*)
		echo "git-upload-pack"
		;;
	esac
}
