git-maintain 0.5.0 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 356c77a387c232c07fd214617a107f32853d3425e1468f25c4f8594d8e5dedc8
4
- data.tar.gz: a791a193557d15988872bef9ef3937591477491cfeb663b2fe4580c8cae8204e
3
+ metadata.gz: 4ccbf8e5307eaae5365b48a6ce99f026f020fa18acd0f34542cf8f4d8ecfbb51
4
+ data.tar.gz: de7a34ab07001688472118280ed5db9bed9016148a50f5009af2e9da5f16722e
5
5
  SHA512:
6
- metadata.gz: '0194ca3ad74a99f24e1910df00dc320d1a277ba9a9bfab48142f176d4a23428fb275e37a2ac932d456ce26690ededa924b5bc15459b5bd348840d4ea7c13174d'
7
- data.tar.gz: d604791372bfa3988518cb5b4216815236885e37e8c7716bc1cc9672306582b4d3a43b7c3c880bc88809e91d1d0441bf3d8312ddc59692daf2ae731f15be737e
6
+ metadata.gz: 8ed529842a7c85ca7e13e969fe561edc671ddc5ef20a559484523a7b0643e01236205b7b9681eff7d123024771ea6037ceaa79cba388032dc2971dbf2938aa58
7
+ data.tar.gz: 2bb5406a3a5d028a7dbde47a2ccdf6ae4b69e4b1ab379e428069af14088dce6a5289e0fe74eccf32e45f9927a94bde7143f86efb172d05c464092852f7edec12
data/CHANGELOG CHANGED
@@ -1,7 +1,23 @@
1
1
  ------------------
2
- 0.5.0 (2019-02-05)
2
+ 0.6.0 (2019-03-21)
3
3
  ------------------
4
4
 
5
+ * Speed up push_stable
6
+ * Add --check option for push_stable
7
+ * Add --no-fetch option
8
+ * Cleanup release email
9
+ * Add build timestamp for running build in monitor
10
+ * Add --watch for monitor and monitor_stable
11
+ * Fix bad output ot git maintain steal
12
+ * Rewrite log system and handle colors
13
+ * Add list_stable command
14
+ * Rework branching constraints to support more flexible names
15
+ * Add create command to create missing local branches
16
+ * Add delete command to remove temporary working branches
17
+
18
+ ------------------
19
+ 0.5.0 (2019-02-05)
20
+ ------------------
5
21
 
6
22
  * Handle bad Fixes tag
7
23
  * Speed up steal feature by remembering last point checked
data/README.md CHANGED
@@ -9,8 +9,11 @@ The idea is to script most of the maintenance tasks so the maintainer can focus
9
9
  # Command summary
10
10
 
11
11
  - **cp**: Backport commits and eventually push them to github
12
+ - **create**: Create missing local branches from all the stable branches
13
+ - **delete**: Delete all local branches using the suffix
12
14
  - **steal**: Steal commit from upstream that fixes commit in the branch or were tagged as stable
13
15
  - **list**: List commit present in the branch but not in the stable branch
16
+ - **list_stable**: List commit present in the stable branch but not in the latest associated relase
14
17
  - **merge**: Merge branch with suffix specified in -m <suff> into the main branch
15
18
  - **push**: Push branches to github for validation
16
19
  - **monitor**: Check the travis state of all branches
@@ -90,6 +93,36 @@ stable-base.dev---stable-v15 v15
90
93
  ```
91
94
  This will allow 'git maintain steal' to figure out what should be backported in the stable branches.
92
95
 
96
+
97
+ You know can use a regexp for dealing with branch names that extends with version
98
+ without any changes to your .gitconfig
99
+
100
+ Configure your local branch name space that also extracts the version:
101
+ ```
102
+ $ git config --get maintain.branch-format
103
+ dev\/stable-v([0-9]*)
104
+ ```
105
+
106
+ Configure the upstream stable branches. Use ruby regexp value extraction (\\1) so git-maintain
107
+ cal automatically insert the right version number
108
+ ```
109
+ $ git config --get maintain.stable-branch-format
110
+ stable-v\1
111
+
112
+ ```
113
+ Finally configure the upstream fork points for stable branches so the `steal` commands know where to start from
114
+ ```
115
+ $ git config --get maintain.stable-base-format
116
+ v\1
117
+
118
+ ```
119
+
120
+ Note that this value can be overriden by the stable-base.XXX value if needed
121
+
122
+ Also, once you set these parameters, you can have git-maintain create all branches for you by running
123
+ ``
124
+ $ git maintain create
125
+ ```
93
126
  ## Day-to-day workflow
94
127
 
95
128
  Watch the mailing-lists (and/or github and/or the upstream branches) for patches that are tagged for maintainance.
data/bin/git-maintain CHANGED
@@ -45,6 +45,8 @@ optsParser.on("-b", "--branch-suffix [SUFFIX]", "Branch suffix. Default is 'mast
45
45
  |val| opts[:br_suff] = val}
46
46
  optsParser.on("-n", "--no", "Assume no to all questions.") {
47
47
  |val| opts[:no] = true}
48
+ optsParser.on("--verbose", "Displays more informations.") {
49
+ |val| GitMaintain::setVerbose(true)}
48
50
  GitMaintain::setOpts(action, optsParser, opts)
49
51
 
50
52
  rest = optsParser.order!(ARGV);
data/lib/branch.rb CHANGED
@@ -10,21 +10,27 @@ module GitMaintain
10
10
 
11
11
  class Branch
12
12
  ACTION_LIST = [
13
- :cp, :steal, :list, :merge,
14
- :push, :monitor,
13
+ :cp, :steal, :list, :list_stable,
14
+ :merge, :push, :monitor,
15
15
  :push_stable, :monitor_stable,
16
- :release, :reset
16
+ :release, :reset, :create, :delete
17
17
  ]
18
18
  NO_FETCH_ACTIONS = [
19
- :cp, :merge, :monitor, :release
19
+ :cp, :merge, :monitor, :release, :delete
20
20
  ]
21
21
  NO_CHECKOUT_ACTIONS = [
22
- :list, :push, :monitor, :monitor_stable
22
+ :create, :delete, :list, :list_stable, :push, :monitor, :monitor_stable
23
+ ]
24
+ ALL_BRANCHES_ACTIONS = [
25
+ :create
23
26
  ]
24
27
  ACTION_HELP = [
25
28
  "* cp: Backport commits and eventually push them to github",
29
+ "* create: Create missing local branches from all the stable branches",
30
+ "* delete: Delete all local branches using the suffix",
26
31
  "* steal: Steal commit from upstream that fixes commit in the branch or were tagged as stable",
27
32
  "* list: List commit present in the branch but not in the stable branch",
33
+ "* list_stable: List commit present in the stable branch but not in the latest associated relase",
28
34
  "* merge: Merge branch with suffix specified in -m <suff> into the main branch",
29
35
  "* push: Push branches to github for validation",
30
36
  "* monitor: Check the travis state of all branches",
@@ -46,16 +52,28 @@ module GitMaintain
46
52
  opts[:do_merge] = false
47
53
  opts[:push_force] = false
48
54
  opts[:no_travis] = false
55
+ opts[:all] = false
56
+ opts[:check_only] = false
57
+ opts[:no_fetch] = false
58
+ opts[:watch] = false
49
59
 
50
60
  optsParser.on("-v", "--base-version [MIN_VER]", Integer, "Older release to consider.") {
51
61
  |val| opts[:base_ver] = val}
52
62
  optsParser.on("-V", "--version [regexp]", Regexp, "Regexp to filter versions.") {
53
63
  |val| opts[:version] = val}
54
64
 
55
- if action != :merge
65
+ if ALL_BRANCHES_ACTIONS.index(action) == nil &&
66
+ action != :merge &&
67
+ action != :delete then
56
68
  optsParser.on("-B", "--manual-branch <branch name>", "Work on a specific (non-stable) branch.") {
57
69
  |val| opts[:manual_branch] = val}
58
70
  end
71
+
72
+ if NO_FETCH_ACTIONS.index(action) == nil
73
+ optsParser.on("--no-fetch", "Skip fetch of stable repo.") {
74
+ |val| opts[:no_fetch] = true}
75
+ end
76
+
59
77
  case action
60
78
  when :cp
61
79
  optsParser.banner += "-c <sha1> [-c <sha1> ...]"
@@ -65,6 +83,10 @@ module GitMaintain
65
83
  optsParser.banner += "-m <suffix>"
66
84
  optsParser.on("-m", "--merge [SUFFIX]", "Merge branch with suffix.") {
67
85
  |val| opts[:do_merge] = val}
86
+ when :monitor, :monitor_stable
87
+ optsParser.on("-w", "--watch <PERIOD>", Integer,
88
+ "Watch and refresh travis status every <PERIOD>.") {
89
+ |val| opts[:watch] = val}
68
90
  when :push
69
91
  optsParser.banner += "[-f]"
70
92
  optsParser.on("-f", "--force", "Add --force to git push (for 'push' action).") {
@@ -73,6 +95,8 @@ module GitMaintain
73
95
  optsParser.banner += "[-T]"
74
96
  optsParser.on("-T", "--no-travis", "Ignore Travis build status and push anyway.") {
75
97
  |val| opts[:no_travis] = true}
98
+ optsParser.on("-c", "--check", "Check if there is something to be pushed.") {
99
+ |val| opts[:check_only] = true}
76
100
  when :steal
77
101
  optsParser.banner += "[-a]"
78
102
  optsParser.on("-a", "--all", "Check all commits from master. "+
@@ -88,26 +112,38 @@ module GitMaintain
88
112
  raise "Action #{opts[:action]} can only be done on 'master' suffixed branches"
89
113
  end
90
114
  end
115
+ if opts[:action] == :delete then
116
+ if opts[:br_suff] == "master" then
117
+ raise "Action #{opts[:action]} can NOT be done on 'master' suffixed branches"
118
+ end
119
+ end
91
120
  end
92
121
 
93
122
  def self.execAction(opts, action)
94
123
  repo = Repo::load()
95
124
  travis = TravisChecker::load(repo)
96
125
 
97
- if NO_FETCH_ACTIONS.index(action) == nil then
126
+ if NO_FETCH_ACTIONS.index(action) == nil && opts[:no_fetch] == false then
98
127
  repo.stableUpdate()
99
128
  end
100
129
 
101
130
  branchList=[]
102
131
  if opts[:manual_branch] == nil then
103
- branchList = repo.getStableList(opts[:br_suff]).map(){|br|
132
+ unfilteredList = nil
133
+ if ALL_BRANCHES_ACTIONS.index(action) != nil then
134
+ unfilteredList = repo.getStableBranchList()
135
+ else
136
+ unfilteredList = repo.getBranchList(opts[:br_suff])
137
+ end
138
+ branchList = unfilteredList.map(){|br|
104
139
  branch = Branch::load(repo, br, travis, opts[:br_suff])
105
140
  case branch.is_targetted?(opts)
106
141
  when :too_old
107
- puts "# Skipping older v#{branch.version}"
142
+ GitMaintain::log(:VERBOSE, "Skipping older v#{branch.version}")
108
143
  next
109
144
  when :no_match
110
- puts "# Skipping v#{branch.version} not matching #{opts[:version].to_s()}"
145
+ GitMaintain::log(:VERBOSE, "Skipping v#{branch.version} not matching" +
146
+ opts[:version].to_s())
111
147
  next
112
148
  end
113
149
  branch
@@ -115,16 +151,20 @@ module GitMaintain
115
151
  else
116
152
  branchList = [ Branch::load(repo, opts[:manual_branch], travis, opts[:br_suff]) ]
117
153
  end
118
- branchList.each(){|branch|
119
- puts "###############################"
120
- puts "# Working on #{branch.verbose_name}"
121
- puts "###############################"
122
154
 
123
- if NO_CHECKOUT_ACTIONS.index(action) == nil then
124
- branch.checkout()
125
- end
126
- branch.send(action, opts)
127
- }
155
+ loop do
156
+ system("clear; date") if opts[:watch] != false
157
+ branchList.each(){|branch|
158
+ if NO_CHECKOUT_ACTIONS.index(action) == nil then
159
+ GitMaintain::log(:INFO, "Working on #{branch.verbose_name}")
160
+ branch.checkout()
161
+ end
162
+ branch.send(action, opts)
163
+ }
164
+ break if opts[:watch] == false
165
+ sleep(opts[:watch])
166
+ travis.emptyCache()
167
+ end
128
168
  end
129
169
 
130
170
  def initialize(repo, version, travis, branch_suff)
@@ -136,8 +176,8 @@ module GitMaintain
136
176
  @branch_suff = branch_suff
137
177
 
138
178
  if version =~ /^[0-9]+$/
139
- @local_branch = "dev/stable-v#{@version}/#{@branch_suff}"
140
- @remote_branch ="stable-v#{@version}"
179
+ @local_branch = @repo.versionToLocalBranch(@version, @branch_suff)
180
+ @remote_branch = @repo.versionToStableBranch(@version)
141
181
  @branch_type = :std
142
182
  @verbose_name = "v"+version
143
183
  else
@@ -146,13 +186,17 @@ module GitMaintain
146
186
  @verbose_name = version
147
187
  end
148
188
 
149
- @head = @repo.runGit("rev-parse #{@local_branch}")
189
+ @head = @repo.runGit("rev-parse --verify --quiet #{@local_branch}")
150
190
  @remote_ref = "#{@repo.stable_repo}/#{@remote_branch}"
151
- @stable_head = @repo.runGit("rev-parse #{@remote_ref}")
191
+ @stable_head = @repo.runGit("rev-parse --verify --quiet #{@remote_ref}")
152
192
  @stable_base = @repo.findStableBase(@local_branch)
193
+ end
194
+ attr_reader :version, :local_branch, :head, :remote_branch, :remote_ref, :stable_head,
195
+ :verbose_name, :exists
153
196
 
197
+ def log(lvl, str)
198
+ GitMaintain::log(lvl, str)
154
199
  end
155
- attr_reader :version, :local_branch, :head, :remote_branch, :remote_ref, :stable_head, :verbose_name
156
200
 
157
201
  def is_targetted?(opts)
158
202
  return true if @branch_type == :user_specified
@@ -176,13 +220,18 @@ module GitMaintain
176
220
  # Cherry pick an array of commits
177
221
  def cp(opts)
178
222
  opts[:commits].each(){|commit|
223
+ prev_head=@repo.runGit("rev-parse HEAD")
224
+ log(:INFO, "Applying #{@repo.getCommitHeadline(commit)}")
179
225
  @repo.runGit("cherry-pick #{commit}")
180
226
  if $? != 0 then
181
- puts "Cherry pick failure. Starting bash for manual fixes. Exit shell to continue"
182
- @repo.runSystem("bash")
183
- puts "Continuing..."
227
+ log(:WARNING, "Cherry pick failure. Starting bash for manual fixes. Exit shell to continue")
228
+ @repo.runBash()
184
229
  end
185
- make_pretty(commit)
230
+ new_head=@repo.runGit("rev-parse HEAD")
231
+ # Do not make commit pretty if it was not applied
232
+ if new_head != prev_head
233
+ make_pretty(commit)
234
+ end
186
235
  }
187
236
  end
188
237
 
@@ -196,8 +245,8 @@ module GitMaintain
196
245
  sha = @repo.runGit("rev-parse 'git-maintain/steal/last/#{@stable_base}' 2>&1")
197
246
  if $? == 0 then
198
247
  base_ref=sha
199
- puts "# INFO: Starting from last successfull run:"
200
- puts "# INFO: " + @repo.runGit("show --format=oneline #{base_ref}")
248
+ log(:VERBOSE, "Starting from last successfull run:")
249
+ log(:VERBOSE, @repo.getCommitHeadline(base_ref))
201
250
  end
202
251
  end
203
252
 
@@ -209,43 +258,74 @@ module GitMaintain
209
258
  # can just steal from this point on the next run
210
259
  if res == true then
211
260
  @repo.runGit("tag -f 'git-maintain/steal/last/#{@stable_base}' origin/master")
212
- puts "# INFO: Marking new last successfull run at:"
213
- puts "# INFO: " + @repo.runGit("show --format=oneline #{master_sha}")
214
- end
261
+ log(:VERBOSE, "Marking new last successfull run at:")
262
+ log(:VERBOSE, @repo.getCommitHeadline(master_sha))
263
+ end
215
264
  end
216
265
 
217
266
  # List commits in the branch that are no in the stable branch
218
267
  def list(opts)
219
- GitMaintain::checkLog(opts, @local_branch, @remote_ref, nil)
268
+ GitMaintain::log(:INFO, "Working on #{@verbose_name}")
269
+ GitMaintain::showLog(opts, @local_branch, @remote_ref)
270
+ end
271
+
272
+ # List commits in the stable_branch that are no in the latest release
273
+ def list_stable(opts)
274
+ GitMaintain::log(:INFO, "Working on #{@verbose_name}")
275
+ GitMaintain::showLog(opts, @remote_ref, @repo.runGit("describe --abbrev=0 #{@local_branch}"))
220
276
  end
221
277
 
222
278
  # Merge merge_branch into this one
223
279
  def merge(opts)
224
- merge_branch = "dev/stable-v#{@version}/#{opts[:do_merge]}"
280
+ merge_branch = @repo.versionToLocalBranch(@version, opts[:do_merge])
281
+
282
+ # Make sure branch exists
283
+ hash_to_merge = @repo.runGit("rev-parse --verify --quiet #{merge_branch}")
284
+ if $? != 0 then
285
+ log(:INFO, "Branch #{merge_branch} does not exists. Skipping...")
286
+ return
287
+ end
288
+
289
+ # See if there is anything worth merging
290
+ merge_base_hash = @repo.runGit("merge-base #{merge_branch} #{@local_branch}")
291
+ if merge_base_hash == hash_to_merge then
292
+ log(:INFO, "Branch #{merge_branch} has no commit that needs to be merged")
293
+ return
294
+ end
295
+
225
296
  rep = GitMaintain::checkLog(opts, merge_branch, @local_branch, "merge")
226
297
  if rep == "y" then
227
298
  @repo.runGit("merge #{merge_branch}")
228
299
  if $? != 0 then
229
- puts "Merge failure. Starting bash for manual fixes. Exit shell to continue"
230
- @repo.runSystem("bash")
231
- puts "Continuing..."
300
+ log(:WARNING, "Merge failure. Starting bash for manual fixes. Exit shell to continue")
301
+ @repo.runBash()
232
302
  end
233
303
  else
234
- puts "Skipping merge"
304
+ log(:INFO, "Skipping merge")
235
305
  return
236
306
  end
237
307
  end
238
308
 
239
309
  # Push the branch to the validation repo
240
310
  def push(opts)
311
+ if same_sha?(@local_branch, @repo.valid_repo + "/" + @local_branch) then
312
+ log(:INFO, "Nothing to push")
313
+ return
314
+ end
315
+
241
316
  @repo.runGit("push #{opts[:push_force] == true ? "-f" : ""} #{@repo.valid_repo} #{@local_branch}")
242
317
  end
243
318
 
244
319
  # Monitor the build status on Travis
245
320
  def monitor(opts)
246
321
  st = @travis.getValidState(head)
247
- puts "Status for v#{@version}: " + st
248
- if st == "failed"
322
+ suff=""
323
+ case st
324
+ when "started"
325
+ suff= " started at #{@travis.getValidTS(head)}"
326
+ end
327
+ log(:INFO, "Status for v#{@version}: " + st + suff)
328
+ if st == "failed" && opts[:watch] == false
249
329
  rep = "y"
250
330
  suff=""
251
331
  while rep == "y"
@@ -268,36 +348,74 @@ module GitMaintain
268
348
  def push_stable(opts)
269
349
  if (opts[:no_travis] != true && @NO_TRAVIS != true) &&
270
350
  @travis.checkValidState(@head) != true then
271
- puts "Build is not passed on travis. Skipping push to stable"
351
+ log(:WARNING, "Build is not passed on travis. Skipping push to stable")
352
+ return
353
+ end
354
+
355
+ if same_sha?(@local_branch, @remote_ref) then
356
+ log(:INFO, "Stable is already up-to-date")
357
+ return
358
+ end
359
+
360
+ if opts[:check_only] == true then
361
+ GitMaintain::checkLog(opts, @local_branch, @remote_ref, "")
272
362
  return
273
363
  end
364
+
274
365
  rep = GitMaintain::checkLog(opts, @local_branch, @remote_ref, "submit")
275
366
  if rep == "y" then
276
367
  @repo.runGit("push #{@repo.stable_repo} #{@local_branch}:#{@remote_branch}")
277
368
  else
278
- puts "Skipping push to stable"
369
+ log(:INFO, "Skipping push to stable")
279
370
  return
280
371
  end
281
372
  end
282
373
 
283
374
  # Monitor the build status of the stable branch on Travis
284
375
  def monitor_stable(opts)
285
- puts "Status for v#{@version}: " + @travis.getStableState(@stable_head)
376
+ st = @travis.getStableState(@stable_head)
377
+ suff=""
378
+ case st
379
+ when "started"
380
+ suff= " started at #{@travis.getStableTS(@stable_head)}"
381
+ end
382
+ log(:INFO, "Status for v#{@version}: " + st + suff)
286
383
  end
287
384
 
288
385
  # Reset the branch to the upstream stable one
289
386
  def reset(opts)
387
+ if same_sha?(@local_branch, @remote_ref) then
388
+ log(:INFO, "Nothing to reset")
389
+ return
390
+ end
391
+
290
392
  rep = GitMaintain::checkLog(opts, @local_branch, @remote_ref, "reset")
291
393
  if rep == "y" then
292
394
  @repo.runGit("reset --hard #{@remote_ref}")
293
395
  else
294
- puts "Skipping reset"
396
+ log(:INFO, "Skipping reset")
295
397
  return
296
398
  end
297
399
  end
298
400
 
299
401
  def release(opts)
300
- puts "#No release command available for this repo"
402
+ log(:ERROR,"#No release command available for this repo")
403
+ end
404
+
405
+ def create(opts)
406
+ return if @head != ""
407
+ log(:INFO, "Creating missing #{@local_branch} from #{@remote_ref}")
408
+ @repo.runGit("branch #{@local_branch} #{@remote_ref}")
409
+ end
410
+
411
+ def delete(opts)
412
+ rep = GitMaintain::confirm(opts, "delete branch #{@local_branch}")
413
+ if rep == "y" then
414
+ @repo.runGit("branch -D #{@local_branch}")
415
+ else
416
+ log(:INFO, "Skipping deletion")
417
+ return
418
+ end
301
419
  end
302
420
 
303
421
  private
@@ -329,7 +447,7 @@ module GitMaintain
329
447
  # This might happen if someone pointed to a commit that doesn't exist in our
330
448
  # tree.
331
449
  if $? != 0 then
332
- puts "# WARNING: Commit #{src_commit} points to a SHA #{commit} not in tree"
450
+ log(:WARNING, "Commit #{src_commit} points to a SHA #{commit} not in tree")
333
451
  return false
334
452
  end
335
453
 
@@ -396,24 +514,23 @@ module GitMaintain
396
514
  def pick_one(commit)
397
515
  @repo.runGit("cherry-pick --strategy=recursive -Xpatience -x #{commit} &> /dev/null")
398
516
  return if $? == 0
399
-
400
- if [ @repo.runGit("status -uno --porcelain | wc -l") != 0 ]; then
517
+ if @repo.runGit("status -uno --porcelain | wc -l") == "0" then
401
518
  @repo.runGit("reset --hard")
402
- return
519
+ raise CherryPickErrorException.new("Failed to cherry pick commit #{commit}", commit)
403
520
  end
404
521
  @repo.runGit("reset --hard")
405
522
  # That didn't work? Let's try that with every variation of the commit
406
523
  # in other stable trees.
407
- find_alts(commit).each(){|alt_commit|
408
- @repo.runCmd("cherry-pick --strategy=recursive -Xpatience -x #{alt_commit} &> /dev/null")
524
+ @repo.find_alts(commit).each(){|alt_commit|
525
+ @repo.runGit("cherry-pick --strategy=recursive -Xpatience -x #{alt_commit} &> /dev/null")
409
526
  if $? == 0 then
410
527
  return
411
528
  end
412
- @repo.runCmd("reset --hard")
529
+ @repo.runGit("reset --hard")
413
530
  }
414
531
  # Still no? Let's go back to the original commit and hand it off to
415
532
  # the user.
416
- @repo.runCmd("cherry-pick --strategy=recursive -Xpatience -x #{commit} &> /dev/null")
533
+ @repo.runGit("cherry-pick --strategy=recursive -Xpatience -x #{commit} &> /dev/null")
417
534
  raise CherryPickErrorException.new("Failed to cherry pick commit #{commit}", commit)
418
535
  return false
419
536
  end
@@ -421,11 +538,11 @@ module GitMaintain
421
538
  def confirm_one(opts, commit)
422
539
  rep=""
423
540
  do_cp=false
424
- puts @repo.runGit("show --format=oneline --no-patch --no-decorate #{commit}")
541
+ puts @repo.getCommitHeadline(commit)
425
542
  while rep != "y" do
426
543
  puts "Do you want to steal this commit ? (y/n/b/?)"
427
544
  if opts[:no] == true then
428
- puts "Auto-replying no due to --no option"
545
+ log(:INFO, "Auto-replying no due to --no option")
429
546
  rep = 'n'
430
547
  break
431
548
  else
@@ -433,10 +550,10 @@ module GitMaintain
433
550
  end
434
551
  case rep
435
552
  when "n"
436
- puts "Skip this commit"
553
+ log(:INFO, "Skip this commit")
437
554
  break
438
555
  when "b"
439
- puts "Blacklisting this commit for the current branch"
556
+ log(:INFO, "Blacklisting this commit for the current branch")
440
557
  add_blacklist(commit)
441
558
  break
442
559
  when "y"
@@ -446,7 +563,7 @@ module GitMaintain
446
563
  when "?"
447
564
  puts @repo.runGit("show #{commit}")
448
565
  else
449
- STDERR.puts "Invalid answer $rep"
566
+ log(:ERROR, "Invalid answer $rep")
450
567
  puts @repo.runGit("show --format=oneline --no-patch --no-decorate #{commit}")
451
568
  end
452
569
  end
@@ -476,31 +593,35 @@ module GitMaintain
476
593
  # Check if it's not blacklisted by a git-notes
477
594
  if is_blacklisted?(orig_cmt) == true then
478
595
  # Commit is blacklisted
479
- puts "Skipping 'blacklisted' commit " +
480
- @repo.runGit("show --format=oneline --no-patch --no-decorate #{orig_cmt}")
596
+ log(:INFO, "Skipping 'blacklisted' commit " +
597
+ @repo.getCommitHeadline(orig_cmt))
481
598
  return true
482
599
  end
483
600
 
484
601
  do_cp = confirm_one(opts, orig_cmt)
485
602
  return false if do_cp != true
486
603
 
604
+ prev_head=@repo.runGit("rev-parse HEAD")
605
+
487
606
  begin
488
607
  pick_one(commit)
489
608
  rescue CherryPickErrorException => e
490
- puts "Cherry pick failed. Fix, commit (or reset) and exit."
609
+ log(:WARNING, "Cherry pick failed. Fix, commit (or reset) and exit.")
491
610
  @repo.runSystem("/bin/bash")
492
- return false
493
611
  end
612
+ new_head=@repo.runGit("rev-parse HEAD")
494
613
 
495
614
  # If we didn't find the commit upstream then this must be a custom commit
496
615
  # in the given tree - make sure the user checks this commit.
497
616
  if orig_cmt == "" then
498
617
  msg="Custom"
499
618
  orig_cmt=@repo.runGit("rev-parse HEAD")
500
- puts "Custom commit, please double-check!"
619
+ log(:WARNING, "Custom commit, please double-check!")
501
620
  @repo.runSystem("/bin/bash")
502
621
  end
503
- make_pretty(orig_cmt, msg)
622
+ if new_head != prev_head
623
+ make_pretty(orig_cmt, msg)
624
+ end
504
625
  end
505
626
 
506
627
  def steal_all(opts, range)
@@ -510,5 +631,12 @@ module GitMaintain
510
631
  }
511
632
  return res
512
633
  end
634
+
635
+ def same_sha?(ref1, ref2)
636
+ c1=@repo.runGit("rev-parse --verify --quiet #{ref1}")
637
+ c2=@repo.runGit("rev-parse --verify --quiet #{ref2}")
638
+ return c1 == c2
639
+
640
+ end
513
641
  end
514
642
  end
data/lib/common.rb CHANGED
@@ -6,6 +6,39 @@ require 'branch'
6
6
 
7
7
  $LOAD_PATH.pop()
8
8
 
9
+ class String
10
+ # colorization
11
+ @@is_a_tty = nil
12
+ def colorize(color_code)
13
+ @@is_a_tty = STDOUT.isatty() if @@is_a_tty == nil
14
+ if @@is_a_tty then
15
+ return "\e[#{color_code}m#{self}\e[0m"
16
+ else
17
+ return self
18
+ end
19
+ end
20
+
21
+ def red
22
+ colorize(31)
23
+ end
24
+
25
+ def green
26
+ colorize(32)
27
+ end
28
+
29
+ def brown
30
+ colorize(33)
31
+ end
32
+
33
+ def blue
34
+ colorize(34)
35
+ end
36
+
37
+ def magenta
38
+ colorize(35)
39
+ end
40
+ end
41
+
9
42
  module GitMaintain
10
43
  class Common
11
44
  ACTION_LIST = [ :list_actions ]
@@ -18,6 +51,8 @@ module GitMaintain
18
51
  ACTION_CLASS = [ Common, Branch, Repo ]
19
52
  @@custom_classes = {}
20
53
 
54
+ @@verbose_log = false
55
+
21
56
  def registerCustom(repo_name, classes)
22
57
  raise("Multiple class for repo #{repo_name}") if @@custom_classes[repo_name] != nil
23
58
  @@custom_classes[repo_name] = classes
@@ -32,10 +67,10 @@ module GitMaintain
32
67
  def loadClass(default_class, repo_name, *more)
33
68
  custom = @@custom_classes[repo_name]
34
69
  if custom != nil && custom[default_class] != nil then
35
- puts "# Detected custom #{default_class} class for repo '#{repo_name}'" if ENV['DEBUG'] == "1"
70
+ log(:DEBUG,"Detected custom #{default_class} class for repo '#{repo_name}'")
36
71
  return custom[default_class].new(*more)
37
72
  else
38
- puts "# Detected NO custom #{default_class} classes for repo '#{repo_name}'" if ENV['DEBUG'] == "1"
73
+ log(:DEBUG,"Detected NO custom #{default_class} classes for repo '#{repo_name}'")
39
74
  return default_class.new(*more)
40
75
  end
41
76
  end
@@ -110,6 +145,42 @@ module GitMaintain
110
145
  end
111
146
  module_function :checkLog
112
147
 
148
+ def showLog(opts, br1, br2)
149
+ log(:INFO, "Diff between #{br1} and #{br2}")
150
+ puts `git log --format=oneline #{br1} ^#{br2}`
151
+ return "n"
152
+ end
153
+ module_function :showLog
154
+
155
+ def _log(lvl, str, out=STDOUT)
156
+ puts("# " + lvl.to_s() + ": " + str)
157
+ end
158
+ module_function :_log
159
+
160
+ def log(lvl, str)
161
+ case lvl
162
+ when :DEBUG
163
+ _log("DEBUG".magenta(), str) if ENV["DEBUG"].to_s() != ""
164
+ when :DEBUG_TRAVIS
165
+ _log("DEBUG_TRAVIS".magenta(), str) if ENV["DEBUG_TRAVIS"].to_s() != ""
166
+ when :VERBOSE
167
+ _log("INFO".blue(), str) if @@verbose_log == true
168
+ when :INFO
169
+ _log("INFO".green(), str)
170
+ when :WARNING
171
+ _log("WARNING".brown(), str)
172
+ when :ERROR
173
+ _log("ERROR".red(), str, STDERR)
174
+ else
175
+ _log(lvl, str)
176
+ end
177
+ end
178
+ module_function :log
179
+
180
+ def setVerbose(val)
181
+ @@verbose_log = val
182
+ end
183
+ module_function :setVerbose
113
184
  end
114
185
  $LOAD_PATH.pop()
115
186
 
data/lib/repo.rb CHANGED
@@ -40,7 +40,7 @@ module GitMaintain
40
40
  GitMaintain::checkDirectConstructor(self.class)
41
41
 
42
42
  @path = path
43
- @stable_list=nil
43
+ @branch_list=nil
44
44
  @stable_branches=nil
45
45
  @suffix_list=nil
46
46
 
@@ -57,16 +57,26 @@ module GitMaintain
57
57
  awk '{ print $2}' | sed -e 's/.*://' -e 's/\\.git//'")
58
58
  @remote_stable=runGit("remote -v | egrep '^#{@stable_repo}' | grep fetch |
59
59
  awk '{ print $2}' | sed -e 's/.*://' -e 's/\\.git//'")
60
+
61
+ @branch_format_raw = runGit("config maintain.branch-format 2> /dev/null").chomp()
62
+ @branch_format = Regexp.new(/#{@branch_format_raw}/)
63
+ @stable_branch_format = runGit("config maintain.stable-branch-format 2> /dev/null").chomp()
64
+ @stable_base_format = runGit("config maintain.stable-base-format 2> /dev/null").chomp()
65
+
60
66
  @stable_base_patterns=
61
67
  runGit("config --get-regexp stable-base | egrep '^stable-base\.' | "+
62
68
  "sed -e 's/stable-base\.//' -e 's/---/\\//g'").split("\n").inject({}){ |m, x|
63
69
  y=x.split(" ");
64
70
  m[y[0]] = y[1]
65
71
  m
66
- }
72
+ }
67
73
  end
68
74
  attr_reader :path, :remote_valid, :remote_stable, :valid_repo, :stable_repo
69
75
 
76
+ def log(lvl, str)
77
+ GitMaintain::log(lvl, str)
78
+ end
79
+
70
80
  def run(cmd)
71
81
  return `cd #{@path} && #{cmd}`
72
82
  end
@@ -74,10 +84,8 @@ module GitMaintain
74
84
  return system("cd #{@path} && #{cmd}")
75
85
  end
76
86
  def runGit(cmd)
77
- if ENV["DEBUG"].to_s() != "" then
78
- puts "Called from #{caller[1]}"
79
- puts "Running git command '#{cmd}'"
80
- end
87
+ log(:DEBUG, "Called from #{caller[1]}")
88
+ log(:DEBUG, "Running git command '#{cmd}'")
81
89
  return `git --work-tree=#{@path} #{cmd}`.chomp()
82
90
  end
83
91
  def runGitImap(cmd)
@@ -90,28 +98,54 @@ module GitMaintain
90
98
  fi; git --work-tree=#{@path} imap-send #{cmd}`
91
99
  end
92
100
 
101
+ def runBash()
102
+ runSystem("bash")
103
+ if $? == 0 then
104
+ log(:INFO, "Continuing...")
105
+ else
106
+ log(:ERROR, "Shell exited with code #{$?}. Exiting")
107
+ raise("Cancelled by user")
108
+ end
109
+ end
110
+
111
+ def getCommitHeadline(sha)
112
+ return runGit("show --format=oneline --no-patch --no-decorate #{sha}")
113
+ end
114
+
93
115
  def stableUpdate()
94
- puts "# Fetching stable updates..."
116
+ log(:VERBOSE, "Fetching stable updates...")
95
117
  runGit("fetch #{@stable_repo}")
96
118
  end
97
- def getStableList(br_suff)
98
- return @stable_list if @stable_list != nil
119
+ def getBranchList(br_suff)
120
+ return @branch_list if @branch_list != nil
121
+
122
+ @branch_list=runGit("branch").split("\n").map(){|x|
123
+ x=~ /#{@branch_format_raw}\/#{br_suff}$/ ?
124
+ $1 : nil
125
+ }.compact().uniq()
126
+
127
+ return @branch_list
128
+ end
129
+
130
+ def getStableBranchList()
131
+ return @stable_branches if @stable_branches != nil
99
132
 
100
- @stable_list=runGit("branch").split("\n").map(){|x|
101
- x=~ /dev\/stable-v[0-9]+\/#{br_suff}/ ?
102
- x.gsub(/\*?\s*dev\/stable-v([0-9]+)\/#{br_suff}\s*$/, '\1') :
103
- nil}.compact().uniq()
133
+ @stable_branches=runGit("branch -a").split("\n").map(){|x|
134
+ x=~ /remotes\/#{@@STABLE_REPO}\/#{@stable_branch_format.gsub(/\\1/, '([0-9]+)')}$/ ?
135
+ $1 : nil
136
+ }.compact().uniq()
104
137
 
105
- return @stable_list
138
+ return @stable_branches
106
139
  end
107
140
 
108
141
  def getSuffixList()
109
142
  return @suffix_list if @suffix_list != nil
110
143
 
111
144
  @suffix_list = runGit("branch").split("\n").map(){|x|
112
- x=~ /dev\/stable-v[0-9]+\/[a-zA-Z0-9_-]+/ ?
113
- x.gsub(/\*?\s*dev\/stable-v[0-9]+\/([a-zA-Z0-9_-]+)\s*$/, '\1') :
114
- nil}.compact().uniq()
145
+ x=~ @branch_format ?
146
+ /^\*?\s*#{@branch_format_raw}\/([a-zA-Z0-9_-]+)\s*$/.match(x)[-1] :
147
+ nil
148
+ }.compact().uniq()
115
149
 
116
150
  return @suffix_list
117
151
  end
@@ -125,11 +159,11 @@ module GitMaintain
125
159
 
126
160
  new_tags = local_tags - remote_tags
127
161
  if new_tags.empty? then
128
- puts "All tags are already submitted."
162
+ log(:INFO, "All tags are already submitted.")
129
163
  return
130
164
  end
131
165
 
132
- puts "This will officially release these tags: #{new_tags.join(", ")}"
166
+ log(:WARNING, "This will officially release these tags: #{new_tags.join(", ")}")
133
167
  rep = GitMaintain::confirm(opts, "release them")
134
168
  if rep != 'y' then
135
169
  raise "Aborting.."
@@ -143,11 +177,27 @@ module GitMaintain
143
177
  " <" + runGit("config user.email") +">"
144
178
  mail.puts "To: " + runGit("config patch.target")
145
179
  mail.puts "Date: " + `date -R`.chomp()
146
- mail.puts "Subject: [ANNOUNCE] " + File.basename(@path) + " " +
147
- (new_tags.length > 1 ?
148
- (new_tags[0 .. -2].join(", ") + " and " + new_tags[-1]) :
149
- new_tags.join(" ")) +
150
- " has been tagged/released"
180
+
181
+ if new_tags.length > 4 then
182
+ mail.puts "Subject: [ANNOUNCE] " + File.basename(@path) + ": new stable releases"
183
+ mail.puts ""
184
+ mail.puts "These version were tagged/released:\n * " +
185
+ new_tags.join("\n * ")
186
+ mail.puts ""
187
+ else
188
+ mail.puts "Subject: [ANNOUNCE] " + File.basename(@path) + " " +
189
+ (new_tags.length > 1 ?
190
+ (new_tags[0 .. -2].join(", ") + " and " + new_tags[-1] + " have ") :
191
+ (new_tags.join(" ") + " has ")) +
192
+ " been tagged/released"
193
+ mail.puts ""
194
+ end
195
+ mail.puts "It's available at the normal places:"
196
+ mail.puts ""
197
+ mail.puts "git://github.com/#{@remote_stable}"
198
+ mail.puts "https://github.com/#{@remote_stable}/releases"
199
+ mail.puts ""
200
+ mail.puts "---"
151
201
  mail.puts ""
152
202
  mail.puts "Here's the information from the tags:"
153
203
  new_tags.sort().each(){|tag|
@@ -163,22 +213,41 @@ module GitMaintain
163
213
  puts runGitImap("< #{mail_path}; rm -f #{mail_path}")
164
214
  end
165
215
 
166
- puts "Last chance to cancel before submitting"
216
+ log(:WARNING, "Last chance to cancel before submitting")
167
217
  rep= GitMaintain::confirm(opts, "submit these releases")
168
218
  if rep != 'y' then
169
219
  raise "Aborting.."
170
220
  end
171
221
  puts `#{@@SUBMIT_BINARY}`
172
222
  end
223
+
224
+ def versionToLocalBranch(version, suff)
225
+ return @branch_format_raw.gsub(/\\\//, '/').
226
+ gsub(/\(.*\)/, version) + "/#{suff}"
227
+ end
228
+
229
+ def versionToStableBranch(version)
230
+ return version.gsub(/^(.*)$/, @stable_branch_format)
231
+ end
232
+
173
233
  def findStableBase(branch)
174
- @stable_base_patterns.each(){|pattern, base|
175
- return base if branch =~ /#{pattern}\// || branch =~ /#{pattern}$/
234
+ base=nil
235
+ if branch =~ @branch_format then
236
+ base = branch.gsub(/^\*?\s*#{@branch_format_raw}\/.*$/, @stable_base_format)
237
+ end
238
+
239
+ @stable_base_patterns.each(){|pattern, b|
240
+ if branch =~ /#{pattern}\// || branch =~ /#{pattern}$/
241
+ base = b
242
+ break
243
+ end
176
244
  }
177
- raise("Could not a find a stable base for branch #{branch}")
245
+ raise("Could not a find a stable base for branch #{branch}") if base == nil
246
+ return base
178
247
  end
179
248
 
180
249
  def list_branches(opts)
181
- puts getStableList(opts[:br_suff])
250
+ puts getBranchList(opts[:br_suff])
182
251
  end
183
252
  def list_suffixes(opts)
184
253
  puts getSuffixList()
@@ -186,5 +255,23 @@ module GitMaintain
186
255
  def submit_release(opts)
187
256
  submitReleases(opts)
188
257
  end
258
+
259
+ def find_alts(commit)
260
+ alts=[]
261
+
262
+ subj=runGit("log -1 --pretty='%s' #{commit}")
263
+ return alts if $? != 0
264
+
265
+ branches = getStableBranchList().map(){|v| @@STABLE_REPO + "/" + versionToStableBranch(v)}
266
+
267
+ runGit("log -F --grep \"$#{subj}\" --format=\"%H\" #{branches.join(" ")}").
268
+ split("\n").each(){|c|
269
+ next if c == commit
270
+ cursubj=runGit("log -1 --pretty='%s' #{c}")
271
+ alts << c if subj == cursubj
272
+ }
273
+
274
+ return alts
275
+ end
189
276
  end
190
277
  end
data/lib/travis.rb CHANGED
@@ -15,6 +15,10 @@ module GitMaintain
15
15
  end
16
16
 
17
17
  private
18
+ def log(lvl, str)
19
+ GitMaintain::log(lvl, str)
20
+ end
21
+
18
22
  def fetch(uri_str, limit = 10)
19
23
  # You should choose a better exception.
20
24
  raise ArgumentError, 'too many HTTP redirects' if limit == 0
@@ -35,8 +39,8 @@ module GitMaintain
35
39
  return @cachedJson[query_label] if @cachedJson[query_label] != nil
36
40
  url = TRAVIS_URL + query
37
41
  uri = URI(url)
38
- puts "# Querying travis..."
39
- puts "# #{url}" if ENV["DEBUG_TRAVIS"].to_s() != ""
42
+ log(:INFO, "Querying travis...")
43
+ log(:DEBUG_TRAVIS, url)
40
44
  response = fetch(uri)
41
45
  raise("Travis request failed '#{url}'") if response.code.to_s() != '200'
42
46
 
@@ -59,6 +63,11 @@ module GitMaintain
59
63
  job_id = br["job_ids"].last().to_s()
60
64
  return getJson("log_" + job_id, 'jobs/' + job_id + '/log', false)
61
65
  end
66
+ def getTS(sha1, resp)
67
+ br = findBranch(sha1, resp)
68
+ raise("Travis build not found") if br == nil
69
+ return br["started_at"]
70
+ end
62
71
  def checkState(sha1, resp)
63
72
  return getState(sha1, resp) == "passed"
64
73
  end
@@ -70,11 +79,11 @@ module GitMaintain
70
79
  return getJson(:br_stable, 'repos/' + @repo.remote_stable + '/branches')
71
80
  end
72
81
  def findBranch(sha1, resp)
73
- puts "# Looking for build for #{sha1}" if ENV["DEBUG_TRAVIS"].to_s() != ""
82
+ log(:DEBUG_TRAVIS, "Looking for build for #{sha1}")
74
83
  resp["branches"].each(){|br|
75
84
  commit=resp["commits"].select(){|e| e["id"] == br["commit_id"]}.first()
76
85
  raise("Incomplete JSON received from Travis") if commit == nil
77
- puts "# Found entry for sha #{commit["sha"]}" if ENV["DEBUG_TRAVIS"].to_s() != ""
86
+ log(:DEBUG_TRAVIS, "Found entry for sha #{commit["sha"]}")
78
87
  next if commit["sha"] != sha1
79
88
  return br
80
89
  }
@@ -91,6 +100,9 @@ module GitMaintain
91
100
  def getValidLog(sha1)
92
101
  return getLog(sha1, getBrValidJson())
93
102
  end
103
+ def getValidTS(sha1)
104
+ return getTS(sha1, getBrValidJson())
105
+ end
94
106
 
95
107
  def getStableState(sha1)
96
108
  return getState(sha1, getBrStableJson())
@@ -101,5 +113,11 @@ module GitMaintain
101
113
  def getStableLog(sha1)
102
114
  return getLog(sha1, getBrStableJson())
103
115
  end
116
+ def getStableTS(sha1)
117
+ return getTS(sha1, getBrStableJson())
118
+ end
119
+ def emptyCache()
120
+ @cachedJson={}
121
+ end
104
122
  end
105
123
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git-maintain
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nicolas Morey-Chaisemartin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-02-05 00:00:00.000000000 Z
11
+ date: 2019-03-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: github-release
@@ -62,7 +62,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
62
62
  - !ruby/object:Gem::Version
63
63
  version: '0'
64
64
  requirements: []
65
- rubygems_version: 3.0.2
65
+ rubygems_version: 3.0.3
66
66
  signing_key:
67
67
  specification_version: 4
68
68
  summary: Your ultimate script for maintaining stable branches.