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