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