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