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