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