Lazily open pack index files on demand
[gitweb.git] / git-fetch.sh
index 87b940b85b5b128f29dc6084bb545640e5bf6b92..0e05cf1195737d2c7afc4b9447d7b4105908bf77 100755 (executable)
@@ -22,9 +22,11 @@ force=
 verbose=
 update_head_ok=
 exec=
-upload_pack=
 keep=
 shallow_depth=
+no_progress=
+test -t 1 || no_progress=--no-progress
+quiet=
 while case "$#" in 0) break ;; esac
 do
        case "$1" in
@@ -34,8 +36,12 @@ do
        --upl|--uplo|--uploa|--upload|--upload-|--upload-p|\
        --upload-pa|--upload-pac|--upload-pack)
                shift
-               exec="--exec=$1" 
-               upload_pack="-u $1"
+               exec="--upload-pack=$1"
+               ;;
+       --upl=*|--uplo=*|--uploa=*|--upload=*|\
+       --upload-=*|--upload-p=*|--upload-pa=*|--upload-pac=*|--upload-pack=*)
+               exec=--upload-pack=$(expr "z$1" : 'z-[^=]*=\(.*\)')
+               shift
                ;;
        -f|--f|--fo|--for|--forc|--force)
                force=t
@@ -51,6 +57,9 @@ do
        --update-head-o|--update-head-ok)
                update_head_ok=t
                ;;
+       -q|--q|--qu|--qui|--quie|--quiet)
+               quiet=--quiet
+               ;;
        -v|--verbose)
                verbose=Yes
                ;;
@@ -82,6 +91,12 @@ case "$#" in
        set x $origin ; shift ;;
 esac
 
+if test -z "$exec"
+then
+       # No command line override and we have configuration for the remote.
+       exec="--upload-pack=$(get_uploadpack $1)"
+fi
+
 remote_nick="$1"
 remote=$(get_remote_url "$@")
 refs=
@@ -94,137 +109,15 @@ then
 fi
 
 # Global that is reused later
-ls_remote_result=$(git ls-remote $upload_pack "$remote") ||
+ls_remote_result=$(git ls-remote $exec "$remote") ||
        die "Cannot get the repository state from $remote"
 
 append_fetch_head () {
-    head_="$1"
-    remote_="$2"
-    remote_name_="$3"
-    remote_nick_="$4"
-    local_name_="$5"
-    case "$6" in
-    t) not_for_merge_='not-for-merge' ;;
-    '') not_for_merge_= ;;
-    esac
-
-    # 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.
-
-    # the $note_ variable will be fed to git-fmt-merge-msg for further
-    # processing.
-    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 " ;;
-    refs/remotes/*)
-       note_="$(expr "$remote_name_" : 'refs/remotes/\(.*\)')"
-       note_="remote branch '$note_' of " ;;
-    *)
-       note_="$remote_name of " ;;
-    esac
-    remote_1_=$(expr "z$remote_" : 'z\(.*\)\.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_   $not_for_merge_ $note_" >>"$GIT_DIR/FETCH_HEAD"
-    else
-       echo "$head_    not-for-merge   $note_" >>"$GIT_DIR/FETCH_HEAD"
-    fi
-
-    update_local_ref "$local_name_" "$head_" "$note_"
-}
-
-update_local_ref () {
-    # If we are storing the head locally make sure that it is
-    # a fast forward (aka "reverse push").
-
-    label_=$(git-cat-file -t $2)
-    newshort_=$(git-rev-parse --short $2)
-    if test -z "$1" ; then
-       [ "$verbose" ] && echo >&2 "* fetched $3"
-       [ "$verbose" ] && echo >&2 "  $label_: $newshort_"
-       return 0
-    fi
-    oldshort_=$(git show-ref --hash --abbrev "$1" 2>/dev/null)
-
-    case "$1" in
-    refs/tags/*)
-       # Tags need not be pointing at commits so there
-       # is no way to guarantee "fast-forward" anyway.
-       if test -n "$oldshort_"
-       then
-               if now_=$(git show-ref --hash "$1") && test "$now_" = "$2"
-               then
-                       [ "$verbose" ] && echo >&2 "* $1: same as $3"
-                       [ "$verbose" ] && echo >&2 "  $label_: $newshort_" ||:
-               else
-                       echo >&2 "* $1: updating with $3"
-                       echo >&2 "  $label_: $newshort_"
-                       git-update-ref -m "$GIT_REFLOG_ACTION: updating tag" "$1" "$2"
-               fi
-       else
-               echo >&2 "* $1: storing $3"
-               echo >&2 "  $label_: $newshort_"
-               git-update-ref -m "$GIT_REFLOG_ACTION: storing tag" "$1" "$2"
-       fi
-       ;;
-
-    refs/heads/* | refs/remotes/*)
-       # $1 is the ref being updated.
-       # $2 is the new value for the ref.
-       local=$(git-rev-parse --verify "$1^0" 2>/dev/null)
-       if test "$local"
-       then
-           # Require fast-forward.
-           mb=$(git-merge-base "$local" "$2") &&
-           case "$2,$mb" in
-           $local,*)
-               if test -n "$verbose"
-               then
-                       echo >&2 "* $1: same as $3"
-                       echo >&2 "  $label_: $newshort_"
-               fi
-               ;;
-           *,$local)
-               echo >&2 "* $1: fast forward to $3"
-               echo >&2 "  old..new: $oldshort_..$newshort_"
-               git-update-ref -m "$GIT_REFLOG_ACTION: fast-forward" "$1" "$2" "$local"
-               ;;
-           *)
-               false
-               ;;
-           esac || {
-               case ",$force,$single_force," in
-               *,t,*)
-                       echo >&2 "* $1: forcing update to non-fast forward $3"
-                       echo >&2 "  old...new: $oldshort_...$newshort_"
-                       git-update-ref -m "$GIT_REFLOG_ACTION: forced-update" "$1" "$2" "$local"
-                       ;;
-               *)
-                       echo >&2 "* $1: not updating to non-fast forward $3"
-                       echo >&2 "  old...new: $oldshort_...$newshort_"
-                       exit 1
-                       ;;
-               esac
-           }
-       else
-           echo >&2 "* $1: storing $3"
-           echo >&2 "  $label_: $newshort_"
-           git-update-ref -m "$GIT_REFLOG_ACTION: storing head" "$1" "$2"
-       fi
-       ;;
-    esac
+       flags=
+       test -n "$verbose" && flags="$flags$LF-v"
+       test -n "$force$single_force" && flags="$flags$LF-f"
+       GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION" \
+               git-fetch--tool $flags append-fetch-head "$@"
 }
 
 # updating the current HEAD with git-fetch in a bare
@@ -234,6 +127,15 @@ then
        orig_head=$(git-rev-parse --verify HEAD 2>/dev/null)
 fi
 
+# Allow --notags from remote.$1.tagopt
+case "$tags$no_tags" in
+'')
+       case "$(git-config --get "remote.$1.tagopt")" in
+       --no-tags)
+               no_tags=t ;;
+       esac
+esac
+
 # 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.
@@ -244,23 +146,10 @@ if test "$tags"
 then
        taglist=`IFS='  ' &&
                  echo "$ls_remote_result" |
+                 git-show-ref --exclude-existing=refs/tags/ |
                  while read sha1 name
                  do
-                       case "$sha1" in
-                       fail)
-                               exit 1
-                       esac
-                       case "$name" in
-                       *^*) continue ;;
-                       refs/tags/*) ;;
-                       *) continue ;;
-                       esac
-                       if git-check-ref-format "$name"
-                       then
-                           echo ".${name}:${name}"
-                       else
-                           echo >&2 "warning: tag ${name} ignored"
-                       fi
+                       echo ".${name}:${name}"
                  done` || exit
        if test "$#" -gt 1
        then
@@ -272,7 +161,65 @@ then
        fi
 fi
 
-fetch_main () {
+fetch_all_at_once () {
+
+  eval=$(echo "$1" | git-fetch--tool parse-reflist "-")
+  eval "$eval"
+
+    ( : subshell because we muck with IFS
+      IFS="    $LF"
+      (
+       if test "$remote" = . ; then
+           git-show-ref $rref || echo failed "$remote"
+       elif test -f "$remote" ; then
+           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.
+                       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
+               then
+                       echo "$ls_remote_result" | \
+                               git-fetch--tool pick-rref "$rref" "-"
+               else
+                       git-fetch-pack --thin $exec $keep $shallow_depth \
+                               $quiet $no_progress "$remote" $rref ||
+                       echo failed "$remote"
+               fi
+       fi
+      ) |
+      (
+       flags=
+       test -n "$verbose" && flags="$flags -v"
+       test -n "$force" && flags="$flags -f"
+       GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION" \
+               git-fetch--tool $flags native-store \
+                       "$remote" "$remote_nick" "$refs"
+      )
+    ) || exit
+
+}
+
+fetch_per_ref () {
   reflist="$1"
   refs=
   rref=
@@ -312,25 +259,18 @@ fetch_main () {
              curl_extra_args="-k"
          fi
          if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \
-               "`git-repo-config --bool http.noEPSV`" = true ]; then
+               "`git-config --bool http.noEPSV`" = true ]; then
              noepsv_opt="--disable-epsv"
          fi
 
          # Find $remote_name from ls-remote output.
-         head=$(
-               IFS='   '
-               echo "$ls_remote_result" |
-               while read sha1 name
-               do
-                       test "z$name" = "z$remote_name" || continue
-                       echo "$sha1"
-                       break
-               done
-         )
+         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"
-         git-http-fetch -v -a "$head" "$remote/" || exit
+         case "$quiet" in '') v=-v ;; *) v= ;; esac
+         git-http-fetch $v -a "$head" "$remote" || exit
          ;;
       rsync://*)
          test -n "$shallow_depth" &&
@@ -339,8 +279,9 @@ fetch_main () {
          rsync -L -q "$remote/$remote_name" "$TMP_HEAD" || exit 1
          head=$(git-rev-parse --verify TMP_HEAD)
          rm -f "$TMP_HEAD"
+         case "$quiet" in '') v=-v ;; *) v= ;; esac
          test "$rsync_slurped_objects" || {
-             rsync -av --ignore-existing --exclude info \
+             rsync -a $v --ignore-existing --exclude info \
                  "$remote/objects/" "$GIT_OBJECT_DIRECTORY/" || exit
 
              # Look at objects/info/alternates for rsync -- http will
@@ -364,9 +305,6 @@ fetch_main () {
              rsync_slurped_objects=t
          }
          ;;
-      *)
-         # We will do git native transport with just one call later.
-         continue ;;
       esac
 
       append_fetch_head "$head" "$remote" \
@@ -374,72 +312,17 @@ fetch_main () {
 
   done
 
-  case "$remote" in
-  http://* | https://* | ftp://* | rsync://* )
-      ;; # we are already done.
-  *)
-    ( : subshell because we muck with IFS
-      IFS="    $LF"
-      (
-         git-fetch-pack --thin $exec $keep $shallow_depth "$remote" $rref ||
-         echo failed "$remote"
-      ) |
-      (
-       trap '
-               if test -n "$keepfile" && test -f "$keepfile"
-               then
-                       rm -f "$keepfile"
-               fi
-       ' 0
-
-        keepfile=
-       while read sha1 remote_name
-       do
-         case "$sha1" in
-         failed)
-                 echo >&2 "Fetch failure: $remote"
-                 exit 1 ;;
-         # special line coming from index-pack with the pack name
-         pack)
-                 continue ;;
-         keep)
-                 keepfile="$GIT_OBJECT_DIRECTORY/pack/pack-$remote_name.keep"
-                 continue ;;
-         esac
-         found=
-         single_force=
-         for ref in $refs
-         do
-             case "$ref" in
-             +$remote_name:*)
-                 single_force=t
-                 not_for_merge=
-                 found="$ref"
-                 break ;;
-             .+$remote_name:*)
-                 single_force=t
-                 not_for_merge=t
-                 found="$ref"
-                 break ;;
-             .$remote_name:*)
-                 not_for_merge=t
-                 found="$ref"
-                 break ;;
-             $remote_name:*)
-                 not_for_merge=
-                 found="$ref"
-                 break ;;
-             esac
-         done
-         local_name=$(expr "z$found" : 'z[^:]*:\(.*\)')
-         append_fetch_head "$sha1" "$remote" \
-                 "$remote_name" "$remote_nick" "$local_name" \
-                 "$not_for_merge" || exit
-        done
-      )
-    ) || exit ;;
-  esac
+}
 
+fetch_main () {
+       case "$remote" in
+       http://* | https://* | ftp://* | rsync://* )
+               fetch_per_ref "$@"
+               ;;
+       *)
+               fetch_all_at_once "$@"
+               ;;
+       esac
 }
 
 fetch_main "$reflist" || exit