From 853a3697dc2bc609650c38ed03a1f8fa9f145f97 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 20 Aug 2005 02:54:34 -0700 Subject: [PATCH] Multi-head fetch. Traditionally, fetch takes these forms: $ git fetch $ git fetch $ git fetch tag This patch updates it to take $ git fetch ... where: - A of form ":" is to fetch the objects needed for the remote ref that matches , and if is not empty, store it as a local . - "tag" followed by is just an old way of saying "refs/tags/:refs/tags/"; this mimics the current behaviour of the third form above and means "fetch that tag and store it under the same name". - A single token without colon is a shorthand for ":" That is, "fetch that ref but do not store anywhere". - when there is no specified - if is the name of a file under $GIT_DIR/remotes/ (i.e. a new-style shorthand), then it is the same as giving the s listed on Pull: line in that file. - if is the name of a file under $GIT_DIR/branches/ (i.e. an old-style shorthand, without trailing path), then it is the same as giving a single ":refs/heads/" on the command line, where is the remote branch name (defaults to HEAD, but can be overridden by .git/branches/ file having the URL fragment notation). That is, "fetch that branch head and store it in refs/heads/". - otherwise, it is the same as giving a single that is "HEAD:". The SHA1 object names of fetched refs are stored in FETCH_HEAD, one name per line, with a comment to describe where it came from. This is later used by "git resolve" and "git octopus". Signed-off-by: Junio C Hamano --- git-fetch-script | 189 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 147 insertions(+), 42 deletions(-) (limited to 'git-fetch-script') diff --git a/git-fetch-script b/git-fetch-script index ea097144f7..9b05e41176 100755 --- a/git-fetch-script +++ b/git-fetch-script @@ -1,54 +1,159 @@ #!/bin/sh # . git-sh-setup-script || die "Not a git archive" -. git-parse-remote "$@" -merge_repo="$_remote_repo" -merge_head="$_remote_head" -merge_store="$_remote_store" - -TMP_HEAD="$GIT_DIR/TMP_HEAD" - -case "$merge_repo" in -http://* | https://*) - if [ -n "$GIT_SSL_NO_VERIFY" ]; then - curl_extra_args="-k" - fi - _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" && - head=$(curl -nsf $curl_extra_args "$merge_repo/$merge_head") && - expr "$head" : "$_x40\$" >/dev/null || { - echo >&2 "Failed to fetch $merge_head from $merge_repo" - exit 1 - } - echo Fetching "$merge_head" using http - git-http-pull -v -a "$head" "$merge_repo/" || exit - ;; -rsync://*) - rsync -L "$merge_repo/$merge_head" "$TMP_HEAD" || exit 1 - head=$(git-rev-parse TMP_HEAD) - rm -f "$TMP_HEAD" - rsync -avz --ignore-existing "$merge_repo/objects/" "$GIT_OBJECT_DIRECTORY/" - ;; +. 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" + +append= +case "$#" in +0) + die "Where do you want to fetch from?" ;; *) - head=$(git-fetch-pack "$merge_repo" "$merge_head") - if h=`expr "$head" : '\([^ ][^ ]*\) '` + case "$1" in + -a|--a|--ap|--app|--appe|--appen|--append) + append=t + shift ;; + esac +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" + + # 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 + head_=$(git-rev-parse --verify "$head_^0") || exit + note_="$head_ $remote_name_ from $remote_nick_" + echo "$note_" >>$GIT_DIR/FETCH_HEAD + echo >&2 "* committish: $note_" + else + echo >&2 "* non-commit: $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_" "$remote_" "$remote_name_" + fi +} + +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. + 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 - head=$h + local=$(git-rev-parse --verify "$1^0") && + mb=$(git-merge-base "$local" "$2") && + case "$2,$mb" in + $local,*) + echo >&2 "* $1: same as $4" + echo >&2 " from $3" + ;; + *,$local) + echo >&2 "* $1: fast forward to $4" + echo >&2 " from $3" + ;; + *) + false + ;; + esac || { + mv "$GIT_DIR/$1.lock" "$GIT_DIR/$1.remote" + echo >&2 "* $1: does not fast forward to $4" + echo >&2 " from $3; leaving it in '$1.remote'" + } + else + echo >&2 "* $1: storing $4" + echo >&2 " from $3." fi + test -f "$GIT_DIR/$1.lock" && + mv "$GIT_DIR/$1.lock" "$GIT_DIR/$1" ;; -esac || exit 1 + esac +} + +for ref in $(get_remote_refs_for_fetch "$@") +do + refs="$refs $ref" -git-rev-parse --verify "$head" > /dev/null || exit 1 + # These are relative path from $GIT_DIR, typically starting at refs/ + # but may be HEAD + remote_name=$(expr "$ref" : '\([^:]*\):') + local_name=$(expr "$ref" : '[^:]*:\(.*\)') -case "$merge_store" in -'') + 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 ;; -*) - echo "$head" > "$GIT_DIR/$merge_store" -esac && + 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" -# FETCH_HEAD is fed to git-resolve-script which will eventually be -# passed to git-commit-tree as one of the parents. Make sure we do -# not give a tag object ID. +done + +case "$remote" in +http://* | https://* | rsync://* ) + ;; # we are already done. +*) + git-fetch-pack "$remote" $rref | + while read sha1 remote_name + do + found= + for ref in $refs + do + case "$ref" in + $remote_name:*) + found="$ref" + break ;; + esac + done -git-rev-parse "$head^0" >"$GIT_DIR/FETCH_HEAD" + local_name=$(expr "$found" : '[^:]*:\(.*\)') + append_fetch_head "$sha1" "$remote" "$remote_name" "$remote_nick" "$local_name" + done + ;; +esac -- cgit v1.3-5-g9baa From 92c533ef0ea2cb43f0d7d493f006f5b4dfa7cda1 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 20 Aug 2005 03:00:03 -0700 Subject: [PATCH] Make "git pull" and "git fetch" default to origin Amos Waterland sent in a patch for the pre-multi-head aware version of "git pull" to do this, but the code changed quite a bit since then. If there is no argument given to pull from, and if "origin" makes sense, default to fetch/pull from "origin" instead of barfing. [jc: besides, the patch by Amos broke the non-default case where explicit refspecs are specified, and did not make sure we know what "origin" means before defaulting to it.] Signed-off-by: Junio C Hamano --- git-fetch-script | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'git-fetch-script') diff --git a/git-fetch-script b/git-fetch-script index 9b05e41176..a70909e4ff 100755 --- a/git-fetch-script +++ b/git-fetch-script @@ -8,7 +8,10 @@ _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" append= case "$#" in 0) - die "Where do you want to fetch from?" ;; + test -f "$GIT_DIR/branches/origin" || + test -f "$GIT_DIR/remotes/origin" || + die "Where do you want to fetch from?" + set origin ;; *) case "$1" in -a|--a|--ap|--app|--appe|--appen|--append) -- cgit v1.3-5-g9baa From ae2da40690a3f3f101de5780a7799781b09e2e05 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 22 Aug 2005 21:28:33 -0700 Subject: [PATCH] "git fetch --force". Just like "git push" can forcibly update a ref to a value that is not a fast-forward, teach "git fetch" to do so as well. Signed-off-by: Junio C Hamano --- git-fetch-script | 45 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) (limited to 'git-fetch-script') diff --git a/git-fetch-script b/git-fetch-script index a70909e4ff..dc7f4d6e44 100755 --- a/git-fetch-script +++ b/git-fetch-script @@ -6,19 +6,32 @@ _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" append= +force= +while case "$#" in 0) break ;; esac +do + case "$1" in + -a|--a|--ap|--app|--appe|--appen|--append) + append=t + shift + ;; + -f|--f|--fo|--for|--forc|--force) + force=t + shift + ;; + *) + break + ;; + esac +done + case "$#" in 0) test -f "$GIT_DIR/branches/origin" || test -f "$GIT_DIR/remotes/origin" || die "Where do you want to fetch from?" set origin ;; -*) - case "$1" in - -a|--a|--ap|--app|--appe|--appen|--append) - append=t - shift ;; - esac esac + remote_nick="$1" remote=$(get_remote_url "$@") refs= @@ -60,7 +73,16 @@ fast_forward_local () { 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 $4" + echo >&2 " from $3." + else + echo >&2 "* $1: storing $4" + echo >&2 " from $3." + fi echo "$2" >"$GIT_DIR/$1" ;; + refs/heads/*) # NEEDSWORK: use the same cmpxchg protocol here. echo "$2" >"$GIT_DIR/$1.lock" @@ -81,9 +103,16 @@ fast_forward_local () { false ;; esac || { - mv "$GIT_DIR/$1.lock" "$GIT_DIR/$1.remote" echo >&2 "* $1: does not fast forward to $4" - echo >&2 " from $3; leaving it in '$1.remote'" + case "$force" in + t) + echo >&2 " from $3; forcing update." + ;; + *) + mv "$GIT_DIR/$1.lock" "$GIT_DIR/$1.remote" + echo >&2 " from $3; leaving it in '$1.remote'" + ;; + esac } else echo >&2 "* $1: storing $4" -- cgit v1.3-5-g9baa From efe9bf0f3b34d12b3b76f8277550f91700609b25 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 22 Aug 2005 22:52:43 -0700 Subject: [PATCH] Allow "+remote:local" refspec to cause --force when fetching. With this we could say: Pull: master:ko-master +pu:ko-pu to mean "fast forward ko-master with master, overwrite ko-pu with pu", and the latter one does not require the remote "pu" to be descendant of local "ko-pu". Signed-off-by: Junio C Hamano --- git-fetch-script | 18 +++++++++++++++--- git-parse-remote-script | 15 +++++++++++---- 2 files changed, 26 insertions(+), 7 deletions(-) (limited to 'git-fetch-script') diff --git a/git-fetch-script b/git-fetch-script index dc7f4d6e44..d55cc85620 100755 --- a/git-fetch-script +++ b/git-fetch-script @@ -104,8 +104,8 @@ fast_forward_local () { ;; esac || { echo >&2 "* $1: does not fast forward to $4" - case "$force" in - t) + case "$force,$single_force" in + t,* | *,t) echo >&2 " from $3; forcing update." ;; *) @@ -130,6 +130,13 @@ do # 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" : '[^:]*:\(.*\)') @@ -175,9 +182,14 @@ http://* | https://* | rsync://* ) 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 ;; @@ -185,7 +197,7 @@ http://* | https://* | rsync://* ) done local_name=$(expr "$found" : '[^:]*:\(.*\)') - append_fetch_head "$sha1" "$remote" "$remote_name" "$remote_nick" "$local_name" + append_fetch_head "$sha1" "$remote" "$remote_name" "$remote_nick" "$local_name" done ;; esac diff --git a/git-parse-remote-script b/git-parse-remote-script index 2da7ae8470..cf37884256 100755 --- a/git-parse-remote-script +++ b/git-parse-remote-script @@ -5,7 +5,7 @@ get_data_source () { case "$1" in */*) - # Not so fast. This could be the partial URL shorthand... + # Not so fast. This could be the partial URL shorthand... token=$(expr "$1" : '\([^/]*\)/') remainder=$(expr "$1" : '[^/]*/\(.*\)') if test -f "$GIT_DIR/branches/$token" @@ -69,6 +69,13 @@ get_remote_default_refs_for_push () { canon_refs_list_for_fetch () { for ref do + force= + case "$ref" in + +*) + ref=$(expr "$ref" : '\+\(.*\)') + force=+ + ;; + esac expr "$ref" : '.*:' >/dev/null || ref="${ref}:" remote=$(expr "$ref" : '\([^:]*\):') local=$(expr "$ref" : '[^:]*:\(.*\)') @@ -80,7 +87,7 @@ canon_refs_list_for_fetch () { '') local= ;; *) local="refs/heads/$local" ;; esac - echo "${remote}:${local}" + echo "${force}${remote}:${local}" done } @@ -132,12 +139,12 @@ get_remote_refs_for_fetch () { else case "$ref" in tag) - tag_just_seen=yes + tag_just_seen=yes continue ;; esac fi - canon_refs_list_for_fetch "$ref" + canon_refs_list_for_fetch "$ref" done ;; esac -- cgit v1.3-5-g9baa