1#!/bin/sh 2 3USAGE='[-f] [-b <new_branch>] [-m] [<branch>] [<paths>...]' 4SUBDIRECTORY_OK=Sometimes 5. git-sh-setup 6 7old=$(git-rev-parse HEAD) 8new= 9force= 10branch= 11newbranch= 12merge= 13while["$#"!="0"];do 14 arg="$1" 15shift 16case"$arg"in 17"-b") 18 newbranch="$1" 19shift 20[-z"$newbranch"] && 21 die "git checkout: -b needs a branch name" 22[-e"$GIT_DIR/refs/heads/$newbranch"] && 23 die "git checkout: branch$newbranchalready exists" 24 git-check-ref-format"heads/$newbranch"|| 25 die "git checkout: we do not like '$newbranch' as a branch name." 26;; 27"-f") 28 force=1 29;; 30-m) 31 merge=1 32;; 33--) 34break 35;; 36-*) 37 usage 38;; 39*) 40ifrev=$(git-rev-parse --verify "$arg^0" 2>/dev/null) 41then 42if[-z"$rev"];then 43echo"unknown flag$arg" 44exit1 45fi 46 new="$rev" 47if[-f"$GIT_DIR/refs/heads/$arg"];then 48 branch="$arg" 49fi 50elifrev=$(git-rev-parse --verify "$arg^{tree}" 2>/dev/null) 51then 52# checking out selected paths from a tree-ish. 53 new="$rev" 54 branch= 55else 56 new= 57 branch= 58set x "$arg""$@" 59shift 60fi 61break 62;; 63esac 64done 65 66# The behaviour of the command with and without explicit path 67# parameters is quite different. 68# 69# Without paths, we are checking out everything in the work tree, 70# possibly switching branches. This is the traditional behaviour. 71# 72# With paths, we are _never_ switching branch, but checking out 73# the named paths from either index (when no rev is given), 74# or the named tree-ish (when rev is given). 75 76iftest"$#"-ge1 77then 78 hint= 79iftest"$#"-eq1 80then 81 hint=" 82Did you intend to checkout '$@' which can not be resolved as commit?" 83fi 84iftest''!="$newbranch$force$merge" 85then 86 die "git checkout: updating paths is incompatible with switching branches/forcing$hint" 87fi 88iftest''!="$new" 89then 90# from a specific tree-ish; note that this is for 91# rescuing paths and is never meant to remove what 92# is not in the named tree-ish. 93 git-ls-tree --full-name -r"$new""$@"| 94 git-update-index --index-info||exit $? 95fi 96 git-checkout-index -f -u --"$@" 97exit $? 98else 99# Make sure we did not fall back on $arg^{tree} codepath 100# since we are not checking out from an arbitrary tree-ish, 101# but switching branches. 102iftest''!="$new" 103then 104 git-rev-parse --verify"$new^{commit}">/dev/null 2>&1|| 105 die "Cannot switch branch to a non-commit." 106fi 107fi 108 109# We are switching branches and checking out trees, so 110# we *NEED* to be at the toplevel. 111cdup=$(git-rev-parse --show-cdup) 112iftest!-z"$cdup" 113then 114cd"$cdup" 115fi 116 117[-z"$new"] && new=$old 118 119# If we don't have an old branch that we're switching to, 120# and we don't have a new branch name for the target we 121# are switching to, then we'd better just be checking out 122# what we already had 123 124[-z"$branch$newbranch"] && 125["$new"!="$old"] && 126 die "git checkout: to checkout the requested commit you need to specify 127 a name for a new branch which is created and switched to" 128 129if["$force"] 130then 131 git-read-tree --reset$new&& 132 git-checkout-index -q -f -u -a 133else 134 git-update-index --refresh>/dev/null 135 merge_error=$(git-read-tree -m -u $old $new 2>&1)|| ( 136case"$merge"in 137'') 138echo>&2"$merge_error" 139exit1;; 140esac 141 142# Match the index to the working tree, and do a three-way. 143 git diff-files --name-only| git update-index --remove --stdin&& 144 work=`git write-tree`&& 145 git read-tree --reset$new&& 146 git checkout-index -f -u -q -a&& 147 git read-tree -m -u --aggressive$old $new $work||exit 148 149if result=`git write-tree 2>/dev/null` 150then 151echo>&2"Trivially automerged." 152else 153 git merge-index -o git-merge-one-file -a 154fi 155 156# Do not register the cleanly merged paths in the index yet. 157# this is not a real merge before committing, but just carrying 158# the working tree changes along. 159 unmerged=`git ls-files -u` 160 git read-tree --reset$new 161case"$unmerged"in 162'') ;; 163*) 164( 165 z40=0000000000000000000000000000000000000000 166echo"$unmerged"| 167sed-e's/^[0-7]* [0-9a-f]* /'"0$z40/" 168echo"$unmerged" 169) | git update-index --index-info 170;; 171esac 172exit0 173) 174 saved_err=$? 175iftest"$saved_err"=0 176then 177test"$new"="$old"|| git diff-index --name-status"$new" 178fi 179(exit$saved_err) 180fi 181 182# 183# Switch the HEAD pointer to the new branch if we 184# checked out a branch head, and remove any potential 185# old MERGE_HEAD's (subsequent commits will clearly not 186# be based on them, since we re-set the index) 187# 188if["$?"-eq0];then 189if["$newbranch"];then 190 leading=`expr "refs/heads/$newbranch" : '\(.*\)/'`&& 191mkdir-p"$GIT_DIR/$leading"&& 192echo$new>"$GIT_DIR/refs/heads/$newbranch"||exit 193 branch="$newbranch" 194fi 195["$branch"] && 196 GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD "refs/heads/$branch" 197rm-f"$GIT_DIR/MERGE_HEAD" 198else 199exit1 200fi