]> git.ipfire.org Git - thirdparty/git.git/blame - git-checkout.sh
Documentation/git-reset: don't mention --mixed for selected-paths reset
[thirdparty/git.git] / git-checkout.sh
CommitLineData
303e5f4c 1#!/bin/sh
b33e9666 2
00df3bed
PH
3OPTIONS_KEEPDASHDASH=t
4OPTIONS_SPEC="\
550f8fd0 5git-checkout [options] [<branch>] [<paths>...]
00df3bed
PH
6--
7b= create a new branch started at <branch>
550f8fd0
JS
8l create the new branch's reflog
9track arrange that the new branch tracks the remote branch
00df3bed 10f proceed even if the index or working tree is not HEAD
550f8fd0 11m merge local modifications into the new branch
00df3bed
PH
12q,quiet be quiet
13"
104f3e03 14SUBDIRECTORY_OK=Sometimes
806f36d4 15. git-sh-setup
7eff28a9 16require_work_tree
b0bafe03 17
969d326d 18old_name=HEAD
5be60078
JH
19old=$(git rev-parse --verify $old_name 2>/dev/null)
20oldbranch=$(git symbolic-ref $old_name 2>/dev/null)
e8b11749 21new=
969d326d 22new_name=
a79944d7 23force=
e8b11749 24branch=
0746d19a 25track=
91dcdfd3 26newbranch=
969d326d 27newbranch_log=
1be0659e 28merge=
6124aee5 29quiet=
4c474b6f 30v=-v
ead80606
JH
31LF='
32'
00df3bed
PH
33
34while test $# != 0; do
35 case "$1" in
36 -b)
91dcdfd3 37 shift
00df3bed 38 newbranch="$1"
91dcdfd3
LT
39 [ -z "$newbranch" ] &&
40 die "git checkout: -b needs a branch name"
5be60078 41 git show-ref --verify --quiet -- "refs/heads/$newbranch" &&
91dcdfd3 42 die "git checkout: branch $newbranch already exists"
5be60078 43 git check-ref-format "heads/$newbranch" ||
babfaf8d 44 die "git checkout: we do not like '$newbranch' as a branch name."
91dcdfd3 45 ;;
00df3bed 46 -l)
0746d19a
PB
47 newbranch_log=-l
48 ;;
00df3bed
PH
49 --track|--no-track)
50 track="$1"
969d326d 51 ;;
00df3bed 52 -f)
e8b11749 53 force=1
303e5f4c 54 ;;
1be0659e
JH
55 -m)
56 merge=1
57 ;;
00df3bed 58 -q|--quiet)
6124aee5 59 quiet=1
4c474b6f 60 v=
6124aee5 61 ;;
4aaa7027 62 --)
00df3bed 63 shift
4aaa7027
JH
64 break
65 ;;
303e5f4c 66 *)
00df3bed 67 usage
e8b11749 68 ;;
00df3bed
PH
69 esac
70 shift
303e5f4c
LT
71done
72
00df3bed 73arg="$1"
7dc46429
JH
74rev=$(git rev-parse --verify "$arg" 2>/dev/null)
75if rev=$(git rev-parse --verify "$rev^0" 2>/dev/null)
00df3bed
PH
76then
77 [ -z "$rev" ] && die "unknown flag $arg"
78 new_name="$arg"
79 if git show-ref --verify --quiet -- "refs/heads/$arg"
80 then
81 rev=$(git rev-parse --verify "refs/heads/$arg^0")
82 branch="$arg"
83 fi
84 new="$rev"
85 shift
7dc46429 86elif rev=$(git rev-parse --verify "$rev^{tree}" 2>/dev/null)
00df3bed
PH
87then
88 # checking out selected paths from a tree-ish.
89 new="$rev"
7dc46429 90 new_name="$rev^{tree}"
00df3bed
PH
91 shift
92fi
93[ "$1" = "--" ] && shift
94
9debc324 95case "$newbranch,$track" in
0746d19a
PB
96,--*)
97 die "git checkout: --track and --no-track require -b"
98esac
99
897643cc
JH
100case "$force$merge" in
10111)
102 die "git checkout: -f and -m are incompatible"
103esac
104
4aaa7027
JH
105# The behaviour of the command with and without explicit path
106# parameters is quite different.
107#
108# Without paths, we are checking out everything in the work tree,
109# possibly switching branches. This is the traditional behaviour.
91dcdfd3 110#
4aaa7027
JH
111# With paths, we are _never_ switching branch, but checking out
112# the named paths from either index (when no rev is given),
113# or the named tree-ish (when rev is given).
114
115if test "$#" -ge 1
116then
babfaf8d
JW
117 hint=
118 if test "$#" -eq 1
119 then
120 hint="
121Did you intend to checkout '$@' which can not be resolved as commit?"
122 fi
1be0659e 123 if test '' != "$newbranch$force$merge"
4aaa7027 124 then
babfaf8d 125 die "git checkout: updating paths is incompatible with switching branches/forcing$hint"
4aaa7027
JH
126 fi
127 if test '' != "$new"
128 then
129 # from a specific tree-ish; note that this is for
130 # rescuing paths and is never meant to remove what
131 # is not in the named tree-ish.
5be60078
JH
132 git ls-tree --full-name -r "$new" "$@" |
133 git update-index --index-info || exit $?
4aaa7027 134 fi
bf7e1472
JH
135
136 # Make sure the request is about existing paths.
4307234a
DS
137 git ls-files --full-name --error-unmatch -- "$@" >/dev/null || exit
138 git ls-files --full-name -- "$@" |
139 (cd_to_toplevel && git checkout-index -f -u --stdin)
1abbe475 140
00df3bed
PH
141 # Run a post-checkout hook -- the HEAD does not change so the
142 # current HEAD is passed in for both args
1abbe475
JE
143 if test -x "$GIT_DIR"/hooks/post-checkout; then
144 "$GIT_DIR"/hooks/post-checkout $old $old 0
145 fi
146
4aaa7027
JH
147 exit $?
148else
149 # Make sure we did not fall back on $arg^{tree} codepath
150 # since we are not checking out from an arbitrary tree-ish,
151 # but switching branches.
152 if test '' != "$new"
153 then
5be60078 154 git rev-parse --verify "$new^{commit}" >/dev/null 2>&1 ||
4aaa7027
JH
155 die "Cannot switch branch to a non-commit."
156 fi
157fi
158
104f3e03
JH
159# We are switching branches and checking out trees, so
160# we *NEED* to be at the toplevel.
514c09fd 161cd_to_toplevel
104f3e03 162
969d326d 163[ -z "$new" ] && new=$old && new_name="$old_name"
4aaa7027 164
73c838e4 165# If we don't have an existing branch that we're switching to,
91dcdfd3 166# and we don't have a new branch name for the target we
73c838e4
JH
167# are switching to, then we are detaching our HEAD from any
168# branch. However, if "git checkout HEAD" detaches the HEAD
169# from the current branch, even though that may be logically
170# correct, it feels somewhat funny. More importantly, we do not
171# want "git checkout" nor "git checkout -f" to detach HEAD.
4aaa7027 172
bfbbb8f8
JH
173detached=
174detach_warn=
175
abba9dbb
JH
176describe_detached_head () {
177 test -n "$quiet" || {
178 printf >&2 "$1 "
f0c4881f 179 GIT_PAGER= git log >&2 -1 --pretty=oneline --abbrev-commit "$2" --
abba9dbb
JH
180 }
181}
182
3e0318a3 183if test -z "$branch$newbranch" && test "$new_name" != "$old_name"
c847f537 184then
bfbbb8f8 185 detached="$new"
6124aee5 186 if test -n "$oldbranch" && test -z "$quiet"
64886104 187 then
92cf9569 188 detach_warn="Note: moving to \"$new_name\" which isn't a local branch
d117452a
NP
189If you want to create a new branch from this checkout, you may do so
190(now or later) by using -b with the checkout command again. Example:
eb3204df 191 git checkout -b <new_branch_name>"
64886104 192 fi
3e0318a3 193elif test -z "$oldbranch" && test "$new" != "$old"
ead80606 194then
abba9dbb 195 describe_detached_head 'Previous HEAD position was' "$old"
c847f537 196fi
91dcdfd3 197
5a03e7f2
SP
198if [ "X$old" = X ]
199then
6124aee5
NP
200 if test -z "$quiet"
201 then
202 echo >&2 "warning: You appear to be on a branch yet to be born."
203 echo >&2 "warning: Forcing checkout of $new_name."
204 fi
5a03e7f2
SP
205 force=1
206fi
207
a79944d7 208if [ "$force" ]
303e5f4c 209then
5be60078 210 git read-tree $v --reset -u $new
303e5f4c 211else
5be60078
JH
212 git update-index --refresh >/dev/null
213 merge_error=$(git read-tree -m -u --exclude-per-directory=.gitignore $old $new 2>&1) || (
1be0659e
JH
214 case "$merge" in
215 '')
216 echo >&2 "$merge_error"
217 exit 1 ;;
19205acf
JH
218 esac
219
1be0659e 220 # Match the index to the working tree, and do a three-way.
a6080a0a 221 git diff-files --name-only | git update-index --remove --stdin &&
19205acf 222 work=`git write-tree` &&
4c474b6f 223 git read-tree $v --reset -u $new || exit
1be0659e 224
41f5d733 225 eval GITHEAD_$new='${new_name:-${branch:-$new}}' &&
d7ebd53d
JH
226 eval GITHEAD_$work=local &&
227 export GITHEAD_$new GITHEAD_$work &&
228 git merge-recursive $old -- $new $work
1be0659e
JH
229
230 # Do not register the cleanly merged paths in the index yet.
231 # this is not a real merge before committing, but just carrying
232 # the working tree changes along.
233 unmerged=`git ls-files -u`
4c474b6f 234 git read-tree $v --reset $new
1be0659e
JH
235 case "$unmerged" in
236 '') ;;
237 *)
238 (
239 z40=0000000000000000000000000000000000000000
240 echo "$unmerged" |
241 sed -e 's/^[0-7]* [0-9a-f]* /'"0 $z40 /"
242 echo "$unmerged"
243 ) | git update-index --index-info
244 ;;
245 esac
246 exit 0
19205acf 247 )
980d8ce5 248 saved_err=$?
6124aee5 249 if test "$saved_err" = 0 && test -z "$quiet"
504fe714 250 then
e4b0e4ab 251 git diff-index --name-status "$new"
504fe714 252 fi
980d8ce5 253 (exit $saved_err)
ef0bfa25
LT
254fi
255
a6080a0a 256#
01385e27 257# Switch the HEAD pointer to the new branch if we
ef0bfa25
LT
258# checked out a branch head, and remove any potential
259# old MERGE_HEAD's (subsequent commits will clearly not
260# be based on them, since we re-set the index)
261#
262if [ "$?" -eq 0 ]; then
91dcdfd3 263 if [ "$newbranch" ]; then
5be60078 264 git branch $track $newbranch_log "$newbranch" "$new_name" || exit
91dcdfd3
LT
265 branch="$newbranch"
266 fi
bfbbb8f8
JH
267 if test -n "$branch"
268 then
49aba0bb 269 old_branch_name=`expr "z$oldbranch" : 'zrefs/heads/\(.*\)'`
c67bbc55 270 GIT_DIR="$GIT_DIR" git symbolic-ref -m "checkout: moving from ${old_branch_name:-$old} to $branch" HEAD "refs/heads/$branch"
57216856 271 if test -n "$quiet"
e4b0e4ab 272 then
57216856
NP
273 true # nothing
274 elif test "refs/heads/$branch" = "$oldbranch"
275 then
276 echo >&2 "Already on branch \"$branch\""
277 else
6124aee5 278 echo >&2 "Switched to${newbranch:+ a new} branch \"$branch\""
e4b0e4ab 279 fi
bfbbb8f8
JH
280 elif test -n "$detached"
281 then
c67bbc55
JS
282 old_branch_name=`expr "z$oldbranch" : 'zrefs/heads/\(.*\)'`
283 git update-ref --no-deref -m "checkout: moving from ${old_branch_name:-$old} to $arg" HEAD "$detached" ||
bfbbb8f8
JH
284 die "Cannot detach HEAD"
285 if test -n "$detach_warn"
286 then
287 echo >&2 "$detach_warn"
288 fi
abba9dbb 289 describe_detached_head 'HEAD is now at' HEAD
bfbbb8f8 290 fi
ef0bfa25 291 rm -f "$GIT_DIR/MERGE_HEAD"
ab22707f
TL
292else
293 exit 1
ef0bfa25 294fi
1abbe475
JE
295
296# Run a post-checkout hook
297if test -x "$GIT_DIR"/hooks/post-checkout; then
00df3bed 298 "$GIT_DIR"/hooks/post-checkout $old $new 1
1abbe475 299fi