git-checkout.shon commit Make sure heads/foo and tags/foo do not confuse things. (9242150)
   1#!/bin/sh
   2. git-sh-setup || die "Not a git archive"
   3
   4usage () {
   5    die "usage: git checkout [-f] [-b <new_branch>] [<branch>] [<paths>...]"
   6}
   7
   8old=$(git-rev-parse HEAD)
   9new=
  10force=
  11branch=
  12newbranch=
  13while [ "$#" != "0" ]; do
  14    arg="$1"
  15    shift
  16    case "$arg" in
  17        "-b")
  18                newbranch="$1"
  19                shift
  20                [ -z "$newbranch" ] &&
  21                        die "git checkout: -b needs a branch name"
  22                [ -e "$GIT_DIR/refs/heads/$newbranch" ] &&
  23                        die "git checkout: branch $newbranch already exists"
  24                git-check-ref-format "heads/$newbranch" ||
  25                        die "we do not like '$newbranch' as a branch name."
  26                ;;
  27        "-f")
  28                force=1
  29                ;;
  30        --)
  31                break
  32                ;;
  33        -*)
  34                usage
  35                ;;
  36        *)
  37                if rev=$(git-rev-parse --verify "$arg^0" 2>/dev/null)
  38                then
  39                        if [ -z "$rev" ]; then
  40                                echo "unknown flag $arg"
  41                                exit 1
  42                        fi
  43                        new="$rev"
  44                        if [ -f "$GIT_DIR/refs/heads/$arg" ]; then
  45                                branch="$arg"
  46                        fi
  47                elif rev=$(git-rev-parse --verify "$arg^{tree}" 2>/dev/null)
  48                then
  49                        # checking out selected paths from a tree-ish.
  50                        new="$rev"
  51                        branch=
  52                else
  53                        new=
  54                        branch=
  55                        set x "$arg" "$@"
  56                        shift
  57                fi
  58                break
  59                ;;
  60    esac
  61done
  62
  63# The behaviour of the command with and without explicit path
  64# parameters is quite different.
  65#
  66# Without paths, we are checking out everything in the work tree,
  67# possibly switching branches.  This is the traditional behaviour.
  68#
  69# With paths, we are _never_ switching branch, but checking out
  70# the named paths from either index (when no rev is given),
  71# or the named tree-ish (when rev is given).
  72
  73if test "$#" -ge 1
  74then
  75        if test '' != "$newbranch$force"
  76        then
  77                die "updating paths and switching branches or forcing are incompatible."
  78        fi
  79        if test '' != "$new"
  80        then
  81                # from a specific tree-ish; note that this is for
  82                # rescuing paths and is never meant to remove what
  83                # is not in the named tree-ish.
  84                git-ls-tree -r "$new" "$@" |
  85                sed -ne 's/^\([0-7]*\) blob \(.*\)$/\1 \2/p' |
  86                git-update-index --index-info || exit $?
  87        fi
  88        git-checkout-index -f -u -- "$@"
  89        exit $?
  90else
  91        # Make sure we did not fall back on $arg^{tree} codepath
  92        # since we are not checking out from an arbitrary tree-ish,
  93        # but switching branches.
  94        if test '' != "$new"
  95        then
  96                git-rev-parse --verify "$new^{commit}" >/dev/null 2>&1 ||
  97                die "Cannot switch branch to a non-commit."
  98        fi
  99fi
 100
 101[ -z "$new" ] && new=$old
 102
 103# If we don't have an old branch that we're switching to,
 104# and we don't have a new branch name for the target we
 105# are switching to, then we'd better just be checking out
 106# what we already had
 107
 108[ -z "$branch$newbranch" ] &&
 109        [ "$new" != "$old" ] &&
 110        die "git checkout: you need to specify a new branch name"
 111
 112if [ "$force" ]
 113then
 114    git-read-tree --reset $new &&
 115        git-checkout-index -q -f -u -a
 116else
 117    git-update-index --refresh >/dev/null
 118    git-read-tree -m -u $old $new
 119fi
 120
 121# 
 122# Switch the HEAD pointer to the new branch if it we
 123# checked out a branch head, and remove any potential
 124# old MERGE_HEAD's (subsequent commits will clearly not
 125# be based on them, since we re-set the index)
 126#
 127if [ "$?" -eq 0 ]; then
 128        if [ "$newbranch" ]; then
 129                leading=`expr "refs/heads/$newbranch" : '\(.*\)/'` &&
 130                mkdir -p "$GIT_DIR/$leading" &&
 131                echo $new >"$GIT_DIR/refs/heads/$newbranch" || exit
 132                branch="$newbranch"
 133        fi
 134        [ "$branch" ] &&
 135        GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD "refs/heads/$branch"
 136        rm -f "$GIT_DIR/MERGE_HEAD"
 137else
 138        exit 1
 139fi