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