git-maintain 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +21 -0
- data/README.md +14 -14
- data/bin/git-maintain +7 -55
- data/git-maintain-completion.sh +3 -3
- data/lib/{addons → git-maintain/addons}/RDMACore.rb +91 -33
- data/lib/{addons → git-maintain/addons}/git-maintain.rb +48 -15
- data/lib/git-maintain/addons/healthd.rb +73 -0
- data/lib/git-maintain/addons/hpc-testing.rb +102 -0
- data/lib/git-maintain/azure.rb +200 -0
- data/lib/git-maintain/branch.rb +944 -0
- data/lib/git-maintain/branch_iterator.rb +148 -0
- data/lib/git-maintain/ci.rb +163 -0
- data/lib/git-maintain/common.rb +117 -0
- data/lib/git-maintain/error.rb +81 -0
- data/lib/git-maintain/repo.rb +573 -0
- data/lib/git-maintain/travis.rb +179 -0
- data/lib/git-maintain.rb +2 -0
- metadata +30 -14
- data/lib/azure.rb +0 -98
- data/lib/branch.rb +0 -759
- data/lib/ci.rb +0 -88
- data/lib/common.rb +0 -253
- data/lib/repo.rb +0 -447
- data/lib/travis.rb +0 -80
data/lib/branch.rb
DELETED
|
@@ -1,759 +0,0 @@
|
|
|
1
|
-
module GitMaintain
|
|
2
|
-
|
|
3
|
-
class CherryPickErrorException < StandardError
|
|
4
|
-
def initialize(str, commit)
|
|
5
|
-
@commit = commit
|
|
6
|
-
super(str)
|
|
7
|
-
end
|
|
8
|
-
attr_reader :commit
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
class Branch
|
|
12
|
-
ACTION_LIST = [
|
|
13
|
-
:cp, :steal, :list,
|
|
14
|
-
:merge, :pull, :push, :monitor,
|
|
15
|
-
:release, :reset, :create, :delete
|
|
16
|
-
]
|
|
17
|
-
NO_FETCH_ACTIONS = [
|
|
18
|
-
:cp, :merge, :monitor, :release, :delete
|
|
19
|
-
]
|
|
20
|
-
NO_CHECKOUT_ACTIONS = [
|
|
21
|
-
:create, :delete, :list, :push, :monitor
|
|
22
|
-
]
|
|
23
|
-
ALL_BRANCHES_ACTIONS = [
|
|
24
|
-
:create
|
|
25
|
-
]
|
|
26
|
-
ACTION_HELP = {
|
|
27
|
-
:cp => "Backport commits and eventually push them to github",
|
|
28
|
-
:create => "Create missing local branches from all the stable branches",
|
|
29
|
-
:delete => "Delete all local branches using the suffix",
|
|
30
|
-
:steal => "Steal commit from upstream that fixes commit in the branch or were tagged as stable",
|
|
31
|
-
:list => "List commit present in the branch but not in the stable branch",
|
|
32
|
-
:merge => "Merge branch with suffix specified in -m <suff> into the main branch",
|
|
33
|
-
:push => "Push branches to github for validation",
|
|
34
|
-
:pull => "Rebase branches on top of the upstream one",
|
|
35
|
-
:monitor => "Check the CI state of all branches",
|
|
36
|
-
:release => "Create new release on all concerned branches",
|
|
37
|
-
:reset => "Reset branch against upstream",
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
def self.load(repo, version, ci, branch_suff)
|
|
41
|
-
repo_name = File.basename(repo.path)
|
|
42
|
-
return GitMaintain::loadClass(Branch, repo_name, repo, version, ci, branch_suff)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def self.set_opts(action, optsParser, opts)
|
|
46
|
-
opts[:base_ver] = 0
|
|
47
|
-
opts[:version] = []
|
|
48
|
-
opts[:commits] = []
|
|
49
|
-
opts[:do_merge] = false
|
|
50
|
-
opts[:push_force] = false
|
|
51
|
-
opts[:no_ci] = false
|
|
52
|
-
opts[:steal_base] = nil
|
|
53
|
-
opts[:check_only] = false
|
|
54
|
-
opts[:fetch] = nil
|
|
55
|
-
opts[:watch] = false
|
|
56
|
-
opts[:delete_remote] = false
|
|
57
|
-
opts[:no_edit] = false
|
|
58
|
-
opts[:stable] = false
|
|
59
|
-
|
|
60
|
-
optsParser.on("-v", "--base-version [MIN_VER]", Integer, "Older release to consider.") {
|
|
61
|
-
|val| opts[:base_ver] = val}
|
|
62
|
-
optsParser.on("-V", "--version [regexp]", Regexp, "Regexp to filter versions.") {
|
|
63
|
-
|val| opts[:version] << val}
|
|
64
|
-
|
|
65
|
-
if ALL_BRANCHES_ACTIONS.index(action) == nil &&
|
|
66
|
-
action != :merge &&
|
|
67
|
-
action != :delete then
|
|
68
|
-
optsParser.on("-B", "--manual-branch <branch name>", "Work on a specific (non-stable) branch.") {
|
|
69
|
-
|val| opts[:manual_branch] = val}
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
if NO_FETCH_ACTIONS.index(action) == nil
|
|
73
|
-
optsParser.on("--[no-]fetch", "Enable/Disable fetch of stable repo.") {
|
|
74
|
-
|val| opts[:fetch] = val}
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
case action
|
|
78
|
-
when :cp
|
|
79
|
-
optsParser.banner += "-c <sha1> [-c <sha1> ...]"
|
|
80
|
-
optsParser.on("-c", "--sha1 [SHA1]", String, "Commit to cherry-pick. Can be used multiple time.") {
|
|
81
|
-
|val| opts[:commits] << val}
|
|
82
|
-
when :delete
|
|
83
|
-
optsParser.on("--remote", "Delete the remote staging branch instead of the local ones.") {
|
|
84
|
-
|val| opts[:delete_remote] = true}
|
|
85
|
-
when :list
|
|
86
|
-
optsParser.on("--stable", "List unreleased commits in the upstream stable branch.") {
|
|
87
|
-
opts[:stable] = true }
|
|
88
|
-
when :merge
|
|
89
|
-
optsParser.banner += "-m <suffix>"
|
|
90
|
-
optsParser.on("-m", "--merge [SUFFIX]", "Merge branch with suffix.") {
|
|
91
|
-
|val| opts[:do_merge] = val}
|
|
92
|
-
when :monitor
|
|
93
|
-
optsParser.on("-w", "--watch <PERIOD>", Integer,
|
|
94
|
-
"Watch and refresh CI status every <PERIOD>.") {
|
|
95
|
-
|val| opts[:watch] = val}
|
|
96
|
-
optsParser.on("--stable", "Check CI status on stable repo.") {
|
|
97
|
-
opts[:stable] = true }
|
|
98
|
-
when :pull
|
|
99
|
-
optsParser.on("--stable", "List unreleased commits in the upstream stable branch.") {
|
|
100
|
-
opts[:stable] = true }
|
|
101
|
-
when :push
|
|
102
|
-
optsParser.banner += "[-f]"
|
|
103
|
-
optsParser.on("-f", "--force", "Add --force to git push (for 'push' action).") {
|
|
104
|
-
opts[:push_force] = true}
|
|
105
|
-
optsParser.on("--stable", "Push to stable repo.") {
|
|
106
|
-
opts[:stable] = true }
|
|
107
|
-
optsParser.banner += "[-T]"
|
|
108
|
-
optsParser.on("-T", "--no-ci", "Ignore CI build status and push anyway.") {
|
|
109
|
-
opts[:no_ci] = true}
|
|
110
|
-
optsParser.on("-c", "--check", "Check if there is something to be pushed.") {
|
|
111
|
-
opts[:check_only] = true}
|
|
112
|
-
when :release
|
|
113
|
-
optsParser.on("--no-edit", "Do not edit release commit nor tag.") {
|
|
114
|
-
opts[:no_edit] = true }
|
|
115
|
-
when :steal
|
|
116
|
-
optsParser.banner += "[-a][-b <HEAD>]"
|
|
117
|
-
optsParser.on("-a", "--all", "Check all commits from master. "+
|
|
118
|
-
"By default only new commits (since last successful run) are considered.") {
|
|
119
|
-
|val| opts[:steal_base] = :all}
|
|
120
|
-
optsParser.on("-b", "--base <HEAD>", "Check all commits from this commit. "+
|
|
121
|
-
"By default only new commits (since last successful run) are considered.") {
|
|
122
|
-
|val| opts[:steal_base] = val}
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def self.check_opts(opts)
|
|
127
|
-
if opts[:action] == :release then
|
|
128
|
-
if opts[:br_suff] != "master" then
|
|
129
|
-
raise "Action #{opts[:action]} can only be done on 'master' suffixed branches"
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
if opts[:action] == :delete && opts[:delete_remote] != true then
|
|
133
|
-
if opts[:br_suff] == "master" then
|
|
134
|
-
raise "Action #{opts[:action]} can NOT be done on 'master' suffixed branches"
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
if opts[:action] == :push
|
|
138
|
-
if opts[:stable] == true && opts[:push_force] == true then
|
|
139
|
-
raise "Action push can NOT be use both --stable and --force"
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
opts[:version] = [ /.*/ ] if opts[:version].length == 0
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
def self.execAction(opts, action)
|
|
146
|
-
repo = Repo::load()
|
|
147
|
-
ci = CI::load(repo)
|
|
148
|
-
opts[:repo] = repo
|
|
149
|
-
opts[:ci] = ci
|
|
150
|
-
brClass = GitMaintain::getClass(self, repo.name)
|
|
151
|
-
|
|
152
|
-
if NO_FETCH_ACTIONS.index(action) == nil && opts[:fetch] != false then
|
|
153
|
-
GitMaintain::log(:INFO, "Fetching stable repo")
|
|
154
|
-
repo.stableUpdate(opts[:fetch])
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
branchList=[]
|
|
158
|
-
if opts[:manual_branch] == nil then
|
|
159
|
-
unfilteredList = nil
|
|
160
|
-
if ALL_BRANCHES_ACTIONS.index(action) != nil then
|
|
161
|
-
unfilteredList = repo.getStableBranchList()
|
|
162
|
-
else
|
|
163
|
-
unfilteredList = repo.getBranchList(opts[:br_suff])
|
|
164
|
-
end
|
|
165
|
-
branchList = unfilteredList.map(){|br|
|
|
166
|
-
branch = Branch::load(repo, br, ci, opts[:br_suff])
|
|
167
|
-
case branch.is_targetted?(opts)
|
|
168
|
-
when :too_old
|
|
169
|
-
GitMaintain::log(:VERBOSE, "Skipping older v#{branch.version}")
|
|
170
|
-
next
|
|
171
|
-
when :no_match
|
|
172
|
-
GitMaintain::log(:VERBOSE, "Skipping v#{branch.version} not matching" +
|
|
173
|
-
opts[:version].to_s())
|
|
174
|
-
next
|
|
175
|
-
end
|
|
176
|
-
branch
|
|
177
|
-
}.compact()
|
|
178
|
-
else
|
|
179
|
-
branchList = [ Branch::load(repo, opts[:manual_branch], ci, opts[:br_suff]) ]
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
loop do
|
|
183
|
-
system("clear; date") if opts[:watch] != false
|
|
184
|
-
|
|
185
|
-
res=[]
|
|
186
|
-
|
|
187
|
-
# Iterate concerned on all branches
|
|
188
|
-
branchList.each(){|branch|
|
|
189
|
-
if NO_CHECKOUT_ACTIONS.index(action) == nil then
|
|
190
|
-
GitMaintain::log(:INFO, "Working on #{branch.verbose_name}")
|
|
191
|
-
branch.checkout()
|
|
192
|
-
end
|
|
193
|
-
res << branch.send(action, opts)
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
# Run epilogue (if it exists)
|
|
197
|
-
begin
|
|
198
|
-
brClass.send(action.to_s() + "_epilogue", opts, res)
|
|
199
|
-
rescue NoMethodError => e
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
break if opts[:watch] == false
|
|
203
|
-
sleep(opts[:watch])
|
|
204
|
-
ci.emptyCache()
|
|
205
|
-
end
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
def initialize(repo, version, ci, branch_suff)
|
|
209
|
-
GitMaintain::checkDirectConstructor(self.class)
|
|
210
|
-
|
|
211
|
-
@repo = repo
|
|
212
|
-
@ci = ci
|
|
213
|
-
@version = version
|
|
214
|
-
@branch_suff = branch_suff
|
|
215
|
-
|
|
216
|
-
if version =~ /^[0-9]+$/
|
|
217
|
-
@local_branch = @repo.versionToLocalBranch(@version, @branch_suff)
|
|
218
|
-
@remote_branch = @repo.versionToStableBranch(@version)
|
|
219
|
-
@branch_type = :std
|
|
220
|
-
@verbose_name = "v"+version
|
|
221
|
-
else
|
|
222
|
-
@remote_branch = @local_branch = version
|
|
223
|
-
@branch_type = :user_specified
|
|
224
|
-
@verbose_name = version
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
@head = @repo.runGit("rev-parse --verify --quiet #{@local_branch}")
|
|
228
|
-
@valid_ref = "#{@repo.valid_repo}/#{@local_branch}"
|
|
229
|
-
@remote_ref = "#{@repo.stable_repo}/#{@remote_branch}"
|
|
230
|
-
@stable_head = @repo.runGit("rev-parse --verify --quiet #{@remote_ref}")
|
|
231
|
-
case @branch_type
|
|
232
|
-
when :std
|
|
233
|
-
@stable_base = @repo.findStableBase(@local_branch)
|
|
234
|
-
when :user_specified
|
|
235
|
-
@stable_base = @remote_ref
|
|
236
|
-
end
|
|
237
|
-
end
|
|
238
|
-
attr_reader :version, :local_branch, :head, :remote_branch, :valid_ref, :remote_ref, :stable_head,
|
|
239
|
-
:verbose_name, :exists, :stable_base
|
|
240
|
-
|
|
241
|
-
def log(lvl, str)
|
|
242
|
-
GitMaintain::log(lvl, str)
|
|
243
|
-
end
|
|
244
|
-
|
|
245
|
-
def is_targetted?(opts)
|
|
246
|
-
return true if @branch_type == :user_specified
|
|
247
|
-
if @version.to_i < opts[:base_ver] then
|
|
248
|
-
return :too_old
|
|
249
|
-
end
|
|
250
|
-
opts[:version].each() {|regexp|
|
|
251
|
-
return true if @version =~ regexp
|
|
252
|
-
}
|
|
253
|
-
return :no_match
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
# Checkout the repo to the given branch
|
|
257
|
-
def checkout()
|
|
258
|
-
print @repo.runGit("checkout -q #{@local_branch}")
|
|
259
|
-
if $? != 0 then
|
|
260
|
-
raise "Error: Failed to checkout the branch"
|
|
261
|
-
end
|
|
262
|
-
end
|
|
263
|
-
|
|
264
|
-
# Cherry pick an array of commits
|
|
265
|
-
def cp(opts)
|
|
266
|
-
opts[:commits].each(){|commit|
|
|
267
|
-
prev_head=@repo.runGit("rev-parse HEAD")
|
|
268
|
-
log(:INFO, "Applying #{@repo.getCommitHeadline(commit)}")
|
|
269
|
-
@repo.runGitInteractive("cherry-pick #{commit}")
|
|
270
|
-
if $? != 0 then
|
|
271
|
-
log(:WARNING, "Cherry pick failure. Starting bash for manual fixes. Exit shell to continue")
|
|
272
|
-
@repo.runBash("PS1_WARNING='CP FIX'")
|
|
273
|
-
end
|
|
274
|
-
new_head=@repo.runGit("rev-parse HEAD")
|
|
275
|
-
# Do not make commit pretty if it was not applied
|
|
276
|
-
if new_head != prev_head
|
|
277
|
-
make_pretty(commit)
|
|
278
|
-
end
|
|
279
|
-
}
|
|
280
|
-
end
|
|
281
|
-
|
|
282
|
-
# Steal upstream commits that are not in the branch
|
|
283
|
-
def steal(opts)
|
|
284
|
-
base_ref=@stable_base
|
|
285
|
-
|
|
286
|
-
# If we are not force checking everything,
|
|
287
|
-
# try to start from the last tag we steal upto
|
|
288
|
-
case opts[:steal_base]
|
|
289
|
-
when nil
|
|
290
|
-
sha = @repo.runGit("rev-parse 'git-maintain/steal/last/#{@stable_base}' 2>&1")
|
|
291
|
-
if $? == 0 then
|
|
292
|
-
base_ref=sha
|
|
293
|
-
log(:VERBOSE, "Starting from last successfull run:")
|
|
294
|
-
log(:VERBOSE, @repo.getCommitHeadline(base_ref))
|
|
295
|
-
end
|
|
296
|
-
when :all
|
|
297
|
-
base_ref=@stable_base
|
|
298
|
-
else
|
|
299
|
-
sha = @repo.runGit("rev-parse #{opts[:steal_base]} 2>&1")
|
|
300
|
-
if $? == 0 then
|
|
301
|
-
base_ref=sha
|
|
302
|
-
log(:VERBOSE, "Starting from base:")
|
|
303
|
-
log(:VERBOSE, @repo.getCommitHeadline(base_ref))
|
|
304
|
-
end
|
|
305
|
-
end
|
|
306
|
-
|
|
307
|
-
master_sha=@repo.runGit("rev-parse origin/master")
|
|
308
|
-
res = steal_all(opts, "#{base_ref}..#{master_sha}", true)
|
|
309
|
-
|
|
310
|
-
# If we picked all the commits (or nothing happened)
|
|
311
|
-
# Mark the current master as the last checked point so we
|
|
312
|
-
# can just steal from this point on the next run
|
|
313
|
-
if res == true then
|
|
314
|
-
@repo.runGit("tag -f 'git-maintain/steal/last/#{@stable_base}' origin/master")
|
|
315
|
-
log(:VERBOSE, "Marking new last successfull run at:")
|
|
316
|
-
log(:VERBOSE, @repo.getCommitHeadline(master_sha))
|
|
317
|
-
end
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
def list(opts)
|
|
321
|
-
GitMaintain::log(:INFO, "Working on #{@verbose_name}")
|
|
322
|
-
if opts[:stable] == true then
|
|
323
|
-
# List commits in the stable_branch that are no in the latest release
|
|
324
|
-
GitMaintain::showLog(opts, @remote_ref, @repo.runGit("describe --abbrev=0 #{@local_branch}"))
|
|
325
|
-
else
|
|
326
|
-
# List commits in the branch that are no in the stable branch
|
|
327
|
-
GitMaintain::showLog(opts, @local_branch, @remote_ref)
|
|
328
|
-
end
|
|
329
|
-
end
|
|
330
|
-
|
|
331
|
-
# Merge merge_branch into this one
|
|
332
|
-
def merge(opts)
|
|
333
|
-
merge_branch = @repo.versionToLocalBranch(@version, opts[:do_merge])
|
|
334
|
-
|
|
335
|
-
# Make sure branch exists
|
|
336
|
-
hash_to_merge = @repo.runGit("rev-parse --verify --quiet #{merge_branch}")
|
|
337
|
-
if $? != 0 then
|
|
338
|
-
log(:INFO, "Branch #{merge_branch} does not exists. Skipping...")
|
|
339
|
-
return
|
|
340
|
-
end
|
|
341
|
-
|
|
342
|
-
# See if there is anything worth merging
|
|
343
|
-
merge_base_hash = @repo.runGit("merge-base #{merge_branch} #{@local_branch}")
|
|
344
|
-
if merge_base_hash == hash_to_merge then
|
|
345
|
-
log(:INFO, "Branch #{merge_branch} has no commit that needs to be merged")
|
|
346
|
-
return
|
|
347
|
-
end
|
|
348
|
-
|
|
349
|
-
rep = GitMaintain::checkLog(opts, merge_branch, @local_branch, "merge")
|
|
350
|
-
if rep == "y" then
|
|
351
|
-
@repo.runGitInteractive("merge #{merge_branch}")
|
|
352
|
-
if $? != 0 then
|
|
353
|
-
log(:WARNING, "Merge failure. Starting bash for manual fixes. Exit shell to continue")
|
|
354
|
-
@repo.runBash("PS1_WARNING='MERGING'")
|
|
355
|
-
end
|
|
356
|
-
else
|
|
357
|
-
log(:INFO, "Skipping merge")
|
|
358
|
-
return
|
|
359
|
-
end
|
|
360
|
-
end
|
|
361
|
-
|
|
362
|
-
def pull(opts)
|
|
363
|
-
remoteRef = opts[:stable] == true ? @remote_ref : @valid_ref
|
|
364
|
-
|
|
365
|
-
# Make sure branch exists
|
|
366
|
-
@repo.runGit("rev-parse --verify --quiet #{remoteRef}")
|
|
367
|
-
if $? != 0 then
|
|
368
|
-
log(:INFO, "Branch #{remoteRef} does not exists. Skipping...")
|
|
369
|
-
return
|
|
370
|
-
end
|
|
371
|
-
@repo.runGitInteractive("rebase #{remoteRef}")
|
|
372
|
-
|
|
373
|
-
end
|
|
374
|
-
# Push the branch to the validation repo
|
|
375
|
-
def push(opts)
|
|
376
|
-
remoteRef = opts[:stable] == true ? @remote_ref : @valid_ref
|
|
377
|
-
|
|
378
|
-
# Check both where we want to push and the final remote_ref
|
|
379
|
-
# We may have destroyed the validation branch but if we already merged
|
|
380
|
-
# in the final repo, no need to worry about it.
|
|
381
|
-
if same_sha?(@local_branch, remoteRef) ||
|
|
382
|
-
same_sha?(@local_branch, @remote_ref) then
|
|
383
|
-
log(:INFO, "Nothing to push on #{@local_branch}")
|
|
384
|
-
return
|
|
385
|
-
end
|
|
386
|
-
|
|
387
|
-
# For stable branches, we need to check for CI
|
|
388
|
-
if opts[:stable] == true &&
|
|
389
|
-
(opts[:no_ci] != true && @NO_CI != true) &&
|
|
390
|
-
@ci.checkValidState(self, @head) != true then
|
|
391
|
-
log(:WARNING, "Build is not passed on CI. Skipping push to stable")
|
|
392
|
-
return
|
|
393
|
-
end
|
|
394
|
-
|
|
395
|
-
if opts[:check_only] == true then
|
|
396
|
-
GitMaintain::checkLog(opts, @local_branch, @remote_ref, "")
|
|
397
|
-
return
|
|
398
|
-
end
|
|
399
|
-
|
|
400
|
-
# For validation/CI push, let's go and push already
|
|
401
|
-
return "#{@local_branch}:#{@local_branch}" if opts[:stable] != true
|
|
402
|
-
|
|
403
|
-
# For stable, we need to confirm with the user that he really wants to push
|
|
404
|
-
rep = GitMaintain::checkLog(opts, @local_branch, @remote_ref, "submit")
|
|
405
|
-
if rep == "y" then
|
|
406
|
-
return "#{@local_branch}:#{@remote_branch}"
|
|
407
|
-
else
|
|
408
|
-
log(:INFO, "Skipping push to stable")
|
|
409
|
-
return
|
|
410
|
-
end
|
|
411
|
-
end
|
|
412
|
-
|
|
413
|
-
def self.push_epilogue(opts, branches)
|
|
414
|
-
# Compact to remove empty entries
|
|
415
|
-
branches.compact!()
|
|
416
|
-
|
|
417
|
-
return if branches.length == 0
|
|
418
|
-
|
|
419
|
-
repo = (opts[:stable] == true) ? opts[:repo].stable_repo : opts[:repo].valid_repo
|
|
420
|
-
opts[:repo].runGit("push #{opts[:push_force] == true ? "-f" : ""} "+
|
|
421
|
-
"#{repo} #{branches.join(" ")}")
|
|
422
|
-
end
|
|
423
|
-
|
|
424
|
-
# Monitor the build status on CI
|
|
425
|
-
def monitor(opts)
|
|
426
|
-
ts = st = head = nil
|
|
427
|
-
suff=""
|
|
428
|
-
if opts[:stable] == true then
|
|
429
|
-
st = @ci.getStableState(self, @stable_head)
|
|
430
|
-
ts = @ci.getStableTS(self, @stable_head) if st == "started"
|
|
431
|
-
else
|
|
432
|
-
st = @ci.getValidState(self, @head)
|
|
433
|
-
ts = @ci.getValidTS(self, @head) if st == "started"
|
|
434
|
-
end
|
|
435
|
-
|
|
436
|
-
case st
|
|
437
|
-
when "started"
|
|
438
|
-
suff= " at #{ts}"
|
|
439
|
-
end
|
|
440
|
-
log(:INFO, "Status for v#{@version}: " + st + suff)
|
|
441
|
-
if @ci.isErrored(self, st) && opts[:watch] == false
|
|
442
|
-
rep = "y"
|
|
443
|
-
suff=""
|
|
444
|
-
while rep == "y"
|
|
445
|
-
rep = GitMaintain::confirm(opts, "see the build log#{suff}")
|
|
446
|
-
if rep == "y" then
|
|
447
|
-
log = @ci.getValidLog(self, @head)
|
|
448
|
-
tmp = `mktemp`.chomp()
|
|
449
|
-
tmpfile = File.open(tmp, "w+")
|
|
450
|
-
tmpfile.puts(log)
|
|
451
|
-
tmpfile.close()
|
|
452
|
-
system("less -r #{tmp}")
|
|
453
|
-
`rm -f #{tmp}`
|
|
454
|
-
end
|
|
455
|
-
suff=" again"
|
|
456
|
-
end
|
|
457
|
-
end
|
|
458
|
-
end
|
|
459
|
-
|
|
460
|
-
# Reset the branch to the upstream stable one
|
|
461
|
-
def reset(opts)
|
|
462
|
-
if same_sha?(@local_branch, @remote_ref) then
|
|
463
|
-
log(:INFO, "Nothing to reset")
|
|
464
|
-
return
|
|
465
|
-
end
|
|
466
|
-
|
|
467
|
-
rep = GitMaintain::checkLog(opts, @local_branch, @remote_ref, "reset")
|
|
468
|
-
if rep == "y" then
|
|
469
|
-
@repo.runGit("reset --hard #{@remote_ref}")
|
|
470
|
-
else
|
|
471
|
-
log(:INFO, "Skipping reset")
|
|
472
|
-
return
|
|
473
|
-
end
|
|
474
|
-
end
|
|
475
|
-
|
|
476
|
-
def release(opts)
|
|
477
|
-
log(:ERROR,"#No release command available for this repo")
|
|
478
|
-
end
|
|
479
|
-
|
|
480
|
-
def create(opts)
|
|
481
|
-
return if @head != ""
|
|
482
|
-
log(:INFO, "Creating missing #{@local_branch} from #{@remote_ref}")
|
|
483
|
-
@repo.runGit("branch #{@local_branch} #{@remote_ref}")
|
|
484
|
-
end
|
|
485
|
-
|
|
486
|
-
def delete(opts)
|
|
487
|
-
if opts[:delete_remote] == true then
|
|
488
|
-
@repo.runGit("rev-parse --verify --quiet #{@repo.valid_repo}/#{@local_branch}")
|
|
489
|
-
if $? != 0 then
|
|
490
|
-
log(:DEBUG, "Skipping non existing remote branch #{@local_branch}.")
|
|
491
|
-
return
|
|
492
|
-
end
|
|
493
|
-
msg = "delete remote branch #{@repo.valid_repo}/#{@local_branch}"
|
|
494
|
-
else
|
|
495
|
-
msg = "delete branch #{@local_branch}"
|
|
496
|
-
end
|
|
497
|
-
rep = GitMaintain::confirm(opts, msg)
|
|
498
|
-
if rep == "y" then
|
|
499
|
-
return @local_branch
|
|
500
|
-
else
|
|
501
|
-
log(:INFO, "Skipping deletion")
|
|
502
|
-
return
|
|
503
|
-
end
|
|
504
|
-
end
|
|
505
|
-
def self.delete_epilogue(opts, branches)
|
|
506
|
-
# Compact to remove empty entries
|
|
507
|
-
branches.compact!()
|
|
508
|
-
|
|
509
|
-
return if branches.length == 0
|
|
510
|
-
puts "Deleting #{opts[:delete_remote] == true ? "remote" : "local"} branches: #{branches.join(" ")}"
|
|
511
|
-
rep = GitMaintain::confirm(opts, "continue", true)
|
|
512
|
-
if rep != "y" then
|
|
513
|
-
log(:INFO, "Cancelling")
|
|
514
|
-
return
|
|
515
|
-
end
|
|
516
|
-
if opts[:delete_remote] == true then
|
|
517
|
-
opts[:repo].runGit("push #{opts[:repo].valid_repo} #{branches.map(){|x| ":" + x}.join(" ")}")
|
|
518
|
-
else
|
|
519
|
-
opts[:repo].runGit("branch -D #{branches.join(" ")}")
|
|
520
|
-
end
|
|
521
|
-
end
|
|
522
|
-
|
|
523
|
-
private
|
|
524
|
-
def add_blacklist(commit)
|
|
525
|
-
@repo.runGit("notes append -m \"#{@local_branch}\" #{commit}")
|
|
526
|
-
end
|
|
527
|
-
|
|
528
|
-
def is_blacklisted?(commit)
|
|
529
|
-
@repo.runGit("notes show #{commit} 2> /dev/null").split("\n").each(){|br|
|
|
530
|
-
return true if br == @local_branch
|
|
531
|
-
}
|
|
532
|
-
return false
|
|
533
|
-
end
|
|
534
|
-
|
|
535
|
-
def make_pretty(orig_commit, commit="")
|
|
536
|
-
orig_sha=@repo.runGit("rev-parse #{orig_commit}")
|
|
537
|
-
msg_commit = (commit.to_s() == "") ? orig_sha : commit
|
|
538
|
-
|
|
539
|
-
msg_path=`mktemp`.chomp()
|
|
540
|
-
msg_file = File.open(msg_path, "w+")
|
|
541
|
-
msg_file.puts @repo.runGit("log -1 --format=\"%s%n%n[ Upstream commit #{msg_commit} ]%n%n%b\" #{orig_commit}")
|
|
542
|
-
msg_file.close()
|
|
543
|
-
@repo.runGit("commit -s --amend -F #{msg_path}")
|
|
544
|
-
`rm -f #{msg_path}`
|
|
545
|
-
end
|
|
546
|
-
|
|
547
|
-
def is_in_tree?(commit, src_commit=commit)
|
|
548
|
-
fullhash=@repo.runGit("rev-parse --verify --quiet #{commit}")
|
|
549
|
-
# This might happen if someone pointed to a commit that doesn't exist in our
|
|
550
|
-
# tree.
|
|
551
|
-
if $? != 0 then
|
|
552
|
-
log(:WARNING, "Commit #{src_commit} points to a SHA #{commit} not in tree")
|
|
553
|
-
return false
|
|
554
|
-
end
|
|
555
|
-
|
|
556
|
-
# Hope for the best, same commit is/isn't in the current branch
|
|
557
|
-
if @repo.runGit("merge-base #{fullhash} HEAD") == fullhash then
|
|
558
|
-
return true
|
|
559
|
-
end
|
|
560
|
-
|
|
561
|
-
# Grab the subject, since commit sha1 is different between branches we
|
|
562
|
-
# have to look it up based on subject.
|
|
563
|
-
subj=@repo.getCommitSubj(commit)
|
|
564
|
-
if $? != 0 then
|
|
565
|
-
return false
|
|
566
|
-
end
|
|
567
|
-
|
|
568
|
-
# Try and find if there's a commit with given subject the hard way
|
|
569
|
-
@repo.runGit("log --pretty=\"%H\" -F --grep \"#{subj.gsub("\"", '\\"')}\" "+
|
|
570
|
-
"#{@stable_base}..HEAD").split("\n").each(){|cmt|
|
|
571
|
-
cursubj=@repo.runGit("log -1 --format=\"%s\" #{cmt}")
|
|
572
|
-
if cursubj = subj then
|
|
573
|
-
return true
|
|
574
|
-
end
|
|
575
|
-
}
|
|
576
|
-
return false
|
|
577
|
-
end
|
|
578
|
-
|
|
579
|
-
def is_relevant?(commit)
|
|
580
|
-
# Let's grab the commit that this commit fixes (if exists (based on the "Fixes:" tag)).
|
|
581
|
-
fixescmt=@repo.runGit("log -1 #{commit} | grep -i \"fixes:\" | head -n 1 | "+
|
|
582
|
-
"sed -e 's/^[ \\t]*//' | cut -f 2 -d ':' | "+
|
|
583
|
-
"sed -e 's/^[ \\t]*//' -e 's/\\([0-9a-f]\\+\\)(/\\1 (/' | cut -f 1 -d ' '")
|
|
584
|
-
|
|
585
|
-
# If this commit fixes anything, but the broken commit isn't in our branch we don't
|
|
586
|
-
# need this commit either.
|
|
587
|
-
if fixescmt != "" then
|
|
588
|
-
if is_in_tree?(fixescmt, commit) then
|
|
589
|
-
return true
|
|
590
|
-
else
|
|
591
|
-
return false
|
|
592
|
-
end
|
|
593
|
-
end
|
|
594
|
-
|
|
595
|
-
if @repo.runGit("show #{commit} | grep -i 'stable@' | wc -l") == "0" then
|
|
596
|
-
return false
|
|
597
|
-
end
|
|
598
|
-
|
|
599
|
-
# Let's see if there's a version tag in this commit
|
|
600
|
-
full=@repo.runGit("show #{commit} | grep -i 'stable@'").gsub(/.* #?/, "")
|
|
601
|
-
|
|
602
|
-
# Sanity check our extraction
|
|
603
|
-
if full =~ /stable/ then
|
|
604
|
-
return false
|
|
605
|
-
end
|
|
606
|
-
|
|
607
|
-
full = @repo.runGit("rev-parse #{full}^{commit}")
|
|
608
|
-
|
|
609
|
-
# Make sure our branch contains this version
|
|
610
|
-
if @repo.runGit("merge-base #{@head} #{full}") == full then
|
|
611
|
-
return true
|
|
612
|
-
end
|
|
613
|
-
|
|
614
|
-
# Tag is not in history, ignore
|
|
615
|
-
return false
|
|
616
|
-
end
|
|
617
|
-
|
|
618
|
-
def pick_one(commit)
|
|
619
|
-
@repo.runGitInteractive("cherry-pick --strategy=recursive -Xpatience -x #{commit} &> /dev/null")
|
|
620
|
-
return if $? == 0
|
|
621
|
-
if @repo.runGit("status -uno --porcelain | wc -l") == "0" then
|
|
622
|
-
@repo.runGit("reset --hard")
|
|
623
|
-
raise CherryPickErrorException.new("Failed to cherry pick commit #{commit}", commit)
|
|
624
|
-
end
|
|
625
|
-
@repo.runGit("reset --hard")
|
|
626
|
-
# That didn't work? Let's try that with every variation of the commit
|
|
627
|
-
# in other stable trees.
|
|
628
|
-
@repo.find_alts(commit).each(){|alt_commit|
|
|
629
|
-
@repo.runGitInteractive("cherry-pick --strategy=recursive -Xpatience -x #{alt_commit} &> /dev/null")
|
|
630
|
-
if $? == 0 then
|
|
631
|
-
return
|
|
632
|
-
end
|
|
633
|
-
@repo.runGit("reset --hard")
|
|
634
|
-
}
|
|
635
|
-
# Still no? Let's go back to the original commit and hand it off to
|
|
636
|
-
# the user.
|
|
637
|
-
@repo.runGitInteractive("cherry-pick --strategy=recursive -Xpatience -x #{commit} &> /dev/null")
|
|
638
|
-
raise CherryPickErrorException.new("Failed to cherry pick commit #{commit}", commit)
|
|
639
|
-
return false
|
|
640
|
-
end
|
|
641
|
-
|
|
642
|
-
def confirm_one(opts, commit)
|
|
643
|
-
rep=""
|
|
644
|
-
do_cp=false
|
|
645
|
-
puts @repo.getCommitHeadline(commit)
|
|
646
|
-
while rep != "y" do
|
|
647
|
-
puts "Do you want to steal this commit ? (y/n/b/?)"
|
|
648
|
-
case opts[:yn_default]
|
|
649
|
-
when :no
|
|
650
|
-
log(:INFO, "Auto-replying no due to --no option")
|
|
651
|
-
rep = 'n'
|
|
652
|
-
break
|
|
653
|
-
when :yes
|
|
654
|
-
log(:INFO, "Auto-replying yes due to --yes option")
|
|
655
|
-
rep = 'y'
|
|
656
|
-
else
|
|
657
|
-
rep = STDIN.gets.chomp()
|
|
658
|
-
end
|
|
659
|
-
|
|
660
|
-
case rep
|
|
661
|
-
when "n"
|
|
662
|
-
log(:INFO, "Skip this commit")
|
|
663
|
-
break
|
|
664
|
-
when "b"
|
|
665
|
-
log(:INFO, "Blacklisting this commit for the current branch")
|
|
666
|
-
add_blacklist(commit)
|
|
667
|
-
break
|
|
668
|
-
when "y"
|
|
669
|
-
rep="y"
|
|
670
|
-
do_cp=true
|
|
671
|
-
break
|
|
672
|
-
when "?"
|
|
673
|
-
puts @repo.runGit("show #{commit}")
|
|
674
|
-
else
|
|
675
|
-
log(:ERROR, "Invalid answer $rep")
|
|
676
|
-
puts @repo.runGit("show --format=oneline --no-patch --no-decorate #{commit}")
|
|
677
|
-
end
|
|
678
|
-
end
|
|
679
|
-
return do_cp
|
|
680
|
-
end
|
|
681
|
-
|
|
682
|
-
def steal_one(opts, commit, mainline=false)
|
|
683
|
-
msg=''
|
|
684
|
-
orig_cmt=commit
|
|
685
|
-
|
|
686
|
-
if mainline == false then
|
|
687
|
-
subj=@repo.getCommitSubj(commit)
|
|
688
|
-
subj.gsub!(/"/, '\"')
|
|
689
|
-
# Let's grab the mainline commit id, this is useful if the version tag
|
|
690
|
-
# doesn't exist in the commit we're looking at but exists upstream.
|
|
691
|
-
orig_cmt=@repo.runGit("log --no-merges --format=\"%H\" -F --grep \"#{subj}\" " +
|
|
692
|
-
"#{@stable_base}..origin/master | tail -n1")
|
|
693
|
-
|
|
694
|
-
if orig_cmt == "" then
|
|
695
|
-
log(:WARNING, "Could not find commit #{commit} in mainline")
|
|
696
|
-
end
|
|
697
|
-
end
|
|
698
|
-
# If the commit doesn't apply for us, skip it
|
|
699
|
-
if is_relevant?(orig_cmt) != true
|
|
700
|
-
return true
|
|
701
|
-
end
|
|
702
|
-
|
|
703
|
-
log(:VERBOSE, "Found relevant commit #{@repo.getCommitHeadline(commit)}")
|
|
704
|
-
if is_in_tree?(orig_cmt) == true
|
|
705
|
-
# Commit is already in the stable branch, skip
|
|
706
|
-
log(:VERBOSE, "Commit is already in tree")
|
|
707
|
-
return true
|
|
708
|
-
end
|
|
709
|
-
|
|
710
|
-
# Check if it's not blacklisted by a git-notes
|
|
711
|
-
if is_blacklisted?(orig_cmt) == true then
|
|
712
|
-
# Commit is blacklisted
|
|
713
|
-
log(:INFO, "Skipping 'blacklisted' commit " +
|
|
714
|
-
@repo.getCommitHeadline(orig_cmt))
|
|
715
|
-
return true
|
|
716
|
-
end
|
|
717
|
-
|
|
718
|
-
do_cp = confirm_one(opts, orig_cmt)
|
|
719
|
-
return false if do_cp != true
|
|
720
|
-
|
|
721
|
-
prev_head=@repo.runGit("rev-parse HEAD")
|
|
722
|
-
|
|
723
|
-
begin
|
|
724
|
-
pick_one(commit)
|
|
725
|
-
rescue CherryPickErrorException => e
|
|
726
|
-
log(:WARNING, "Cherry pick failed. Fix, commit (or reset) and exit.")
|
|
727
|
-
@repo.runBash("PS1_WARNING='CP FIX'")
|
|
728
|
-
end
|
|
729
|
-
new_head=@repo.runGit("rev-parse HEAD")
|
|
730
|
-
|
|
731
|
-
# If we didn't find the commit upstream then this must be a custom commit
|
|
732
|
-
# in the given tree - make sure the user checks this commit.
|
|
733
|
-
if orig_cmt == "" then
|
|
734
|
-
msg="Custom"
|
|
735
|
-
orig_cmt=@repo.runGit("rev-parse HEAD")
|
|
736
|
-
log(:WARNING, "Custom commit, please double-check!")
|
|
737
|
-
@repo.runBash("PS1_WARNING='CHECK'")
|
|
738
|
-
end
|
|
739
|
-
if new_head != prev_head
|
|
740
|
-
make_pretty(orig_cmt, msg)
|
|
741
|
-
end
|
|
742
|
-
end
|
|
743
|
-
|
|
744
|
-
def steal_all(opts, range, mainline = false)
|
|
745
|
-
res = true
|
|
746
|
-
@repo.runGit("log --no-merges --format=\"%H\" #{range} | tac").split("\n").each(){|commit|
|
|
747
|
-
res &= steal_one(opts, commit, mainline)
|
|
748
|
-
}
|
|
749
|
-
return res
|
|
750
|
-
end
|
|
751
|
-
|
|
752
|
-
def same_sha?(ref1, ref2)
|
|
753
|
-
c1=@repo.runGit("rev-parse --verify --quiet #{ref1}")
|
|
754
|
-
c2=@repo.runGit("rev-parse --verify --quiet #{ref2}")
|
|
755
|
-
return c1 == c2
|
|
756
|
-
|
|
757
|
-
end
|
|
758
|
-
end
|
|
759
|
-
end
|