git-maintain 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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.