git-fetch.shon commit git-pickaxe: tighten sanity checks. (ae86ad6)
   1#!/bin/sh
   2#
   3
   4USAGE='<fetch-options> <repository> <refspec>...'
   5. git-sh-setup
   6. git-parse-remote
   7_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
   8_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
   9
  10LF='
  11'
  12IFS="$LF"
  13
  14rloga=fetch
  15no_tags=
  16tags=
  17append=
  18force=
  19verbose=
  20update_head_ok=
  21exec=
  22upload_pack=
  23keep=--thin
  24while case "$#" in 0) break ;; esac
  25do
  26        case "$1" in
  27        -a|--a|--ap|--app|--appe|--appen|--append)
  28                append=t
  29                ;;
  30        --upl|--uplo|--uploa|--upload|--upload-|--upload-p|\
  31        --upload-pa|--upload-pac|--upload-pack)
  32                shift
  33                exec="--exec=$1" 
  34                upload_pack="-u $1"
  35                ;;
  36        -f|--f|--fo|--for|--forc|--force)
  37                force=t
  38                ;;
  39        -t|--t|--ta|--tag|--tags)
  40                tags=t
  41                ;;
  42        -n|--n|--no|--no-|--no-t|--no-ta|--no-tag|--no-tags)
  43                no_tags=t
  44                ;;
  45        -u|--u|--up|--upd|--upda|--updat|--update|--update-|--update-h|\
  46        --update-he|--update-hea|--update-head|--update-head-|\
  47        --update-head-o|--update-head-ok)
  48                update_head_ok=t
  49                ;;
  50        -v|--verbose)
  51                verbose=Yes
  52                ;;
  53        -k|--k|--ke|--kee|--keep)
  54                keep=--keep
  55                ;;
  56        --reflog-action=*)
  57                rloga=`expr "z$1" : 'z-[^=]*=\(.*\)'`
  58                ;;
  59        -*)
  60                usage
  61                ;;
  62        *)
  63                break
  64                ;;
  65        esac
  66        shift
  67done
  68
  69case "$#" in
  700)
  71        origin=$(get_default_remote)
  72        test -n "$(get_remote_url ${origin})" ||
  73                die "Where do you want to fetch from today?"
  74        set x $origin ; shift ;;
  75esac
  76
  77remote_nick="$1"
  78remote=$(get_remote_url "$@")
  79refs=
  80rref=
  81rsync_slurped_objects=
  82
  83rloga="$rloga $remote_nick"
  84test "$remote_nick" = "$remote" || rloga="$rloga $remote"
  85
  86if test "" = "$append"
  87then
  88        : >"$GIT_DIR/FETCH_HEAD"
  89fi
  90
  91append_fetch_head () {
  92    head_="$1"
  93    remote_="$2"
  94    remote_name_="$3"
  95    remote_nick_="$4"
  96    local_name_="$5"
  97    case "$6" in
  98    t) not_for_merge_='not-for-merge' ;;
  99    '') not_for_merge_= ;;
 100    esac
 101
 102    # remote-nick is the URL given on the command line (or a shorthand)
 103    # remote-name is the $GIT_DIR relative refs/ path we computed
 104    # for this refspec.
 105
 106    # the $note_ variable will be fed to git-fmt-merge-msg for further
 107    # processing.
 108    case "$remote_name_" in
 109    HEAD)
 110        note_= ;;
 111    refs/heads/*)
 112        note_="$(expr "$remote_name_" : 'refs/heads/\(.*\)')"
 113        note_="branch '$note_' of " ;;
 114    refs/tags/*)
 115        note_="$(expr "$remote_name_" : 'refs/tags/\(.*\)')"
 116        note_="tag '$note_' of " ;;
 117    refs/remotes/*)
 118        note_="$(expr "$remote_name_" : 'refs/remotes/\(.*\)')"
 119        note_="remote branch '$note_' of " ;;
 120    *)
 121        note_="$remote_name of " ;;
 122    esac
 123    remote_1_=$(expr "z$remote_" : 'z\(.*\)\.git/*$') &&
 124        remote_="$remote_1_"
 125    note_="$note_$remote_"
 126
 127    # 2.6.11-tree tag would not be happy to be fed to resolve.
 128    if git-cat-file commit "$head_" >/dev/null 2>&1
 129    then
 130        headc_=$(git-rev-parse --verify "$head_^0") || exit
 131        echo "$headc_   $not_for_merge_ $note_" >>"$GIT_DIR/FETCH_HEAD"
 132    else
 133        echo "$head_    not-for-merge   $note_" >>"$GIT_DIR/FETCH_HEAD"
 134    fi
 135
 136    update_local_ref "$local_name_" "$head_" "$note_"
 137}
 138
 139update_local_ref () {
 140    # If we are storing the head locally make sure that it is
 141    # a fast forward (aka "reverse push").
 142
 143    label_=$(git-cat-file -t $2)
 144    newshort_=$(git-rev-parse --short $2)
 145    if test -z "$1" ; then
 146        [ "$verbose" ] && echo >&2 "* fetched $3"
 147        [ "$verbose" ] && echo >&2 "  $label_: $newshort_"
 148        return 0
 149    fi
 150    oldshort_=$(git-rev-parse --short "$1" 2>/dev/null)
 151    mkdir -p "$(dirname "$GIT_DIR/$1")"
 152    case "$1" in
 153    refs/tags/*)
 154        # Tags need not be pointing at commits so there
 155        # is no way to guarantee "fast-forward" anyway.
 156        if test -f "$GIT_DIR/$1"
 157        then
 158                if now_=$(cat "$GIT_DIR/$1") && test "$now_" = "$2"
 159                then
 160                        [ "$verbose" ] && echo >&2 "* $1: same as $3"
 161                        [ "$verbose" ] && echo >&2 "  $label_: $newshort_" ||:
 162                else
 163                        echo >&2 "* $1: updating with $3"
 164                        echo >&2 "  $label_: $newshort_"
 165                        git-update-ref -m "$rloga: updating tag" "$1" "$2"
 166                fi
 167        else
 168                echo >&2 "* $1: storing $3"
 169                echo >&2 "  $label_: $newshort_"
 170                git-update-ref -m "$rloga: storing tag" "$1" "$2"
 171        fi
 172        ;;
 173
 174    refs/heads/* | refs/remotes/*)
 175        # $1 is the ref being updated.
 176        # $2 is the new value for the ref.
 177        local=$(git-rev-parse --verify "$1^0" 2>/dev/null)
 178        if test "$local"
 179        then
 180            # Require fast-forward.
 181            mb=$(git-merge-base "$local" "$2") &&
 182            case "$2,$mb" in
 183            $local,*)
 184                if test -n "$verbose"
 185                then
 186                        echo >&2 "* $1: same as $3"
 187                        echo >&2 "  $label_: $newshort_"
 188                fi
 189                ;;
 190            *,$local)
 191                echo >&2 "* $1: fast forward to $3"
 192                echo >&2 "  old..new: $oldshort_..$newshort_"
 193                git-update-ref -m "$rloga: fast-forward" "$1" "$2" "$local"
 194                ;;
 195            *)
 196                false
 197                ;;
 198            esac || {
 199                case ",$force,$single_force," in
 200                *,t,*)
 201                        echo >&2 "* $1: forcing update to non-fast forward $3"
 202                        echo >&2 "  old...new: $oldshort_...$newshort_"
 203                        git-update-ref -m "$rloga: forced-update" "$1" "$2" "$local"
 204                        ;;
 205                *)
 206                        echo >&2 "* $1: not updating to non-fast forward $3"
 207                        echo >&2 "  old...new: $oldshort_...$newshort_"
 208                        exit 1
 209                        ;;
 210                esac
 211            }
 212        else
 213            echo >&2 "* $1: storing $3"
 214            echo >&2 "  $label_: $newshort_"
 215            git-update-ref -m "$rloga: storing head" "$1" "$2"
 216        fi
 217        ;;
 218    esac
 219}
 220
 221case "$update_head_ok" in
 222'')
 223        orig_head=$(git-rev-parse --verify HEAD 2>/dev/null)
 224        ;;
 225esac
 226
 227# If --tags (and later --heads or --all) is specified, then we are
 228# not talking about defaults stored in Pull: line of remotes or
 229# branches file, and just fetch those and refspecs explicitly given.
 230# Otherwise we do what we always did.
 231
 232reflist=$(get_remote_refs_for_fetch "$@")
 233if test "$tags"
 234then
 235        taglist=`IFS="  " &&
 236                  (
 237                        git-ls-remote $upload_pack --tags "$remote" ||
 238                        echo fail ouch
 239                  ) |
 240                  while read sha1 name
 241                  do
 242                        case "$sha1" in
 243                        fail)
 244                                exit 1
 245                        esac
 246                        case "$name" in
 247                        *^*) continue ;;
 248                        esac
 249                        if git-check-ref-format "$name"
 250                        then
 251                            echo ".${name}:${name}"
 252                        else
 253                            echo >&2 "warning: tag ${name} ignored"
 254                        fi
 255                  done` || exit
 256        if test "$#" -gt 1
 257        then
 258                # remote URL plus explicit refspecs; we need to merge them.
 259                reflist="$reflist$LF$taglist"
 260        else
 261                # No explicit refspecs; fetch tags only.
 262                reflist=$taglist
 263        fi
 264fi
 265
 266fetch_main () {
 267  reflist="$1"
 268  refs=
 269  rref=
 270
 271  for ref in $reflist
 272  do
 273      refs="$refs$LF$ref"
 274
 275      # These are relative path from $GIT_DIR, typically starting at refs/
 276      # but may be HEAD
 277      if expr "z$ref" : 'z\.' >/dev/null
 278      then
 279          not_for_merge=t
 280          ref=$(expr "z$ref" : 'z\.\(.*\)')
 281      else
 282          not_for_merge=
 283      fi
 284      if expr "z$ref" : 'z+' >/dev/null
 285      then
 286          single_force=t
 287          ref=$(expr "z$ref" : 'z+\(.*\)')
 288      else
 289          single_force=
 290      fi
 291      remote_name=$(expr "z$ref" : 'z\([^:]*\):')
 292      local_name=$(expr "z$ref" : 'z[^:]*:\(.*\)')
 293
 294      rref="$rref$LF$remote_name"
 295
 296      # There are transports that can fetch only one head at a time...
 297      case "$remote" in
 298      http://* | https://* | ftp://*)
 299          if [ -n "$GIT_SSL_NO_VERIFY" ]; then
 300              curl_extra_args="-k"
 301          fi
 302          if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \
 303                "`git-repo-config --bool http.noEPSV`" = true ]; then
 304              noepsv_opt="--disable-epsv"
 305          fi
 306          max_depth=5
 307          depth=0
 308          head="ref: $remote_name"
 309          while (expr "z$head" : "zref:" && expr $depth \< $max_depth) >/dev/null
 310          do
 311            remote_name_quoted=$(@@PERL@@ -e '
 312              my $u = $ARGV[0];
 313              $u =~ s/^ref:\s*//;
 314              $u =~ s{([^-a-zA-Z0-9/.])}{sprintf"%%%02x",ord($1)}eg;
 315              print "$u";
 316          ' "$head")
 317            head=$(curl -nsfL $curl_extra_args $noepsv_opt "$remote/$remote_name_quoted")
 318            depth=$( expr \( $depth + 1 \) )
 319          done
 320          expr "z$head" : "z$_x40\$" >/dev/null ||
 321              die "Failed to fetch $remote_name from $remote"
 322          echo >&2 Fetching "$remote_name from $remote" using http
 323          git-http-fetch -v -a "$head" "$remote/" || exit
 324          ;;
 325      rsync://*)
 326          TMP_HEAD="$GIT_DIR/TMP_HEAD"
 327          rsync -L -q "$remote/$remote_name" "$TMP_HEAD" || exit 1
 328          head=$(git-rev-parse --verify TMP_HEAD)
 329          rm -f "$TMP_HEAD"
 330          test "$rsync_slurped_objects" || {
 331              rsync -av --ignore-existing --exclude info \
 332                  "$remote/objects/" "$GIT_OBJECT_DIRECTORY/" || exit
 333
 334              # Look at objects/info/alternates for rsync -- http will
 335              # support it natively and git native ones will do it on
 336              # the remote end.  Not having that file is not a crime.
 337              rsync -q "$remote/objects/info/alternates" \
 338                  "$GIT_DIR/TMP_ALT" 2>/dev/null ||
 339                  rm -f "$GIT_DIR/TMP_ALT"
 340              if test -f "$GIT_DIR/TMP_ALT"
 341              then
 342                  resolve_alternates "$remote" <"$GIT_DIR/TMP_ALT" |
 343                  while read alt
 344                  do
 345                      case "$alt" in 'bad alternate: '*) die "$alt";; esac
 346                      echo >&2 "Getting alternate: $alt"
 347                      rsync -av --ignore-existing --exclude info \
 348                      "$alt" "$GIT_OBJECT_DIRECTORY/" || exit
 349                  done
 350                  rm -f "$GIT_DIR/TMP_ALT"
 351              fi
 352              rsync_slurped_objects=t
 353          }
 354          ;;
 355      *)
 356          # We will do git native transport with just one call later.
 357          continue ;;
 358      esac
 359
 360      append_fetch_head "$head" "$remote" \
 361          "$remote_name" "$remote_nick" "$local_name" "$not_for_merge"
 362
 363  done
 364
 365  case "$remote" in
 366  http://* | https://* | ftp://* | rsync://* )
 367      ;; # we are already done.
 368  *)
 369    ( : subshell because we muck with IFS
 370      IFS="     $LF"
 371      (
 372          git-fetch-pack $exec $keep "$remote" $rref || echo failed "$remote"
 373      ) |
 374      while read sha1 remote_name
 375      do
 376          case "$sha1" in
 377          failed)
 378                  echo >&2 "Fetch failure: $remote"
 379                  exit 1 ;;
 380          esac
 381          found=
 382          single_force=
 383          for ref in $refs
 384          do
 385              case "$ref" in
 386              +$remote_name:*)
 387                  single_force=t
 388                  not_for_merge=
 389                  found="$ref"
 390                  break ;;
 391              .+$remote_name:*)
 392                  single_force=t
 393                  not_for_merge=t
 394                  found="$ref"
 395                  break ;;
 396              .$remote_name:*)
 397                  not_for_merge=t
 398                  found="$ref"
 399                  break ;;
 400              $remote_name:*)
 401                  not_for_merge=
 402                  found="$ref"
 403                  break ;;
 404              esac
 405          done
 406          local_name=$(expr "z$found" : 'z[^:]*:\(.*\)')
 407          append_fetch_head "$sha1" "$remote" \
 408                  "$remote_name" "$remote_nick" "$local_name" "$not_for_merge"
 409      done
 410    ) || exit ;;
 411  esac
 412
 413}
 414
 415fetch_main "$reflist"
 416
 417# automated tag following
 418case "$no_tags$tags" in
 419'')
 420        case "$reflist" in
 421        *:refs/*)
 422                # effective only when we are following remote branch
 423                # using local tracking branch.
 424                taglist=$(IFS=" " &&
 425                git-ls-remote $upload_pack --tags "$remote" |
 426                sed -ne 's|^\([0-9a-f]*\)[      ]\(refs/tags/.*\)^{}$|\1 \2|p' |
 427                while read sha1 name
 428                do
 429                        test -f "$GIT_DIR/$name" && continue
 430                        git-check-ref-format "$name" || {
 431                                echo >&2 "warning: tag ${name} ignored"
 432                                continue
 433                        }
 434                        git-cat-file -t "$sha1" >/dev/null 2>&1 || continue
 435                        echo >&2 "Auto-following $name"
 436                        echo ".${name}:${name}"
 437                done)
 438        esac
 439        case "$taglist" in
 440        '') ;;
 441        ?*)
 442                fetch_main "$taglist" ;;
 443        esac
 444esac
 445
 446# If the original head was empty (i.e. no "master" yet), or
 447# if we were told not to worry, we do not have to check.
 448case "$orig_head" in
 449'')
 450        ;;
 451?*)
 452        curr_head=$(git-rev-parse --verify HEAD 2>/dev/null)
 453        if test "$curr_head" != "$orig_head"
 454        then
 455            git-update-ref \
 456                        -m "$rloga: Undoing incorrectly fetched HEAD." \
 457                        HEAD "$orig_head"
 458                die "Cannot fetch into the current branch."
 459        fi
 460        ;;
 461esac