#!/bin/sh # setglobal USAGE = '' ...'' setglobal SUBDIRECTORY_OK = 'Yes' source git-sh-setup set_reflog_action "fetch $ifsjoin(Argv)" cd_to_toplevel ;# probably unnecessary... source git-parse-remote setglobal _x40 = ''[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'' setglobal _x40 = ""$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"" setglobal LF = '' '' setglobal IFS = $LF setglobal no_tags = '' setglobal tags = '' setglobal append = '' setglobal force = '' setglobal verbose = '' setglobal update_head_ok = '' setglobal exec = '' setglobal keep = '' setglobal shallow_depth = '' setglobal no_progress = '' test -t 1 || setglobal no_progress = '--no-progress' setglobal quiet = '' while test $# != 0 { match $1 { with -a|--a|--ap|--app|--appe|--appen|--append setglobal append = 't' with --upl|--uplo|--uploa|--upload|--upload-|--upload-p|\ --upload-pa|--upload-pac|--upload-pack shift setglobal exec = ""--upload-pack=$1"" with --upl=*|--uplo=*|--uploa=*|--upload=*|\ --upload-=*|--upload-p=*|--upload-pa=*|--upload-pac=*|--upload-pack=* setglobal exec = "--upload-pack=$[expr "z$1" : 'z-[^=]*=\(.*\)]" shift with -f|--f|--fo|--for|--forc|--force setglobal force = 't' with -t|--t|--ta|--tag|--tags setglobal tags = 't' with -n|--n|--no|--no-|--no-t|--no-ta|--no-tag|--no-tags setglobal no_tags = 't' with -u|--u|--up|--upd|--upda|--updat|--update|--update-|--update-h|\ --update-he|--update-hea|--update-head|--update-head-|\ --update-head-o|--update-head-ok setglobal update_head_ok = 't' with -q|--q|--qu|--qui|--quie|--quiet setglobal quiet = '--quiet' with -v|--verbose setglobal verbose = ""$verbose"Yes" with -k|--k|--ke|--kee|--keep setglobal keep = ''-k -k'' with --depth=* setglobal shallow_depth = ""--depth=$[expr "z$1" : 'z-[^=]*=\(.*\)]"" with --depth shift setglobal shallow_depth = ""--depth=$1"" with -* usage with * break } shift } match "$Argc" { with 0 setglobal origin = $[get_default_remote] test -n $[get_remote_url $(origin)] || die "Where do you want to fetch from today?" set x $origin ; shift } if test -z $exec { # No command line override and we have configuration for the remote. setglobal exec = ""--upload-pack=$[get_uploadpack $1]"" } setglobal remote_nick = $1 setglobal remote = $[get_remote_url @Argv] setglobal refs = '' setglobal rref = '' setglobal rsync_slurped_objects = '' if test "" = $append { : >"$GIT_DIR/FETCH_HEAD" } # Global that is reused later setglobal ls_remote_result = $[git ls-remote $exec $remote] || die "Cannot get the repository state from $remote" proc append_fetch_head { setglobal flags = '' test -n $verbose && setglobal flags = ""$flags$LF-v"" test -n "$force$single_force" && setglobal flags = ""$flags$LF-f"" env GIT_REFLOG_ACTION=$GIT_REFLOG_ACTION \ git fetch--tool $flags append-fetch-head @Argv } # updating the current HEAD with git-fetch in a bare # repository is always fine. if test -z $update_head_ok && test $[is_bare_repository] = false { setglobal orig_head = $[git rev-parse --verify HEAD !2 >/dev/null] } # Allow --tags/--notags from remote.$1.tagopt match "$tags$no_tags" { with '' match $[git config --get "remote.$1.tagopt] { with --tags setglobal tags = 't' with --no-tags setglobal no_tags = 't' } } # If --tags (and later --heads or --all) is specified, then we are # not talking about defaults stored in Pull: line of remotes or # branches file, and just fetch those and refspecs explicitly given. # Otherwise we do what we always did. setglobal reflist = $[get_remote_refs_for_fetch @Argv] if test $tags { setglobal taglist = $[setglobal IFS = '' '' && echo $ls_remote_result | git show-ref --exclude-existing=refs/tags/ | while read sha1 name { echo ".$(name):$(name)" }] || exit if test "$Argc" -gt 1 { # remote URL plus explicit refspecs; we need to merge them. setglobal reflist = ""$reflist$LF$taglist"" } else { # No explicit refspecs; fetch tags only. setglobal reflist = $taglist } } proc fetch_all_at_once { setglobal eval = $[echo $1 | git fetch--tool parse-reflist "-] eval $eval shell { : subshell because we muck with IFS setglobal IFS = "" $LF"" shell { if test $remote = . { git show-ref $rref || echo failed $remote } elif test -f $remote { test -n $shallow_depth && die "shallow clone with bundle is not supported" git bundle unbundle $remote $rref || echo failed $remote } else { if test -d $remote && # The remote might be our alternate. With # this optimization we will bypass fetch-pack # altogether, which means we cannot be doing # the shallow stuff at all. test ! -f "$GIT_DIR/shallow" && test -z $shallow_depth && # See if all of what we are going to fetch are # connected to our repository's tips, in which # case we do not have to do any fetch. setglobal theirs = $[echo $ls_remote_result | \ git fetch--tool -s pick-rref $rref "-] && # This will barf when $theirs reach an object that # we do not have in our repository. Otherwise, # we already have everything the fetch would bring in. git rev-list --objects $theirs --not --all \ >/dev/null !2 >/dev/null { echo $ls_remote_result | \ git fetch--tool pick-rref $rref "-" } else { setglobal flags = '' match $verbose { with YesYes* setglobal flags = '"-v'" } git-fetch-pack --thin $exec $keep $shallow_depth \ $quiet $no_progress $flags $remote $rref || echo failed $remote } } } | shell { setglobal flags = '' test -n $verbose && setglobal flags = ""$flags -v"" test -n $force && setglobal flags = ""$flags -f"" env GIT_REFLOG_ACTION=$GIT_REFLOG_ACTION \ git fetch--tool $flags native-store \ $remote $remote_nick $refs } } || exit } proc fetch_per_ref { setglobal reflist = $1 setglobal refs = '' setglobal rref = '' for ref in [$reflist] { setglobal refs = ""$refs$LF$ref"" # These are relative path from $GIT_DIR, typically starting at refs/ # but may be HEAD if expr "z$ref" : 'z\.' >/dev/null { setglobal not_for_merge = 't' setglobal ref = $[expr "z$ref" : 'z\.\(.*\)] } else { setglobal not_for_merge = '' } if expr "z$ref" : 'z+' >/dev/null { setglobal single_force = 't' setglobal ref = $[expr "z$ref" : 'z+\(.*\)] } else { setglobal single_force = '' } setglobal remote_name = $[expr "z$ref" : 'z\([^:]*\):] setglobal local_name = $[expr "z$ref" : 'z[^:]*:\(.*\)] setglobal rref = ""$rref$LF$remote_name"" # There are transports that can fetch only one head at a time... match $remote { with http://* | https://* | ftp://* test -n $shallow_depth && die "shallow clone with http not supported" setglobal proto = $[expr $remote : '\([^:]*\):] if test -n $GIT_SSL_NO_VERIFY { setglobal curl_extra_args = '"-k'" } if test -n $GIT_CURL_FTP_NO_EPSV -o \ $[git config --bool http.noEPSV] = true { setglobal noepsv_opt = '"--disable-epsv'" } # Find $remote_name from ls-remote output. setglobal head = $[echo $ls_remote_result | \ git fetch--tool -s pick-rref $remote_name "-] expr "z$head" : "z$_x40\$" >/dev/null || die "No such ref $remote_name at $remote" echo >&2 "Fetching $remote_name from $remote using $proto> !2 "Fetching $remote_name from $remote using $proto" match $quiet { with '' setglobal v = '-v' with * setglobal v = '' } git-http-fetch $v -a $head $remote || exit with rsync://* test -n $shallow_depth && die "shallow clone with rsync not supported" setglobal TMP_HEAD = ""$GIT_DIR/TMP_HEAD"" rsync -L -q "$remote/$remote_name" $TMP_HEAD || exit 1 setglobal head = $[git rev-parse --verify TMP_HEAD] rm -f $TMP_HEAD match $quiet { with '' setglobal v = '-v' with * setglobal v = '' } test $rsync_slurped_objects || do { rsync -a $v --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" !2 >/dev/null || rm -f "$GIT_DIR/TMP_ALT" if test -f "$GIT_DIR/TMP_ALT" { resolve_alternates $remote <"$GIT_DIR/TMP_ALT" | while read alt { match $alt { with 'bad alternate: '* die $alt } echo >&2 "Getting alternate: $alt> !2 "Getting alternate: $alt" rsync -av --ignore-existing --exclude info \ $alt "$GIT_OBJECT_DIRECTORY/" || exit } rm -f "$GIT_DIR/TMP_ALT" } setglobal rsync_slurped_objects = 't' } } append_fetch_head $head $remote \ $remote_name $remote_nick $local_name $not_for_merge || exit } } proc fetch_main { match $remote { with http://* | https://* | ftp://* | rsync://* fetch_per_ref @Argv with * fetch_all_at_once @Argv } } fetch_main $reflist || exit # automated tag following match "$no_tags$tags" { with '' match $reflist { with *:refs/* # effective only when we are following remote branch # using local tracking branch. setglobal taglist = $[setglobal IFS = '' '' && echo $ls_remote_result | git show-ref --exclude-existing=refs/tags/ | while read sha1 name { git cat-file -t $sha1 >/dev/null !2 > !1 || continue echo >&2 "Auto-following $name> !2 "Auto-following $name" echo ".$(name):$(name)" }] } match $taglist { with '' with ?* # do not deepen a shallow tree when following tags setglobal shallow_depth = '' fetch_main $taglist || exit } } # 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. match $orig_head { with '' with ?* setglobal curr_head = $[git rev-parse --verify HEAD !2 >/dev/null] if test $curr_head != $orig_head { git update-ref \ -m "$GIT_REFLOG_ACTION: Undoing incorrectly fetched HEAD." \ HEAD $orig_head die "Cannot fetch into the current branch." } }