squared 0.5.26 → 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.
@@ -41,13 +41,13 @@ module Squared
41
41
  end
42
42
  end
43
43
  data.each do |key, val|
44
- if val.is_a?(Hash)
45
- uri = val.fetch(:uri, '')
46
- opts = val.fetch(:options, {})
47
- else
48
- uri = val.is_a?(String) ? val : key.to_s
49
- opts = options
50
- end
44
+ uri = if val.is_a?(Hash)
45
+ opts = val.fetch(:options, {})
46
+ val.fetch(:uri, '')
47
+ else
48
+ opts = options
49
+ val.is_a?(String) ? val : key.to_s
50
+ end
51
51
  unless uri.match?(GIT_PROTO) || Pathname.new(uri).absolute?
52
52
  if uri.start_with?('.')
53
53
  uri = @root + uri
@@ -57,8 +57,7 @@ module Squared
57
57
  next
58
58
  end
59
59
  end
60
- key = task_name key
61
- GIT_REPO[main][key] = [uri.to_s, opts]
60
+ GIT_REPO[main][key = task_name(key)] = [uri.to_s, opts]
62
61
  @kind[key] << Project::Git
63
62
  end
64
63
  if cache == true
@@ -69,6 +68,16 @@ module Squared
69
68
  self
70
69
  end
71
70
 
71
+ def git_repo(name)
72
+ (ret = GIT_REPO[main]) && ret[name]
73
+ end
74
+
75
+ def git_clone?(path, name = nil)
76
+ return false if name && !git_repo(name)
77
+
78
+ !path.exist? || path.empty?
79
+ end
80
+
72
81
  def revbuild(file: nil)
73
82
  @revfile = @home.join(file || "#{@main}.revb")
74
83
  @revdoc = JSON.parse(@revfile.read) if @revfile.exist?
@@ -81,10 +90,6 @@ module Squared
81
90
  self
82
91
  end
83
92
 
84
- def git_repo(name)
85
- (ret = GIT_REPO[main]) && ret[name]
86
- end
87
-
88
93
  def rev_entry(*keys, val: nil, create: true)
89
94
  return unless @revdoc
90
95
  return @revdoc.dig(*keys) unless val
@@ -133,22 +138,11 @@ module Squared
133
138
  end
134
139
  File.write(@revfile, JSON.pretty_generate(@revdoc))
135
140
  rescue StandardError => e
136
- warn log_message(Logger::WARN, e, pass: true) if warning
141
+ log&.debug e
142
+ warn log_message(Logger::WARN, e, pass: true) if warning?
137
143
  ensure
138
144
  @revlock = false
139
145
  end
140
-
141
- def git_clone?(path, name = nil)
142
- return false if name && !git_repo(name)
143
-
144
- !path.exist? || path.empty?
145
- end
146
-
147
- private
148
-
149
- def rev_timenow
150
- Time.now.utc.strftime('%s%L').to_i
151
- end
152
146
  end
153
147
  Application.include Git
154
148
 
@@ -158,12 +152,11 @@ module Squared
158
152
  common: %w[c=q bare glob-pathspecs icase-pathspecs literal-pathspecs no-optional-locks no-pager
159
153
  no-replace-objects noglob-pathspecs paginate attr-source=b config-env=q exec-path=p
160
154
  namespace=p].freeze,
161
- add: %w[A|all e|edit f|force ignore-errors ignore-missing ignore-removal i|interactive no-all
162
- no-ignore-removal n|dry-run p|patch pathspec-file-nul renormalize sparse u|update v|verbose
163
- chmod=b pathspec-from-file=p].freeze,
164
- branch: %w[a|all create-reflog i|ignore-case omit-empty q|quiet r|remotes v|verbose abbrev=i color=b
165
- column=b contains=b format=q merged=b no-contains=b no-merged=b points-at=b u|set-upstream-to=b
166
- sort=q t|track=b].freeze,
155
+ add: %w[A e|edit f|force ignore-errors ignore-missing i|interactive n|dry-run p|patch pathspec-file-nul
156
+ refresh renormalize sparse u|update v|verbose chmod=b pathspec-from-file=p].freeze,
157
+ branch: %w[a|all create-reflog i|ignore-case omit-empty q|quiet r|remotes v|verbose abbrev=i color=b column=b
158
+ contains=b format=q merged=b no-contains=b no-merged=b points-at=b u|set-upstream-to=b sort=q
159
+ t|track=b].freeze,
167
160
  checkout: %w[l d|detach f|force ignore-other-worktrees ignore-skip-worktree-bits m|merge p|patch
168
161
  pathspec-file-nul q|quiet ours theirs conflict=b orphan=b pathspec-from-file=p t|track=b].freeze,
169
162
  diff: {
@@ -173,17 +166,22 @@ module Squared
173
166
  fetch: {
174
167
  base: %w[multiple porcelain progress P|prune-tags refetch stdin u|update-head-ok
175
168
  recurse-submodules-default=b].freeze,
176
- pull: %w[n t 4|ipv4 6|ipv6 a|append atomic dry-run f|force k|keep negotiate-only prefetch p|prune q|quiet
177
- set-upstream unshallow update-shallow v|verbose deepen=i depth=i j|jobs=i negotiation-tip=q
178
- recurse-submodules=v refmap=q o|server-option=q shallow-exclude=b shallow-since=v
179
- upload-pack=q].freeze
169
+ pull: %w[4 6 n t a|append atomic dry-run f|force k|keep negotiate-only prefetch p|prune q|quiet set-upstream
170
+ unshallow update-shallow v|verbose deepen=i depth=i j|jobs=i negotiation-tip=q recurse-submodules=v
171
+ refmap=q o|server-option=q shallow-exclude=b shallow-since=v upload-pack=q].freeze
180
172
  }.freeze,
181
173
  git: {
182
174
  add: %w[N|intent-to-add refresh].freeze,
183
175
  blame: %w[b c l s t w C=im? L=q M=im? S=p color-by-age color-lines first-parent incremental line-porcelain
184
- p|porcelain root score-debug f|show-name e|show-email n|show-number show-stats abbrev=i
185
- contents=p date=q encoding=b ignore-rev=b ignore-revs-file=p reverse=q].freeze,
176
+ p|porcelain root score-debug f|show-name e|show-email n|show-number show-stats abbrev=i contents=p
177
+ date=q encoding=b ignore-rev=b ignore-revs-file=p reverse=q].freeze,
186
178
  clean: %w[d x X f|force n|dry-run i|interactive q|quiet e|exclude=q].freeze,
179
+ grep: %w[e f=p h H I O=bm r all-match and G|basic-regexp break cached column c|count E|extended-regexp
180
+ l|files-with-matches L|files-without-match F|fixed-strings full-name W|function-context heading
181
+ i|ignore-case v|invert-match n|line-number name-only no-index not z|null o|only-matching or
182
+ P|perl-regexp q|quiet recurse-submodules p|show-function a|text untracked w|word-regexp
183
+ A|after-context=i B|before-context=i color=b C|context=i m|max-count=n max-depth=i
184
+ open-files-in-pager=b threads=n].freeze,
187
185
  mv: %w[k f|force n|dry-run v|verbose].freeze,
188
186
  revert: %w[e S=bm? abort continue n|no-commit quit reference skip cleanup=b gpg-sign=b? m|mainline=i
189
187
  s|signoff strategy=b X|strategy-option=b].freeze,
@@ -203,26 +201,27 @@ module Squared
203
201
  format: %w[t children combined-all-paths dd oneline left-right no-diff-merges parents relative-date
204
202
  show-notes-by-default show-signature date=q diff-merges=b encoding=b expand-tabs=i format=q
205
203
  notes=b pretty=q? show-linear-break=q?].freeze,
206
- diff: %w[p R u z B=bm? C=bm? l=im G=qm I=qm M=bm? O=qm S=qm U=im binary check compact-summary cumulative
204
+ diff: %w[p R u z B=bm? C=bm? l=im G=qm I=qm M=bm? O=qm S=qm binary check compact-summary cumulative
207
205
  find-copies-harder full-index W|function-context w|ignore-all-space ignore-blank-lines
208
206
  ignore-cr-at-eol ignore-space-at-eol b|ignore-space-change D|irreversible-delete graph
209
207
  ita-invisible-in-index minimal name-only name-status no-color-moved-ws no-prefix no-renames numstat
210
208
  patch-with-raw patch-with-stat patience pickaxe-all pickaxe-regex raw shortstat summary a|text
211
209
  abbrev=i? anchored=q break-rewrites=b? color=b color-moved=b color-moved-ws=b color-words=q?
212
- diff-algorithm=b diff-filter=q? X|dirstat=b? dirstat-by-file=b? dst-prefix=q find-copies=i?
213
- find-object=b find-renames=b? ignore-matching-lines=q ignore-submodules=b? inter-hunk-context=i
214
- line-prefix=q output=p output-indicator-context=q output-indicator-new=q output-indicator-old=q
215
- relative=p rotate-to=p skip-to=p src-prefix=q stat=b? stat-count=i stat-width=i stat-name-width=i
216
- submodule=b? unified=i word-diff=b? word-diff-regex=q ws-error-highlight=b].freeze
210
+ diff-algorithm=b diff-filter=e? X|dirstat=b? dirstat-by-file=b? dst-prefix=q find-copies=i?
211
+ find-object=b find-renames=b? ignore-matching-lines=q ignore-submodules=b? line-prefix=q output=p
212
+ output-indicator-context=q output-indicator-new=q output-indicator-old=q relative=p rotate-to=p
213
+ skip-to=p src-prefix=q stat=b? stat-count=i stat-width=i stat-name-width=i submodule=b?
214
+ word-diff=b? word-diff-regex=q ws-error-highlight=b].freeze,
215
+ diff_context: %w[U=im inter-hunk-context=i unified=i].freeze
217
216
  }.freeze,
218
217
  ls_files: %w[f t v z debug deduplicate directory eol error-unmatch exclude-standard full-name i|ignored
219
218
  k|killed no-empty-directory recurse-submodules sparse s|stage u|unmerged abbrev=i x|exclude=q
220
219
  X|exclude-from=p exclude-per-directory=p format=q with-tree=q].freeze,
221
220
  ls_remote: %w[exit-code get-url q|quiet symref o|server-option=q sort=q upload-pack=q].freeze,
222
- merge: %w[e n S=bm? allow-unrelated-histories ff-only m=q q|quiet v|verbose cleanup=b F|file=p gpg-sign=b?
223
- into-name=b log=i s|strategy=b X|strategy-option=b].freeze,
224
- pull: %w[e n S=bm? allow-unrelated-histories ff-only cleanup=b gpg-sign=b? log=i r|rebase=v? s|strategy=b
225
- X|strategy-option=b].freeze,
221
+ merge: %w[e n S=bm? allow-unrelated-histories compact-summary ff-only m=q q|quiet v|verbose cleanup=b F|file=p
222
+ gpg-sign=b? into-name=b log=i s|strategy=b X|strategy-option=b].freeze,
223
+ pull: %w[e n S=bm? allow-unrelated-histories compact-summary ff-only cleanup=b gpg-sign=b? log=i r|rebase=v?
224
+ s|strategy=b X|strategy-option=b].freeze,
226
225
  rebase: %w[n C=im S=bm? allow-empty-message apply committer-date-is-author-date edit-todo empty=b
227
226
  f|force-rebase ignore-date ignore-whitespace i|interactive keep-base m|merge no-ff q|quiet quit
228
227
  reset-author-date root show-current-patch signoff v|verbose empty=b x|exec=q gpg-sign=b? onto=b
@@ -241,8 +240,8 @@ module Squared
241
240
  encoding=b expand-tabs=i notes=q show-notes=q?].freeze,
242
241
  stash: {
243
242
  common: %w[q|quiet].freeze,
244
- push: %w[a|all u|include-untracked k|keep-index no-keep-index no-include-untracked pathspec-file-nul
245
- p|patch S|staged m|message=q pathspec-from-file=p].freeze,
243
+ push: %w[a|all u|include-untracked k|keep-index no-keep-index no-include-untracked pathspec-file-nul p|patch
244
+ S|staged m|message=q pathspec-from-file=p].freeze,
246
245
  pop: %w[index].freeze,
247
246
  apply: %w[index].freeze
248
247
  }.freeze,
@@ -260,13 +259,15 @@ module Squared
260
259
  tag: %w[n=im cleanup=b create-reflog i|ignore-case omit-empty color=b? column=b contains=b? format=q merged=b?
261
260
  no-contains=b? no-merged=b? points-at=q sort=q trailer=q].freeze,
262
261
  no: {
262
+ add: %w[all ignore-removal].freeze,
263
263
  blame: %w[progress].freeze,
264
264
  branch: %w[color color-moved column track].freeze,
265
265
  checkout: %w[overwrite-ignore guess overlay progress recurse-submodules track].freeze,
266
266
  fetch: {
267
267
  base: %w[auto-gc auto-maintenance write-commit-graph write-fetch-head].freeze,
268
- pull: %w[all recurse-submodules show-forced-updates tags].freeze
268
+ pull: %w[all ipv4 ipv6 recurse-submodules show-forced-updates tags].freeze
269
269
  },
270
+ grep: %w[color exclude-standard recursive textconv].freeze,
270
271
  log: {
271
272
  base: %w[decorate mailmap merges use-mailmap].freeze,
272
273
  diff: %w[color color-moved ext-diff indent-heuristic patch relative rename-empty textconv].freeze,
@@ -302,33 +303,6 @@ module Squared
302
303
  class << self
303
304
  include Rake::DSL
304
305
 
305
- def populate(ws, **)
306
- return if ws.series.exclude?(:pull, true) || ws.size == 1
307
-
308
- namespace ws.task_name('git') do |ns|
309
- ws.format_desc(all = ws.task_join(ns.scope.path, 'all'), 'stash|rebase|autostash?,depend?')
310
- task 'all' do |_, args|
311
- args = args.to_a
312
- cmd = if args.include?('stash')
313
- ['stash', 'pull']
314
- elsif args.include?('rebase')
315
- ['rebase']
316
- elsif args.include?('autostash')
317
- ['autostash']
318
- else
319
- ['pull']
320
- end
321
- cmd.map! { |val| ws.task_sync(val) }
322
- cmd << ws.task_sync('depend') if args.include?('depend') && !ws.series.exclude?(:depend, true)
323
- cmd << ws.task_sync('build')
324
- Common::Utils.task_invoke(*cmd, **ws.invokeargs)
325
- end
326
-
327
- ws.series.sync << all
328
- ws.series.multiple << all
329
- end
330
- end
331
-
332
306
  def tasks
333
307
  %i[pull rebase autostash fetch clone stash status branch revbuild].freeze
334
308
  end
@@ -341,13 +315,13 @@ module Squared
341
315
  end
342
316
 
343
317
  subtasks({
344
- 'branch' => %i[create track delete move copy list all current].freeze,
318
+ 'branch' => %i[create track delete move copy list current].freeze,
345
319
  'checkout' => %i[commit branch track detach path].freeze,
346
320
  'commit' => %i[add all amend amend-orig fixup].freeze,
347
321
  'diff' => %i[head branch files view between contain].freeze,
348
322
  'fetch' => %i[origin remote all].freeze,
349
323
  'files' => %i[cached modified deleted others].freeze,
350
- 'git' => %i[add blame clean mv revert rm status].freeze,
324
+ 'git' => %i[add blame clean grep mv revert rm status].freeze,
351
325
  'log' => %i[view between contain].freeze,
352
326
  'merge' => %i[commit no-commit send].freeze,
353
327
  'pull' => %i[origin remote all].freeze,
@@ -357,7 +331,7 @@ module Squared
357
331
  'restore' => %i[source staged worktree].freeze,
358
332
  'rev' => %i[commit build output].freeze,
359
333
  'show' => %i[format oneline textconv].freeze,
360
- 'stash' => %i[push pop apply branch drop clear list all].freeze,
334
+ 'stash' => %i[push pop apply branch drop clear list all staged worktree].freeze,
361
335
  'submodule' => %i[status update branch url sync].freeze,
362
336
  'switch' => %i[branch create detach].freeze,
363
337
  'tag' => %i[add sign delete list].freeze
@@ -365,7 +339,7 @@ module Squared
365
339
 
366
340
  def initialize(*, **)
367
341
  super
368
- @submodule = basepath('.gitmodules').exist?
342
+ @submodule = exist?('.gitmodules')
369
343
  initialize_ref Git.ref if gitpath.exist?
370
344
  end
371
345
 
@@ -388,16 +362,16 @@ module Squared
388
362
  if flag == :remote
389
363
  format_desc action, flag, 'remote?,opts*'
390
364
  task flag, [:remote] do |_, args|
391
- if (remote = args.remote)
392
- args = args.extras
393
- else
394
- remote = choice_remote
395
- args = args.to_a
396
- end
365
+ args = if (remote = args.remote)
366
+ args.extras
367
+ else
368
+ remote = choice_remote
369
+ args.to_a
370
+ end
397
371
  __send__(action, flag, args, remote: remote)
398
372
  end
399
373
  else
400
- format_desc(action, flag, 'opts*', after: flag == :all && action == 'pull' ? 'pattern*' : nil)
374
+ format_desc(action, flag, 'opts*', after: ('pattern*' if flag == :all && action == 'pull'))
401
375
  task flag do |_, args|
402
376
  __send__ action, flag, args.to_a
403
377
  end
@@ -434,11 +408,11 @@ module Squared
434
408
  commit(flag, message: args.message)
435
409
  end
436
410
  else
437
- format_desc(action, flag, 'pathspec+', before: flag == :add ? 'opts*' : nil)
411
+ format_desc(action, flag, 'pathspec+', before: ('opts*' if flag == :add))
438
412
  task flag do |_, args|
439
413
  if flag == :fixup
440
- ref, squash, pick = choice_commit(accept: [['Auto squash?', true]], reflog: false,
441
- values: ['Pick [amend|reword]'])
414
+ ref, squash, pick = choice_commit(reflog: false, accept: [accept_b('Auto squash?')],
415
+ values: 'Pick [amend|reword]')
442
416
  pick &&= case pick.downcase
443
417
  when 'a', 'amend'
444
418
  'amend'
@@ -480,7 +454,7 @@ module Squared
480
454
  task flag do |_, args|
481
455
  refs = args.to_a
482
456
  if refs.empty?
483
- refs = choice_refs('Choose a tag', 'tags', multiple: true, accept: 'Delete?', series: true)
457
+ refs = choice_refs('Choose a tag', 'tags', multiple: true, series: true, accept: 'Delete?')
484
458
  remote = choice_remote
485
459
  end
486
460
  tag(flag, refs: refs, remote: remote)
@@ -493,12 +467,13 @@ module Squared
493
467
  commit = commithead args.commit
494
468
  remote = args.remote
495
469
  else
496
- commit, name, message = choice_commit(values: [['Enter tag name', true], 'Enter message'],
497
- series: true, reflog: false)
470
+ commit, name, message = choice_commit(reflog: false, series: true,
471
+ values: [['Enter tag name', true], 'Enter message'])
498
472
  remote = choice_remote
499
473
  end
500
- ret = tag(flag, refs: [name], message: message, commit: commit, remote: remote)
501
- print_success if success?(ret, !remote)
474
+ tag(flag, refs: [name], message: message, commit: commit, remote: remote).tap do |ret|
475
+ success?(ret, !remote)
476
+ end
502
477
  end
503
478
  end
504
479
  when 'stash'
@@ -514,9 +489,8 @@ module Squared
514
489
  when 'log', 'diff'
515
490
  case flag
516
491
  when :view, :between, :contain
517
- view = flag == :view
518
- if view && action == 'log'
519
- format_desc action, flag, '(^)commit*|:,opts*,ref?,pathspec*'
492
+ if action == 'log' && flag == :view
493
+ format_desc action, flag, '(^)commit*|:,opts*,pathspec*'
520
494
  task flag do |_, args|
521
495
  args = args.to_a
522
496
  if args.first == ':'
@@ -543,16 +517,17 @@ module Squared
543
517
  format_desc action, flag, 'commit1,commit2,opts*,pathspec*'
544
518
  task flag, [:commit1, :commit2] do |_, args|
545
519
  commit1 = commithead args.commit1
546
- if commit1
547
- commit2 = commithead param_guard(action, flag, args: args, key: :commit2)
548
- args = args.extras
549
- range = [commit1, commit2]
550
- else
551
- range, opts, refs = choice_commit(multiple: view ? true : 2, values: %w[Options Pathspec])
552
- range = range.reverse
553
- args = OptionPartition.strip(opts)
554
- args.concat(refs.shellsplit) if refs
555
- end
520
+ range = if commit1
521
+ commit2 = commithead param_guard(action, flag, args: args, key: :commit2)
522
+ args = args.extras
523
+ [commit1, commit2]
524
+ else
525
+ range, opts, refs = choice_commit(multiple: flag == :view ? true : 2,
526
+ values: %w[Options Pathspec])
527
+ args = OptionPartition.strip(opts)
528
+ args.concat(refs.shellsplit) if refs
529
+ range.reverse
530
+ end
556
531
  __send__(action == 'log' ? :log! : :diff, flag, args, range: range)
557
532
  end
558
533
  end
@@ -565,7 +540,11 @@ module Squared
565
540
  index = choice_commit(multiple: true)
566
541
  else
567
542
  index = []
568
- args.each { |val| index << (commithead(val) || commithash(val) || break) }
543
+ args.each do |val|
544
+ break unless (sha = commithead(val) || commithash(val))
545
+
546
+ index << sha
547
+ end
569
548
  args = args.drop(index.size)
570
549
  end
571
550
  diff(flag, args, index: index)
@@ -577,36 +556,36 @@ module Squared
577
556
  diff(flag, args.extras, branch: branch)
578
557
  end
579
558
  when :files
580
- format_desc action, flag, 'path1,path2'
581
- task flag, [:path1, :path2] do |_, args|
559
+ format_desc action, flag, 'path1,path2,patch?'
560
+ task flag, [:path1, :path2, :patch] do |_, args|
582
561
  path1 = param_guard(action, flag, args: args, key: :path1)
583
562
  path2 = param_guard(action, flag, args: args, key: :path2)
584
- diff(flag, refs: [path1, path2])
563
+ diff(flag, refs: [path1, path2, args.patch])
585
564
  end
586
565
  end
587
566
  when 'checkout'
588
567
  case flag
589
568
  when :branch
590
- format_desc action, flag, 'name,create?=[bB],commit?,detach?=d'
569
+ format_desc action, flag, 'name,create?=[bB],commit?,d/etach?'
591
570
  task flag, [:name, :create, :commit, :detach] do |_, args|
592
571
  if (branch = args.name)
593
572
  branch = param_guard(action, flag, args: args, key: :name)
594
573
  create = args.create
595
- if args.commit == 'd'
596
- detach = 'd'
597
- commit = nil
598
- elsif create == 'd'
599
- create = nil
600
- commit = nil
601
- detach = 'd'
602
- elsif create && create.size > 1
603
- commit = commithead create
604
- create = nil
605
- detach = args.commit
606
- else
607
- detach = args.detach
608
- commit = commithead args.commit
609
- end
574
+ detach = if args.commit == 'd'
575
+ commit = nil
576
+ 'd'
577
+ elsif create == 'd'
578
+ create = nil
579
+ commit = nil
580
+ 'd'
581
+ elsif create && create.size > 1
582
+ commit = commithead create
583
+ create = nil
584
+ args.commit
585
+ else
586
+ commit = commithead args.commit
587
+ args.detach
588
+ end
610
589
  param_guard(action, flag, args: { create: create }, key: :create, pat: /\A[Bb]\z/) if create
611
590
  else
612
591
  branch = choice_refs 'Choose a branch to switch'
@@ -619,7 +598,7 @@ module Squared
619
598
  if (origin = args.origin)
620
599
  branch = args.name
621
600
  else
622
- origin, branch = choice_refs('Choose a remote', 'remotes', values: ['Enter branch name'])
601
+ origin, branch = choice_refs('Choose a remote', 'remotes', values: 'Enter branch name')
623
602
  end
624
603
  checkout(flag, branch: branch, origin: origin)
625
604
  end
@@ -630,7 +609,7 @@ module Squared
630
609
  args = if commit
631
610
  args.extras
632
611
  else
633
- commit, opts = choice_commit(values: ['Options'])
612
+ commit, opts = choice_commit(values: 'Options')
634
613
  OptionPartition.strip(opts)
635
614
  end
636
615
  checkout(flag, args, commit: commit)
@@ -640,7 +619,7 @@ module Squared
640
619
  task flag, [:commit] do |_, args|
641
620
  commit = commithead args.commit
642
621
  unless commit
643
- commit, merge = choice_commit(values: ['Merge? [y/N]'])
622
+ commit, merge = choice_commit(values: 'Merge? [y/N]')
644
623
  merge = merge&.upcase == 'Y'
645
624
  end
646
625
  checkout(flag, commit: commit, merge: merge)
@@ -654,11 +633,13 @@ module Squared
654
633
  when 'branch'
655
634
  case flag
656
635
  when :create
657
- format_desc action, flag, 'name,ref?=HEAD|:'
636
+ format_desc action, flag, 'name,ref?|:'
658
637
  task flag, [:name, :ref] do |_, args|
659
638
  target = param_guard(action, flag, args: args, key: :name)
660
639
  ref = commithead args.ref
661
- ref, remote = choice_refs('Choose a remote', 'remotes', accept: [['Push?', true]]) if ref == ':'
640
+ if ref == ':'
641
+ ref, remote = choice_refs('Choose a remote', 'remotes', accept: [accept_b('Push?')])
642
+ end
662
643
  branch(flag, target: target, ref: ref, remote: remote)
663
644
  end
664
645
  when :track
@@ -668,18 +649,18 @@ module Squared
668
649
  target = args.name
669
650
  remote = true if ref.delete_prefix!('~')
670
651
  else
671
- ref, remote, target = choice_refs('Choose a remote', 'remotes', accept: [['Push?', true]],
672
- values: ['Enter branch name'])
652
+ ref, remote, target = choice_refs('Choose a remote', 'remotes', accept: [accept_b('Push?')],
653
+ values: 'Enter branch name')
673
654
  end
674
655
  branch(flag, target: target, ref: ref, remote: remote)
675
656
  end
676
657
  when :delete
677
- format_desc action, flag, '[^~]name*,:?'
658
+ format_desc action, flag, '(^~)name*,:?'
678
659
  task flag do |_, args|
679
660
  refs = args.to_a
680
661
  if refs.empty? || (r = refs.last == ':')
681
662
  accept = ['Delete?']
682
- accept << ['Force?', true] unless r
663
+ accept << accept_b('Force?') unless r
683
664
  remote = choice_refs('Choose a branch', r ? 'remotes' : 'heads', multiple: true,
684
665
  accept: accept)
685
666
  if r
@@ -697,7 +678,12 @@ module Squared
697
678
  task flag do |_, args|
698
679
  branch flag, args.to_a
699
680
  end
700
- when :move, :copy
681
+ when :current
682
+ format_desc action, flag
683
+ task flag do
684
+ branch flag
685
+ end
686
+ else
701
687
  format_desc action, flag, 'branch,oldbranch?'
702
688
  task flag, [:branch, :oldbranch] do |_, args|
703
689
  if (branch = args.branch)
@@ -708,24 +694,19 @@ module Squared
708
694
  end
709
695
  branch(flag, refs: [oldbranch, branch])
710
696
  end
711
- else
712
- format_desc action, flag
713
- task flag do
714
- branch flag
715
- end
716
697
  end
717
698
  when 'switch'
718
699
  case flag
719
700
  when :create
720
- format_desc action, flag, '(^)name,ref?=HEAD|:'
701
+ format_desc action, flag, '(^)name,ref?|:'
721
702
  task flag, [:name, :commit] do |_, args|
722
703
  branch = param_guard(action, flag, args: args, key: :name)
723
704
  commit = commithead args.commit
724
- commit, track = choice_commit(values: ['Track? [Y/n]'], force: false) if commit == ':'
705
+ commit, track = choice_commit(force: false, values: 'Track? [Y/n]') if commit == ':'
725
706
  switch(flag, branch: branch, commit: commit, track: track)
726
707
  end
727
708
  when :detach
728
- format_desc action, flag, 'ref?=HEAD'
709
+ format_desc action, flag, 'ref?'
729
710
  task flag, [:commit] do |_, args|
730
711
  commit = commithead(args.commit) || choice_commit(force: false)
731
712
  switch(flag, commit: commit)
@@ -733,12 +714,12 @@ module Squared
733
714
  when :branch
734
715
  format_desc action, flag, 'name|:,opts*'
735
716
  task flag, [:name] do |_, args|
736
- if (branch = args.name)
737
- args = args.extras
738
- branch = nil if branch == ':'
739
- else
740
- args = []
741
- end
717
+ args = if (branch = args.name)
718
+ branch = nil if branch == ':'
719
+ args.extras
720
+ else
721
+ []
722
+ end
742
723
  switch(flag, args, branch: branch || choice_refs('Choose a branch'))
743
724
  end
744
725
  end
@@ -748,26 +729,26 @@ module Squared
748
729
  format_desc action, flag, 'ref|:,opts*'
749
730
  task flag, [:commit] do |_, args|
750
731
  commit = commithead args.commit
751
- if commit && commit != ':'
752
- args = args.extras
753
- else
754
- commit, mode = choice_commit(values: ['Mode [mixed|soft|hard|N]'])
755
- args = args.extras.concat(case mode&.downcase
732
+ args = if commit && commit != ':'
733
+ args.extras
734
+ else
735
+ commit, mode = choice_commit(values: ['Mode [mixed|soft|hard|N]'])
736
+ args.extras.concat(case mode&.downcase
756
737
  when 'h', 'hard' then ['hard']
757
738
  when 's', 'soft' then ['soft']
758
- when 'n', 'N' then ['mixed', 'N']
739
+ when 'n', 'N' then %w[mixed N]
759
740
  else ['mixed']
760
741
  end)
761
- end
762
- print_success if success?(reset(flag, args, commit: commit))
742
+ end
743
+ success?(reset(flag, args, commit: commit))
763
744
  end
764
745
  when :index, :undo
765
- format_desc(action, flag, flag == :index ? 'opts*,pathspec*' : nil)
746
+ format_desc(action, flag, ('opts*,pathspec*' if flag == :index))
766
747
  task flag do |_, args|
767
748
  reset(flag, flag == :index ? args.to_a : [])
768
749
  end
769
750
  when :mode
770
- format_desc action, flag, 'mode,ref?=HEAD|:'
751
+ format_desc action, flag, 'mode,ref?|:'
771
752
  task flag, [:mode, :ref] do |_, args|
772
753
  mode = param_guard(action, flag, args: args, key: :mode)
773
754
  ref = commithead args.ref
@@ -775,7 +756,7 @@ module Squared
775
756
  reset(flag, mode: mode, ref: ref)
776
757
  end
777
758
  when :patch
778
- format_desc action, flag, 'ref?=HEAD|:,pathspec*'
759
+ format_desc action, flag, 'ref?|:,pathspec*'
779
760
  task flag, [:ref] do |_, args|
780
761
  ref = commithead args.ref
781
762
  ref = choice_commit(reflog: false) unless ref && ref != ':'
@@ -804,18 +785,18 @@ module Squared
804
785
  when 'rebase', 'merge'
805
786
  case flag
806
787
  when :branch
807
- format_desc action, flag, 'upstream,branch?=HEAD,opts*'
788
+ format_desc action, flag, 'upstream,branch?,opts*'
808
789
  task flag, [:upstream] do |_, args|
809
790
  args = if (upstream = args.upstream)
810
791
  args.extras
811
792
  else
812
- upstream, opts = choice_refs('Choose upstream branch', values: ['Options'])
793
+ upstream, opts = choice_refs('Choose upstream branch', values: 'Options')
813
794
  OptionPartition.strip(opts)
814
795
  end
815
796
  rebase(flag, args, upstream: upstream)
816
797
  end
817
798
  when :onto
818
- format_desc action, flag, 'ref,upstream,branch?=HEAD'
799
+ format_desc action, flag, 'ref,upstream,branch?'
819
800
  task flag, [:commit, :upstream, :branch] do |_, args|
820
801
  commit = commithead args.commit
821
802
  args = if commit
@@ -824,7 +805,7 @@ module Squared
824
805
  []
825
806
  else
826
807
  commit = choice_refs 'Choose "onto" branch'
827
- target, opts = choice_commit(reflog: false, multiple: 2, values: ['Options'])
808
+ target, opts = choice_commit(reflog: false, multiple: 2, values: 'Options')
828
809
  branch, upstream = target
829
810
  OptionPartition.strip(opts)
830
811
  end
@@ -836,7 +817,7 @@ module Squared
836
817
  args = args.to_a
837
818
  if args.empty?
838
819
  accept = "Merge with #{`#{git_output('branch --show-current')}`.chomp}?"
839
- branch, opts = choice_refs('Choose a branch', values: ['Options'], accept: accept)
820
+ branch, opts = choice_refs('Choose a branch', values: 'Options', accept: accept)
840
821
  args = OptionPartition.strip(opts)
841
822
  end
842
823
  merge(flag, args, branch: branch)
@@ -852,7 +833,7 @@ module Squared
852
833
  when 'rev'
853
834
  case flag
854
835
  when :commit
855
- format_desc action, flag, 'ref?=HEAD,size?'
836
+ format_desc action, flag, 'ref?,size?'
856
837
  task flag, [:ref, :size] do |_, args|
857
838
  ref = commithead args.ref
858
839
  size = args.size
@@ -863,8 +844,6 @@ module Squared
863
844
  rev_parse(flag, ref: ref, size: size)
864
845
  end
865
846
  when :build
866
- next unless build?
867
-
868
847
  format_desc action, flag, 'opts*'
869
848
  task flag do |_, args|
870
849
  revbuild flag, args.to_a
@@ -882,7 +861,7 @@ module Squared
882
861
  ls_remote(flag, args.extras, remote: args.remote)
883
862
  end
884
863
  else
885
- format_desc(action, flag, 'opts*,pattern*', after: action == 'files' ? 'pathspec*' : nil)
864
+ format_desc(action, flag, 'opts*,pattern*', after: ('pathspec*' if action == 'files'))
886
865
  task flag do |_, args|
887
866
  __send__(action == 'refs' ? :ls_remote : :ls_files, flag, args.to_a)
888
867
  end
@@ -923,16 +902,22 @@ module Squared
923
902
  end
924
903
  when 'git'
925
904
  before = case flag
926
- when :blame then 'file'
927
- when :mv then 'source+,destination'
928
- when :revert then 'commit+'
905
+ when :blame
906
+ 'file'
907
+ when :mv
908
+ 'source+,destination'
909
+ when :revert
910
+ 'commit+'
929
911
  end
930
- format_desc(action, flag, 'opts*', before: before, after: case flag
931
- when :add
932
- 'pathspec*,pattern*'
933
- when :clean, :rm, :status
934
- 'pathspec*'
935
- end)
912
+ after = case flag
913
+ when :add
914
+ 'pathspec*,pattern*'
915
+ when :grep
916
+ 'tree*,pathspec*'
917
+ when :clean, :rm, :status
918
+ 'pathspec*'
919
+ end
920
+ format_desc(action, flag, 'opts*', before: before, after: after)
936
921
  task flag do |_, args|
937
922
  __send__(flag == :status ? :status : :git, flag, args.to_a)
938
923
  end
@@ -983,8 +968,12 @@ module Squared
983
968
  printsucc
984
969
  end
985
970
  op = OptionPartition.new(opts, OPT_GIT[:pull], cmd, project: self, no: OPT_GIT[:no][:pull])
986
- opts -= op.extras
987
- reg = matchmap op
971
+ reg = if op.empty?
972
+ []
973
+ else
974
+ opts = op.uniq(opts)
975
+ matchmap op
976
+ end
988
977
  session_done op.target
989
978
  heads = []
990
979
  cur = nil
@@ -993,10 +982,10 @@ module Squared
993
982
  cur ||= line.delete_prefix!('* ')
994
983
  heads << line if matchany?(line, reg)
995
984
  end
996
- raise_error('head not found', hint: 'for-each-ref') unless cur
985
+ raise_error 'head not found', hint: 'for-each-ref' unless cur
997
986
  opts << 'ff-only' if opts.empty? && !option('ff-only', equals: '0')
998
- (heads.dup << cur).each_with_index do |branch, index|
999
- next unless (index < heads.size && cur != branch) || index == heads.size
987
+ (heads.dup << cur).each_with_index do |branch, i|
988
+ next unless (i < heads.size && cur != branch) || i == heads.size
1000
989
 
1001
990
  git_spawn 'switch --quiet', force && '--force', shell_quote(branch)
1002
991
  pull(nil, opts, sync: false, hint: branch) if heads.include?(branch)
@@ -1010,8 +999,8 @@ module Squared
1010
999
  no: OPT_GIT[:no][:pull] + OPT_GIT[:no][:fetch][:pull], remote: remote, flag: flag, from: :pull)
1011
1000
  source(sync: sync, sub: if stdout?
1012
1001
  [
1013
- { pat: /^(.+)(\|\s+\d+\s+)([^-]*)(-+)(.*)$/, styles: color(:red), index: 4 },
1014
- { pat: /^(.+)(\|\s+\d+\s+)(\++)(.*)$/, styles: color(:green), index: 3 }
1002
+ opt_style(color(:red), /^(.+)(\|\s+\d+\s+)([^-]*)(-+)(.*)$/, 4),
1003
+ opt_style(color(:green), /^(.+)(\|\s+\d+\s+)(\++)(.*)$/, 3)
1015
1004
  ]
1016
1005
  end, hint: hint, **threadargs)
1017
1006
  end
@@ -1027,7 +1016,7 @@ module Squared
1027
1016
 
1028
1017
  op = OptionPartition.new(opts, OPT_GIT[:rebase], cmd, project: self, no: OPT_GIT[:no][:rebase])
1029
1018
  op << upstream
1030
- append_head op.shift
1019
+ append_head op.shift&.delete_prefix(':')
1031
1020
  op.clear(pass: false)
1032
1021
  when :onto
1033
1022
  return unless upstream
@@ -1039,13 +1028,13 @@ module Squared
1039
1028
  else
1040
1029
  unless gitpath('REBASE_HEAD').exist?
1041
1030
  puts log_message(Logger::INFO, name, 'no rebase in progress', hint: command) if stdout?
1042
- return
1031
+ exit 1
1043
1032
  end
1044
1033
  return unless VAL_GIT[:rebase][:send].include?(command)
1045
1034
 
1046
1035
  cmd << "--#{command}"
1047
1036
  end
1048
- source(sync: sync)
1037
+ source
1049
1038
  end
1050
1039
 
1051
1040
  def autostash(*, sync: invoked_sync?('autostash'), **)
@@ -1089,22 +1078,33 @@ module Squared
1089
1078
  append_hash opts
1090
1079
  cmd << '--quiet' unless verbose
1091
1080
  append_value(data[0], path, delim: true)
1092
- source(sync: sync, banner: sync && !quiet?, multiple: !sync || quiet?)
1081
+ source(banner: sync && !quiet?, multiple: !sync || quiet?)
1093
1082
  end
1094
1083
 
1095
1084
  def stash(flag = nil, opts = [], sync: invoked_sync?('stash', flag))
1096
1085
  if flag
1097
- if flag == :all
1086
+ case flag
1087
+ when :all
1098
1088
  opts << 'include-untracked'
1099
1089
  flag = :push
1090
+ when :staged
1091
+ opts << 'staged'
1092
+ flag = :push
1093
+ when :worktree
1094
+ opts << 'keep-index'
1095
+ flag = :push
1096
+ end
1097
+ unless (file = gitpath('logs/refs/stash')).exist? || flag == :push
1098
+ puts log_message(Logger::INFO, name, 'no stashes were found', hint: flag) if stdout?
1099
+ exit 1
1100
1100
  end
1101
1101
  cmd, opts = git_session('stash', flag, opts: opts)
1102
1102
  list = OPT_GIT[:stash][:common] + OPT_GIT[:stash].fetch(flag, [])
1103
1103
  if flag == :list
1104
- list += collect_hash OPT_GIT[:log]
1104
+ list.concat(collect_hash(OPT_GIT[:log]))
1105
1105
  no = collect_hash OPT_GIT[:no][:log]
1106
1106
  end
1107
- op = OptionPartition.new(opts, list, cmd, project: self, no: no, first: flag == :push ? matchpathspec : nil)
1107
+ op = OptionPartition.new(opts, list, cmd, project: self, no: no, first: (matchpathspec if flag == :push))
1108
1108
  case flag
1109
1109
  when :push
1110
1110
  op.append?('message', readline('Enter message', force: true), force: true) if op.remove(':')
@@ -1115,30 +1115,32 @@ module Squared
1115
1115
  if op.empty?
1116
1116
  values = [['Branch name', true]]
1117
1117
  else
1118
- op << op.shift
1118
+ op.add_first(prefix: ':')
1119
1119
  end
1120
1120
  end
1121
1121
  out = choice_index('Choose a stash', git_spawn('stash list', stdout: false),
1122
- values: values, column: /^[^@]+@\{(\d+)\}/)
1122
+ values: values, column: /^[^@]+@\{(\d+)}/, force: true)
1123
1123
  if values
1124
1124
  op.merge(out.reverse)
1125
1125
  else
1126
1126
  op << out
1127
1127
  end
1128
1128
  elsif !op.empty?
1129
- op << op.shift
1129
+ op.add_first(prefix: ':')
1130
1130
  elsif flag == :branch
1131
- raise_error 'no branch name'
1131
+ raise_error ArgumentError, 'no branch name'
1132
1132
  end
1133
1133
  op.clear
1134
1134
  when :clear
1135
- source(stdout: true) if confirm("Remove #{sub_style('all', styles: theme[:active])} stash entries?", 'N')
1135
+ n = sub_style(file.read.lines.size, styles: theme[:inline])
1136
+ s = sub_style(name, styles: theme[:active])
1137
+ source(stdout: true) if confirm("Remove #{n} stash entries from #{s}?", 'N')
1136
1138
  return
1137
1139
  when :list
1138
1140
  op.clear
1139
1141
  out, banner, from = source(io: true)
1140
1142
  print_item banner
1141
- list_result(write_lines(out), 'objects', banner: banner, from: from)
1143
+ list_result(write_lines(out), 'objects', from: from)
1142
1144
  return
1143
1145
  end
1144
1146
  else
@@ -1176,18 +1178,18 @@ module Squared
1176
1178
  g = color(:green)
1177
1179
  sub = if session_arg?('short')
1178
1180
  [
1179
- { pat: /^(.)([A-Z?!])(.+)$/, styles: r, index: 2 },
1180
- { pat: /^([A-Z?!])(.+)$/, styles: g },
1181
- { pat: /^(\?\?)(.+)$/, styles: r },
1182
- { pat: /^(## )((?~\.{3}))(\.{3})(.+)$/, styles: [nil, g, nil, r], index: -1 }
1181
+ opt_style(r, /^(.)([A-Z?!])(.+)$/, 2),
1182
+ opt_style(g, /^([A-Z?!])(.+)$/),
1183
+ opt_style(r, /^(\?\?)(.+)$/),
1184
+ opt_style([nil, g, nil, r], /^(## )((?~\.{3}))(\.{3})(.+)$/, -1)
1183
1185
  ]
1184
1186
  else
1185
- [{ pat: /^(\t+)([a-z]+: +.+)$/, styles: r, index: 2 }]
1187
+ opt_style(r, /^(\t+)([a-z]+: +.+)$/, 2)
1186
1188
  end
1187
1189
  end
1188
1190
  out, banner, from = source(io: true)
1189
1191
  ret = write_lines(out, banner: banner, sub: sub)
1190
- list_result(ret, 'files', action: 'modified', banner: banner, from: from)
1192
+ list_result(ret, 'files', from: from, action: 'modified')
1191
1193
  end
1192
1194
 
1193
1195
  def revbuild(flag = nil, opts = [], sync: nil, **kwargs)
@@ -1216,26 +1218,24 @@ module Squared
1216
1218
  op.clear(append: true)
1217
1219
  args = op.to_a
1218
1220
  else
1219
- args = []
1220
- option('untracked-files', prefix: 'git') { |val| args << basic_option('untracked-files', val) }
1221
- option('ignore-submodules', prefix: 'git') { |val| args << basic_option('ignore-submodules', val) }
1222
- option('ignored', prefix: 'git') { |val| args << basic_option('ignored', val) }
1221
+ args = [
1222
+ option('untracked-files', prefix: 'git') { |val| basic_option('untracked-files', val) },
1223
+ option('ignore-submodules', prefix: 'git') { |val| basic_option('ignore-submodules', val) },
1224
+ option('ignored', prefix: 'git') { |val| basic_option('ignored', val) }
1225
+ ].compact
1223
1226
  end
1224
1227
  if (cur = workspace.rev_entry(name)) && cur['revision'] == sha && !env('REVBUILD_FORCE')
1225
1228
  files = status_digest(*args, **kwargs)
1226
1229
  if cur['files'].size == files.size && cur['files'].find { |key, val| files[key] != val }.nil?
1230
+ workspace.rev_timeutc(name, 'build') unless (since = workspace.rev_timesince(name, 'build'))
1227
1231
  if stdout?
1228
- if (since = workspace.rev_timesince(name, 'build'))
1229
- puts log_message(Logger::INFO, name, 'no changes', subject: 'revbuild', hint: "#{since} ago")
1230
- else
1231
- workspace.rev_timeutc(name, 'build')
1232
- end
1232
+ puts log_message(Logger::INFO, name, 'no changes', subject: 'revbuild', hint: since && "#{since} ago")
1233
1233
  end
1234
1234
  return
1235
1235
  end
1236
1236
  end
1237
1237
  start = time_epoch
1238
- build(*@output, sync: sync, from: :'git:revbuild')
1238
+ build(@output, sync: sync, from: :'git:revbuild')
1239
1239
  rescue StandardError => e
1240
1240
  print_error(e, pass: true)
1241
1241
  else
@@ -1248,9 +1248,8 @@ module Squared
1248
1248
  cmd, opts = git_session('reset', opts: opts)
1249
1249
  case flag
1250
1250
  when :commit, :index
1251
- op = OptionPartition.new(opts, OPT_GIT[:reset] + VAL_GIT[:reset], cmd,
1252
- project: self, no: OPT_GIT[:no][:reset],
1253
- first: flag == :index ? matchpathspec : nil)
1251
+ op = OptionPartition.new(opts, OPT_GIT[:reset] + VAL_GIT[:reset] + OPT_GIT[:log][:diff_context], cmd,
1252
+ project: self, no: OPT_GIT[:no][:reset], first: (matchpathspec if flag == :index))
1254
1253
  if flag == :commit
1255
1254
  op.append(commit)
1256
1255
  .clear(pass: false)
@@ -1281,7 +1280,7 @@ module Squared
1281
1280
 
1282
1281
  def checkout(flag, opts = [], branch: nil, origin: nil, create: nil, commit: nil, detach: nil, merge: false)
1283
1282
  cmd, opts = git_session('checkout', opts: opts)
1284
- append_option 'force', 'f', 'merge'
1283
+ append_option 'f', 'force', 'merge'
1285
1284
  case flag
1286
1285
  when :branch
1287
1286
  cmd << '--detach' if detach == 'd' || option('detach')
@@ -1294,13 +1293,13 @@ module Squared
1294
1293
  cmd << '-m' if merge
1295
1294
  cmd << '--detach' << commit
1296
1295
  else
1297
- op = OptionPartition.new(opts, OPT_GIT[:checkout], cmd, project: self, no: OPT_GIT[:no][:checkout],
1298
- first: flag == :path ? matchpathspec : nil)
1296
+ list = OPT_GIT[:checkout] + OPT_GIT[:log][:diff_context]
1297
+ op = OptionPartition.new(opts, list, cmd, project: self, no: OPT_GIT[:no][:checkout],
1298
+ first: (matchpathspec if flag == :path))
1299
1299
  if flag == :path
1300
1300
  append_head
1301
1301
  append_pathspec(op.extras, pass: false)
1302
- print_success if success?(source)
1303
- return
1302
+ return success?(source)
1304
1303
  end
1305
1304
  op.append(commit)
1306
1305
  .clear(pass: false)
@@ -1317,7 +1316,7 @@ module Squared
1317
1316
  elsif !session_arg?('s', 'sign', 'u', 'local-user')
1318
1317
  cmd << '--annotate'
1319
1318
  end
1320
- cmd << '--force' if option('force', 'f')
1319
+ cmd << '--force' if option('f', 'force')
1321
1320
  if !commit && message && (sha = commithash(message))
1322
1321
  commit = sha
1323
1322
  message = nil
@@ -1333,12 +1332,15 @@ module Squared
1333
1332
  out, banner, from = source(io: true)
1334
1333
  print_item banner
1335
1334
  ret = write_lines(out, grep: op.extras)
1336
- list_result(ret, 'tags', grep: op.extras, banner: banner, from: from)
1335
+ list_result(ret, 'tags', from: from, grep: op.extras)
1337
1336
  return
1338
1337
  end
1339
- remote ||= option 'remote'
1340
- source
1341
- git_spawn('push', flag == :delete ? '-d' : nil, remote, *refs.map { |val| shell_quote(val) }) if remote
1338
+ remote ||= option('remote')
1339
+ source.tap do |ret|
1340
+ next unless ret && remote
1341
+
1342
+ git_spawn('push', ('-d' if flag == :delete), remote, *refs.map! { |val| shell_quote(val) })
1343
+ end
1342
1344
  end
1343
1345
 
1344
1346
  def log!(flag, opts = [], range: [], index: [])
@@ -1356,13 +1358,14 @@ module Squared
1356
1358
  append_pathspec op.extras
1357
1359
  source(exception: false)
1358
1360
  end
1359
- alias log_ log!
1360
1361
 
1361
1362
  def diff(flag, opts = [], refs: [], branch: nil, range: [], index: [])
1362
1363
  cmd, opts = git_session('diff', opts: opts)
1363
- op = OptionPartition.new(opts, collect_hash(OPT_GIT[:diff]) + OPT_GIT[:log][:diff], cmd,
1364
+ op = OptionPartition.new(opts,
1365
+ collect_hash(OPT_GIT[:diff]) + OPT_GIT[:log][:diff] + OPT_GIT[:log][:diff_context],
1366
+ cmd,
1364
1367
  project: self, no: OPT_GIT[:no][:log][:diff],
1365
- first: flag == :files ? nil : matchpathspec)
1368
+ first: (matchpathspec unless flag == :files))
1366
1369
  case flag
1367
1370
  when :files, :view, :between, :contain
1368
1371
  op.delete('--cached')
@@ -1370,7 +1373,16 @@ module Squared
1370
1373
  append_nocolor
1371
1374
  if flag == :files
1372
1375
  op << '--no-index'
1376
+ patch = refs.pop
1373
1377
  append_pathspec(refs, parent: true)
1378
+ if patch
1379
+ patch = basepath patch
1380
+ exit 1 if patch.exist? && !confirm_basic('Overwrite?', patch)
1381
+ op << '>' << shell_quote(patch)
1382
+ source(banner: false)
1383
+ puts patch.read if patch.exist? && (stdin? || verbose?)
1384
+ return
1385
+ end
1374
1386
  else
1375
1387
  op << '--merge-base' if option('merge-base')
1376
1388
  case flag
@@ -1383,7 +1395,7 @@ module Squared
1383
1395
  op.add_quote(branch) if branch
1384
1396
  if !index.empty?
1385
1397
  if op.arg?('cached')
1386
- raise_error("one commit only: #{index.join(', ')}", hint: 'cached') if index.size > 1
1398
+ raise_error "single commit: #{index.join(', ')}", hint: 'cached' unless index.size == 1
1387
1399
  op << index.first
1388
1400
  else
1389
1401
  op.merge(index)
@@ -1399,7 +1411,7 @@ module Squared
1399
1411
 
1400
1412
  def commit(flag, opts = [], refs: [], ref: nil, squash: nil, pick: nil, message: nil, pass: false)
1401
1413
  fixup = flag == :fixup
1402
- amend = !fixup && flag.to_s.start_with?('amend')
1414
+ amend = flag.match?(/^amend/) && !fixup
1403
1415
  unless flag == :add || pick == 'reword'
1404
1416
  pathspec = if flag == :all || ((fixup || amend) && refs.size == 1 && refs.first == '*')
1405
1417
  '--all'
@@ -1410,9 +1422,9 @@ module Squared
1410
1422
  end
1411
1423
  end
1412
1424
  if fixup
1413
- source git_session('commit', basic_option('fixup', "#{pick ? "#{pick}:" : ''}#{ref}"), pathspec)
1414
- source git_output('rebase --autosquash', squash) if squash.is_a?(String)
1415
- return
1425
+ ret = source(git_session('commit', basic_option('fixup', pick ? "#{pick}:#{ref}" : ref), pathspec))
1426
+ source git_output('rebase --autosquash', squash) if ret && squash.is_a?(String)
1427
+ return ret
1416
1428
  end
1417
1429
  message ||= messageopt
1418
1430
  if !message && !amend
@@ -1424,7 +1436,8 @@ module Squared
1424
1436
  origin = nil
1425
1437
  upstream = nil
1426
1438
  cmd, opts = git_session('add', opts: opts)
1427
- op = OptionPartition.new(opts, OPT_GIT[:add], cmd, project: self, first: matchpathspec)
1439
+ op = OptionPartition.new(opts, OPT_GIT[:add] + OPT_GIT[:log][:diff_context], cmd,
1440
+ project: self, no: OPT_GIT[:no][:add], first: matchpathspec)
1428
1441
  op << '--verbose' if verbose
1429
1442
  format = '%(if)%(HEAD)%(then)%(refname:short)...%(upstream:short)...%(upstream:track)%(end)'
1430
1443
  git_spawn 'fetch --no-tags --quiet'
@@ -1433,10 +1446,9 @@ module Squared
1433
1446
 
1434
1447
  branch, origin, hint = line.split('...')
1435
1448
  if hint && !hint.match?(/^\[(\D+0,\D+0)\]$/)
1436
- raise_error('work tree is not usable', hint: hint[1..-2])
1437
- elsif !origin || origin.empty?
1449
+ raise_error 'work tree is not usable', hint: hint[1..-2]
1450
+ elsif (!origin || origin.empty?) && !dryrun?
1438
1451
  return nil if pass
1439
- break if dryrun?
1440
1452
 
1441
1453
  unless (origin = option('upstream', prefix: 'git', ignore: false))
1442
1454
  if (origin = choice_refs('Choose an upstream', 'remotes', attempts: 1, force: false))
@@ -1445,6 +1457,7 @@ module Squared
1445
1457
  end
1446
1458
  origin = readline('Enter an upstream', force: true)
1447
1459
  end
1460
+ raise_error ArgumentError, 'missing remote name', hint: origin unless origin.include?('/')
1448
1461
  upstream = true
1449
1462
  end
1450
1463
  break
@@ -1455,14 +1468,13 @@ module Squared
1455
1468
  append_pathspec op.extras
1456
1469
  end
1457
1470
  co = git_session('commit', options: false)
1458
- pu = git_output 'push'
1459
- co << '--amend' if amend
1460
- pu << '--set-upstream' if upstream
1471
+ pu = git_output 'push', upstream && '--set-upstream'
1461
1472
  if dryrun?
1462
- op.adjoin('--dry-run')
1473
+ op.delete('--dry-run')
1463
1474
  co << '--dry-run'
1464
1475
  pu << '--dry-run'
1465
1476
  end
1477
+ co << '--amend' if amend
1466
1478
  if message
1467
1479
  append_message message
1468
1480
  elsif flag == :'amend-orig' || option('edit', equals: '0')
@@ -1470,6 +1482,7 @@ module Squared
1470
1482
  end
1471
1483
  pu << '--force-with-lease' if amend
1472
1484
  pu.merge(repotrack(origin, branch))
1485
+ puts if pass
1473
1486
  adding = git_spawn 'diff --name-only --no-color'
1474
1487
  source op
1475
1488
  cached = git_spawn 'diff --cached --name-only --no-color'
@@ -1479,10 +1492,13 @@ module Squared
1479
1492
  end
1480
1493
  source co
1481
1494
  source pu
1482
- elsif banner?
1483
- puts 'Nothing to commit'
1484
- elsif stdout?
1485
- puts log_message(Logger::INFO, name, 'nothing to commit', hint: flag)
1495
+ else
1496
+ if banner?
1497
+ puts 'Nothing to commit'
1498
+ elsif stdout?
1499
+ puts log_message(Logger::INFO, name, 'nothing to commit', hint: flag)
1500
+ end
1501
+ exit 1
1486
1502
  end
1487
1503
  end
1488
1504
 
@@ -1492,26 +1508,25 @@ module Squared
1492
1508
  case flag
1493
1509
  when :commit, :'no-commit'
1494
1510
  op = OptionPartition.new(opts, OPT_GIT[:merge], cmd, project: self, no: OPT_GIT[:no][:merge])
1495
- op << "--#{flag}"
1496
- op.delim
1511
+ op << "--#{flag}" << '--'
1497
1512
  if branch
1498
1513
  op << branch
1499
1514
  op.clear(pass: false)
1500
1515
  else
1501
- raise_error 'no branch/commit' if op.empty?
1516
+ raise_error ArgumentError, 'no branch/commit' if op.empty?
1502
1517
  append_commit(*op.extras)
1503
1518
  end
1504
1519
  else
1505
1520
  unless gitpath('MERGE_HEAD').exist?
1506
1521
  puts log_message(Logger::INFO, name, 'no merge in progress', hint: command) if stdout?
1507
- return
1522
+ exit 1
1508
1523
  end
1509
1524
  return unless VAL_GIT[:merge][:send].include?(command)
1510
1525
 
1511
1526
  cmd << "--#{command}"
1512
1527
  display = command == 'abort'
1513
1528
  end
1514
- print_success if success?(source, display)
1529
+ success?(source, display)
1515
1530
  end
1516
1531
 
1517
1532
  def branch(flag = nil, opts = [], refs: [], ref: nil, target: nil, remote: nil)
@@ -1529,11 +1544,11 @@ module Squared
1529
1544
  '--track'
1530
1545
  end
1531
1546
  end
1532
- cmd << '--force' if option('force', 'f')
1547
+ cmd << '--force' if option('f', 'force')
1533
1548
  cmd << shell_quote(target)
1534
1549
  cmd << shell_quote(ref) if ref
1535
1550
  when :track
1536
- raise_error('invalid upstream', hint: ref) unless ref.include?('/')
1551
+ raise_error 'invalid upstream', hint: ref unless ref.include?('/')
1537
1552
  if ref.delete_prefix!('^')
1538
1553
  cmd << '--unset-upstream' << shell_quote(ref)
1539
1554
  remote = false
@@ -1544,7 +1559,7 @@ module Squared
1544
1559
  end
1545
1560
  when :delete
1546
1561
  remote&.each do |val|
1547
- source git_output('push --delete', *val.split('/', 2).map { |s| shell_quote(s) })
1562
+ source git_output('push --delete', *val.split('/', 2).map! { |s| shell_quote(s) })
1548
1563
  end
1549
1564
  force, list = refs.partition { |val| val.start_with?(/[~^]/) }
1550
1565
  force.each do |val|
@@ -1558,13 +1573,13 @@ module Squared
1558
1573
  remote = nil
1559
1574
  when :move, :copy
1560
1575
  s = +"-#{flag.to_s[0]}"
1561
- s.upcase! if option('force', 'f')
1576
+ s.upcase! if option('f', 'force')
1562
1577
  cmd << s
1563
1578
  refs.compact.each { |val| cmd << shell_quote(val) }
1564
1579
  stdout = true
1565
1580
  when :current
1566
1581
  cmd << '--show-current'
1567
- source(banner: verbosetype > 1, stdout: true)
1582
+ source(banner: verbose?, stdout: true)
1568
1583
  return
1569
1584
  when :list
1570
1585
  op = OptionPartition.new(opts, OPT_GIT[:branch], cmd << '--list',
@@ -1573,10 +1588,10 @@ module Squared
1573
1588
  out, banner, from = source(io: true)
1574
1589
  print_item banner
1575
1590
  ret = write_lines(out, sub: [
1576
- { pat: /^(\*\s+)(\S+)(.*)$/, styles: color(:green), index: 2 },
1577
- { pat: %r{^(\s*)(remotes/\S+)(.*)$}, styles: color(:red), index: 2 }
1591
+ opt_style(color(:green), /^(\*\s+)(\S+)(.*)$/, 2),
1592
+ opt_style(color(:red), %r{^(\s*)(remotes/\S+)(.*)$}, 2)
1578
1593
  ])
1579
- list_result(ret, 'branches', banner: banner, from: from)
1594
+ list_result(ret, 'branches', from: from)
1580
1595
  return
1581
1596
  else
1582
1597
  if (head = git_spawn('rev-parse --abbrev-ref HEAD').chomp).empty?
@@ -1599,7 +1614,7 @@ module Squared
1599
1614
  r3 = write.call($4, $5)
1600
1615
  end
1601
1616
  r1 = nil if r1 == "origin/#{data[0]}"
1602
- [" Branch: #{a + (r1 ? " (#{r1})" : '')}", r2, r3, " Commit: #{b}", "Message: #{r[2]}"]
1617
+ [" Branch: #{a.subhint(r1)}", r2, r3, " Commit: #{b}", "Message: #{r[2]}"]
1603
1618
  .compact
1604
1619
  .join("\n")
1605
1620
  end
@@ -1608,21 +1623,17 @@ module Squared
1608
1623
  print_error(name, 'no ref found', subject: 'branch', hint: 'head', pass: true) if ret == 0
1609
1624
  return
1610
1625
  end
1611
- return unless success?(source(stdout: stdout))
1626
+ return unless success?(source(stdout: stdout), !ref && flag == :create) && !ref && remote && target
1612
1627
 
1613
- if !ref
1614
- print_success if flag == :create
1615
- elsif remote && target
1616
- source git_output('push -u', shell_quote(ref.split('/', 2).first), shell_quote(target))
1617
- end
1628
+ source git_output('push -u', shell_quote(ref.split('/', 2).first), shell_quote(target))
1618
1629
  end
1619
1630
 
1620
1631
  def switch(flag, opts = [], branch: nil, commit: nil, track: nil)
1621
1632
  cmd, opts = git_session('switch', opts: opts)
1622
- cmd << '--force' if option('force', 'f')
1633
+ cmd << '--force' if option('f', 'force')
1623
1634
  if flag == :branch
1624
- op = OptionPartition.new(opts, OPT_GIT[:switch], cmd, project: self, no: OPT_GIT[:no][:switch])
1625
- op.add_quote(branch)
1635
+ OptionPartition.new(opts, OPT_GIT[:switch], cmd, project: self, no: OPT_GIT[:no][:switch])
1636
+ .add_quote(branch)
1626
1637
  else
1627
1638
  case flag
1628
1639
  when :create
@@ -1652,16 +1663,18 @@ module Squared
1652
1663
  op.add_quote(branch, '--', path, url)
1653
1664
  else
1654
1665
  op.adjoin(flag)
1655
- op << '--recursive' if option('recursive', 'r')
1666
+ op << '--recursive' if option('r', 'recursive')
1656
1667
  op.splice(path: true)
1657
1668
  end
1658
- print_success if success?(source, flag == :branch)
1669
+ source.tap { |ret| success?(ret, flag == :branch) }
1659
1670
  end
1660
1671
 
1661
1672
  def restore(flag, opts = [], commit: nil, files: nil)
1662
1673
  cmd, opts = git_session('restore', shell_option(flag, commit, escape: false, force: false), opts: opts)
1663
- op = OptionPartition.new(opts, OPT_GIT[:restore], cmd, project: self, no: OPT_GIT[:no][:restore],
1664
- first: matchpathspec)
1674
+ op = OptionPartition.new(opts,
1675
+ OPT_GIT[:restore] + OPT_GIT[:log][:diff_context],
1676
+ cmd,
1677
+ project: self, no: OPT_GIT[:no][:restore], first: matchpathspec)
1665
1678
  append_pathspec(op.extras + (files || []), pass: false)
1666
1679
  source(sync: false, stderr: true)
1667
1680
  end
@@ -1687,9 +1700,9 @@ module Squared
1687
1700
  else
1688
1701
  opts << format if format
1689
1702
  end
1690
- op = OptionPartition.new(opts, OPT_GIT[:show] + OPT_GIT[:diff][:show] + OPT_GIT[:log][:diff], cmd,
1691
- project: self,
1692
- no: OPT_GIT[:no][:show] + collect_hash(OPT_GIT[:no][:log], pass: [:base]))
1703
+ list = OPT_GIT[:show] + OPT_GIT[:diff][:show] + OPT_GIT[:log][:diff] + OPT_GIT[:log][:diff_context]
1704
+ op = OptionPartition.new(opts, list, cmd, project: self, pass: [:base],
1705
+ no: OPT_GIT[:no][:show] + collect_hash(OPT_GIT[:no][:log]))
1693
1706
  op.append(delim: true)
1694
1707
  source(exception: false, banner: flag != :oneline)
1695
1708
  end
@@ -1698,7 +1711,7 @@ module Squared
1698
1711
  cmd, opts = git_session('rev-parse', opts: opts)
1699
1712
  case flag
1700
1713
  when :commit
1701
- cmd << (size.to_i.zero? ? '--verify' : basic_option('short', [size.to_i, 5].max))
1714
+ cmd << ((n = size.to_i) > 0 ? basic_option('short', [n, 5].max) : '--verify')
1702
1715
  append_commit(ref, head: true)
1703
1716
  when :branch
1704
1717
  cmd << '--abbrev-ref'
@@ -1708,85 +1721,100 @@ module Squared
1708
1721
  cmd << '--sq-quote'
1709
1722
  args = true
1710
1723
  end
1711
- op = OptionPartition.new(opts, OPT_GIT[:rev_parse], cmd, project: self, no: OPT_GIT[:no][:rev_parse],
1712
- args: args)
1713
- op.append(escape: args)
1724
+ OptionPartition.new(opts, OPT_GIT[:rev_parse], cmd, project: self, no: OPT_GIT[:no][:rev_parse], args: args)
1725
+ .append(escape: args)
1714
1726
  end
1715
- source(banner: verbosetype > 1)
1727
+ source(banner: verbose?)
1716
1728
  end
1717
1729
 
1718
1730
  def ls_remote(flag, opts = [], remote: nil)
1719
- cmd, opts = git_session('ls-remote', '--refs', opts: opts)
1731
+ cmd, opts = git_session('ls-remote --refs', opts: opts)
1720
1732
  cmd << "--#{flag}" unless flag == :remote
1721
1733
  op = OptionPartition.new(opts, OPT_GIT[:ls_remote], cmd, project: self)
1722
1734
  op.add_quote(remote) if remote
1723
1735
  out, banner, from = source(io: true)
1724
1736
  print_item banner
1725
- ret = write_lines(out, grep: op.extras)
1726
- list_result(ret, flag.to_s, grep: op.extras, banner: banner, from: from)
1737
+ ret = write_lines(out, grep: op.extras, prefix: "refs/#{flag}/")
1738
+ list_result(ret, flag.to_s, from: from, grep: op.extras)
1727
1739
  end
1728
1740
 
1729
1741
  def ls_files(flag, opts = [])
1730
- cmd, opts = git_session('ls-files', "--#{flag}", opts: opts)
1742
+ cmd, opts = git_session("ls-files --#{flag}", opts: opts)
1731
1743
  op = OptionPartition.new(opts, OPT_GIT[:ls_files], cmd, project: self)
1732
1744
  op.splice(path: true, pattern: true)
1733
1745
  out, banner, from = source(io: true)
1734
1746
  print_item banner
1735
1747
  ret = write_lines(out, grep: op.extras)
1736
- list_result(ret, 'files', grep: op.extras, banner: banner, from: from)
1748
+ list_result(ret, 'files', from: from, grep: op.extras)
1737
1749
  end
1738
1750
 
1739
1751
  def git(flag, opts = [])
1740
1752
  cmd, opts = git_session(flag, opts: opts)
1741
- op = OptionPartition.new(opts, OPT_GIT[:git].fetch(flag, []) + OPT_GIT.fetch(flag, []), cmd,
1742
- project: self, no: OPT_GIT[:no][flag], first: case flag
1743
- when :blame, :revert
1744
- nil
1745
- else matchpathspec
1746
- end)
1753
+ list = OPT_GIT[:git].fetch(flag, []) + OPT_GIT.fetch(flag, [])
1754
+ list.concat(OPT_GIT[:log][:diff_context]) if flag == :add
1755
+ op = OptionPartition.new(opts, list, cmd, project: self, no: OPT_GIT[:no][flag], single: /\A\d+\z/,
1756
+ first: case flag
1757
+ when :blame, :revert then nil
1758
+ else matchpathspec
1759
+ end)
1747
1760
  case flag
1748
1761
  when :blame
1749
- raise_error 'no file found' unless (n = op.index { |s| basepath(s).file? })
1750
- op.delim << shell_quote(basepath(op.delete_at(n)))
1751
- op.clear
1762
+ raise_error Errno::ENOENT, 'no file target' unless (n = op.index { |s| basepath(s).file? })
1763
+ op.append(basepath(op.remove_at(n)), delim: true)
1764
+ .clear
1752
1765
  when :revert
1753
1766
  if VAL_GIT[:rebase][:send].any? { |val| op.arg?(val) }
1754
1767
  op.clear
1755
1768
  elsif op.empty?
1756
- raise_error 'no commit given'
1769
+ raise_error 'no commit target'
1757
1770
  else
1758
1771
  append_commit(*op.extras)
1759
1772
  end
1760
1773
  when :add
1761
1774
  if flag == :add && !op.arg?('pathspec-from-file')
1762
- grep, list = op.partition { |val| OptionPartition.pattern?(val) }
1763
- unless grep.empty? && !list.empty?
1775
+ grep, pathspec = op.partition { |val| OptionPartition.pattern?(val) }
1776
+ unless grep.empty? && !pathspec.empty?
1764
1777
  grep.map! { |val| Regexp.new(val[1..-2]) }
1765
- files = []
1766
- status_data.each do |a, b|
1767
- next if b.strip.empty? || (!grep.empty? && grep.none? { |pat| pat.match?(a) })
1778
+ files = [].tap do |out|
1779
+ status_data.each do |a, b|
1780
+ next if b.strip.empty? || (!grep.empty? && grep.none? { |pat| pat.match?(a) })
1768
1781
 
1769
- files << "#{sub_style(b, styles: color(:red))} #{a}"
1782
+ out << "#{sub_style(b, styles: color(:red))} #{a}"
1783
+ end
1770
1784
  end
1771
1785
  unless files.empty?
1772
- files = choice_index('Select files', files, multiple: true, trim: /^\S+\s/,
1773
- accept: [['Add?', false, true]])
1786
+ files = choice_index('Select files', files, multiple: true, force: true, trim: /^\S+\s/,
1787
+ accept: [accept_y('Add?')])
1774
1788
  end
1775
- op.swap(list + files)
1789
+ op.swap(pathspec + files)
1776
1790
  end
1777
1791
  end
1778
1792
  return source(git_session('status -s'), banner: false) unless append_pathspec(op.extras)
1779
-
1780
- print_success if success?(source, flag == :add && !op.arg?('verbose'))
1781
- return
1793
+ return success?(source) if flag == :add && !op.arg?('verbose')
1782
1794
  when :mv
1783
1795
  refs = projectmap op.extras
1784
1796
  raise_error 'no source/destination' unless refs.size > 1
1785
1797
  op.merge(refs)
1786
1798
  when :rm, :clean
1787
1799
  append_pathspec(op.extras, expect: flag == :rm)
1800
+ when :grep
1801
+ op.each do |val|
1802
+ if op.include?('--')
1803
+ op.add_path(val)
1804
+ elsif op.exist?(val, glob: true)
1805
+ op << '--'
1806
+ op.add_path(val)
1807
+ else
1808
+ op.add_quote(val)
1809
+ end
1810
+ end
1811
+ end
1812
+ case flag
1813
+ when :revert, :mv, :rm
1814
+ source(sync: false, stderr: true)
1815
+ else
1816
+ source
1788
1817
  end
1789
- source(sync: false, stderr: true)
1790
1818
  end
1791
1819
 
1792
1820
  def clone?
@@ -1805,10 +1833,6 @@ module Squared
1805
1833
 
1806
1834
  def source(cmd = @session, exception: true, io: false, sync: true, stdout: false, stderr: false, banner: true,
1807
1835
  multiple: false, hint: nil, from: nil, send: :system, **kwargs)
1808
- unless cmd
1809
- print_error('no git session started', subject: project, hint: from, pass: true)
1810
- return
1811
- end
1812
1836
  cmd = cmd.target if cmd.is_a?(OptionPartition)
1813
1837
  if io && banner == false
1814
1838
  from = nil
@@ -1818,20 +1842,15 @@ module Squared
1818
1842
  banner = nil unless banner? && !multiple
1819
1843
  args = true
1820
1844
  end
1821
- if cmd.respond_to?(:done)
1822
- if from.nil? && (from = cmd.drop(1).find { |val| val.match?(/\A[a-z]{1,2}[a-z-]*\z/) })
1823
- from = :"git:#{from}"
1824
- end
1825
- banner &&= cmd.temp { |val| val.start_with?(/--(?:work-tree|git-dir)/) }
1845
+ if from.nil? && (from = cmd.drop(1).find { |val| val.match?(/\A[a-z]{1,2}[a-z-]*\z/) })
1846
+ from = :"git:#{from}"
1826
1847
  end
1848
+ banner &&= cmd.temp { |val| val.start_with?(/--(?:work-tree|git-dir)/) } if cmd.respond_to?(:temp)
1827
1849
  from = nil if from == false
1828
1850
  end
1829
1851
  cmd = session_done cmd
1830
1852
  log&.info cmd
1831
- banner = if banner
1832
- banner = (banner.is_a?(String) ? banner : cmd).gsub(File.join(path, ''), '')
1833
- format_banner(hint ? "#{banner} (#{hint})" : banner)
1834
- end
1853
+ banner = format_banner(banner.is_a?(String) ? banner : cmd, hint: hint, strip: true) if banner
1835
1854
  on :first, from
1836
1855
  begin
1837
1856
  if io
@@ -1840,11 +1859,13 @@ module Squared
1840
1859
  return args ? [IO.popen(cmd), banner || '', from] : IO.popen(cmd)
1841
1860
  elsif stdin? ? sync : stdout
1842
1861
  print_item banner unless multiple
1843
- ret = `#{cmd}`.chomp
1844
- if !ret.empty?
1862
+ ret = `#{cmd}`
1863
+ raise(ret.chomp.empty? ? $?.to_s : ret) unless $?.success?
1864
+
1865
+ if ret.empty?
1866
+ success?(nil, !banner.nil?)
1867
+ else
1845
1868
  puts ret
1846
- elsif success?(!banner.nil?)
1847
- print_success
1848
1869
  end
1849
1870
  elsif !kwargs[:sub] && (sync || (!exception && !stderr))
1850
1871
  print_item banner unless multiple
@@ -1856,7 +1877,7 @@ module Squared
1856
1877
  n = write_lines(out, banner: banner, pass: true, **kwargs)
1857
1878
  if n == 0
1858
1879
  n = write_lines(err, banner: banner)
1859
- print_success if success?(n == 0 && !banner.nil?)
1880
+ success?(nil, n == 0 && !banner.nil?)
1860
1881
  else
1861
1882
  write_lines(err, loglevel: Logger::DEBUG)
1862
1883
  end
@@ -1875,17 +1896,14 @@ module Squared
1875
1896
  end
1876
1897
 
1877
1898
  def write_lines(data, grep: [], prefix: nil, sub: nil, banner: nil, loglevel: nil, pass: false, first: false)
1878
- grep = grep.empty? ? nil : matchmap(grep, prefix)
1879
- sub = nil if stdin?
1899
+ grep = (matchmap(grep, prefix) unless grep.empty?)
1900
+ sub = (as_a sub unless stdin?)
1880
1901
  ret = 0
1881
1902
  out = []
1882
1903
  data.each do |line|
1883
1904
  next if grep&.none? { |pat| pat.match?(line) }
1905
+ next if block_given? && !(line = yield line)
1884
1906
 
1885
- if block_given?
1886
- line = yield line
1887
- next unless line
1888
- end
1889
1907
  if loglevel
1890
1908
  log&.add loglevel, line
1891
1909
  else
@@ -1899,24 +1917,18 @@ module Squared
1899
1917
  ret += 1
1900
1918
  break if first
1901
1919
  end
1902
- if banner && (ret > 0 || (!pass && !first))
1903
- if banner.empty?
1904
- print_item(*out)
1905
- else
1906
- print_item(banner, *out)
1907
- end
1908
- end
1920
+ print_item banner, out if banner && (ret > 0 || (!pass && !first))
1909
1921
  ret
1910
1922
  end
1911
1923
 
1912
- def list_result(size, type, action: 'found', grep: [], banner: nil, from: nil)
1924
+ def list_result(size, type, grep: [], action: 'found', from: nil)
1913
1925
  if size == 0
1914
1926
  puts empty_status("No #{type} were #{action}", 'grep', grep.join(', '))
1915
- elsif stdout? && (banner.nil? || (banner.is_a?(String) && !banner.empty?))
1927
+ elsif stdout?
1916
1928
  styles = theme.fetch(:banner, []).reject { |s| s.to_s.end_with?('!') }
1917
1929
  styles << :bold if styles.size <= 1
1918
1930
  puts print_footer("#{size} #{size == 1 ? type.sub(/(?:(?<!l)e)?s\z/, '') : type}",
1919
- sub: [{ pat: /^(\d+)(.+)$/, styles: styles }])
1931
+ sub: opt_style(styles, /^(\d+)(.+)$/))
1920
1932
  end
1921
1933
  on :last, from
1922
1934
  end
@@ -1932,7 +1944,7 @@ module Squared
1932
1944
  end
1933
1945
  args = []
1934
1946
  args << quote_option('sort', sort) if sort
1935
- args << basic_option('count', env('GIT_COUNT', ARG[:CHOICE])) if count
1947
+ args << shell_option('count', env('GIT_COUNT', ARG[:CHOICE])) if count
1936
1948
  choice_index(msg, foreachref(type, *args, format: format), trim: trim, **kwargs)
1937
1949
  end
1938
1950
 
@@ -1965,17 +1977,17 @@ module Squared
1965
1977
  end
1966
1978
 
1967
1979
  def status_data(*args)
1968
- ret = []
1969
- git_spawn('status -z -uall', *args).split("\x0").each do |line|
1970
- next unless line =~ /^(.)(.) (.+)$/
1980
+ [].tap do |ret|
1981
+ git_spawn('status -z -uall', *args).split("\x0").each do |line|
1982
+ next unless line =~ /^(.)(.) (.+)$/
1971
1983
 
1972
- ret << [$3, $2, $1]
1984
+ ret << [$3, $2, $1]
1985
+ end
1973
1986
  end
1974
- ret
1975
1987
  end
1976
1988
 
1977
1989
  def append_pull(opts, list, target: @session, flag: nil, no: nil, remote: nil, from: nil)
1978
- target << '--force' if option('force', 'f', target: target)
1990
+ target << '--force' if option('f', 'force', target: target)
1979
1991
  append_submodules(target: target, from: from)
1980
1992
  return if !remote && opts.empty?
1981
1993
 
@@ -1987,6 +1999,7 @@ module Squared
1987
1999
  when 'rebase'
1988
2000
  op << basic_option($1, $2) if VAL_GIT[:rebase][:value].include?($2)
1989
2001
  when 'shallow-since'
2002
+ require 'date'
1990
2003
  op.append?($1) { Date.parse($2).strftime('%F %T') }
1991
2004
  when 'recurse-submodules'
1992
2005
  op.append?($1, $2, type: :basic)
@@ -2001,7 +2014,7 @@ module Squared
2001
2014
  end
2002
2015
  op << '--verbose' if (flag || from == :fetch) && stdout? && !op.arg?('quiet')
2003
2016
  if remote
2004
- op.append(remote)
2017
+ op.append(remote, delim: true)
2005
2018
  if (val = option('refspec', target: target, strict: true))
2006
2019
  op.append(*split_escape(val))
2007
2020
  else
@@ -2046,9 +2059,7 @@ module Squared
2046
2059
 
2047
2060
  def append_message(val = nil, target: @session)
2048
2061
  val = messageopt if val.to_s.empty?
2049
- return unless val
2050
-
2051
- target << quote_option('message', val)
2062
+ target << quote_option('message', val) if val
2052
2063
  end
2053
2064
 
2054
2065
  def append_head(val = nil, target: @session)
@@ -2066,9 +2077,7 @@ module Squared
2066
2077
  when '1', 'true'
2067
2078
  target << '--recurse-submodules'
2068
2079
  else
2069
- projectmap(split_escape(val)).each do |path|
2070
- target << basic_option('recurse-submodules', path)
2071
- end
2080
+ projectmap(split_escape(val)).each { |path| target << basic_option('recurse-submodules', path) }
2072
2081
  end
2073
2082
  else
2074
2083
  target << case val
@@ -2115,28 +2124,24 @@ module Squared
2115
2124
  stdout: stdout, banner: banner, **kwargs)
2116
2125
  end
2117
2126
 
2118
- def dryrun?(*, target: @session, **)
2119
- return false unless target
2120
-
2121
- target.include?('--dry-run') || !option('dry-run', target: target).nil?
2127
+ def dryrun?(*, target: @session, prefix: target&.first)
2128
+ Array(target).include?('--dry-run') || !option('dry-run', target: target, prefix: prefix).nil?
2122
2129
  end
2123
2130
 
2124
2131
  def quiet?(*, target: @session, **)
2125
2132
  return false unless target
2126
2133
 
2127
- target.include?('--quiet') || (target.include?('-q') && stripext(target.first) == 'git')
2134
+ target.include?('--quiet') || (target.include?('-q') && target.first.stripext == 'git')
2128
2135
  end
2129
2136
 
2130
2137
  def gitpath(*args)
2131
- path.join('.git', *args)
2138
+ basepath('.git', *args)
2132
2139
  end
2133
2140
 
2134
2141
  def repotrack(origin, branch, quote: true)
2135
- unless origin && branch && (i = origin.index('/'))
2136
- raise_error(ArgumentError, "missing #{origin ? 'branch' : 'remote'} name", hint: origin)
2137
- end
2138
- branch = "#{branch}:#{origin[(i + 1)..-1]}" unless origin.end_with?("/#{branch}")
2139
- [origin[0..(i - 1)], branch].tap { |ret| ret.map! { |val| shell_quote(val) } if quote }
2142
+ i = origin.index('/')
2143
+ branch = "#{branch}:#{origin[i.succ..-1]}" unless origin.end_with?("/#{branch}")
2144
+ [origin[0..i.pred], branch].tap { |ret| ret.map! { |val| shell_quote(val) } if quote }
2140
2145
  end
2141
2146
 
2142
2147
  def commithash(val)