X-Git-Url: https://git.verplant.org/?a=blobdiff_plain;f=git-fetch-script;h=f9f90b6db15d9e178bf7cea586f0fd41c75ca032;hb=2ab141a26f93c4d97e243a6be8a38a7dd4eb35a3;hp=2e62f001b189249243ad27b88bf8357526095f14;hpb=5569bf9bbedd63a00780fc5c110e0cfab3aa97b9;p=git.git diff --git a/git-fetch-script b/git-fetch-script index 2e62f001..f9f90b6d 100755 --- a/git-fetch-script +++ b/git-fetch-script @@ -1,41 +1,244 @@ #!/bin/sh # -merge_repo=$1 -merge_name=${2:-HEAD} +. git-sh-setup-script || die "Not a git archive" +. git-parse-remote-script +_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' +_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" -: ${GIT_DIR=.git} -: ${GIT_OBJECT_DIRECTORY="${SHA1_FILE_DIRECTORY-"$GIT_DIR/objects"}"} - -download_one () { - # remote_path="$1" local_file="$2" +append= +force= +update_head_ok= +while case "$#" in 0) break ;; esac +do case "$1" in - http://*) - wget -q -O "$2" "$1" ;; - /*) - test -f "$1" && cat >"$2" "$1" ;; + -a|--a|--ap|--app|--appe|--appen|--append) + append=t + ;; + -f|--f|--fo|--for|--forc|--force) + force=t + ;; + -u|--u|--up|--upd|--upda|--updat|--update|--update-|--update-h|\ + --update-he|--update-hea|--update-head|--update-head-|\ + --update-head-o|--update-head-ok) + update_head_ok=t + ;; *) - rsync -L "$1" "$2" ;; + break + ;; esac + shift +done + +case "$#" in +0) + test -f "$GIT_DIR/branches/origin" || + test -f "$GIT_DIR/remotes/origin" || + die "Where do you want to fetch from today?" + set origin ;; +esac + +remote_nick="$1" +remote=$(get_remote_url "$@") +refs= +rref= +rsync_slurped_objects= + +if test "" = "$append" +then + : >$GIT_DIR/FETCH_HEAD +fi + +append_fetch_head () { + head_="$1" + remote_="$2" + remote_name_="$3" + remote_nick_="$4" + local_name_="$5" + + # remote-nick is the URL given on the command line (or a shorthand) + # remote-name is the $GIT_DIR relative refs/ path we computed + # for this refspec. + case "$remote_name_" in + HEAD) + note_= ;; + refs/heads/*) + note_="$(expr "$remote_name_" : 'refs/heads/\(.*\)')" + note_="branch '$note_' of " ;; + refs/tags/*) + note_="$(expr "$remote_name_" : 'refs/tags/\(.*\)')" + note_="tag '$note_' of " ;; + *) + note_="$remote_name of " ;; + esac + remote_1_=$(expr "$remote_" : '\(.*\)\.git/*$') && + remote_="$remote_1_" + note_="$note_$remote_" + + # 2.6.11-tree tag would not be happy to be fed to resolve. + if git-cat-file commit "$head_" >/dev/null 2>&1 + then + headc_=$(git-rev-parse --verify "$head_^0") || exit + echo "$headc_ $note_" >>$GIT_DIR/FETCH_HEAD + echo >&2 "* committish: $head_" + echo >&2 " $note_" + else + echo >&2 "* non-commit: $head_" + echo >&2 " $note_" + fi + if test "$local_name_" != "" + then + # We are storing the head locally. Make sure that it is + # a fast forward (aka "reverse push"). + fast_forward_local "$local_name_" "$head_" "$note_" + fi } -download_objects () { - # remote_repo="$1" head_sha1="$2" - case "$1" in - http://*) - git-http-pull -a "$2" "$1/" +fast_forward_local () { + case "$1" in + refs/tags/*) + # Tags need not be pointing at commits so there + # is no way to guarantee "fast-forward" anyway. + if test -f "$GIT_DIR/$1" + then + echo >&2 "* $1: updating with $3" + else + echo >&2 "* $1: storing $3" + fi + echo "$2" >"$GIT_DIR/$1" ;; + + refs/heads/*) + # NEEDSWORK: use the same cmpxchg protocol here. + echo "$2" >"$GIT_DIR/$1.lock" + if test -f "$GIT_DIR/$1" + then + local=$(git-rev-parse --verify "$1^0") && + mb=$(git-merge-base "$local" "$2") && + case "$2,$mb" in + $local,*) + echo >&2 "* $1: same as $3" ;; - /*) - git-local-pull -l -a "$2" "$1/" + *,$local) + echo >&2 "* $1: fast forward to $3" ;; - *) - rsync -avz --ignore-existing \ - "$1/objects/." "$GIT_OBJECT_DIRECTORY"/. + *) + false ;; - esac + esac || { + echo >&2 "* $1: does not fast forward to $3;" + case "$force,$single_force" in + t,* | *,t) + echo >&2 " forcing update." + ;; + *) + mv "$GIT_DIR/$1.lock" "$GIT_DIR/$1.remote" + echo >&2 " leaving it in '$1.remote'" + ;; + esac + } + else + echo >&2 "* $1: storing $3" + fi + test -f "$GIT_DIR/$1.lock" && + mv "$GIT_DIR/$1.lock" "$GIT_DIR/$1" + ;; + esac } -echo "Getting remote $merge_name" -download_one "$merge_repo/$merge_name" "$GIT_DIR"/FETCH_HEAD || exit 1 +case "$update_head_ok" in +'') + orig_head=$(cat "$GIT_DIR/HEAD" 2>/dev/null) + ;; +esac + +for ref in $(get_remote_refs_for_fetch "$@") +do + refs="$refs $ref" + + # These are relative path from $GIT_DIR, typically starting at refs/ + # but may be HEAD + if expr "$ref" : '\+' >/dev/null + then + single_force=t + ref=$(expr "$ref" : '\+\(.*\)') + else + single_force= + fi + remote_name=$(expr "$ref" : '\([^:]*\):') + local_name=$(expr "$ref" : '[^:]*:\(.*\)') + + rref="$rref $remote_name" + + # There are transports that can fetch only one head at a time... + case "$remote" in + http://* | https://*) + if [ -n "$GIT_SSL_NO_VERIFY" ]; then + curl_extra_args="-k" + fi + head=$(curl -nsf $curl_extra_args "$remote/$remote_name") && + expr "$head" : "$_x40\$" >/dev/null || + die "Failed to fetch $remote_name from $remote" + echo Fetching "$remote_name from $remote" using http + git-http-pull -v -a "$head" "$remote/" || exit + ;; + rsync://*) + TMP_HEAD="$GIT_DIR/TMP_HEAD" + rsync -L "$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_slurped_objects=t + } + ;; + *) + # We will do git native transport with just one call later. + continue ;; + esac + + append_fetch_head "$head" "$remote" "$remote_name" "$remote_nick" "$local_name" + +done + +case "$remote" in +http://* | https://* | rsync://* ) + ;; # we are already done. +*) + git-fetch-pack "$remote" $rref | + while read sha1 remote_name + do + found= + single_force= + for ref in $refs + do + case "$ref" in + +$remote_name:*) + single_force=t + found="$ref" + break ;; + $remote_name:*) + found="$ref" + break ;; + esac + done + + local_name=$(expr "$found" : '[^:]*:\(.*\)') + append_fetch_head "$sha1" "$remote" "$remote_name" "$remote_nick" "$local_name" + done + ;; +esac -echo "Getting object database" -download_objects "$merge_repo" "$(cat "$GIT_DIR"/FETCH_HEAD)" || exit 1 +# If the original head was empty (i.e. no "master" yet), or +# if we were told not to worry, we do not have to check. +case ",$update_head_ok,$orig_head," in +*,, | t,* ) + ;; +*) + curr_head=$(cat "$GIT_DIR/HEAD" 2>/dev/null) + if test "$curr_head" != "$orig_head" + then + echo "$orig_head" >$GIT_DIR/HEAD + die "Cannot fetch into the current branch." + fi + ;; +esac