3250f64ccc683606d420a82b3ec95e2f41bb52b3
   1#!/bin/sh
   2
   3USAGE='[-f] [-b <new_branch>] [-m] [<branch>] [<paths>...]'
   4SUBDIRECTORY_OK=Sometimes
   5. git-sh-setup
   6
   7old_name=HEAD
   8old=$(git-rev-parse --verify $old_name 2>/dev/null)
   9oldbranch=$(git-symbolic-ref $old_name 2>/dev/null)
  10new=
  11new_name=
  12force=
  13branch=
  14newbranch=
  15newbranch_log=
  16merge=
  17while [ "$#" != "0" ]; do
  18    arg="$1"
  19    shift
  20    case "$arg" in
  21        "-b")
  22                newbranch="$1"
  23                shift
  24                [ -z "$newbranch" ] &&
  25                        die "git checkout: -b needs a branch name"
  26                git-show-ref --verify --quiet -- "refs/heads/$newbranch" &&
  27                        die "git checkout: branch $newbranch already exists"
  28                git-check-ref-format "heads/$newbranch" ||
  29                        die "git checkout: we do not like '$newbranch' as a branch name."
  30                ;;
  31        "-l")
  32                newbranch_log=1
  33                ;;
  34        "-f")
  35                force=1
  36                ;;
  37        -m)
  38                merge=1
  39                ;;
  40        --)
  41                break
  42                ;;
  43        -*)
  44                usage
  45                ;;
  46        *)
  47                if rev=$(git-rev-parse --verify "$arg^0" 2>/dev/null)
  48                then
  49                        if [ -z "$rev" ]; then
  50                                echo "unknown flag $arg"
  51                                exit 1
  52                        fi
  53                        new="$rev"
  54                        new_name="$arg^0"
  55                        if git-show-ref --verify --quiet -- "refs/heads/$arg"
  56                        then
  57                                branch="$arg"
  58                        fi
  59                elif rev=$(git-rev-parse --verify "$arg^{tree}" 2>/dev/null)
  60                then
  61                        # checking out selected paths from a tree-ish.
  62                        new="$rev"
  63                        new_name="$arg^{tree}"
  64                        branch=
  65                else
  66                        new=
  67                        new_name=
  68                        branch=
  69                        set x "$arg" "$@"
  70                        shift
  71                fi
  72                case "$1" in
  73                --)
  74                        shift ;;
  75                esac
  76                break
  77                ;;
  78    esac
  79done
  80
  81case "$force$merge" in
  8211)
  83        die "git checkout: -f and -m are incompatible"
  84esac
  85
  86# The behaviour of the command with and without explicit path
  87# parameters is quite different.
  88#
  89# Without paths, we are checking out everything in the work tree,
  90# possibly switching branches.  This is the traditional behaviour.
  91#
  92# With paths, we are _never_ switching branch, but checking out
  93# the named paths from either index (when no rev is given),
  94# or the named tree-ish (when rev is given).
  95
  96if test "$#" -ge 1
  97then
  98        hint=
  99        if test "$#" -eq 1
 100        then
 101                hint="
 102Did you intend to checkout '$@' which can not be resolved as commit?"
 103        fi
 104        if test '' != "$newbranch$force$merge"
 105        then
 106                die "git checkout: updating paths is incompatible with switching branches/forcing$hint"
 107        fi
 108        if test '' != "$new"
 109        then
 110                # from a specific tree-ish; note that this is for
 111                # rescuing paths and is never meant to remove what
 112                # is not in the named tree-ish.
 113                git-ls-tree --full-name -r "$new" "$@" |
 114                git-update-index --index-info || exit $?
 115        fi
 116
 117        # Make sure the request is about existing paths.
 118        git-ls-files --error-unmatch -- "$@" >/dev/null || exit
 119        git-ls-files -- "$@" |
 120        git-checkout-index -f -u --stdin
 121        exit $?
 122else
 123        # Make sure we did not fall back on $arg^{tree} codepath
 124        # since we are not checking out from an arbitrary tree-ish,
 125        # but switching branches.
 126        if test '' != "$new"
 127        then
 128                git-rev-parse --verify "$new^{commit}" >/dev/null 2>&1 ||
 129                die "Cannot switch branch to a non-commit."
 130        fi
 131fi
 132
 133# We are switching branches and checking out trees, so
 134# we *NEED* to be at the toplevel.
 135cdup=$(git-rev-parse --show-cdup)
 136if test ! -z "$cdup"
 137then
 138        cd "$cdup"
 139fi
 140
 141[ -z "$new" ] && new=$old && new_name="$old_name"
 142
 143# If we don't have an existing branch that we're switching to,
 144# and we don't have a new branch name for the target we
 145# are switching to, then we are detaching our HEAD from any
 146# branch.  However, if "git checkout HEAD" detaches the HEAD
 147# from the current branch, even though that may be logically
 148# correct, it feels somewhat funny.  More importantly, we do not
 149# want "git checkout" nor "git checkout -f" to detach HEAD.
 150
 151if test -z "$branch$newbranch" && test "$new" != "$old"
 152then
 153        # NEEDSWORK: we would want to have a command here
 154        # that allows us to detach the HEAD atomically.  Perhaps
 155        # something like "git update-ref --detach HEAD $new"
 156        echo "$new" >"$GIT_DIR/HEAD.new" &&
 157        mv "$GIT_DIR/HEAD.new" "$GIT_DIR/HEAD" || die "Cannot detach HEAD"
 158
 159        if test -n "$oldbranch"
 160        then
 161                echo >&2 "warning: you are not on ANY branch anymore.
 162If you meant to create a new branch from the commit, you need -b to
 163associate a new branch with the wanted checkout.  Example:
 164  git checkout -b <new_branch_name> $arg
 165"
 166        fi
 167fi
 168
 169if [ "X$old" = X ]
 170then
 171        echo >&2 "warning: You appear to be on a branch yet to be born."
 172        echo >&2 "warning: Forcing checkout of $new_name."
 173        force=1
 174fi
 175
 176if [ "$force" ]
 177then
 178    git-read-tree --reset -u $new
 179else
 180    git-update-index --refresh >/dev/null
 181    merge_error=$(git-read-tree -m -u --exclude-per-directory=.gitignore $old $new 2>&1) || (
 182        case "$merge" in
 183        '')
 184                echo >&2 "$merge_error"
 185                exit 1 ;;
 186        esac
 187
 188        # Match the index to the working tree, and do a three-way.
 189        git diff-files --name-only | git update-index --remove --stdin &&
 190        work=`git write-tree` &&
 191        git read-tree --reset -u $new &&
 192        git read-tree -m -u --aggressive --exclude-per-directory=.gitignore $old $new $work ||
 193        exit
 194
 195        if result=`git write-tree 2>/dev/null`
 196        then
 197            echo >&2 "Trivially automerged."
 198        else
 199            git merge-index -o git-merge-one-file -a
 200        fi
 201
 202        # Do not register the cleanly merged paths in the index yet.
 203        # this is not a real merge before committing, but just carrying
 204        # the working tree changes along.
 205        unmerged=`git ls-files -u`
 206        git read-tree --reset $new
 207        case "$unmerged" in
 208        '')     ;;
 209        *)
 210                (
 211                        z40=0000000000000000000000000000000000000000
 212                        echo "$unmerged" |
 213                        sed -e 's/^[0-7]* [0-9a-f]* /'"0 $z40 /"
 214                        echo "$unmerged"
 215                ) | git update-index --index-info
 216                ;;
 217        esac
 218        exit 0
 219    )
 220    saved_err=$?
 221    if test "$saved_err" = 0
 222    then
 223        test "$new" = "$old" || git diff-index --name-status "$new"
 224    fi
 225    (exit $saved_err)
 226fi
 227
 228# 
 229# Switch the HEAD pointer to the new branch if we
 230# checked out a branch head, and remove any potential
 231# old MERGE_HEAD's (subsequent commits will clearly not
 232# be based on them, since we re-set the index)
 233#
 234if [ "$?" -eq 0 ]; then
 235        if [ "$newbranch" ]; then
 236                if [ "$newbranch_log" ]; then
 237                        mkdir -p $(dirname "$GIT_DIR/logs/refs/heads/$newbranch")
 238                        touch "$GIT_DIR/logs/refs/heads/$newbranch"
 239                fi
 240                git-update-ref -m "checkout: Created from $new_name" "refs/heads/$newbranch" $new || exit
 241                branch="$newbranch"
 242        fi
 243        [ "$branch" ] &&
 244        GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD "refs/heads/$branch"
 245        rm -f "$GIT_DIR/MERGE_HEAD"
 246else
 247        exit 1
 248fi