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 git read-tree$v-m -u --exclude-per-directory=.gitignore $old $new|| ( 214case"$merge,$v"in 215,*) 216exit1;; 2171,) 218;;# quiet 219*) 220echo>&2"Falling back to 3-way merge...";; 221esac 222 223# Match the index to the working tree, and do a three-way. 224 git diff-files --name-only| git update-index --remove --stdin&& 225 work=`git write-tree`&& 226 git read-tree$v--reset -u$new||exit 227 228eval GITHEAD_$new='${new_name:-${branch:-$new}}'&& 229eval GITHEAD_$work=local&& 230export GITHEAD_$new GITHEAD_$work&& 231 git merge-recursive$old--$new $work 232 233# Do not register the cleanly merged paths in the index yet. 234# this is not a real merge before committing, but just carrying 235# the working tree changes along. 236 unmerged=`git ls-files -u` 237 git read-tree$v--reset$new 238case"$unmerged"in 239'') ;; 240*) 241( 242 z40=0000000000000000000000000000000000000000 243echo"$unmerged"| 244sed-e's/^[0-7]* [0-9a-f]* /'"0$z40/" 245echo"$unmerged" 246) | git update-index --index-info 247;; 248esac 249exit0 250) 251 saved_err=$? 252iftest"$saved_err"=0&&test -z"$quiet" 253then 254 git diff-index --name-status"$new" 255fi 256(exit$saved_err) 257fi 258 259# 260# Switch the HEAD pointer to the new branch if we 261# checked out a branch head, and remove any potential 262# old MERGE_HEAD's (subsequent commits will clearly not 263# be based on them, since we re-set the index) 264# 265if["$?"-eq0];then 266if["$newbranch"];then 267 git branch $track $newbranch_log"$newbranch""$new_name"||exit 268 branch="$newbranch" 269fi 270iftest -n"$branch" 271then 272 old_branch_name=`expr "z$oldbranch" : 'zrefs/heads/\(.*\)'` 273 GIT_DIR="$GIT_DIR" git symbolic-ref -m"checkout: moving from${old_branch_name:-$old}to$branch" HEAD "refs/heads/$branch" 274iftest -n"$quiet" 275then 276 true # nothing 277eliftest"refs/heads/$branch"="$oldbranch" 278then 279echo>&2"Already on branch\"$branch\"" 280else 281echo>&2"Switched to${newbranch:+ a new}branch\"$branch\"" 282fi 283eliftest -n"$detached" 284then 285 old_branch_name=`expr "z$oldbranch" : 'zrefs/heads/\(.*\)'` 286 git update-ref --no-deref -m"checkout: moving from${old_branch_name:-$old}to$arg" HEAD "$detached"|| 287 die "Cannot detach HEAD" 288iftest -n"$detach_warn" 289then 290echo>&2"$detach_warn" 291fi 292 describe_detached_head 'HEAD is now at' HEAD 293fi 294rm-f"$GIT_DIR/MERGE_HEAD" 295else 296exit1 297fi 298 299# Run a post-checkout hook 300iftest -x"$GIT_DIR"/hooks/post-checkout;then 301"$GIT_DIR"/hooks/post-checkout$old $new1 302fi