Lazily open pack index files on demand
[gitweb.git] / git-fetch.sh
index 9d45dd266a8110a2d39fe29ffeb84aacb6053623..0e05cf1195737d2c7afc4b9447d7b4105908bf77 100755 (executable)
@@ -26,6 +26,7 @@ keep=
 shallow_depth=
 no_progress=
 test -t 1 || no_progress=--no-progress
+quiet=
 while case "$#" in 0) break ;; esac
 do
        case "$1" in
@@ -56,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
                ;;
@@ -110,8 +114,8 @@ ls_remote_result=$(git ls-remote $exec "$remote") ||
 
 append_fetch_head () {
        flags=
-       test -n "$verbose" && flags="$flags -v"
-       test -n "$force" && flags="$flags -f"
+       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 "$@"
 }
@@ -157,7 +161,7 @@ then
        fi
 fi
 
-fetch_native () {
+fetch_all_at_once () {
 
   eval=$(echo "$1" | git-fetch--tool parse-reflist "-")
   eval "$eval"
@@ -165,15 +169,42 @@ fetch_native () {
     ( : subshell because we muck with IFS
       IFS="    $LF"
       (
-       if test -f "$remote" ; then
+       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
-         git-fetch-pack --thin $exec $keep $shallow_depth $no_progress \
-               "$remote" $rref ||
-         echo failed "$remote"
+               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
       ) |
       (
@@ -188,7 +219,7 @@ fetch_native () {
 
 }
 
-fetch_dumb () {
+fetch_per_ref () {
   reflist="$1"
   refs=
   rref=
@@ -233,20 +264,13 @@ fetch_dumb () {
          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" &&
@@ -255,8 +279,9 @@ fetch_dumb () {
          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
@@ -292,10 +317,10 @@ fetch_dumb () {
 fetch_main () {
        case "$remote" in
        http://* | https://* | ftp://* | rsync://* )
-               fetch_dumb "$@"
+               fetch_per_ref "$@"
                ;;
        *)
-               fetch_native "$@"
+               fetch_all_at_once "$@"
                ;;
        esac
 }