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.
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