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