Test commit
[cogito/jonas.git] / cg-switch
blob0291e91880cae9d528ec0f109fa083267137cd12
1 #!/usr/bin/env bash
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`.
17 # OPTIONS
18 # -------
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_
44 # from.)
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'.
74 # EXAMPLE USAGE
75 # -------------
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:
89 # $ cg-switch master
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
97 # Testsuite: TODO
99 USAGE="cg-switch [-f] [-l | -n | -p] [-o REMBRANCH] [-c | -r COMMIT_ID] BRANCH"
100 _git_requires_root=1
101 _git_wc_unneeded=1
103 . "${COGITO_LIB}"cg-Xlib || exit 1
105 set -e
108 repoint_head() {
109 # $oldcommit is optional
110 local dsthead="$1" dstcommit="$2" oldcommit="$3"
111 git-update-ref "refs/heads/$dsthead" "$dstcommit" $oldcommit
115 force=
116 savelocal=
117 seek=1
118 dstcommit=
119 roll=1
120 origin=
121 while optparse; do
122 if optparse -f; then
123 force=1
124 elif optparse -l; then
125 savelocal=1
126 elif optparse -n; then
127 seek=
128 elif optparse -o=; then
129 origin="$OPTARG"
130 elif optparse -p; then
131 roll=
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
136 else
137 optfail
139 done
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
149 dsthead="${ARGS[0]}"
150 else
151 dsthead="$_git_head"
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
171 exit 0
172 else
173 die "already on branch $dsthead"
178 curcommit="$(cg-object-id -c)"
180 if exists_ref "refs/heads/$dsthead"; then
181 # Existing branch
182 if [ -n "$dstcommit" ]; then
183 [ "$force" ] ||
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"
190 else
191 # New branch
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"
198 if [ "$seek" ]; then
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"
210 exit 1
213 export _git_head="$dsthead"
214 unshelve_changes # Eats $dstcommit
215 else
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"