-
Notifications
You must be signed in to change notification settings - Fork 10
/
repo
executable file
·376 lines (334 loc) · 8.23 KB
/
repo
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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
#!/bin/sh
USAGE="
Usage: repo [options]
sync will synchronize the master branch with 'hg sync' and commit the
changes. If the current branch is not the master, it will also merge
the changes made to master into the current branch.
Options:
--get #[-<info>] Sync the provided CL to a local branch with the
provided name, creating the branch if necessary.
--new [<info>] Create a new CL and an empty branch named #-<info>
to hold it
--push Push all branches to the origin remote
--submit Submit the current CL branch to hg
--sync Synchronize master with hg tip (or revision of -r)
-r <revision> Use the provided revision
"
set -e
cd `dirname $0`
# current_branch [<branch name>]
#
# If a branch name is provided, tests whether this is the current branch
# or not. Otherwise, outputs the current git branch name.
current_branch() {
CURRENT_BRANCH=`git branch | grep '^\*' | cut -d' ' -f2`
if [ $# -eq 1 ]; then
if [ "$1" = "$CURRENT_BRANCH" ]; then
return 0
fi
return 1
fi
echo $CURRENT_BRANCH
}
# current_status [<path>]
#
# Outputs git status for the provided path, or for the whole branch
# if a path is not provided.
current_status() {
git status --porcelain $1
}
INITIAL_BRANCH=`current_branch`
# fatal <error message>
#
# Show the provided error message, and exists with 1.
fatal() {
echo "error: $1"
EXIT_BRANCH=`current_branch`
if [ "$INITIAL_BRANCH" != "$EXIT_BRANCH" ]; then
echo "Now in branch '$EXIT_BRANCH'."
fi
exit 1
}
# branch_exists <branch name>
#
# Returns 0 if the git branch exists, 1 otherwise.
branch_exists() {
if git branch | grep "^\*\?\s*$1$" > /dev/null; then
return 0
fi
return 1
}
# checkout [-b] <branch name>
#
# Checks out the given git branch, in case it's not the current one.
# With -b, will attempt to create the branch, and fail if it exists.
# Outputs messages showing what happened.
checkout() {
CREATE=0
if [ "$1" = "-b" ]; then
CREATE=1
shift
fi
if current_branch "$1"; then
return 0
fi
if branch_exists "$1"; then
echo "Checking out branch '$1'..."
git checkout $1 $2 > /dev/null
return
fi
if [ "$CREATE" = 0 ]; then
fatal "branch '$1' does not exist"
fi
echo "Creating branch '$1'..."
git checkout -b $1 $2 > /dev/null
}
# enable_sync
#
# Enable synchronization with hg.
enable_sync() {
sed -i 's/^\(pre.*enable-.*=\).*$/\1 true/' .hg/hgrc
}
# disable_sync
#
# Disable synchronization with hg.
disable_sync() {
sed -i 's/^\(pre.*enable-.*=\).*$/\1 false/' .hg/hgrc
}
# add_all
#
# Adds all changes to the current git branch.
add_all() {
git add -A $1 > /dev/null 2> /dev/null || true
}
# commit
#
# Commit changes with provided arguments, possibly committing
# dirstate changes separately.
commit() {
if [ "`current_status .hg/dirstate`" != "" ]; then
git commit -m "Updated dirstate." .hg/dirstate
fi
git commit "$@"
}
if [ $# -eq 0 ]; then
echo "$USAGE"
exit 1
fi
CL_NUMBER=
NEW=0
GET=0
PUSH=0
SYNC=0
NO_SYNC=0
SUBMIT=0
REVISION_REQ=
REVISION_ARG=
while [ $# -gt 0 ]; do
OPTION=$1
shift
case "$OPTION" in
--sync)
SYNC=1
;;
-r)
if [ -z "$1" ]; then
fatal "$OPTION takes an argument"
fi
REVISION_REQ="$1"
REVISION_ARG="-r $1"
shift
;;
--no-sync)
NO_SYNC=1
;;
--submit)
SUBMIT=1
;;
--push)
PUSH=1
if [ -n "$1" ]; then
fatal "$OPTION takes no arguments"
fi
;;
--new)
NEW=1
if [ -z "$1" ]; then
fatal "--new requires a branch suffix argument"
else
CL_BRANCH_SUFFIX="-$1"
fi
shift
;;
--get)
GET=1
if [ "$1" = "" ]; then
fatal "$OPTION needs a # or #-info argument"
fi
CL_BRANCH=$1
CL_NUMBER=`echo $CL_BRANCH | cut -d- -f1`
shift
;;
*)
echo "$USAGE"
exit 1
;;
esac
done
if [ -n "$REVISION_REQ" -a "$SYNC" = 0 ]; then
fatal "-r provided without a supported action"
fi
if [ "$PUSH" = 1 ]; then
echo "Listing remote branches..."
REMOTE_BRANCHES=`git ls-remote -h -t origin | cut -d/ -f3`
LOCAL_BRANCHES=`git branch | sed 's/^\*\?\s*//'`
REMOVE_BRANCHES=
for REMOTE_BRANCH in $REMOTE_BRANCHES; do
FOUND=0
for LOCAL_BRANCH in $LOCAL_BRANCHES; do
if [ $REMOTE_BRANCH = $LOCAL_BRANCH ]; then
FOUND=1
break
fi
done
if [ $FOUND = 0 ]; then
REMOVE_BRANCHES="$REMOTE_BRANCH $REMOVE_BRANCHES"
fi
done
if [ "$REMOVE_BRANCHES" != "" ]; then
echo "Deleting remote branches: $REMOVE_BRANCHES"
git push --delete origin $REMOVE_BRANCHES
else
echo "No remote branches to delete."
fi
echo "Pushing all local branches..."
git push --force --all
exit $?
fi
STATUS=`current_status`
if [ "$STATUS" != "" ]; then
fatal "git status is not clean:\n$STATUS"
fi
HG_UNKNOWN=`hg status --unknown`
if [ "$HG_UNKNOWN" != "" ]; then
fatal "hg has unknown files:\n$HG_UNKOWN"
fi
if [ "$SUBMIT" = 1 ]; then
CL_BRANCH=`current_branch`
CL_NUMBER=`echo $CL_BRANCH | cut -d- -f1`
if ! echo "$CL_NUMBER" | grep '^[0-9]\+$' > /dev/null; then
fatal "can't submit branch '$CL_BRANCH'"
fi
enable_sync
SUBMIT_FAILED=0
if ! hg submit $CL_NUMBER; then
SUBMIT_FAILED=1
fi
disable_sync
if [ "$SUBMIT_FAILED" = 1 ]; then
fatal "hg submit failed"
else
commit -m "Removed submitted CL $CL_NUMBER description." .hg/codereview/cl.$CL_NUMBER
fi
# Remove changes done to .hg on the submit.
git reset --hard
# Drop all revisions which modified the dirstate.
for REVISION in `git rev-list master...HEAD --grep='Updated dirstate.'`; do
echo "Dropping dirstate revision $REVISION..."
git rebase --onto $REVISION^ $REVISION
done
echo "Merging $CL_BRANCH onto master..."
checkout master
git merge --no-ff $CL_BRANCH
echo "Removing $CL_BRANCH..."
git branch -d $CL_BRANCH
fi
checkout master
if [ "$NO_SYNC" = 0 ]; then
echo "Running 'hg sync' to synchronize with tip..."
enable_sync
OLD_ID=`hg id -i -n`
HG_FAILED=0
if ! ( hg sync && hg update $REVISION_ARG ); then
HG_FAILED=1
fi
NEW_ID=`hg id -i -n`
disable_sync
if [ "$HG_FAILED" = 1 ]; then
fatal "'hg sync' failed"
fi
REVIEW_STATUS=`current_status .hg/codereview | sed 's/.hg\/codereview//'`
if [ "$NEW_ID" = "$OLD_ID" ]; then
echo "hg is already up-to-date."
else
echo "Old version: $OLD_ID"
echo "New version: $NEW_ID"
add_all
STATUS=`current_status`
#git update-index --no-assume-unchanged .hg/dirstate
COMMIT_FAILED=0
if [ "$STATUS" != "" ]; then
echo "Committing changes to 'master' branch..."
MESSAGE="Updated to hg $NEW_ID (`hg id -t`)"
# Don't use commit function here so that dirstate goes together.
if ! git commit -m "$MESSAGE" > /dev/null; then
COMMIT_FAILED=1
fi
fi
#git update-index --assume-unchanged .hg/dirstate
if [ $COMMIT_FAILED = 1 ]; then
fatal "git commit failed"
fi
fi
if [ "$REVIEW_STATUS" != "" ]; then
echo "Code reviews changed:\n$REVIEW_STATUS"
if [ "$NEW_ID" = "$OLD_ID" ]; then
add_all .hg/codereview
echo "Committing code review changes to 'master' branch..."
echo "Code reviews changed:\n$REVIEW_STATUS" | commit -F - > /dev/null
fi
fi
fi
if [ "$GET" = 1 -o "$NEW" = 1 ]; then
if [ "$NEW" = 1 ]; then
hg change
if [ $? != 0 ]; then
fatal "'hg change' failed"
fi
CL_NUMBER=`current_status .hg/codereview/ | sed -n 's/^.*\/cl\.\([0-9]\+\)$/\1/p'`
if ! echo "$CL_NUMBER" | grep '^[0-9]\+$' > /dev/null; then
fatal "failed to extract CL number"
fi
CL_BRANCH=$CL_NUMBER$CL_BRANCH_SUFFIX
MESSAGE="Created CL $CL_NUMBER"
checkout -b $CL_BRANCH
elif git checkout -b $CL_BRANCH > /dev/null 2> /dev/null; then
MESSAGE="Imported CL $CL_NUMBER"
echo "Created branch '$CL_BRANCH'."
else
MESSAGE="Updated from CL $CL_NUMBER"
checkout $CL_BRANCH
echo "Rebasing branch '$CL_BRANCH' on master..."
git rebase master
echo "Reverting previous changes from CL $CL_NUMBER..."
hg revert @$CL_NUMBER > /dev/null 2> /dev/null || true
fi
if [ "$NEW" = 0 ]; then
echo "Fetching CL $CL_NUMBER and applying in branch '$CL_BRANCH'..."
hg clpatch $CL_NUMBER
fi
add_all
STATUS=`current_status`
if [ "`current_status`" != "" ]; then
echo "Committing changes to '$CL_BRANCH' branch..."
commit -m "$MESSAGE" > /dev/null
else
echo "CL has no changes."
fi
elif [ "$SUBMIT" = 0 ]; then
checkout $INITIAL_BRANCH
#if [ "$INITIAL_BRANCH" != master -a "$NEW_ID" != "$OLD_ID" ]; then
# echo "Merging branch 'master' in branch '$INITIAL_BRANCH'..."
# git merge master
#fi
fi