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