-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcg-switch
executable file
·228 lines (202 loc) · 7.22 KB
/
cg-switch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
#!/usr/bin/env bash
#
# Switch the working tree to a different (or new) local branch
# Copyright (c) Yann Dirson, Petr Baudis 2005
#
# `cg-switch` can switch your current local branch (and working copy)
# to an existing branch, or create a new branch based on a given commit.
#
# Terminology note: This command concerns local branches (also called
# "heads"), not remote branches (those managed by `cg-branch-add`).
#
# Note that `cg-switch` is meant for permanent switching of your current
# local branch (permanent in the sense that you are going to work on it;
# you can obviously `cg-switch` again later). If you want to just casually
# explore the current state of a particular branch of commit, use `cg-seek`.
#
# OPTIONS
# -------
# -c:: Create new branch based on HEAD
# Create a new branch of given name, based on your current commit
# (HEAD). This option is equivalent to specifying '-r HEAD'. If
# '-f' is passed and the branch already exists, it is forcibly
# repointed to point to the current commit.
#
# -f:: Enable overwriting of existing branches
# Force the branch's head pointer to be updated to whatever you
# passed as the '-r' argument even if the branch already exists.
# WARNING: The pointer to the original contents of the branch will
# be lost! The contents itself will not be deleted right away,
# `git-fsck-objects --unreachable` might help you to find it.
# Besides, this can get very troublesome if you are pushing the
# branch out - please refer to the documentation of a close
# relative, `cg-admin-uncommit`.
#
# -l:: Preserve local changes in the branch
# If your working tree has uncommitted local changes, the default
# behaviour is that the changes will be reapplied to the new
# branch after switching. With this option, however, the local
# changes will be "kept" with your previous branch, you will
# get a pristine tree of the new branch and when you switch back
# to the original branch you will also get back the local changes.
# (You do not need to pass any special switches when switching
# back, '-l' has effect only on the branch you are switching _away_
# from.)
#
# -n:: No switch; only create and update the branch
# Do not switch your current branch to the given branch. This
# will make cg-switch to only create or update the branch, but
# leave your working copy alone.
#
# -o REMBRANCH:: Set the default remote branch for the branch
# Along switching the branches, this will also set the default
# remote branch to be associated with the target branch (used as
# default by e.g. `cg-update` and `cg-push`, falls back to 'origin').
# You may want to combine this with '-n' to change this for a branch
# other than the current one without actual switching. As a special
# present just for you, in case of '-o' the BRANCH `cg-switch` argument
# defaults to the current branch.
#
# This setting is saved in Git configuration file under the key
# 'branch.<name>.merge' and is understood by core Git as well.
#
# -p:: Do not touch the working copy
# Do not touch the working copy when switching. This _will_ switch
# your current branch, but the checked out working copy will have
# the original contents kept (so further `cg-diff` will list a lot
# of changes, relative to the new branch).
#
# -r COMMIT_ID:: Branch off the given COMMIT_ID, create branch if non-existing
# Point the branch at the given commit. Required when creating
# a new branch. When switching to an existing branch, the branch
# pointer is modified if '-r' is passed and confirmed by '-f'.
#
# EXAMPLE USAGE
# -------------
# To create a "v1.x" branch based on the commit "v1.0" and switch the
# working copy to it, making it your current branch, do:
#
# $ cg-switch -r v1.0 v1.x
#
# If you want to create the branch (let's say based on the current
# commit) but don't switch your working copy to it (so that your
# current branch stays the same as before), do:
#
# $ cg-switch -n -c v1.x
#
# If you want to go back to the 'master' branch, just do:
#
# $ cg-switch master
#
# To change the "v1.x" branch to refer to the latest commit on the
# "testing" branch, do (WARNING: you will lose the pointer to the
# original contents of the "v1.x" branch, be careful!):
#
# $ cg-switch -f -r testing v1.x
# Testsuite: TODO
USAGE="cg-switch [-f] [-l | -n | -p] [-o REMBRANCH] [-c | -r COMMIT_ID] BRANCH"
_git_requires_root=1
_git_wc_unneeded=1
. "${COGITO_LIB}"cg-Xlib || exit 1
set -e
repoint_head() {
# $oldcommit is optional
local dsthead="$1" dstcommit="$2" oldcommit="$3"
git-update-ref "refs/heads/$dsthead" "$dstcommit" $oldcommit
}
force=
savelocal=
seek=1
dstcommit=
roll=1
origin=
while optparse; do
if optparse -f; then
force=1
elif optparse -l; then
savelocal=1
elif optparse -n; then
seek=
elif optparse -o=; then
origin="$OPTARG"
elif optparse -p; then
roll=
elif optparse -r=; then
dstcommit="$(cg-object-id -c "$OPTARG")" || exit 1
elif optparse -c; then
dstcommit="$(cg-object-id -c)" || exit 1
else
optfail
fi
done
[ "$origin" -o "${#ARGS[@]}" -eq "1" ] || usage
[ "$seek$roll" = "" ] && usage
[ "$savelocal" = "1" -a "$seek$roll" != "11" ] && usage
[ "$_git_no_wc" ] && [ "$seek" ] &&
die "only cg-switch -n allowed outside a working copy"
if [ "${#ARGS[@]}" -gt 0 ]; then
dsthead="${ARGS[0]}"
else
dsthead="$_git_head"
fi
[ -s "$_git/branches/$dsthead" ] &&
die "refusing to switch to a remote branch - see README for lengthy explanation; use cg-seek to just quickly inspect it"
if [ "$seek" -a "$dsthead" != "$_git_head" ]; then
[ -s "$_git/blocked" ] && die "switch blocked: $(cat "$_git/blocked")"
fi
if [ "$origin" ]; then
[ -s "$_git/branches/$origin" ] ||
die "$origin is not a valid remote branch"
echo "Setting $dsthead's origin: $origin"
git-repo-config "branch.$dsthead.merge" "refs/heads/$origin" || exit 1
fi
if ! [ "$(git-symbolic-ref HEAD)" != "refs/heads/$dsthead" -o -n "$dstcommit" ]; then
if [ "$origin" ]; then
exit 0
else
die "already on branch $dsthead"
fi
fi
curcommit="$(cg-object-id -c)"
if exists_ref "refs/heads/$dsthead"; then
# Existing branch
if [ -n "$dstcommit" ]; then
[ "$force" ] ||
die "branch $dsthead already exists - use -f to force the switch"
srccommit="$(cg-object-id -c "$dsthead")"
echo "Repointing branch $dsthead: $srccommit -> $dstcommit"
repoint_head "$dsthead" "$dstcommit" "$srccommit"
fi
else
# New branch
[ "$dstcommit" ] || die "branch $dsthead does not exist - you must pass -r if you want to create a new branch"
echo "Creating new branch $dsthead: $dstcommit"
repoint_head "$dsthead" "$dstcommit"
fi
if [ "$seek" ]; then
[ -n "$dstcommit" ] || dstcommit="$(cg-object-id -c "$dsthead")"
if [ "$roll" ] && [ "x$curcommit" != "x$dstcommit" ]; then
# Shelve local changes
if [ "$savelocal" ]; then
echo "Saving local changes..."
shelve_changes # Sets $curcommit
fi
echo "Switching to branch $dsthead..."
if ! tree_timewarp --no-head-update "along" "please rollback" "$curcommit" "$dstcommit"; then
abort_shelve "$curcommit"
exit 1
fi
export _git_head="$dsthead"
unshelve_changes # Eats $dstcommit
else
# Shelve local changes
if [ "$savelocal" ]; then
echo "Saving local changes..."
shelve_changes # Sets $curcommit
fi
echo "Switching to branch $dsthead..."
export _git_head="$dsthead"
unshelve_changes # Eats $dstcommit
fi
git-symbolic-ref HEAD "refs/heads/$dsthead"
fi