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" 25shift 26case"$arg"in 27"-b") 28 newbranch="$1" 29shift 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$newbranchalready 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--) 54break 55;; 56-*) 57 usage 58;; 59*) 60ifrev=$(git rev-parse --verify "$arg^0" 2>/dev/null) 61then 62if[-z"$rev"];then 63echo"unknown flag$arg" 64exit1 65fi 66 new_name="$arg" 67if git show-ref --verify --quiet --"refs/heads/$arg" 68then 69rev=$(git rev-parse --verify "refs/heads/$arg^0") 70 branch="$arg" 71fi 72 new="$rev" 73elifrev=$(git rev-parse --verify "$arg^{tree}" 2>/dev/null) 74then 75# checking out selected paths from a tree-ish. 76 new="$rev" 77 new_name="$arg^{tree}" 78 branch= 79else 80 new= 81 new_name= 82 branch= 83set x "$arg""$@" 84shift 85fi 86case"$1"in 87--) 88shift;; 89esac 90break 91;; 92esac 93done 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 --error-unmatch --"$@">/dev/null ||exit 138 git ls-files --"$@"| 139 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_nameto$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 git update-ref --no-deref -m"checkout: moving to$arg" HEAD "$detached"|| 283 die "Cannot detach HEAD" 284iftest -n"$detach_warn" 285then 286echo>&2"$detach_warn" 287fi 288 describe_detached_head 'HEAD is now at' HEAD 289fi 290rm-f"$GIT_DIR/MERGE_HEAD" 291else 292exit1 293fi 294 295# Run a post-checkout hook 296iftest -x"$GIT_DIR"/hooks/post-checkout;then 297"$GIT_DIR"/hooks/post-checkout$old $new1 298fi