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 "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 78iftest''!="$newbranch$force$merge" 79then 80 die "updating paths and switching branches or forcing are incompatible." 81fi 82iftest''!="$new" 83then 84# from a specific tree-ish; note that this is for 85# rescuing paths and is never meant to remove what 86# is not in the named tree-ish. 87 git-ls-tree --full-name -r"$new""$@"| 88 git-update-index --index-info||exit $? 89fi 90 git-checkout-index -f -u --"$@" 91exit $? 92else 93# Make sure we did not fall back on $arg^{tree} codepath 94# since we are not checking out from an arbitrary tree-ish, 95# but switching branches. 96iftest''!="$new" 97then 98 git-rev-parse --verify"$new^{commit}">/dev/null 2>&1|| 99 die "Cannot switch branch to a non-commit." 100fi 101fi 102 103# We are switching branches and checking out trees, so 104# we *NEED* to be at the toplevel. 105cdup=$(git-rev-parse --show-cdup) 106iftest!-z"$cdup" 107then 108cd"$cdup" 109fi 110 111[-z"$new"] && new=$old 112 113# If we don't have an old branch that we're switching to, 114# and we don't have a new branch name for the target we 115# are switching to, then we'd better just be checking out 116# what we already had 117 118[-z"$branch$newbranch"] && 119["$new"!="$old"] && 120 die "git checkout: you need to specify a new branch name" 121 122if["$force"] 123then 124 git-read-tree --reset$new&& 125 git-checkout-index -q -f -u -a 126else 127 git-update-index --refresh>/dev/null 128 merge_error=$(git-read-tree -m -u $old $new 2>&1)|| ( 129case"$merge"in 130'') 131echo>&2"$merge_error" 132exit1;; 133esac 134 135# Match the index to the working tree, and do a three-way. 136 git diff-files --name-only| git update-index --remove --stdin&& 137 work=`git write-tree`&& 138 git read-tree --reset$new&& 139 git checkout-index -f -u -q -a&& 140 git read-tree -m -u$old $new $work||exit 141 142if result=`git write-tree 2>/dev/null` 143then 144echo>&2"Trivially automerged." 145else 146 git merge-index -o git-merge-one-file -a 147fi 148 149# Do not register the cleanly merged paths in the index yet. 150# this is not a real merge before committing, but just carrying 151# the working tree changes along. 152 unmerged=`git ls-files -u` 153 git read-tree --reset$new 154case"$unmerged"in 155'') ;; 156*) 157( 158 z40=0000000000000000000000000000000000000000 159echo"$unmerged"| 160sed-e's/^[0-7]* [0-9a-f]* /'"0$z40/" 161echo"$unmerged" 162) | git update-index --index-info 163;; 164esac 165exit0 166) 167 saved_err=$? 168iftest"$saved_err"=0 169then 170test"$new"="$old"|| git diff-index --name-status"$new" 171fi 172(exit$saved_err) 173fi 174 175# 176# Switch the HEAD pointer to the new branch if we 177# checked out a branch head, and remove any potential 178# old MERGE_HEAD's (subsequent commits will clearly not 179# be based on them, since we re-set the index) 180# 181if["$?"-eq0];then 182if["$newbranch"];then 183 leading=`expr "refs/heads/$newbranch" : '\(.*\)/'`&& 184mkdir-p"$GIT_DIR/$leading"&& 185echo$new>"$GIT_DIR/refs/heads/$newbranch"||exit 186 branch="$newbranch" 187fi 188["$branch"] && 189 GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD "refs/heads/$branch" 190rm-f"$GIT_DIR/MERGE_HEAD" 191else 192exit1 193fi