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