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