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 166iftest -z"$branch$newbranch"&&test"$new"!="$old" 167then 168 detached="$new" 169iftest -n"$oldbranch"&&test -z"$quiet" 170then 171 detach_warn="Note: moving to\"$new_name\"which isn't a local branch 172If you want to create a new branch from this checkout, you may do so 173(now or later) by using -b with the checkout command again. Example: 174 git checkout -b <new_branch_name>" 175fi 176eliftest -z"$oldbranch"&&test -z"$quiet" 177then 178echo>&2"Previous HEAD position was$old" 179fi 180 181if["X$old"= X ] 182then 183iftest -z"$quiet" 184then 185echo>&2"warning: You appear to be on a branch yet to be born." 186echo>&2"warning: Forcing checkout of$new_name." 187fi 188 force=1 189fi 190 191if["$force"] 192then 193 git-read-tree --reset -u$new 194else 195 git-update-index --refresh>/dev/null 196 merge_error=$(git-read-tree -m -u --exclude-per-directory=.gitignore $old $new 2>&1)|| ( 197case"$merge"in 198'') 199echo>&2"$merge_error" 200exit1;; 201esac 202 203# Match the index to the working tree, and do a three-way. 204 git diff-files --name-only| git update-index --remove --stdin&& 205 work=`git write-tree`&& 206 git read-tree --reset -u$new||exit 207 208eval GITHEAD_$new='${new_name:-${branch:-$new}}'&& 209eval GITHEAD_$work=local&& 210export GITHEAD_$new GITHEAD_$work&& 211 git merge-recursive$old--$new $work 212 213# Do not register the cleanly merged paths in the index yet. 214# this is not a real merge before committing, but just carrying 215# the working tree changes along. 216 unmerged=`git ls-files -u` 217 git read-tree --reset$new 218case"$unmerged"in 219'') ;; 220*) 221( 222 z40=0000000000000000000000000000000000000000 223echo"$unmerged"| 224sed-e's/^[0-7]* [0-9a-f]* /'"0$z40/" 225echo"$unmerged" 226) | git update-index --index-info 227;; 228esac 229exit0 230) 231 saved_err=$? 232iftest"$saved_err"=0&&test -z"$quiet" 233then 234 git diff-index --name-status"$new" 235fi 236(exit$saved_err) 237fi 238 239# 240# Switch the HEAD pointer to the new branch if we 241# checked out a branch head, and remove any potential 242# old MERGE_HEAD's (subsequent commits will clearly not 243# be based on them, since we re-set the index) 244# 245if["$?"-eq0];then 246if["$newbranch"];then 247 git-branch$track $newbranch_log"$newbranch""$new_name"||exit 248 branch="$newbranch" 249fi 250iftest -n"$branch" 251then 252 GIT_DIR="$GIT_DIR" git-symbolic-ref -m"checkout: moving to$branch" HEAD "refs/heads/$branch" 253iftest -n"$quiet" 254then 255 true # nothing 256eliftest"refs/heads/$branch"="$oldbranch" 257then 258echo>&2"Already on branch\"$branch\"" 259else 260echo>&2"Switched to${newbranch:+ a new}branch\"$branch\"" 261fi 262eliftest -n"$detached" 263then 264# NEEDSWORK: we would want a command to detach the HEAD 265# atomically, instead of this handcrafted command sequence. 266# Perhaps: 267# git update-ref --detach HEAD $new 268# or something like that... 269# 270 git-rev-parse HEAD >"$GIT_DIR/HEAD.new"&& 271mv"$GIT_DIR/HEAD.new""$GIT_DIR/HEAD"&& 272 git-update-ref -m"checkout: moving to$arg" HEAD "$detached"|| 273 die "Cannot detach HEAD" 274iftest -n"$detach_warn" 275then 276echo>&2"$detach_warn" 277fi 278fi 279rm-f"$GIT_DIR/MERGE_HEAD" 280else 281exit1 282fi