git-checkout.shon commit git-svn: add tests for command-line usage of init and clone commands (41337e2)
   1#!/bin/sh
   2
   3USAGE='[-q] [-f] [-b <new_branch>] [-m] [<branch>] [<paths>...]'
   4SUBDIRECTORY_OK=Sometimes
   5. git-sh-setup
   6require_work_tree
   7
   8old_name=HEAD
   9old=$(git rev-parse --verify $old_name 2>/dev/null)
  10oldbranch=$(git symbolic-ref $old_name 2>/dev/null)
  11new=
  12new_name=
  13force=
  14branch=
  15track=
  16newbranch=
  17newbranch_log=
  18merge=
  19quiet=
  20v=-v
  21LF='
  22'
  23while [ "$#" != "0" ]; do
  24    arg="$1"
  25    shift
  26    case "$arg" in
  27        "-b")
  28                newbranch="$1"
  29                shift
  30                [ -z "$newbranch" ] &&
  31                        die "git checkout: -b needs a branch name"
  32                git show-ref --verify --quiet -- "refs/heads/$newbranch" &&
  33                        die "git checkout: branch $newbranch already exists"
  34                git check-ref-format "heads/$newbranch" ||
  35                        die "git checkout: we do not like '$newbranch' as a branch name."
  36                ;;
  37        "-l")
  38                newbranch_log=-l
  39                ;;
  40        "--track"|"--no-track")
  41                track="$arg"
  42                ;;
  43        "-f")
  44                force=1
  45                ;;
  46        -m)
  47                merge=1
  48                ;;
  49        "-q")
  50                quiet=1
  51                v=
  52                ;;
  53        --)
  54                break
  55                ;;
  56        -*)
  57                usage
  58                ;;
  59        *)
  60                if rev=$(git rev-parse --verify "$arg^0" 2>/dev/null)
  61                then
  62                        if [ -z "$rev" ]; then
  63                                echo "unknown flag $arg"
  64                                exit 1
  65                        fi
  66                        new_name="$arg"
  67                        if git show-ref --verify --quiet -- "refs/heads/$arg"
  68                        then
  69                                rev=$(git rev-parse --verify "refs/heads/$arg^0")
  70                                branch="$arg"
  71                        fi
  72                        new="$rev"
  73                elif rev=$(git rev-parse --verify "$arg^{tree}" 2>/dev/null)
  74                then
  75                        # checking out selected paths from a tree-ish.
  76                        new="$rev"
  77                        new_name="$arg^{tree}"
  78                        branch=
  79                else
  80                        new=
  81                        new_name=
  82                        branch=
  83                        set x "$arg" "$@"
  84                        shift
  85                fi
  86                case "$1" in
  87                --)
  88                        shift ;;
  89                esac
  90                break
  91                ;;
  92    esac
  93done
  94
  95case "$newbranch,$track" in
  96,--*)
  97        die "git checkout: --track and --no-track require -b"
  98esac
  99
 100case "$force$merge" in
 10111)
 102        die "git checkout: -f and -m are incompatible"
 103esac
 104
 105# The behaviour of the command with and without explicit path
 106# parameters is quite different.
 107#
 108# Without paths, we are checking out everything in the work tree,
 109# possibly switching branches.  This is the traditional behaviour.
 110#
 111# With paths, we are _never_ switching branch, but checking out
 112# the named paths from either index (when no rev is given),
 113# or the named tree-ish (when rev is given).
 114
 115if test "$#" -ge 1
 116then
 117        hint=
 118        if test "$#" -eq 1
 119        then
 120                hint="
 121Did you intend to checkout '$@' which can not be resolved as commit?"
 122        fi
 123        if test '' != "$newbranch$force$merge"
 124        then
 125                die "git checkout: updating paths is incompatible with switching branches/forcing$hint"
 126        fi
 127        if test '' != "$new"
 128        then
 129                # from a specific tree-ish; note that this is for
 130                # rescuing paths and is never meant to remove what
 131                # is not in the named tree-ish.
 132                git ls-tree --full-name -r "$new" "$@" |
 133                git update-index --index-info || exit $?
 134        fi
 135
 136        # Make sure the request is about existing paths.
 137        git ls-files --error-unmatch -- "$@" >/dev/null || exit
 138        git ls-files -- "$@" |
 139        git checkout-index -f -u --stdin
 140
 141        # Run a post-checkout hook -- the HEAD does not change so the
 142        # current HEAD is passed in for both args
 143        if test -x "$GIT_DIR"/hooks/post-checkout; then
 144            "$GIT_DIR"/hooks/post-checkout $old $old 0
 145        fi
 146
 147        exit $?
 148else
 149        # Make sure we did not fall back on $arg^{tree} codepath
 150        # since we are not checking out from an arbitrary tree-ish,
 151        # but switching branches.
 152        if test '' != "$new"
 153        then
 154                git rev-parse --verify "$new^{commit}" >/dev/null 2>&1 ||
 155                die "Cannot switch branch to a non-commit."
 156        fi
 157fi
 158
 159# We are switching branches and checking out trees, so
 160# we *NEED* to be at the toplevel.
 161cd_to_toplevel
 162
 163[ -z "$new" ] && new=$old && new_name="$old_name"
 164
 165# If we don't have an existing branch that we're switching to,
 166# and we don't have a new branch name for the target we
 167# are switching to, then we are detaching our HEAD from any
 168# branch.  However, if "git checkout HEAD" detaches the HEAD
 169# from the current branch, even though that may be logically
 170# correct, it feels somewhat funny.  More importantly, we do not
 171# want "git checkout" nor "git checkout -f" to detach HEAD.
 172
 173detached=
 174detach_warn=
 175
 176describe_detached_head () {
 177        test -n "$quiet" || {
 178                printf >&2 "$1 "
 179                GIT_PAGER= git log >&2 -1 --pretty=oneline --abbrev-commit "$2"
 180        }
 181}
 182
 183if test -z "$branch$newbranch" && test "$new_name" != "$old_name"
 184then
 185        detached="$new"
 186        if test -n "$oldbranch" && test -z "$quiet"
 187        then
 188                detach_warn="Note: moving to \"$new_name\" which isn't a local branch
 189If you want to create a new branch from this checkout, you may do so
 190(now or later) by using -b with the checkout command again. Example:
 191  git checkout -b <new_branch_name>"
 192        fi
 193elif test -z "$oldbranch" && test "$new" != "$old"
 194then
 195        describe_detached_head 'Previous HEAD position was' "$old"
 196fi
 197
 198if [ "X$old" = X ]
 199then
 200        if test -z "$quiet"
 201        then
 202                echo >&2 "warning: You appear to be on a branch yet to be born."
 203                echo >&2 "warning: Forcing checkout of $new_name."
 204        fi
 205        force=1
 206fi
 207
 208if [ "$force" ]
 209then
 210    git read-tree $v --reset -u $new
 211else
 212    git update-index --refresh >/dev/null
 213    merge_error=$(git read-tree -m -u --exclude-per-directory=.gitignore $old $new 2>&1) || (
 214        case "$merge" in
 215        '')
 216                echo >&2 "$merge_error"
 217                exit 1 ;;
 218        esac
 219
 220        # Match the index to the working tree, and do a three-way.
 221        git diff-files --name-only | git update-index --remove --stdin &&
 222        work=`git write-tree` &&
 223        git read-tree $v --reset -u $new || exit
 224
 225        eval GITHEAD_$new='${new_name:-${branch:-$new}}' &&
 226        eval GITHEAD_$work=local &&
 227        export GITHEAD_$new GITHEAD_$work &&
 228        git merge-recursive $old -- $new $work
 229
 230        # Do not register the cleanly merged paths in the index yet.
 231        # this is not a real merge before committing, but just carrying
 232        # the working tree changes along.
 233        unmerged=`git ls-files -u`
 234        git read-tree $v --reset $new
 235        case "$unmerged" in
 236        '')     ;;
 237        *)
 238                (
 239                        z40=0000000000000000000000000000000000000000
 240                        echo "$unmerged" |
 241                        sed -e 's/^[0-7]* [0-9a-f]* /'"0 $z40 /"
 242                        echo "$unmerged"
 243                ) | git update-index --index-info
 244                ;;
 245        esac
 246        exit 0
 247    )
 248    saved_err=$?
 249    if test "$saved_err" = 0 && test -z "$quiet"
 250    then
 251        git diff-index --name-status "$new"
 252    fi
 253    (exit $saved_err)
 254fi
 255
 256#
 257# Switch the HEAD pointer to the new branch if we
 258# checked out a branch head, and remove any potential
 259# old MERGE_HEAD's (subsequent commits will clearly not
 260# be based on them, since we re-set the index)
 261#
 262if [ "$?" -eq 0 ]; then
 263        if [ "$newbranch" ]; then
 264                git branch $track $newbranch_log "$newbranch" "$new_name" || exit
 265                branch="$newbranch"
 266        fi
 267        if test -n "$branch"
 268        then
 269                old_branch_name=`expr "z$oldbranch" : 'zrefs/heads/\(.*\)'`
 270                GIT_DIR="$GIT_DIR" git symbolic-ref -m "checkout: moving from $old_branch_name to $branch" HEAD "refs/heads/$branch"
 271                if test -n "$quiet"
 272                then
 273                        true    # nothing
 274                elif test "refs/heads/$branch" = "$oldbranch"
 275                then
 276                        echo >&2 "Already on branch \"$branch\""
 277                else
 278                        echo >&2 "Switched to${newbranch:+ a new} branch \"$branch\""
 279                fi
 280        elif test -n "$detached"
 281        then
 282                git update-ref --no-deref -m "checkout: moving to $arg" HEAD "$detached" ||
 283                        die "Cannot detach HEAD"
 284                if test -n "$detach_warn"
 285                then
 286                        echo >&2 "$detach_warn"
 287                fi
 288                describe_detached_head 'HEAD is now at' HEAD
 289        fi
 290        rm -f "$GIT_DIR/MERGE_HEAD"
 291else
 292        exit 1
 293fi
 294
 295# Run a post-checkout hook
 296if test -x "$GIT_DIR"/hooks/post-checkout; then
 297        "$GIT_DIR"/hooks/post-checkout $old $new 1
 298fi