3 # Switch the working tree to a different (or new) local branch
4 # Copyright (c) Yann Dirson, Petr Baudis 2005
6 # `cg-switch` can switch your current local branch (and working copy)
7 # to an existing branch, or create a new branch based on a given commit.
9 # Terminology note: This command concerns local branches (also called
10 # "heads"), not remote branches (those managed by `cg-branch-add`).
12 # Note that `cg-switch` is meant for permanent switching of your current
13 # local branch (permanent in the sense that you are going to work on it;
14 # you can obviously `cg-switch` again later). If you want to just casually
15 # explore the current state of a particular branch of commit, use `cg-seek`.
19 # -c:: Create new branch based on HEAD
20 # Create a new branch of given name, based on your current commit
21 # (HEAD). This option is equivalent to specifying '-r HEAD'. If
22 # '-f' is passed and the branch already exists, it is forcibly
23 # repointed to point to the current commit.
25 # -f:: Enable overwriting of existing branches
26 # Force the branch's head pointer to be updated to whatever you
27 # passed as the '-r' argument even if the branch already exists.
28 # WARNING: The pointer to the original contents of the branch will
29 # be lost! The contents itself will not be deleted right away,
30 # `git-fsck-objects --unreachable` might help you to find it.
31 # Besides, this can get very troublesome if you are pushing the
32 # branch out - please refer to the documentation of a close
33 # relative, `cg-admin-uncommit`.
35 # -l:: Preserve local changes in the branch
36 # If your working tree has uncommitted local changes, the default
37 # behaviour is that the changes will be reapplied to the new
38 # branch after switching. With this option, however, the local
39 # changes will be "kept" with your previous branch, you will
40 # get a pristine tree of the new branch and when you switch back
41 # to the original branch you will also get back the local changes.
42 # (You do not need to pass any special switches when switching
43 # back, '-l' has effect only on the branch you are switching _away_
46 # -n:: No switch; only create and update the branch
47 # Do not switch your current branch to the given branch. This
48 # will make cg-switch to only create or update the branch, but
49 # leave your working copy alone.
51 # -o REMBRANCH:: Set the default remote branch for the branch
52 # Along switching the branches, this will also set the default
53 # remote branch to be associated with the target branch (used as
54 # default by e.g. `cg-update` and `cg-push`, falls back to 'origin').
55 # You may want to combine this with '-n' to change this for a branch
56 # other than the current one without actual switching. As a special
57 # present just for you, in case of '-o' the BRANCH `cg-switch` argument
58 # defaults to the current branch.
60 # This setting is saved in Git configuration file under the key
61 # 'branch.<name>.merge' and is understood by core Git as well.
63 # -p:: Do not touch the working copy
64 # Do not touch the working copy when switching. This _will_ switch
65 # your current branch, but the checked out working copy will have
66 # the original contents kept (so further `cg-diff` will list a lot
67 # of changes, relative to the new branch).
69 # -r COMMIT_ID:: Branch off the given COMMIT_ID, create branch if non-existing
70 # Point the branch at the given commit. Required when creating
71 # a new branch. When switching to an existing branch, the branch
72 # pointer is modified if '-r' is passed and confirmed by '-f'.
76 # To create a "v1.x" branch based on the commit "v1.0" and switch the
77 # working copy to it, making it your current branch, do:
79 # $ cg-switch -r v1.0 v1.x
81 # If you want to create the branch (let's say based on the current
82 # commit) but don't switch your working copy to it (so that your
83 # current branch stays the same as before), do:
85 # $ cg-switch -n -c v1.x
87 # If you want to go back to the 'master' branch, just do:
91 # To change the "v1.x" branch to refer to the latest commit on the
92 # "testing" branch, do (WARNING: you will lose the pointer to the
93 # original contents of the "v1.x" branch, be careful!):
95 # $ cg-switch -f -r testing v1.x
99 USAGE
="cg-switch [-f] [-l | -n | -p] [-o REMBRANCH] [-c | -r COMMIT_ID] BRANCH"
103 .
"${COGITO_LIB}"cg-Xlib ||
exit 1
109 # $oldcommit is optional
110 local dsthead
="$1" dstcommit
="$2" oldcommit
="$3"
111 git-update-ref
"refs/heads/$dsthead" "$dstcommit" $oldcommit
124 elif optparse
-l; then
126 elif optparse
-n; then
128 elif optparse
-o=; then
130 elif optparse
-p; then
132 elif optparse
-r=; then
133 dstcommit
="$(cg-object-id -c "$OPTARG")" ||
exit 1
134 elif optparse
-c; then
135 dstcommit
="$(cg-object-id -c)" ||
exit 1
141 [ "$origin" -o "${#ARGS[@]}" -eq "1" ] || usage
142 [ "$seek$roll" = "" ] && usage
143 [ "$savelocal" = "1" -a "$seek$roll" != "11" ] && usage
145 [ "$_git_no_wc" ] && [ "$seek" ] &&
146 die
"only cg-switch -n allowed outside a working copy"
148 if [ "${#ARGS[@]}" -gt 0 ]; then
153 [ -s "$_git/branches/$dsthead" ] &&
154 die
"refusing to switch to a remote branch - see README for lengthy explanation; use cg-seek to just quickly inspect it"
156 if [ "$seek" -a "$dsthead" != "$_git_head" ]; then
157 [ -s "$_git/blocked" ] && die
"switch blocked: $(cat "$_git/blocked
")"
161 if [ "$origin" ]; then
162 [ -s "$_git/branches/$origin" ] ||
163 die
"$origin is not a valid remote branch"
164 echo "Setting $dsthead's origin: $origin"
165 git-repo-config
"branch.$dsthead.merge" "refs/heads/$origin" ||
exit 1
169 if ! [ "$(git-symbolic-ref HEAD)" != "refs/heads/$dsthead" -o -n "$dstcommit" ]; then
170 if [ "$origin" ]; then
173 die
"already on branch $dsthead"
178 curcommit
="$(cg-object-id -c)"
180 if exists_ref
"refs/heads/$dsthead"; then
182 if [ -n "$dstcommit" ]; then
184 die
"branch $dsthead already exists - use -f to force the switch"
185 srccommit
="$(cg-object-id -c "$dsthead")"
186 echo "Repointing branch $dsthead: $srccommit -> $dstcommit"
187 repoint_head
"$dsthead" "$dstcommit" "$srccommit"
192 [ "$dstcommit" ] || die
"branch $dsthead does not exist - you must pass -r if you want to create a new branch"
193 echo "Creating new branch $dsthead: $dstcommit"
194 repoint_head
"$dsthead" "$dstcommit"
199 [ -n "$dstcommit" ] || dstcommit
="$(cg-object-id -c "$dsthead")"
200 if [ "$roll" ] && [ "x$curcommit" != "x$dstcommit" ]; then
201 # Shelve local changes
202 if [ "$savelocal" ]; then
203 echo "Saving local changes..."
204 shelve_changes
# Sets $curcommit
207 echo "Switching to branch $dsthead..."
208 if ! tree_timewarp
--no-head-update "along" "please rollback" "$curcommit" "$dstcommit"; then
209 abort_shelve
"$curcommit"
213 export _git_head
="$dsthead"
214 unshelve_changes
# Eats $dstcommit
216 # Shelve local changes
217 if [ "$savelocal" ]; then
218 echo "Saving local changes..."
219 shelve_changes
# Sets $curcommit
222 echo "Switching to branch $dsthead..."
224 export _git_head
="$dsthead"
225 unshelve_changes
# Eats $dstcommit
227 git-symbolic-ref HEAD
"refs/heads/$dsthead"