git-checkout.shon commit Merge branch 'maint' of git://linux-nfs.org/~bfields/git into maint (dc3e3ea)
   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        exit $?
 141else
 142        # Make sure we did not fall back on $arg^{tree} codepath
 143        # since we are not checking out from an arbitrary tree-ish,
 144        # but switching branches.
 145        if test '' != "$new"
 146        then
 147                git rev-parse --verify "$new^{commit}" >/dev/null 2>&1 ||
 148                die "Cannot switch branch to a non-commit."
 149        fi
 150fi
 151
 152# We are switching branches and checking out trees, so
 153# we *NEED* to be at the toplevel.
 154cd_to_toplevel
 155
 156[ -z "$new" ] && new=$old && new_name="$old_name"
 157
 158# If we don't have an existing branch that we're switching to,
 159# and we don't have a new branch name for the target we
 160# are switching to, then we are detaching our HEAD from any
 161# branch.  However, if "git checkout HEAD" detaches the HEAD
 162# from the current branch, even though that may be logically
 163# correct, it feels somewhat funny.  More importantly, we do not
 164# want "git checkout" nor "git checkout -f" to detach HEAD.
 165
 166detached=
 167detach_warn=
 168
 169describe_detached_head () {
 170        test -n "$quiet" || {
 171                printf >&2 "$1 "
 172                GIT_PAGER= git log >&2 -1 --pretty=oneline --abbrev-commit "$2" --
 173        }
 174}
 175
 176if test -z "$branch$newbranch" && test "$new_name" != "$old_name"
 177then
 178        detached="$new"
 179        if test -n "$oldbranch" && test -z "$quiet"
 180        then
 181                detach_warn="Note: moving to \"$new_name\" which isn't a local branch
 182If you want to create a new branch from this checkout, you may do so
 183(now or later) by using -b with the checkout command again. Example:
 184  git checkout -b <new_branch_name>"
 185        fi
 186elif test -z "$oldbranch" && test "$new" != "$old"
 187then
 188        describe_detached_head 'Previous HEAD position was' "$old"
 189fi
 190
 191if [ "X$old" = X ]
 192then
 193        if test -z "$quiet"
 194        then
 195                echo >&2 "warning: You appear to be on a branch yet to be born."
 196                echo >&2 "warning: Forcing checkout of $new_name."
 197        fi
 198        force=1
 199fi
 200
 201if [ "$force" ]
 202then
 203    git read-tree $v --reset -u $new
 204else
 205    git update-index --refresh >/dev/null
 206    merge_error=$(git read-tree -m -u --exclude-per-directory=.gitignore $old $new 2>&1) || (
 207        case "$merge" in
 208        '')
 209                echo >&2 "$merge_error"
 210                exit 1 ;;
 211        esac
 212
 213        # Match the index to the working tree, and do a three-way.
 214        git diff-files --name-only | git update-index --remove --stdin &&
 215        work=`git write-tree` &&
 216        git read-tree $v --reset -u $new || exit
 217
 218        eval GITHEAD_$new='${new_name:-${branch:-$new}}' &&
 219        eval GITHEAD_$work=local &&
 220        export GITHEAD_$new GITHEAD_$work &&
 221        git merge-recursive $old -- $new $work
 222
 223        # Do not register the cleanly merged paths in the index yet.
 224        # this is not a real merge before committing, but just carrying
 225        # the working tree changes along.
 226        unmerged=`git ls-files -u`
 227        git read-tree $v --reset $new
 228        case "$unmerged" in
 229        '')     ;;
 230        *)
 231                (
 232                        z40=0000000000000000000000000000000000000000
 233                        echo "$unmerged" |
 234                        sed -e 's/^[0-7]* [0-9a-f]* /'"0 $z40 /"
 235                        echo "$unmerged"
 236                ) | git update-index --index-info
 237                ;;
 238        esac
 239        exit 0
 240    )
 241    saved_err=$?
 242    if test "$saved_err" = 0 && test -z "$quiet"
 243    then
 244        git diff-index --name-status "$new"
 245    fi
 246    (exit $saved_err)
 247fi
 248
 249#
 250# Switch the HEAD pointer to the new branch if we
 251# checked out a branch head, and remove any potential
 252# old MERGE_HEAD's (subsequent commits will clearly not
 253# be based on them, since we re-set the index)
 254#
 255if [ "$?" -eq 0 ]; then
 256        if [ "$newbranch" ]; then
 257                git branch $track $newbranch_log "$newbranch" "$new_name" || exit
 258                branch="$newbranch"
 259        fi
 260        if test -n "$branch"
 261        then
 262                old_branch_name=`expr "z$oldbranch" : 'zrefs/heads/\(.*\)'`
 263                GIT_DIR="$GIT_DIR" git symbolic-ref -m "checkout: moving from $old_branch_name to $branch" HEAD "refs/heads/$branch"
 264                if test -n "$quiet"
 265                then
 266                        true    # nothing
 267                elif test "refs/heads/$branch" = "$oldbranch"
 268                then
 269                        echo >&2 "Already on branch \"$branch\""
 270                else
 271                        echo >&2 "Switched to${newbranch:+ a new} branch \"$branch\""
 272                fi
 273        elif test -n "$detached"
 274        then
 275                git update-ref --no-deref -m "checkout: moving to $arg" HEAD "$detached" ||
 276                        die "Cannot detach HEAD"
 277                if test -n "$detach_warn"
 278                then
 279                        echo >&2 "$detach_warn"
 280                fi
 281                describe_detached_head 'HEAD is now at' HEAD
 282        fi
 283        rm -f "$GIT_DIR/MERGE_HEAD"
 284else
 285        exit 1
 286fi