]> git.ipfire.org Git - thirdparty/git.git/blame - contrib/examples/git-checkout.sh
Git 1.7.8-rc2
[thirdparty/git.git] / contrib / examples / 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 212 git update-index --refresh >/dev/null
e8548645
LT
213 git read-tree $v -m -u --exclude-per-directory=.gitignore $old $new || (
214 case "$merge,$v" in
215 ,*)
1be0659e 216 exit 1 ;;
e8548645
LT
217 1,)
218 ;; # quiet
219 *)
220 echo >&2 "Falling back to 3-way merge..." ;;
19205acf
JH
221 esac
222
1be0659e 223 # Match the index to the working tree, and do a three-way.
a6080a0a 224 git diff-files --name-only | git update-index --remove --stdin &&
19205acf 225 work=`git write-tree` &&
4c474b6f 226 git read-tree $v --reset -u $new || exit
1be0659e 227
41f5d733 228 eval GITHEAD_$new='${new_name:-${branch:-$new}}' &&
d7ebd53d
JH
229 eval GITHEAD_$work=local &&
230 export GITHEAD_$new GITHEAD_$work &&
231 git merge-recursive $old -- $new $work
1be0659e
JH
232
233 # Do not register the cleanly merged paths in the index yet.
234 # this is not a real merge before committing, but just carrying
235 # the working tree changes along.
236 unmerged=`git ls-files -u`
4c474b6f 237 git read-tree $v --reset $new
1be0659e
JH
238 case "$unmerged" in
239 '') ;;
240 *)
241 (
242 z40=0000000000000000000000000000000000000000
243 echo "$unmerged" |
244 sed -e 's/^[0-7]* [0-9a-f]* /'"0 $z40 /"
245 echo "$unmerged"
246 ) | git update-index --index-info
247 ;;
248 esac
249 exit 0
19205acf 250 )
980d8ce5 251 saved_err=$?
6124aee5 252 if test "$saved_err" = 0 && test -z "$quiet"
504fe714 253 then
e4b0e4ab 254 git diff-index --name-status "$new"
504fe714 255 fi
980d8ce5 256 (exit $saved_err)
ef0bfa25
LT
257fi
258
a6080a0a 259#
01385e27 260# Switch the HEAD pointer to the new branch if we
ef0bfa25
LT
261# checked out a branch head, and remove any potential
262# old MERGE_HEAD's (subsequent commits will clearly not
263# be based on them, since we re-set the index)
264#
265if [ "$?" -eq 0 ]; then
91dcdfd3 266 if [ "$newbranch" ]; then
5be60078 267 git branch $track $newbranch_log "$newbranch" "$new_name" || exit
91dcdfd3
LT
268 branch="$newbranch"
269 fi
bfbbb8f8
JH
270 if test -n "$branch"
271 then
49aba0bb 272 old_branch_name=`expr "z$oldbranch" : 'zrefs/heads/\(.*\)'`
c67bbc55 273 GIT_DIR="$GIT_DIR" git symbolic-ref -m "checkout: moving from ${old_branch_name:-$old} to $branch" HEAD "refs/heads/$branch"
57216856 274 if test -n "$quiet"
e4b0e4ab 275 then
57216856
NP
276 true # nothing
277 elif test "refs/heads/$branch" = "$oldbranch"
278 then
279 echo >&2 "Already on branch \"$branch\""
280 else
6124aee5 281 echo >&2 "Switched to${newbranch:+ a new} branch \"$branch\""
e4b0e4ab 282 fi
bfbbb8f8
JH
283 elif test -n "$detached"
284 then
c67bbc55
JS
285 old_branch_name=`expr "z$oldbranch" : 'zrefs/heads/\(.*\)'`
286 git update-ref --no-deref -m "checkout: moving from ${old_branch_name:-$old} to $arg" HEAD "$detached" ||
bfbbb8f8
JH
287 die "Cannot detach HEAD"
288 if test -n "$detach_warn"
289 then
290 echo >&2 "$detach_warn"
291 fi
abba9dbb 292 describe_detached_head 'HEAD is now at' HEAD
bfbbb8f8 293 fi
ef0bfa25 294 rm -f "$GIT_DIR/MERGE_HEAD"
ab22707f
TL
295else
296 exit 1
ef0bfa25 297fi
1abbe475
JE
298
299# Run a post-checkout hook
300if test -x "$GIT_DIR"/hooks/post-checkout; then
00df3bed 301 "$GIT_DIR"/hooks/post-checkout $old $new 1
1abbe475 302fi