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