git-checkout.shon commit Disallow working directory commands in a bare repository. (7eff28a)
   1#!/bin/sh
   2
   3USAGE='[-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)
  10new=
  11new_name=
  12force=
  13branch=
  14newbranch=
  15newbranch_log=
  16merge=
  17while [ "$#" != "0" ]; do
  18    arg="$1"
  19    shift
  20    case "$arg" in
  21        "-b")
  22                newbranch="$1"
  23                shift
  24                [ -z "$newbranch" ] &&
  25                        die "git checkout: -b needs a branch name"
  26                git-show-ref --verify --quiet -- "refs/heads/$newbranch" &&
  27                        die "git checkout: branch $newbranch already exists"
  28                git-check-ref-format "heads/$newbranch" ||
  29                        die "git checkout: we do not like '$newbranch' as a branch name."
  30                ;;
  31        "-l")
  32                newbranch_log=1
  33                ;;
  34        "-f")
  35                force=1
  36                ;;
  37        -m)
  38                merge=1
  39                ;;
  40        --)
  41                break
  42                ;;
  43        -*)
  44                usage
  45                ;;
  46        *)
  47                if rev=$(git-rev-parse --verify "$arg^0" 2>/dev/null)
  48                then
  49                        if [ -z "$rev" ]; then
  50                                echo "unknown flag $arg"
  51                                exit 1
  52                        fi
  53                        new="$rev"
  54                        new_name="$arg^0"
  55                        if git-show-ref --verify --quiet -- "refs/heads/$arg"
  56                        then
  57                                branch="$arg"
  58                        fi
  59                elif rev=$(git-rev-parse --verify "$arg^{tree}" 2>/dev/null)
  60                then
  61                        # checking out selected paths from a tree-ish.
  62                        new="$rev"
  63                        new_name="$arg^{tree}"
  64                        branch=
  65                else
  66                        new=
  67                        new_name=
  68                        branch=
  69                        set x "$arg" "$@"
  70                        shift
  71                fi
  72                case "$1" in
  73                --)
  74                        shift ;;
  75                esac
  76                break
  77                ;;
  78    esac
  79done
  80
  81case "$force$merge" in
  8211)
  83        die "git checkout: -f and -m are incompatible"
  84esac
  85
  86# The behaviour of the command with and without explicit path
  87# parameters is quite different.
  88#
  89# Without paths, we are checking out everything in the work tree,
  90# possibly switching branches.  This is the traditional behaviour.
  91#
  92# With paths, we are _never_ switching branch, but checking out
  93# the named paths from either index (when no rev is given),
  94# or the named tree-ish (when rev is given).
  95
  96if test "$#" -ge 1
  97then
  98        hint=
  99        if test "$#" -eq 1
 100        then
 101                hint="
 102Did you intend to checkout '$@' which can not be resolved as commit?"
 103        fi
 104        if test '' != "$newbranch$force$merge"
 105        then
 106                die "git checkout: updating paths is incompatible with switching branches/forcing$hint"
 107        fi
 108        if test '' != "$new"
 109        then
 110                # from a specific tree-ish; note that this is for
 111                # rescuing paths and is never meant to remove what
 112                # is not in the named tree-ish.
 113                git-ls-tree --full-name -r "$new" "$@" |
 114                git-update-index --index-info || exit $?
 115        fi
 116
 117        # Make sure the request is about existing paths.
 118        git-ls-files --error-unmatch -- "$@" >/dev/null || exit
 119        git-ls-files -- "$@" |
 120        git-checkout-index -f -u --stdin
 121        exit $?
 122else
 123        # Make sure we did not fall back on $arg^{tree} codepath
 124        # since we are not checking out from an arbitrary tree-ish,
 125        # but switching branches.
 126        if test '' != "$new"
 127        then
 128                git-rev-parse --verify "$new^{commit}" >/dev/null 2>&1 ||
 129                die "Cannot switch branch to a non-commit."
 130        fi
 131fi
 132
 133# We are switching branches and checking out trees, so
 134# we *NEED* to be at the toplevel.
 135cdup=$(git-rev-parse --show-cdup)
 136if test ! -z "$cdup"
 137then
 138        cd "$cdup"
 139fi
 140
 141[ -z "$new" ] && new=$old && new_name="$old_name"
 142
 143# If we don't have an old branch that we're switching to,
 144# and we don't have a new branch name for the target we
 145# are switching to, then we'd better just be checking out
 146# what we already had
 147
 148[ -z "$branch$newbranch" ] &&
 149        [ "$new" != "$old" ] &&
 150        die "git checkout: provided reference cannot be checked out directly
 151
 152  You need -b to associate a new branch with the wanted checkout. Example:
 153  git checkout -b <new_branch_name> $arg
 154"
 155
 156if [ "X$old" = X ]
 157then
 158        echo "warning: You do not appear to currently be on a branch." >&2
 159        echo "warning: Forcing checkout of $new_name." >&2
 160        force=1
 161fi
 162
 163if [ "$force" ]
 164then
 165    git-read-tree --reset -u $new
 166else
 167    git-update-index --refresh >/dev/null
 168    merge_error=$(git-read-tree -m -u --exclude-per-directory=.gitignore $old $new 2>&1) || (
 169        case "$merge" in
 170        '')
 171                echo >&2 "$merge_error"
 172                exit 1 ;;
 173        esac
 174
 175        # Match the index to the working tree, and do a three-way.
 176        git diff-files --name-only | git update-index --remove --stdin &&
 177        work=`git write-tree` &&
 178        git read-tree --reset -u $new &&
 179        git read-tree -m -u --aggressive --exclude-per-directory=.gitignore $old $new $work ||
 180        exit
 181
 182        if result=`git write-tree 2>/dev/null`
 183        then
 184            echo >&2 "Trivially automerged."
 185        else
 186            git merge-index -o git-merge-one-file -a
 187        fi
 188
 189        # Do not register the cleanly merged paths in the index yet.
 190        # this is not a real merge before committing, but just carrying
 191        # the working tree changes along.
 192        unmerged=`git ls-files -u`
 193        git read-tree --reset $new
 194        case "$unmerged" in
 195        '')     ;;
 196        *)
 197                (
 198                        z40=0000000000000000000000000000000000000000
 199                        echo "$unmerged" |
 200                        sed -e 's/^[0-7]* [0-9a-f]* /'"0 $z40 /"
 201                        echo "$unmerged"
 202                ) | git update-index --index-info
 203                ;;
 204        esac
 205        exit 0
 206    )
 207    saved_err=$?
 208    if test "$saved_err" = 0
 209    then
 210        test "$new" = "$old" || git diff-index --name-status "$new"
 211    fi
 212    (exit $saved_err)
 213fi
 214
 215# 
 216# Switch the HEAD pointer to the new branch if we
 217# checked out a branch head, and remove any potential
 218# old MERGE_HEAD's (subsequent commits will clearly not
 219# be based on them, since we re-set the index)
 220#
 221if [ "$?" -eq 0 ]; then
 222        if [ "$newbranch" ]; then
 223                if [ "$newbranch_log" ]; then
 224                        mkdir -p $(dirname "$GIT_DIR/logs/refs/heads/$newbranch")
 225                        touch "$GIT_DIR/logs/refs/heads/$newbranch"
 226                fi
 227                git-update-ref -m "checkout: Created from $new_name" "refs/heads/$newbranch" $new || exit
 228                branch="$newbranch"
 229        fi
 230        [ "$branch" ] &&
 231        GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD "refs/heads/$branch"
 232        rm -f "$GIT_DIR/MERGE_HEAD"
 233else
 234        exit 1
 235fi