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