git-maintain 0.12.0 → 0.14.2
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 +23 -0
- data/README.md +14 -14
- data/bin/git-maintain +7 -55
- data/git-maintain-completion.sh +1 -1
- data/lib/{addons → git-maintain/addons}/RDMACore.rb +79 -20
- data/lib/{addons → git-maintain/addons}/git-maintain.rb +61 -6
- 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/{branch.rb → git-maintain/branch.rb} +356 -237
- 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/{repo.rb → git-maintain/repo.rb} +199 -104
- 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/ci.rb +0 -88
- data/lib/common.rb +0 -259
- data/lib/travis.rb +0 -80
|
@@ -1,28 +1,27 @@
|
|
|
1
|
+
# Main module for git-maintain repository maintenance tool.
|
|
1
2
|
module GitMaintain
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
super(str)
|
|
7
|
-
end
|
|
8
|
-
attr_reader :commit
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
class Branch
|
|
4
|
+
# Class representing a single stable branch and actions that can be performed on it.
|
|
5
|
+
class Branch < Common
|
|
6
|
+
# List of all available maintenance actions.
|
|
12
7
|
ACTION_LIST = [
|
|
13
8
|
:cp, :steal, :list,
|
|
14
9
|
:merge, :pull, :push, :monitor,
|
|
15
10
|
:release, :reset, :create, :delete
|
|
16
11
|
]
|
|
12
|
+
# Actions that do not require updating the remote repository first.
|
|
17
13
|
NO_FETCH_ACTIONS = [
|
|
18
14
|
:cp, :merge, :monitor, :release, :delete
|
|
19
15
|
]
|
|
16
|
+
# Actions that do not require checking out the local branch before running.
|
|
20
17
|
NO_CHECKOUT_ACTIONS = [
|
|
21
18
|
:create, :delete, :list, :push, :monitor
|
|
22
19
|
]
|
|
20
|
+
# Actions that run on all branches, regardless of target versions.
|
|
23
21
|
ALL_BRANCHES_ACTIONS = [
|
|
24
22
|
:create
|
|
25
23
|
]
|
|
24
|
+
# Description map of actions for CLI help output.
|
|
26
25
|
ACTION_HELP = {
|
|
27
26
|
:cp => "Backport commits and eventually push them to github",
|
|
28
27
|
:create => "Create missing local branches from all the stable branches",
|
|
@@ -36,16 +35,16 @@ module GitMaintain
|
|
|
36
35
|
:release => "Create new release on all concerned branches",
|
|
37
36
|
:reset => "Reset branch against upstream",
|
|
38
37
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
# Configure action-specific command line options.
|
|
39
|
+
#
|
|
40
|
+
# @param action [Symbol] Selected action name
|
|
41
|
+
# @param optsParser [OptionParser] The OptionParser instance to configure
|
|
42
|
+
# @param opts [Hash] The options hash to populate
|
|
45
43
|
def self.set_opts(action, optsParser, opts)
|
|
46
44
|
opts[:base_ver] = 0
|
|
47
45
|
opts[:version] = []
|
|
48
46
|
opts[:commits] = []
|
|
47
|
+
opts[:breaker] = nil
|
|
49
48
|
opts[:do_merge] = false
|
|
50
49
|
opts[:push_force] = false
|
|
51
50
|
opts[:no_ci] = false
|
|
@@ -79,6 +78,8 @@ module GitMaintain
|
|
|
79
78
|
optsParser.banner += "-c <sha1> [-c <sha1> ...]"
|
|
80
79
|
optsParser.on("-c", "--sha1 [SHA1]", String, "Commit to cherry-pick. Can be used multiple time.") {
|
|
81
80
|
|val| opts[:commits] << val}
|
|
81
|
+
optsParser.on("--breaker SHA", String, "Only apply patch if the breaker (or a cherry-pick of it) is contained within the branch.") {
|
|
82
|
+
|val| opts[:breaker] = val}
|
|
82
83
|
when :delete
|
|
83
84
|
optsParser.on("--remote", "Delete the remote staging branch instead of the local ones.") {
|
|
84
85
|
|val| opts[:delete_remote] = true}
|
|
@@ -123,93 +124,82 @@ module GitMaintain
|
|
|
123
124
|
end
|
|
124
125
|
end
|
|
125
126
|
|
|
127
|
+
# Sanity check and normalize the parsed options for the given action.
|
|
128
|
+
#
|
|
129
|
+
# @param opts [Hash] Options hash to validate and configure
|
|
130
|
+
# @raise [InvalidArgumentError] If options are invalid or conflicting
|
|
126
131
|
def self.check_opts(opts)
|
|
127
132
|
if opts[:action] == :release then
|
|
128
133
|
if opts[:br_suff] != "master" then
|
|
129
|
-
raise "Action #{opts[:action]} can only be done on 'master' suffixed branches"
|
|
134
|
+
raise InvalidArgumentError.new("Action #{opts[:action]} can only be done on 'master' suffixed branches")
|
|
130
135
|
end
|
|
131
136
|
end
|
|
132
137
|
if opts[:action] == :delete && opts[:delete_remote] != true then
|
|
133
138
|
if opts[:br_suff] == "master" then
|
|
134
|
-
raise "Action #{opts[:action]} can NOT be done on 'master' suffixed branches"
|
|
139
|
+
raise InvalidArgumentError.new("Action #{opts[:action]} can NOT be done on 'master' suffixed branches")
|
|
135
140
|
end
|
|
136
141
|
end
|
|
137
142
|
if opts[:action] == :push
|
|
138
143
|
if opts[:stable] == true && opts[:push_force] == true then
|
|
139
|
-
raise "Action push can NOT be use both --stable and --force"
|
|
144
|
+
raise InvalidArgumentError.new("Action push can NOT be use both --stable and --force")
|
|
140
145
|
end
|
|
141
146
|
end
|
|
142
147
|
opts[:version] = [ /.*/ ] if opts[:version].length == 0
|
|
143
148
|
end
|
|
144
149
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
GitMaintain::log(:INFO, "Done working on selected branches")
|
|
207
|
-
|
|
150
|
+
# Factory method to load an instance of the Branch class or its repository-specific subclass.
|
|
151
|
+
#
|
|
152
|
+
# @param repo [Repo] The Repo instance
|
|
153
|
+
# @param version [String] The branch version (e.g., '1.0')
|
|
154
|
+
# @param ci [CI] The CI instance
|
|
155
|
+
# @param branch_suff [String] Suffix of the branch (e.g., 'master')
|
|
156
|
+
# @return [Branch] The loaded Branch instance (or subclass)
|
|
157
|
+
# @raise [GitMaintainError] If class loading fails
|
|
158
|
+
def self.load(repo, version, ci, branch_suff)
|
|
159
|
+
repo_name = File.basename(repo.path)
|
|
160
|
+
return GitMaintain::loadClass(Branch, repo_name, repo, version, ci, branch_suff)
|
|
208
161
|
end
|
|
209
162
|
|
|
163
|
+
protected
|
|
164
|
+
# Print the diff between two branches and prompt for confirmation.
|
|
165
|
+
#
|
|
166
|
+
# @param opts [Hash] Options hash
|
|
167
|
+
# @param br1 [String] First branch/ref
|
|
168
|
+
# @param br2 [String] Second branch/ref
|
|
169
|
+
# @param action_msg [String] Action message to prompt (e.g. 'submit')
|
|
170
|
+
# @return [String] User prompt response ('y' or 'n')
|
|
171
|
+
# @raise [RunError] If running git log fails
|
|
172
|
+
def checkLog(opts, br1, br2, action_msg)
|
|
173
|
+
puts "Diff between #{br1} and #{br2}"
|
|
174
|
+
puts `git log --format=oneline #{br1} ^#{br2}`
|
|
175
|
+
return "n" if action_msg.to_s() == ""
|
|
176
|
+
rep = confirm(opts, "#{action_msg} this branch")
|
|
177
|
+
return rep
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Print the diff/log between two branches.
|
|
181
|
+
#
|
|
182
|
+
# @param opts [Hash] Options hash
|
|
183
|
+
# @param br1 [String] First branch/ref
|
|
184
|
+
# @param br2 [String] Second branch/ref
|
|
185
|
+
# @raise [RunError] If running git log fails
|
|
186
|
+
def showLog(opts, br1, br2)
|
|
187
|
+
log(:INFO, "Diff between #{br1} and #{br2}")
|
|
188
|
+
puts `git log --format=oneline #{br1} ^#{br2}`
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
public
|
|
192
|
+
# Initialize a new Branch instance, resolving local/remote branches, head references, and stable base.
|
|
193
|
+
#
|
|
194
|
+
# @param repo [Repo] The Repo instance
|
|
195
|
+
# @param version [String] Suffix version (e.g. '1.0') or branch name
|
|
196
|
+
# @param ci [CI] The CI instance
|
|
197
|
+
# @param branch_suff [String] Branch suffix (e.g. 'master')
|
|
198
|
+
# @raise [NoRefError] If resolving git references fails
|
|
210
199
|
def initialize(repo, version, ci, branch_suff)
|
|
211
200
|
GitMaintain::checkDirectConstructor(self.class)
|
|
212
201
|
|
|
202
|
+
@path = repo.path
|
|
213
203
|
@repo = repo
|
|
214
204
|
@ci = ci
|
|
215
205
|
@version = version
|
|
@@ -229,7 +219,7 @@ module GitMaintain
|
|
|
229
219
|
@head = @repo.ref_exist?(@local_branch)
|
|
230
220
|
@valid_ref = "#{@repo.valid_repo}/#{@local_branch}"
|
|
231
221
|
@remote_ref = "#{@repo.stable_repo}/#{@remote_branch}"
|
|
232
|
-
@stable_head =
|
|
222
|
+
@stable_head =
|
|
233
223
|
begin
|
|
234
224
|
@repo.ref_exist?(@remote_ref)
|
|
235
225
|
rescue
|
|
@@ -245,10 +235,11 @@ module GitMaintain
|
|
|
245
235
|
attr_reader :version, :local_branch, :head, :remote_branch, :valid_ref, :remote_ref, :stable_head,
|
|
246
236
|
:verbose_name, :exists, :stable_base
|
|
247
237
|
|
|
248
|
-
def log(lvl, str)
|
|
249
|
-
GitMaintain::log(lvl, str)
|
|
250
|
-
end
|
|
251
238
|
|
|
239
|
+
# Check if the branch matches specified target version options.
|
|
240
|
+
#
|
|
241
|
+
# @param opts [Hash] Options hash with version/base filters
|
|
242
|
+
# @return [Boolean, Symbol] `true` if targeted, `:too_old` or `:no_match` otherwise
|
|
252
243
|
def is_targetted?(opts)
|
|
253
244
|
return true if @branch_type == :user_specified
|
|
254
245
|
if @version.to_i < opts[:base_ver] then
|
|
@@ -260,27 +251,44 @@ module GitMaintain
|
|
|
260
251
|
return :no_match
|
|
261
252
|
end
|
|
262
253
|
|
|
263
|
-
# Checkout the
|
|
254
|
+
# Checkout the git repository to this branch's local branch.
|
|
255
|
+
#
|
|
256
|
+
# @raise [RunError] If git checkout execution fails
|
|
264
257
|
def checkout()
|
|
265
|
-
|
|
266
|
-
print @repo.runGit("checkout -q #{@local_branch}")
|
|
267
|
-
rescue RuntimeError => e
|
|
268
|
-
crit("Failed to checkout the branch #{@local_branch}")
|
|
269
|
-
end
|
|
258
|
+
runGitInteractive("checkout -q #{@local_branch}")
|
|
270
259
|
end
|
|
271
260
|
|
|
272
|
-
#
|
|
261
|
+
# Backport/cherry-pick the given array of commits into this branch.
|
|
262
|
+
#
|
|
263
|
+
# @param opts [Hash] Options hash containing `:commits` to pick
|
|
264
|
+
# @raise [CPAbort] If cherry-pick is aborted by the user
|
|
273
265
|
def cp(opts)
|
|
266
|
+
if opts[:breaker] != nil then
|
|
267
|
+
if !is_in_tree?(opts[:breaker]) then
|
|
268
|
+
log(:INFO, "Skipping #{verbose_name}: breaker #{opts[:breaker]} not in tree")
|
|
269
|
+
return
|
|
270
|
+
end
|
|
271
|
+
end
|
|
274
272
|
opts[:commits].each(){|commit|
|
|
275
|
-
|
|
273
|
+
if is_in_tree?(commit) then
|
|
274
|
+
log(:INFO, "Commit #{commit} is already in tree, skipping")
|
|
275
|
+
next
|
|
276
|
+
end
|
|
277
|
+
prev_head=runGit("rev-parse HEAD")
|
|
276
278
|
log(:INFO, "Applying #{@repo.getCommitHeadline(commit)}")
|
|
277
279
|
begin
|
|
278
|
-
|
|
279
|
-
rescue
|
|
280
|
-
|
|
281
|
-
|
|
280
|
+
runGitInteractive("cherry-pick #{commit}")
|
|
281
|
+
rescue RunError
|
|
282
|
+
begin
|
|
283
|
+
cp_fix(opts, commit)
|
|
284
|
+
rescue CPSkip => e
|
|
285
|
+
log(:INFO, e.message)
|
|
286
|
+
rescue CPAbort => e
|
|
287
|
+
log(:INFO, "Cherry-pick aborted by user.")
|
|
288
|
+
raise e
|
|
289
|
+
end
|
|
282
290
|
end
|
|
283
|
-
new_head
|
|
291
|
+
new_head=runGit("rev-parse HEAD")
|
|
284
292
|
# Do not make commit pretty if it was not applied
|
|
285
293
|
if new_head != prev_head
|
|
286
294
|
make_pretty(commit)
|
|
@@ -288,7 +296,11 @@ module GitMaintain
|
|
|
288
296
|
}
|
|
289
297
|
end
|
|
290
298
|
|
|
291
|
-
# Steal upstream commits that are not in
|
|
299
|
+
# Steal upstream commits that are not present in this branch.
|
|
300
|
+
# Uses `steal_all` and marks the last successful run with a git tag if successful.
|
|
301
|
+
#
|
|
302
|
+
# @param opts [Hash] Options hash
|
|
303
|
+
# @raise [RunError] If git tags/execution fails
|
|
292
304
|
def steal(opts)
|
|
293
305
|
base_ref=@stable_base
|
|
294
306
|
|
|
@@ -297,51 +309,61 @@ module GitMaintain
|
|
|
297
309
|
case opts[:steal_base]
|
|
298
310
|
when nil
|
|
299
311
|
begin
|
|
300
|
-
sha =
|
|
312
|
+
sha = runGit("rev-parse 'git-maintain/steal/last/#{@stable_base}' 2>&1")
|
|
301
313
|
base_ref=sha
|
|
302
314
|
log(:VERBOSE, "Starting from last successfull run:")
|
|
303
315
|
log(:VERBOSE, @repo.getCommitHeadline(base_ref))
|
|
304
|
-
rescue
|
|
316
|
+
rescue RunError
|
|
305
317
|
# No matching tag found. Not an issue
|
|
306
318
|
end
|
|
307
319
|
when :all
|
|
308
320
|
base_ref=@stable_base
|
|
309
321
|
else
|
|
310
322
|
begin
|
|
311
|
-
sha =
|
|
323
|
+
sha = runGit("rev-parse #{opts[:steal_base]} 2>&1")
|
|
312
324
|
base_ref=sha
|
|
313
325
|
log(:VERBOSE, "Starting from base:")
|
|
314
326
|
log(:VERBOSE, @repo.getCommitHeadline(base_ref))
|
|
315
|
-
rescue
|
|
327
|
+
rescue RunError
|
|
316
328
|
crit("Could not find specified base '#{opts[:steal_base]}'")
|
|
317
329
|
end
|
|
318
330
|
end
|
|
319
331
|
|
|
320
|
-
master_sha
|
|
321
|
-
res = steal_all(opts, "#{base_ref}..#{master_sha}", true)
|
|
332
|
+
master_sha=runGit("rev-parse origin/master")
|
|
322
333
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
334
|
+
begin
|
|
335
|
+
steal_all(opts, "#{base_ref}..#{master_sha}", true)
|
|
336
|
+
|
|
337
|
+
# We picked all the commits (or nothing happened)
|
|
338
|
+
# Mark the current master as the last checked point so we
|
|
339
|
+
# can just steal from this point on the next run
|
|
340
|
+
runGit("tag -f 'git-maintain/steal/last/#{@stable_base}' origin/master")
|
|
328
341
|
log(:VERBOSE, "Marking new last successfull run at:")
|
|
329
342
|
log(:VERBOSE, @repo.getCommitHeadline(master_sha))
|
|
343
|
+
rescue CPSkip
|
|
344
|
+
# Ignore the error
|
|
330
345
|
end
|
|
331
346
|
end
|
|
332
347
|
|
|
348
|
+
# List all unreleased stable commits or commits in this branch but not in stable.
|
|
349
|
+
#
|
|
350
|
+
# @param opts [Hash] Options hash
|
|
351
|
+
# @raise [RunError] If running git commands fails
|
|
333
352
|
def list(opts)
|
|
334
|
-
|
|
353
|
+
log(:INFO, "Working on #{@verbose_name}")
|
|
335
354
|
if opts[:stable] == true then
|
|
336
355
|
# List commits in the stable_branch that are no in the latest release
|
|
337
|
-
|
|
356
|
+
showLog(opts, @remote_ref, runGit("describe --abbrev=0 #{@local_branch}"))
|
|
338
357
|
else
|
|
339
358
|
# List commits in the branch that are no in the stable branch
|
|
340
|
-
|
|
359
|
+
showLog(opts, @local_branch, @remote_ref)
|
|
341
360
|
end
|
|
342
361
|
end
|
|
343
362
|
|
|
344
|
-
# Merge
|
|
363
|
+
# Merge the specified merge branch into this branch.
|
|
364
|
+
#
|
|
365
|
+
# @param opts [Hash] Options hash containing `:do_merge` branch name
|
|
366
|
+
# @raise [RunError] If running git merge or system commands fails
|
|
345
367
|
def merge(opts)
|
|
346
368
|
merge_branch = @repo.versionToLocalBranch(@version, opts[:do_merge])
|
|
347
369
|
|
|
@@ -354,26 +376,31 @@ module GitMaintain
|
|
|
354
376
|
end
|
|
355
377
|
|
|
356
378
|
# See if there is anything worth merging
|
|
357
|
-
merge_base_hash =
|
|
379
|
+
merge_base_hash = runGit("merge-base #{merge_branch} #{@local_branch}")
|
|
358
380
|
if merge_base_hash == hash_to_merge then
|
|
359
381
|
log(:INFO, "Branch #{merge_branch} has no commit that needs to be merged")
|
|
360
382
|
return
|
|
361
383
|
end
|
|
362
384
|
|
|
363
|
-
rep =
|
|
385
|
+
rep = checkLog(opts, merge_branch, @local_branch, "merge")
|
|
364
386
|
if rep == "y" then
|
|
365
387
|
begin
|
|
366
|
-
|
|
367
|
-
rescue
|
|
388
|
+
runGitInteractive("merge #{merge_branch}")
|
|
389
|
+
rescue RunError
|
|
368
390
|
log(:WARNING, "Merge failure. Starting bash for manual fixes. Exit shell to continue")
|
|
369
|
-
|
|
391
|
+
runBash("PS1_WARNING='MERGING'")
|
|
370
392
|
end
|
|
371
393
|
else
|
|
372
394
|
log(:INFO, "Skipping merge")
|
|
373
395
|
return
|
|
374
|
-
end
|
|
396
|
+
end
|
|
375
397
|
end
|
|
376
398
|
|
|
399
|
+
# Pull/rebase the current branch against the upstream stable or validation reference.
|
|
400
|
+
#
|
|
401
|
+
# @param opts [Hash] Options hash
|
|
402
|
+
# @raise [NoRefError] If checking remote ref existence fails with unexpected ref errors
|
|
403
|
+
# @raise [RunError] If rebasing fails
|
|
377
404
|
def pull(opts)
|
|
378
405
|
remoteRef = opts[:stable] == true ? @remote_ref : @valid_ref
|
|
379
406
|
|
|
@@ -384,10 +411,14 @@ module GitMaintain
|
|
|
384
411
|
log(:INFO, "Branch #{remoteRef} does not exists. Skipping...")
|
|
385
412
|
return
|
|
386
413
|
end
|
|
387
|
-
|
|
414
|
+
runGitInteractive("rebase #{remoteRef}")
|
|
388
415
|
|
|
389
416
|
end
|
|
390
|
-
# Push the branch to the validation
|
|
417
|
+
# Push the branch to the validation or stable repository.
|
|
418
|
+
# Saves the push specs to `opts[:push_branches]` accumulator array.
|
|
419
|
+
#
|
|
420
|
+
# @param opts [Hash] Options hash containing configuration and the accumulator array
|
|
421
|
+
# @raise [GitMaintainError] If CI checks fail or other git/CI errors occur
|
|
391
422
|
def push(opts)
|
|
392
423
|
remoteRef = opts[:stable] == true ? @remote_ref : @valid_ref
|
|
393
424
|
|
|
@@ -409,35 +440,47 @@ module GitMaintain
|
|
|
409
440
|
end
|
|
410
441
|
|
|
411
442
|
if opts[:check_only] == true then
|
|
412
|
-
|
|
443
|
+
checkLog(opts, @local_branch, @remote_ref, "")
|
|
413
444
|
return
|
|
414
445
|
end
|
|
415
446
|
|
|
416
447
|
# For validation/CI push, let's go and push already
|
|
417
|
-
|
|
448
|
+
if opts[:stable] != true
|
|
449
|
+
opts[:push_branches] ||= []
|
|
450
|
+
opts[:push_branches] << "#{@local_branch}:#{@local_branch}"
|
|
451
|
+
return
|
|
452
|
+
end
|
|
418
453
|
|
|
419
454
|
# For stable, we need to confirm with the user that he really wants to push
|
|
420
|
-
rep =
|
|
455
|
+
rep = checkLog(opts, @local_branch, @remote_ref, "submit")
|
|
421
456
|
if rep == "y" then
|
|
422
|
-
|
|
457
|
+
opts[:push_branches] ||= []
|
|
458
|
+
opts[:push_branches] << "#{@local_branch}:#{@remote_branch}"
|
|
423
459
|
else
|
|
424
460
|
log(:INFO, "Skipping push to stable")
|
|
425
461
|
return
|
|
426
462
|
end
|
|
427
463
|
end
|
|
428
464
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
465
|
+
# Run the epilogue for the push action, executing the accumulated pushes.
|
|
466
|
+
#
|
|
467
|
+
# @param opts [Hash] Options hash containing configuration and accumulated branch specs in `opts[:push_branches]`
|
|
468
|
+
# @param branches [Array] Ignored branch list from map send (we use accumulator in `opts[:push_branches]`)
|
|
469
|
+
# @raise [RunError] If git push execution fails
|
|
470
|
+
def push_epilogue(opts, branches)
|
|
471
|
+
push_list = opts[:push_branches] || []
|
|
472
|
+
return if push_list.length == 0
|
|
434
473
|
|
|
435
474
|
repo = (opts[:stable] == true) ? opts[:repo].stable_repo : opts[:repo].valid_repo
|
|
436
475
|
opts[:repo].runGit("push #{opts[:push_force] == true ? "-f" : ""} "+
|
|
437
|
-
"#{repo} #{
|
|
476
|
+
"#{repo} #{push_list.join(" ")}")
|
|
438
477
|
end
|
|
439
478
|
|
|
440
|
-
# Monitor the build status on CI
|
|
479
|
+
# Monitor the build status on CI for the branch.
|
|
480
|
+
# Displays the current status (e.g. success, started, errored) and handles prompt to show logs.
|
|
481
|
+
#
|
|
482
|
+
# @param opts [Hash] Options hash
|
|
483
|
+
# @raise [GitMaintainError] If querying CI state or fetching logs fails
|
|
441
484
|
def monitor(opts)
|
|
442
485
|
ts = st = head = nil
|
|
443
486
|
suff=""
|
|
@@ -458,7 +501,7 @@ module GitMaintain
|
|
|
458
501
|
rep = "y"
|
|
459
502
|
suff=""
|
|
460
503
|
while rep == "y"
|
|
461
|
-
rep =
|
|
504
|
+
rep = confirm(opts, "see the build log#{suff}")
|
|
462
505
|
if rep == "y" then
|
|
463
506
|
log = @ci.getValidLog(self, @head)
|
|
464
507
|
tmp = `mktemp`.chomp()
|
|
@@ -473,32 +516,47 @@ module GitMaintain
|
|
|
473
516
|
end
|
|
474
517
|
end
|
|
475
518
|
|
|
476
|
-
# Reset the branch to the upstream stable
|
|
519
|
+
# Reset the branch to the upstream stable reference (warning: hard reset).
|
|
520
|
+
#
|
|
521
|
+
# @param opts [Hash] Options hash
|
|
522
|
+
# @raise [RunError] If running git commands fails
|
|
477
523
|
def reset(opts)
|
|
478
524
|
if same_sha?(@local_branch, @remote_ref) then
|
|
479
525
|
log(:INFO, "Nothing to reset")
|
|
480
526
|
return
|
|
481
527
|
end
|
|
482
528
|
|
|
483
|
-
rep =
|
|
529
|
+
rep = checkLog(opts, @local_branch, @remote_ref, "reset")
|
|
484
530
|
if rep == "y" then
|
|
485
|
-
|
|
531
|
+
runGit("reset --hard #{@remote_ref}")
|
|
486
532
|
else
|
|
487
533
|
log(:INFO, "Skipping reset")
|
|
488
534
|
return
|
|
489
535
|
end
|
|
490
536
|
end
|
|
491
537
|
|
|
538
|
+
# Create a release on the current branch (dummy/unsupported method for the base Branch class).
|
|
539
|
+
#
|
|
540
|
+
# @param opts [Hash] Options hash
|
|
492
541
|
def release(opts)
|
|
493
542
|
log(:ERROR,"#No release command available for this repo")
|
|
494
543
|
end
|
|
495
544
|
|
|
545
|
+
# Create the missing local branch tracking the upstream remote stable reference.
|
|
546
|
+
#
|
|
547
|
+
# @param opts [Hash] Options hash
|
|
548
|
+
# @raise [RunError] If git branch creation fails
|
|
496
549
|
def create(opts)
|
|
497
550
|
return if @head != ""
|
|
498
551
|
log(:INFO, "Creating missing #{@local_branch} from #{@remote_ref}")
|
|
499
|
-
|
|
552
|
+
runGit("branch #{@local_branch} #{@remote_ref}")
|
|
500
553
|
end
|
|
501
554
|
|
|
555
|
+
# Delete the branch (locally or remotely).
|
|
556
|
+
# Saves the deletion specs to `opts[:delete_branches]` accumulator array.
|
|
557
|
+
#
|
|
558
|
+
# @param opts [Hash] Options hash containing configuration and the accumulator array
|
|
559
|
+
# @raise [NoRefError] If checking remote ref existence fails with unexpected ref errors
|
|
502
560
|
def delete(opts)
|
|
503
561
|
if opts[:delete_remote] == true then
|
|
504
562
|
begin
|
|
@@ -511,40 +569,49 @@ module GitMaintain
|
|
|
511
569
|
else
|
|
512
570
|
msg = "delete branch #{@local_branch}"
|
|
513
571
|
end
|
|
514
|
-
rep =
|
|
572
|
+
rep = confirm(opts, msg)
|
|
515
573
|
if rep == "y" then
|
|
516
|
-
|
|
574
|
+
opts[:delete_branches] ||= []
|
|
575
|
+
opts[:delete_branches] << @local_branch
|
|
517
576
|
else
|
|
518
577
|
log(:INFO, "Skipping deletion")
|
|
519
578
|
return
|
|
520
579
|
end
|
|
521
580
|
end
|
|
522
|
-
def self.delete_epilogue(opts, branches)
|
|
523
|
-
# Compact to remove empty entries
|
|
524
|
-
branches.compact!()
|
|
525
581
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
582
|
+
# Run the epilogue for the delete action, executing the accumulated deletions.
|
|
583
|
+
#
|
|
584
|
+
# @param opts [Hash] Options hash containing configuration and accumulated branch specs in `opts[:delete_branches]`
|
|
585
|
+
# @param branches [Array] Ignored branch list from map send (we use accumulator in `opts[:delete_branches]`)
|
|
586
|
+
# @raise [RunError] If git branch deletion fails
|
|
587
|
+
def delete_epilogue(opts, branches)
|
|
588
|
+
delete_list = opts[:delete_branches] || []
|
|
589
|
+
return if delete_list.length == 0
|
|
590
|
+
puts "Deleting #{opts[:delete_remote] == true ? "remote" : "local"} branches: #{delete_list.join(" ")}"
|
|
591
|
+
rep = confirm(opts, "continue", true)
|
|
529
592
|
if rep != "y" then
|
|
530
593
|
log(:INFO, "Cancelling")
|
|
531
594
|
return
|
|
532
595
|
end
|
|
533
596
|
if opts[:delete_remote] == true then
|
|
534
|
-
opts[:repo].runGit("push #{opts[:repo].valid_repo} #{
|
|
597
|
+
opts[:repo].runGit("push #{opts[:repo].valid_repo} #{delete_list.map(){|x| ":" + x}.join(" ")}")
|
|
535
598
|
else
|
|
536
|
-
opts[:repo].runGit("branch -D #{
|
|
599
|
+
opts[:repo].runGit("branch -D #{delete_list.join(" ")}")
|
|
537
600
|
end
|
|
538
601
|
end
|
|
539
602
|
|
|
540
603
|
private
|
|
604
|
+
# Add the given commit to the git notes blacklist for the current branch.
|
|
605
|
+
#
|
|
606
|
+
# @param commit [String] The commit SHA to blacklist
|
|
607
|
+
# @raise [RunError] If running git notes fails
|
|
541
608
|
def add_blacklist(commit)
|
|
542
|
-
|
|
609
|
+
runGit("notes append -m \"#{@local_branch}\" #{commit}")
|
|
543
610
|
end
|
|
544
611
|
|
|
545
612
|
def is_blacklisted?(commit)
|
|
546
613
|
begin
|
|
547
|
-
|
|
614
|
+
runGit("notes show #{commit} 2> /dev/null").split("\n").each(){|br|
|
|
548
615
|
return true if br == @local_branch
|
|
549
616
|
}
|
|
550
617
|
rescue
|
|
@@ -552,15 +619,21 @@ module GitMaintain
|
|
|
552
619
|
return false
|
|
553
620
|
end
|
|
554
621
|
|
|
622
|
+
# Rewrite the commit message of the HEAD commit to reference the upstream commit.
|
|
623
|
+
# Adds an "[ Upstream commit <SHA> ]" label.
|
|
624
|
+
#
|
|
625
|
+
# @param orig_commit [String] Original upstream commit SHA or reference
|
|
626
|
+
# @param commit [String] Override commit SHA to display in the label
|
|
627
|
+
# @raise [RunError] If running git commands fails
|
|
555
628
|
def make_pretty(orig_commit, commit="")
|
|
556
|
-
orig_sha
|
|
629
|
+
orig_sha=runGit("rev-parse #{orig_commit}")
|
|
557
630
|
msg_commit = (commit.to_s() == "") ? orig_sha : commit
|
|
558
631
|
|
|
559
632
|
msg_path=`mktemp`.chomp()
|
|
560
633
|
msg_file = File.open(msg_path, "w+")
|
|
561
|
-
msg_file.puts
|
|
634
|
+
msg_file.puts runGit("log -1 --format=\"%s%n%n[ Upstream commit #{msg_commit} ]%n%n%b\" #{orig_commit}")
|
|
562
635
|
msg_file.close()
|
|
563
|
-
|
|
636
|
+
runGit("commit -s --amend -F #{msg_path}")
|
|
564
637
|
`rm -f #{msg_path}`
|
|
565
638
|
end
|
|
566
639
|
|
|
@@ -576,7 +649,7 @@ module GitMaintain
|
|
|
576
649
|
end
|
|
577
650
|
|
|
578
651
|
# Hope for the best, same commit is/isn't in the current branch
|
|
579
|
-
if
|
|
652
|
+
if runGit("merge-base #{fullhash} HEAD") == fullhash then
|
|
580
653
|
return true
|
|
581
654
|
end
|
|
582
655
|
|
|
@@ -585,10 +658,10 @@ module GitMaintain
|
|
|
585
658
|
subj=@repo.getCommitSubj(commit)
|
|
586
659
|
|
|
587
660
|
# Try and find if there's a commit with given subject the hard way
|
|
588
|
-
|
|
661
|
+
runGit("log --pretty=\"%H\" -F --grep \"#{subj.gsub("\"", '\\"')}\" "+
|
|
589
662
|
"#{@stable_base}..HEAD").split("\n").each(){|cmt|
|
|
590
|
-
cursubj
|
|
591
|
-
if cursubj
|
|
663
|
+
cursubj=runGit("log -1 --format=\"%s\" #{cmt}")
|
|
664
|
+
if cursubj == subj then
|
|
592
665
|
return true
|
|
593
666
|
end
|
|
594
667
|
}
|
|
@@ -597,7 +670,7 @@ module GitMaintain
|
|
|
597
670
|
|
|
598
671
|
def is_relevant?(commit)
|
|
599
672
|
# Let's grab the commit that this commit fixes (if exists (based on the "Fixes:" tag)).
|
|
600
|
-
fixescmt
|
|
673
|
+
fixescmt=runGit("log -1 #{commit} | grep -i \"fixes:\" | head -n 1 | "+
|
|
601
674
|
"sed -e 's/^[ \\t]*//' | cut -f 2 -d ':' | "+
|
|
602
675
|
"sed -e 's/^[ \\t]*//' -e 's/\\([0-9a-f]\\+\\)(/\\1 (/' | cut -f 1 -d ' '")
|
|
603
676
|
|
|
@@ -611,22 +684,22 @@ module GitMaintain
|
|
|
611
684
|
end
|
|
612
685
|
end
|
|
613
686
|
|
|
614
|
-
if
|
|
687
|
+
if runGit("show #{commit} | grep -i 'stable@' | wc -l") == "0" then
|
|
615
688
|
return false
|
|
616
689
|
end
|
|
617
690
|
|
|
618
691
|
# Let's see if there's a version tag in this commit
|
|
619
|
-
full
|
|
692
|
+
full=runGit("show #{commit} | grep -i 'stable@'").gsub(/.* #?/, "")
|
|
620
693
|
|
|
621
694
|
# Sanity check our extraction
|
|
622
695
|
if full =~ /stable/ then
|
|
623
696
|
return false
|
|
624
697
|
end
|
|
625
698
|
|
|
626
|
-
full =
|
|
699
|
+
full = runGit("rev-parse #{full}^{commit}")
|
|
627
700
|
|
|
628
701
|
# Make sure our branch contains this version
|
|
629
|
-
if
|
|
702
|
+
if runGit("merge-base #{@head} #{full}") == full then
|
|
630
703
|
return true
|
|
631
704
|
end
|
|
632
705
|
|
|
@@ -634,73 +707,67 @@ module GitMaintain
|
|
|
634
707
|
return false
|
|
635
708
|
end
|
|
636
709
|
|
|
710
|
+
# Cherry-pick a single commit, trying other stable alternatives if it fails.
|
|
711
|
+
#
|
|
712
|
+
# @param commit [String] Commit SHA to cherry-pick
|
|
713
|
+
# @raise [CherryPickErrorException] If cherry-picking the commit (and its alternatives) fails
|
|
637
714
|
def pick_one(commit)
|
|
638
715
|
cpCmd="cherry-pick --strategy=recursive -Xpatience -x"
|
|
639
|
-
|
|
716
|
+
runGitInteractive("#{cpCmd} #{commit} &> /dev/null", {}, false)
|
|
640
717
|
return if $? == 0
|
|
641
718
|
|
|
642
|
-
if
|
|
643
|
-
|
|
719
|
+
if runGit("status -uno --porcelain | wc -l") == "0" then
|
|
720
|
+
runGit("reset --hard")
|
|
644
721
|
raise CherryPickErrorException.new("Failed to cherry pick commit #{commit}", commit)
|
|
645
722
|
end
|
|
646
|
-
|
|
723
|
+
runGit("reset --hard")
|
|
647
724
|
|
|
648
725
|
# That didn't work? Let's try that with every variation of the commit
|
|
649
726
|
# in other stable trees.
|
|
650
727
|
@repo.find_alts(commit).each(){|alt_commit|
|
|
651
|
-
|
|
728
|
+
runGitInteractive("#{cpCmd} #{alt_commit} &> /dev/null", {}, false)
|
|
652
729
|
if $? == 0 then
|
|
653
730
|
return
|
|
654
731
|
end
|
|
655
|
-
|
|
732
|
+
runGit("reset --hard")
|
|
656
733
|
}
|
|
657
734
|
|
|
658
735
|
# Still no? Let's go back to the original commit and hand it off to
|
|
659
736
|
# the user.
|
|
660
|
-
|
|
737
|
+
runGitInteractive("#{cpCmd} #{commit} &> /dev/null", {}, false)
|
|
661
738
|
raise CherryPickErrorException.new("Failed to cherry pick commit #{commit}", commit)
|
|
662
739
|
end
|
|
663
740
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
when "?"
|
|
695
|
-
puts @repo.runGit("show #{commit}")
|
|
696
|
-
else
|
|
697
|
-
log(:ERROR, "Invalid answer $rep")
|
|
698
|
-
puts @repo.runGit("show --format=oneline --no-patch --no-decorate #{commit}")
|
|
699
|
-
end
|
|
700
|
-
end
|
|
701
|
-
return do_cp
|
|
702
|
-
end
|
|
703
|
-
|
|
741
|
+
# Handle user interaction to fix cherry-pick conflicts via an interactive shell.
|
|
742
|
+
#
|
|
743
|
+
# @param opts [Hash] Options hash
|
|
744
|
+
# @param commit [String] Commit SHA with conflicts
|
|
745
|
+
# @raise [CPAbort] If cherry-pick is aborted by the user
|
|
746
|
+
# @raise [CPSkip] If the user skips this commit
|
|
747
|
+
def cp_fix(opts, commit)
|
|
748
|
+
runGitInteractive("diff")
|
|
749
|
+
log( :INFO, "Entering subshell to fix conflicts. Exit when done")
|
|
750
|
+
runSystem("PS1_WARNING='CP FIX' bash", false)
|
|
751
|
+
rep = confirm(opts, "continue with scp [y(es), n(o), s(kip)]?", true, ["y", "n", "s"])
|
|
752
|
+
case rep
|
|
753
|
+
when "n"
|
|
754
|
+
runGitInteractive("cherry-pick --abort")
|
|
755
|
+
raise(CPAbort)
|
|
756
|
+
when "s"
|
|
757
|
+
runGitInteractive("cherry-pick --abort")
|
|
758
|
+
e = CPSkip.new(commit.to_s())
|
|
759
|
+
log(:INFO, e.to_s())
|
|
760
|
+
raise(e)
|
|
761
|
+
end
|
|
762
|
+
end
|
|
763
|
+
|
|
764
|
+
# Steal/cherry-pick a single commit from upstream, asking for confirmation if needed.
|
|
765
|
+
#
|
|
766
|
+
# @param opts [Hash] Options hash
|
|
767
|
+
# @param commit [String] Commit SHA to cherry-pick
|
|
768
|
+
# @param mainline [Boolean] Whether to treat this as a mainline cherry-pick (skips mapping checks)
|
|
769
|
+
# @raise [CPSkip] If the user chooses to skip this commit
|
|
770
|
+
# @raise [CPAbort] If the user chooses to abort cherry-picking
|
|
704
771
|
def steal_one(opts, commit, mainline=false)
|
|
705
772
|
msg=''
|
|
706
773
|
orig_cmt=commit
|
|
@@ -710,7 +777,7 @@ module GitMaintain
|
|
|
710
777
|
subj.gsub!(/"/, '\"')
|
|
711
778
|
# Let's grab the mainline commit id, this is useful if the version tag
|
|
712
779
|
# doesn't exist in the commit we're looking at but exists upstream.
|
|
713
|
-
orig_cmt
|
|
780
|
+
orig_cmt=runGit("log --no-merges --format=\"%H\" -F --grep \"#{subj}\" " +
|
|
714
781
|
"#{@stable_base}..origin/master | tail -n1")
|
|
715
782
|
|
|
716
783
|
if orig_cmt == "" then
|
|
@@ -719,14 +786,14 @@ module GitMaintain
|
|
|
719
786
|
end
|
|
720
787
|
# If the commit doesn't apply for us, skip it
|
|
721
788
|
if is_relevant?(orig_cmt) != true
|
|
722
|
-
return
|
|
789
|
+
return
|
|
723
790
|
end
|
|
724
791
|
|
|
725
792
|
log(:VERBOSE, "Found relevant commit #{@repo.getCommitHeadline(commit)}")
|
|
726
793
|
if is_in_tree?(orig_cmt) == true
|
|
727
794
|
# Commit is already in the stable branch, skip
|
|
728
795
|
log(:VERBOSE, "Commit is already in tree")
|
|
729
|
-
return
|
|
796
|
+
return
|
|
730
797
|
end
|
|
731
798
|
|
|
732
799
|
# Check if it's not blacklisted by a git-notes
|
|
@@ -734,41 +801,75 @@ module GitMaintain
|
|
|
734
801
|
# Commit is blacklisted
|
|
735
802
|
log(:INFO, "Skipping 'blacklisted' commit " +
|
|
736
803
|
@repo.getCommitHeadline(orig_cmt))
|
|
737
|
-
return
|
|
804
|
+
return
|
|
738
805
|
end
|
|
739
806
|
|
|
740
|
-
|
|
741
|
-
|
|
807
|
+
commit_desc = @repo.getCommitHeadline(commit)
|
|
808
|
+
rep = "t"
|
|
809
|
+
while rep != "y"
|
|
810
|
+
rep = confirm(opts, "pick commit '#{commit_desc}' up ([y]es, [n]o, [b]lacklist)",
|
|
811
|
+
false, ["y", "n", "?", "b"])
|
|
742
812
|
|
|
743
|
-
|
|
813
|
+
case rep
|
|
814
|
+
when "y"
|
|
815
|
+
break
|
|
816
|
+
when "n"
|
|
817
|
+
raise CPSkip.new(commit)
|
|
818
|
+
when "b"
|
|
819
|
+
log(:INFO, "Blacklisting this commit for the current branch")
|
|
820
|
+
add_blacklist(commit)
|
|
821
|
+
raise CPSkip.new(commit)
|
|
822
|
+
when "?"
|
|
823
|
+
runGitInteractive("show #{commit}", {}, false)
|
|
824
|
+
end
|
|
825
|
+
end
|
|
744
826
|
|
|
827
|
+
prev_head=runGit("rev-parse HEAD")
|
|
745
828
|
begin
|
|
746
829
|
pick_one(commit)
|
|
747
|
-
rescue CherryPickErrorException
|
|
748
|
-
|
|
749
|
-
@repo.runBash("PS1_WARNING='CP FIX'")
|
|
830
|
+
rescue CherryPickErrorException
|
|
831
|
+
cp_fix(opts, commit)
|
|
750
832
|
end
|
|
751
|
-
new_head
|
|
833
|
+
new_head=runGit("rev-parse HEAD")
|
|
752
834
|
|
|
753
835
|
# If we didn't find the commit upstream then this must be a custom commit
|
|
754
836
|
# in the given tree - make sure the user checks this commit.
|
|
755
837
|
if orig_cmt == "" then
|
|
756
838
|
msg="Custom"
|
|
757
|
-
orig_cmt
|
|
839
|
+
orig_cmt=runGit("rev-parse HEAD")
|
|
758
840
|
log(:WARNING, "Custom commit, please double-check!")
|
|
759
|
-
|
|
841
|
+
runBash("PS1_WARNING='CHECK'")
|
|
760
842
|
end
|
|
761
843
|
if new_head != prev_head
|
|
762
844
|
make_pretty(orig_cmt, msg)
|
|
763
845
|
end
|
|
846
|
+
return
|
|
764
847
|
end
|
|
765
848
|
|
|
849
|
+
# Steal/cherry-pick all upstream commits in the given revision range.
|
|
850
|
+
#
|
|
851
|
+
# @param opts [Hash] Options hash
|
|
852
|
+
# @param range [String] Git revision range (e.g., 'base..HEAD')
|
|
853
|
+
# @param mainline [Boolean] Whether to treat commits as mainline cherry-picks
|
|
854
|
+
# @raise [CPSkip] If any cherry-pick of a patch is skipped by the user
|
|
855
|
+
# @raise [CPAbort] If cherry-pick is aborted by the user
|
|
766
856
|
def steal_all(opts, range, mainline = false)
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
857
|
+
skipped = []
|
|
858
|
+
runGit("log --no-merges --format=\"%H\" #{range} | tac").split("\n").each(){|commit|
|
|
859
|
+
begin
|
|
860
|
+
steal_one(opts, commit, mainline)
|
|
861
|
+
rescue CPSkip => e
|
|
862
|
+
log(:INFO, e.message)
|
|
863
|
+
skipped << commit
|
|
864
|
+
rescue CPAbort => e
|
|
865
|
+
log(:INFO, "Cherry-pick aborted by user.")
|
|
866
|
+
raise e
|
|
867
|
+
end
|
|
770
868
|
}
|
|
771
|
-
|
|
869
|
+
if skipped.length > 0
|
|
870
|
+
raise CPSkip.new(skipped.join(" "))
|
|
871
|
+
end
|
|
872
|
+
return
|
|
772
873
|
end
|
|
773
874
|
|
|
774
875
|
def same_sha?(ref1, ref2)
|
|
@@ -782,11 +883,19 @@ module GitMaintain
|
|
|
782
883
|
end
|
|
783
884
|
|
|
784
885
|
|
|
886
|
+
# Add files and commit them to the release branch.
|
|
887
|
+
#
|
|
888
|
+
# @param opts [Hash] Options hash
|
|
889
|
+
# @param filelist [Array<String>] List of file paths to add
|
|
890
|
+
# @param commit_path [String, nil] Path to a file containing the commit message
|
|
891
|
+
# @param commit_msg [String, nil] Direct commit message text
|
|
892
|
+
# @raise [MissingArgumentError] If both commit_msg and commit_path are nil
|
|
893
|
+
# @raise [GitMaintainError] If the git commit fails
|
|
785
894
|
def release_do_add_commit(opts, filelist, commit_path, commit_msg=nil)
|
|
786
895
|
edit_flag = ""
|
|
787
896
|
edit_flag = "--edit" if opts[:no_edit] == false
|
|
788
897
|
|
|
789
|
-
raise("
|
|
898
|
+
raise MissingArgumentError.new("commit message/path") if commit_path == nil && commit_msg == nil
|
|
790
899
|
commit_flag=""
|
|
791
900
|
if commit_msg != nil
|
|
792
901
|
commit_flag = "-m '#{commit_msg}'"
|
|
@@ -796,29 +905,39 @@ module GitMaintain
|
|
|
796
905
|
|
|
797
906
|
# Add and commit
|
|
798
907
|
begin
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
rescue
|
|
802
|
-
raise("Failed to commit on branch #{@local_branch}")
|
|
908
|
+
runGit("add " + filelist.join(" "))
|
|
909
|
+
runGitInteractive("commit #{commit_flag} --verbose #{edit_flag} --signoff")
|
|
910
|
+
rescue RunError
|
|
911
|
+
raise GitMaintainError.new("Failed to commit on branch #{@local_branch}")
|
|
803
912
|
end
|
|
804
|
-
return 0
|
|
805
913
|
end
|
|
806
914
|
|
|
915
|
+
# Create a signed annotated release tag.
|
|
916
|
+
#
|
|
917
|
+
# @param opts [Hash] Options hash
|
|
918
|
+
# @param version [String] The version string/tag name
|
|
919
|
+
# @param tag_path [String] Path to file containing the tag message
|
|
920
|
+
# @raise [GitMaintainError] If tagging fails
|
|
807
921
|
def release_do_tag(opts, version, tag_path)
|
|
808
922
|
edit_flag = ""
|
|
809
923
|
edit_flag = "--edit" if opts[:no_edit] == false
|
|
810
924
|
begin
|
|
811
|
-
|
|
812
|
-
rescue
|
|
813
|
-
raise("Failed to tag branch #{@local_branch}")
|
|
925
|
+
runGitInteractive("tag -a -s #{version} #{edit_flag} -F #{tag_path}")
|
|
926
|
+
rescue RunError
|
|
927
|
+
raise GitMaintainError.new("Failed to tag branch #{@local_branch}")
|
|
814
928
|
end
|
|
815
|
-
return 0
|
|
816
929
|
end
|
|
817
930
|
|
|
931
|
+
# Add, commit, and tag files for a release.
|
|
932
|
+
#
|
|
933
|
+
# @param opts [Hash] Options hash
|
|
934
|
+
# @param filelist [Array<String>] List of file paths to add and commit
|
|
935
|
+
# @param version [String] The version string/tag name
|
|
936
|
+
# @param message_path [String] Path to file containing the commit/tag message
|
|
937
|
+
# @raise [GitMaintainError] If committing or tagging fails
|
|
818
938
|
def release_do_add_commit_tag(opts, filelist, version, message_path)
|
|
819
939
|
release_do_add_commit(opts, filelist, message_path)
|
|
820
940
|
release_do_tag(opts, version, message_path)
|
|
821
|
-
return 0
|
|
822
941
|
end
|
|
823
942
|
|
|
824
943
|
end
|