-
Notifications
You must be signed in to change notification settings - Fork 2
/
git-verify
executable file
·351 lines (302 loc) · 10.2 KB
/
git-verify
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
#!/bin/bash
### Usage:
### git verify [-b [-|verify-branch]] [--all] [branchA, branchB, ...]
###
### You can use this to automate mass-branch-merging of
### multiple independant/separate patch-branches to one superior,
### for example to verify multiple changes together.
### This can rebuild 'verify' branch from those previously used,
### on top of new base (master) branch and opt. add more patch branches.
###
### After merging those, it can automatically push them to your
### own git remote (personal mirror). This requires more configuration
### see bellow for auto-push related config.
###
### Example:
### Have your git repo cloned somewhere.
### Have remote named 'my' registered there.
### Have few patch branches with (non-conflicting) changes.
### Opt. have your branch with personal changes there
### (default is empty which means no 'always merge' branches).
###
### go to your repo-working-copy-dir
### execute:
###
### git verify patch1 patch-extra
### # (re)create 'verify'
### # - drops verify, creates again from master (base branch)
### # merge personal branch:
### # - merge in 'personal' if enabled by 'always merge' opt.
### # merge patch branches as provided: 'patch1', 'patch-extra'
###
### git verify
### # lists what personal/patch branches are contained in the 'verify' branch
###
### git verify --all
### # recreate 'verify' branch:
### # - destroy 'verify' branch
### # - checkout branch 'master' as 'verify'
### # - opt. merge you personal branch there
### # - in order, merge all previously merged branches (patch1, patch-extra)
###
### git verify --all additional-extra
### # recreate, merge those already contained (see above)
### # + merge in 'additional-extra' branch
###
### This refuses to continue if your working-space or index (staged) contain any modifications.
###
### Three basic things are configurable (via cli-opt, env-var or git-config key)
### - base branch (defaults to 'master')
### * on top of which branch should all other (personal, patches) branches be merged
### * git config verify.branch.base
### * env var BASE_BR
### - verify branch (defaults to 'verify')
### * name of target branch to be (re)created (will be dropped in some cases!)
### or from which the list of 'previously merged' branches should be obtained/listed
### * git config verify.branch.target
### * env var VERIFY_BR
### * -b <name> cli option, has to be specified first before any other arg,
### when dash value is provided (-b -)
### it means recreate currently active branch!
### - always merge branch(es) (empty by default)
### * aka personal branches, string with space delimited branch names
### * git config verify.branch.alwaysmerge (e.g. 'personal')
### * env var GIT_VERIFY_ALWAYS_MERGE
### Be aware that these personal branches will always be rebased
### on top of master (base branch) before merging into verify,
### and they are merged first before the rest of patch branches.
###
### To skip missing branches (whatever if specified explicitely or via --all)
### use -m (--ignore-missing) (or verify.branch.ignoremissing=true).
###
### For auto-pushing you need to configure also:
### - name of the remote where to auto-push the verify branch (empty)
### * git config verify.push.remote (e.g. 'my')
### - name of remote branch where to auto-push the verify branch (empty)
### * git config verify.push.branch (e.g. 'verify')
### - which branches to also always-auto-push (empty)
### * git config verify.push.always (e.g. 'personal')
### - to temporarily skip pushing (when it's configured)
### * -np || --no-push CLI opt can be used
###
### If you don't like having verify (target) branch active
### after this command finishes, and instead You want to be
### returned to whatever branch was active before git-verify invocation:
### - git config verify.switchback true
### (default=false, means stay at the verify branch after finishing)
###
### To automatically send all the commits from patch-branches
### on gerrit for review, you can use
### -r || --review [--all] [branches-to-upload ...]
### which will instead of rebuilding/pushing the verify branch
### invoke 'git review --yes' on every branch specified,
### or contained in the 'verify' (in case of --all)
### excluding the personal 'always_merge' branches.
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
# show the above comment as help
sed -n 's/^###/ /p' $0
exit
fi
if ! git rev-parse --git-dir &> /dev/null; then
echo "Does not looks that $(pwd) is a git repo!" >&2
exit 1
fi
# config
BASE_BR="${BASE_BR:-$(git config verify.branch.base 2> /dev/null)}"
BASE_BR="${BASE_BR:-master}"
VERIFY_BR="${VERIFY_BR:-$(git config verify.branch.target 2> /dev/null)}"
VERIFY_BR="${VERIFY_BR:-verify}"
ALWAYS_MERGE="${GIT_VERIFY_ALWAYS_MERGE:-$(git config verify.branch.alwaysmerge 2> /dev/null)}"
IGNORE_MISSING="${IGNORE_MISSING:-$(git config verify.branch.ignoremissing 2> /dev/null)}"
IGNORE_MISSING="${IGNORE_MISSING:-false}"
AUTOPUSH_REMOTE="$(git config verify.push.remote 2> /dev/null)"
AUTOPUSH_BR="$(git config verify.push.branch 2> /dev/null)"
AUTOPUSH_ALWAYS="$(git config verify.push.always 2> /dev/null)"
SWITCH_BACK="$(git config --bool verify.switchback 2> /dev/null)"
FILES_TO_CLEAN=""
set -o errexit
# helpers
list_merged() {
git log $VERIFY_BR --merges \
| sed -n "s/.*branch '\(.*\)' into ${VERIFY_BR}.*/\1/p" | tac;
}
test_branch() {
if git branch | grep -q "$1" && git log -1 "$1" -- &>/dev/null; then
return
fi
if [[ "$IGNORE_MISSING" = "true" ]]; then
echo "WARNING: Branch $1 not found."
return 1
fi
echo "ERROR: Branch $1 not found!"
exit 128
}
active_branch() {
git branch | sed -nr 's/\* (.+)$/\1/p'
}
finish() {
echo "==[ DONE ]=="
if [[ ! -z "$FILES_TO_CLEAN" ]]; then
rm -rf "$(ls $FILES_TO_CLEAN)"
fi
if [[ "$SWITCH_BACK" = "true" ]]; then
git checkout $ORIG_BRANCH
fi
exit ${1:-0}
}
# parse the arguments
PREV_BRANCHES=""
ARGS_BRANCHES=""
PUSH_ALLOWED=y
UPDATE_PREVIEW=n
UPDATE_REVIEW=n
while [[ $# -gt 0 ]]; do
case "$1" in
-b|--branch)
shift
VERIFY_BR="$1"
if [[ "$VERIFY_BR" = "-" ]]; then
VERIFY_BR=$(active_branch)
fi
;;
-np|--no-push)
PUSH_ALLOWED=n
;;
-pr|--preview)
UPDATE_PREVIEW=y
;;
-r|--review)
UPDATE_PREVIEW=y
UPDATE_REVIEW=y
;;
-m|--ignore-missing)
IGNORE_MISSING=true
;;
-a|--all)
PREV_BRANCHES="$(list_merged)"
;;
*)
ARGS_BRANCHES="$ARGS_BRANCHES $1"
;;
esac
shift
done
# if nothing specified, just list the verify branch
echo "==/ $VERIFY_BR /=="
if [[ -z "$PREV_BRANCHES" && -z "$ARGS_BRANCHES" ]]; then
echo "==[ No branches specified ... listing last merges ]=="
IGNORE_MISSING=true
if test_branch "$VERIFY_BR"; then
echo ""
BRANCHES="$(list_merged)"
echo " " $BRANCHES # unquoted variable used to get the names on one line
else
echo "No already merged branches as there is no $VERIFY_BR branch"
fi
echo ""
echo "---- all existing branches ----"
git branch
echo ""
for BR in $BRANCHES; do
test_branch $BR
done
exit 0
fi
# Stuff which does have impact (rebase, push, review) follows
# so we do first check for clean state.
if [[ ! -z "$(git status --porcelain)" ]]; then
echo "==[ Unclean working copy ]=="
echo "Please first commit/stash/remove your local modifications.";
echo ""
git status
exit 1
fi
ORIG_BRANCH="$(active_branch)"
BRANCHES="$PREV_BRANCHES $ARGS_BRANCHES"
echo $BRANCHES
WANTED_BRANCHES="$BRANCHES"
BRANCHES=""
for BR in $WANTED_BRANCHES; do
if test_branch "$BR"; then
BRANCHES="$BRANCHES $BR"
fi
done
if [[ "$UPDATE_PREVIEW" = "y" ]]; then
FAILED_REVIEWS=""
NOCHANGE_REVIEWS=""
REVIEWLOG=$(mktemp)
FILES_TO_CLEAN="$FILES_TO_CLEAN $REVIEWLOG"
for BR in $BRANCHES; do
echo "==[ Uploading $BR ]=="
git log ${BASE_BR}..${BR}
if [[ "$UPDATE_REVIEW" = "y" ]]; then
git checkout $BR
git review --yes | tee $REVIEWLOG
rc="${PIPESTATUS[0]}"
if [[ "$rc" != "0" ]]; then
if grep -q '(no new changes)' $REVIEWLOG; then
NOCHANGE_REVIEWS="$NOCHANGE_REVIEWS $BR"
else
FAILED_REVIEWS="$FAILED_REVIEWS $BR"
fi
else
UPDATED_REVIEWS="$UPDATED_REVIEWS $BR"
fi
fi
echo ""
done
echo "Updated reviews for branches: $UPDATED_REVIEWS"
echo "Already up-to-date reviews for: $NOCHANGE_REVIEWS"
if [[ ! -z "$FAILED_REVIEWS" ]]; then
echo "Failed to upload branches: $FAILED_REVIEWS" >&2
finish 1
fi
finish
fi
echo "==[ Going to merge branches: ]=="
for BR in $ALWAYS_MERGE; do
echo "* $BR"
test_branch "$BR" || continue
git checkout "$BR"
git rebase $BASE_BR
if [[ $? != 0 ]]; then
echo "Personal branch $BR conflicts with $BASE_BR!"
git rebase --abort
exit 1
fi
done
echo ""
git checkout $BASE_BR
git branch -D $VERIFY_BR || :
git checkout -b $VERIFY_BR
set -e
for BR in $ALWAYS_MERGE; do
echo "==== $BR"
git merge --ff-only $BR
done
for BR in $BRANCHES; do
echo "==== $BR"
git merge --no-ff --commit --no-edit $BR
done
echo "==[ Auto-push ]=="
if [[ "$PUSH_ALLOWED" != "y" ]]; then
echo "Auto-push disabled."
elif [[ -z "$AUTOPUSH_REMOTE" ]]; then
echo "No remote configured - skipping."
else
if [[ -z "$AUTOPUSH_ALWAYS" ]]; then
echo "No always-push branches configured - skipping."
else
for BR in $AUTOPUSH_ALWAYS; do
echo "Always-Pushing branch: $AUTOPUSH_ALWAYS:"
git push --force "$AUTOPUSH_REMOTE" "${BR}:${BR}"
done
fi
if [[ -z "$AUTOPUSH_BR" ]]; then
echo "No target verify branch configured - skipping."
else
echo "Pushing target branch: $AUTOPUSH_BR:"
git push --force "$AUTOPUSH_REMOTE" "${VERIFY_BR}:${AUTOPUSH_BR}"
fi
fi
finish