squared 0.7.8 → 0.8.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.
@@ -61,7 +61,8 @@ module Squared
61
61
  stop: %w[s|signal=b t|timeout=i].freeze,
62
62
  restart: %w[s|signal=b t|timeout=i].freeze,
63
63
  kill: %w[s|signal=b].freeze,
64
- stats: %w[a|all no-stream no-trunc format|q].freeze
64
+ stats: %w[a|all no-stream no-trunc format|q].freeze,
65
+ attach: %w[no-stdin detach-keys=q sig-proxy=!?].freeze
65
66
  }.freeze,
66
67
  image: {
67
68
  ls: %w[a|all digests no-trunc q|quiet tree f|filter=q format=q].freeze,
@@ -114,8 +115,8 @@ module Squared
114
115
  'compose' => %i[build create publish run exec up down service].freeze,
115
116
  'bake' => %i[build compose check].freeze,
116
117
  'image' => %i[ls rm pull push tag save].freeze,
117
- 'container' => %i[run create exec update commit inspect diff start stop restart pause unpause top stats kill
118
- rm].freeze,
118
+ 'container' => %i[attach commit create diff exec inspect kill pause restart rm run start stats stop top
119
+ unpause update].freeze,
119
120
  'network' => %i[connect disconnect create].freeze,
120
121
  'ls' => nil
121
122
  })
@@ -165,7 +166,7 @@ module Squared
165
166
  when 'compose'
166
167
  'ps'
167
168
  else
168
- raise_error ArgumentError, 'unrecognized command', hint: command
169
+ raise ArgumentError, message('unrecognized command', hint: command)
169
170
  end
170
171
  cmd << '-a' if has_value!(args, 'a', 'all') && command != 'network'
171
172
  data = VAL_DOCKER[:ls][command.to_sym]
@@ -226,19 +227,20 @@ module Squared
226
227
  format_desc action, flag, "service/:,command#{'?' unless flag == :exec}/::,args*,opts*"
227
228
  task flag, [:service] do |_, args|
228
229
  service = param_guard(action, flag, args: args, key: :service)
229
- compose!(flag, args.extras, service: service)
230
+ compose_(flag, args.extras, service: service)
230
231
  end
231
232
  when :service
232
233
  cmds = %w[down kill pause restart rm start stop top unpause watch].freeze
233
234
  format_desc(action, flag, cmds, arg: nil, after: 'name+|:')
234
235
  task flag, [:command] do |_, args|
235
- command = param_guard(action, flag, args: args, key: :command)
236
- raise_error ArgumentError, 'unrecognized command', hint: command unless cmds.include?(command)
236
+ cmd = param_guard(action, flag, args: args, key: :command)
237
+ raise ArgumentError, message('unrecognized command', hint: cmd) unless cmds.include?(cmd)
238
+
237
239
  service = args.extras
238
240
  if service.first == ':'
239
- choice_command flag, command
241
+ choice_command flag, cmd
240
242
  else
241
- compose!(flag, [command], service: service.empty? || service)
243
+ compose_(flag, [cmd], service: service.empty? || service)
242
244
  end
243
245
  end
244
246
  when :publish
@@ -246,12 +248,12 @@ module Squared
246
248
 
247
249
  format_desc action, flag, 'tag?,repository?,opts*'
248
250
  task flag, [:tag] do |_, args|
249
- compose! flag, args.to_a
251
+ compose_ flag, args.to_a
250
252
  end
251
253
  else
252
254
  format_desc action, flag, 'opts*,service*|:'
253
255
  task flag do |_, args|
254
- compose!(flag, args.to_a, multiple: true)
256
+ compose_(flag, args.to_a, multiple: true)
255
257
  end
256
258
  end
257
259
  when 'container'
@@ -434,7 +436,7 @@ module Squared
434
436
  run(from: from || symjoin('buildx', flag))
435
437
  end
436
438
 
437
- def compose!(flag, opts = [], id: nil, service: nil, multiple: false)
439
+ def compose_(flag, opts = [], id: nil, service: nil, multiple: false)
438
440
  from = symjoin 'compose', flag
439
441
  if flag == :service
440
442
  command = opts.first
@@ -471,14 +473,14 @@ module Squared
471
473
  op.concat(service) if service
472
474
  op.append(delim: true, escape: true, strip: /^:/)
473
475
  else
474
- raise_error ArgumentError, 'no service was selected', hint: flag unless service
476
+ raise ArgumentError, message('no service was selected', hint: flag) unless service
477
+
475
478
  append_command(flag, service, op.extras, prompt: '::')
476
479
  end
477
480
  end
478
481
  end
479
482
  run(from: from)
480
483
  end
481
- alias compose_ compose!
482
484
 
483
485
  def container(flag, opts = [], id: nil)
484
486
  cmd, opts = docker_session('container', flag, opts: opts)
@@ -502,7 +504,7 @@ module Squared
502
504
  when 'bind', 'volume', 'image', 'tmpfs'
503
505
  type = v
504
506
  else
505
- raise_error TypeError, "unknown: #{v || "''"}", hint: flag
507
+ raise TypeError, message('unknown', v || "''", hint: flag)
506
508
  end
507
509
  elsif all.include?(k)
508
510
  unless type
@@ -518,7 +520,8 @@ module Squared
518
520
  out << k
519
521
  next
520
522
  when 'source', 'src', 'destination', 'dst', 'target', 'volume-subpath', 'image-path'
521
- raise_error ArgumentError, "#{k}: no path value", hint: flag unless v
523
+ raise ArgumentError, message('no path value', k, hint: flag) unless v
524
+
522
525
  v = basepath v
523
526
  v = shell_quote(v, option: false, force: false) if q == ''
524
527
  end
@@ -527,18 +530,20 @@ module Squared
527
530
  log_message('unrecognized option', subject: from, hint: k)
528
531
  end
529
532
  end
530
- raise_error TypeError, 'none specified', hint: flag unless type
533
+ raise TypeError, message('none specified', hint: flag) unless type
534
+
531
535
  cmd << "--mount type=#{type},#{args.join(',')}"
532
536
  end
533
537
  end
534
538
  append_command(flag, id || tagmain, op.extras)
535
539
  when :update
536
- raise_error ArgumentError, 'missing container', hint: flag if op.empty?
540
+ raise ArgumentError, message('missing container', hint: flag) if op.empty?
541
+
537
542
  op.append(escape: true, strip: /^:/)
538
543
  when :commit
539
544
  latest = op.shift || tagmain
540
545
  cmd << id << latest
541
- raise_error ArgumentError, "unrecognized args: #{op.join(', ')}", hint: flag unless op.empty?
546
+ raise ArgumentError, message('unrecognized args', op.join(', '), hint: flag) unless op.empty?
542
547
  return unless confirm_command(cmd.to_s, title: from, target: id, as: latest)
543
548
 
544
549
  registry = option('registry') || @registry
@@ -559,7 +564,9 @@ module Squared
559
564
  if op.empty?
560
565
  ps, status, no = filter_ps flag, from
561
566
  cmd << '--no-stream' if flag == :stats
562
- list_image(flag, ps, no: no, hint: status, from: from) { |img| run(cmd.temp(img), from: from) }
567
+ list_image(flag, ps, no: no, hint: status, multiple: flag != :attach, from: from) do |img|
568
+ run(cmd.temp(img), from: from)
569
+ end
563
570
  return
564
571
  end
565
572
  op.append(escape: true, strip: /^:/)
@@ -620,7 +627,7 @@ module Squared
620
627
  break
621
628
  end
622
629
  end
623
- raise_error ArgumentError, 'target not specified', hint: flag unless found
630
+ raise ArgumentError, message('target not specified', hint: flag) unless found
624
631
  when :pull
625
632
  if !id
626
633
  id = tagmain
@@ -636,7 +643,8 @@ module Squared
636
643
  id ||= option('tag', ignore: false) || op.shift || tagmain
637
644
  registry ||= option('registry') || op.shift || @registry
638
645
  emptyargs op, flag
639
- raise_error ArgumentError, 'username/registry not specified', hint: flag unless registry
646
+ raise ArgumentError, message('username/registry not specified', hint: flag) unless registry
647
+
640
648
  uri = shell_quote tagjoin(registry, id)
641
649
  op << uri
642
650
  img = docker_output 'image', 'tag', id, uri
@@ -739,7 +747,7 @@ module Squared
739
747
  target << basic_option('name', dnsname("#{name}_%s" % rand_s(6)))
740
748
  end
741
749
  when :exec
742
- raise_error ArgumentError, 'nothing to execute', hint: flag if list.empty?
750
+ raise ArgumentError, message('nothing to execute', hint: flag) if list.empty?
743
751
  end
744
752
  target << val << list.shift
745
753
  target << list.join(' && ') unless list.empty?
@@ -793,7 +801,7 @@ module Squared
793
801
  %w[running paused exited]
794
802
  when :unpause
795
803
  %w[paused]
796
- when :top, :stats, :watch
804
+ when :top, :stats, :watch, :attach
797
805
  %w[running]
798
806
  when :kill
799
807
  no = true
@@ -809,7 +817,8 @@ module Squared
809
817
  [cmd, status, no]
810
818
  end
811
819
 
812
- def list_image(flag, cmd = docker_output('image ls -a'), filter: nil, hint: nil, no: true, from: nil)
820
+ def list_image(flag, cmd = docker_output('image ls -a'), filter: nil, hint: nil, no: true, multiple: true,
821
+ from: nil)
813
822
  pwd_set(from: from) do
814
823
  index = 1
815
824
  all = option('all', prefix: 'docker')
@@ -820,8 +829,8 @@ module Squared
820
829
  elsif filter.match?(/[:_-]$/)
821
830
  /\b#{Regexp.escape(filter)}/
822
831
  else
823
- filter = filter.empty? ? '(?:[:_-]|$)' : "[:_-]#{filter}"
824
- /\b(?:#{dnsname(name)}|#{tagname(project)}|#{tagmain.split(':', 2).first})#{filter}/
832
+ filter = filter.empty? ? '([:_-]|$)' : "[:_-]#{filter}"
833
+ /\b(#{dnsname(name)}|#{tagname(project)}|#{tagmain.split(':', 2).first})#{filter}/
825
834
  end
826
835
  IO.popen(cmd.temp('--format=json')).each do |line|
827
836
  data = JSON.parse(line)
@@ -868,6 +877,7 @@ module Squared
868
877
  puts if printfirst?
869
878
  end
870
879
  yield id
880
+ break unless multiple
871
881
  end
872
882
  list_empty(hint: hint || from) if index == 1 && !y
873
883
  end
@@ -1001,7 +1011,7 @@ module Squared
1001
1011
  end
1002
1012
 
1003
1013
  def emptyargs(list, hint = nil)
1004
- raise_error ArgumentError, "unrecognized args: #{list.join(', ')}", hint: hint unless list.empty?
1014
+ raise ArgumentError, message('unrecognized args', list.join(', '), hint: hint) unless list.empty?
1005
1015
  end
1006
1016
 
1007
1017
  def anypath?(*args)
@@ -2,10 +2,12 @@
2
2
 
3
3
  module Squared
4
4
  module Workspace
5
+ GIT_PROTO = %r{\A(https?|ssh|git|file)://}i.freeze
6
+ private_constant :GIT_PROTO
7
+
5
8
  module Git
6
9
  GIT_REPO = Support.hashobj
7
- GIT_PROTO = %r{\A(https?|ssh|git|file)://}i.freeze
8
- private_constant :GIT_REPO, :GIT_PROTO
10
+ private_constant :GIT_REPO
9
11
 
10
12
  attr_reader :revfile
11
13
 
@@ -187,6 +189,7 @@ module Squared
187
189
  A|after-context=i B|before-context=i color=b C|context=i m|max-count=n max-depth=n
188
190
  open-files-in-pager=b threads=n].freeze,
189
191
  mv: %w[k f|force n|dry-run v|verbose].freeze,
192
+ remote: %w[f m=b t=b no-tags tags mirror=b].freeze,
190
193
  revert: %w[e S=bm? n|no-commit reference cleanup=b gpg-sign=b? m|mainline=i s|signoff strategy=b
191
194
  X|strategy-option=b].freeze,
192
195
  rm: %w[r cached f|force n|dry-run ignore-unmatch pathspec-file-nul q|quiet sparse
@@ -233,6 +236,16 @@ module Squared
233
236
  f|force-rebase ignore-date ignore-whitespace i|interactive keep-base m|merge no-ff q|quiet quit
234
237
  reset-author-date root show-current-patch signoff v|verbose empty=b x|exec=q gpg-sign=b? onto=b
235
238
  r|rebase-merges=b s|strategy=b X|strategy-option=b trailer=q whitespace=b].freeze,
239
+ remote: {
240
+ rename: %w[no-progress progress].freeze,
241
+ remove: [].freeze,
242
+ 'set-head': %w[a|auto d|delete].freeze,
243
+ 'set-branches': %w[add].freeze,
244
+ 'get-url': %w[all push].freeze,
245
+ 'set-url': %w[add delete push].freeze,
246
+ show: %w[n].freeze,
247
+ prune: %w[n dry-run].freeze
248
+ }.freeze,
236
249
  reset: %w[N pathspec-file-nul q|quiet pathspec-from-file=p].freeze,
237
250
  restore: %w[ignore-skip-worktree-bits ignore-unmerged m|merge ours p|patch pathspec-file-nul q|quiet S|staged
238
251
  theirs W|worktree conflict=b pathspec-from-file=p s|source=b].freeze,
@@ -345,12 +358,13 @@ module Squared
345
358
  'pull' => %i[origin remote all].freeze,
346
359
  'rebase' => %i[branch onto send].freeze,
347
360
  'refs' => %i[heads tags remote].freeze,
361
+ 'remote' => %i[add select].freeze,
348
362
  'reset' => %i[commit index patch mode undo].freeze,
349
363
  'restore' => %i[source staged worktree].freeze,
350
364
  'rev' => %i[commit branch build output].freeze,
351
365
  'show' => %i[format oneline textconv].freeze,
352
366
  'sparse-checkout' => %i[add reapply list clean disable].freeze,
353
- 'stash' => %i[push pop apply branch drop clear list all staged worktree].freeze,
367
+ 'stash' => %i[push pop apply drop command].freeze,
354
368
  'submodule' => %i[status update branch url sync].freeze,
355
369
  'switch' => %i[branch create detach].freeze,
356
370
  'tag' => %i[add sign delete list].freeze
@@ -481,31 +495,37 @@ module Squared
481
495
  when :add, :sign
482
496
  format_desc action, flag, 'name,message?,commit?,remote?'
483
497
  task flag, [:name, :message, :commit, :remote] do |_, args|
484
- remote = if (name = args.name)
485
- message = args.message
486
- commit = commithead args.commit
487
- args.remote
488
- else
489
- commit, name, message = choice_commit(reflog: false, series: true,
490
- values: [
491
- ['Enter tag name', true],
492
- 'Enter message'
493
- ])
494
- choice_remote
495
- end
498
+ if (name = args.name)
499
+ message = args.message
500
+ commit = commithead args.commit
501
+ remote = args.remote
502
+ if !remote && commit && !commit.start_with?('@') && !git_grep('remote', commit).empty?
503
+ remote = commit
504
+ commit = nil
505
+ end
506
+ else
507
+ commit, name, message = choice_commit(reflog: false, series: true,
508
+ values: [['Enter tag name', true], 'Enter message'])
509
+ remote = choice_remote
510
+ end
496
511
  ret = tag(flag, refs: [name], message: message, commit: commit, remote: remote)
497
512
  success?(ret, !remote)
498
513
  end
499
514
  end
500
515
  when 'stash'
501
- format_desc(action, flag, 'opts*', after: case flag
516
+ format_desc(action, flag, 'opts*', before: ('clear|list|all|staged|worktree' if flag == :command),
517
+ after: case flag
502
518
  when :push then 'pathspec*,:'
503
- when :branch then 'name,stash/:'
504
- when :clear, :list, :all then nil
519
+ when :command then nil
505
520
  else 'stash?|:'
506
521
  end)
507
522
  task flag do |_, args|
508
- stash flag, args.to_a
523
+ args = args.to_a
524
+ if flag == :command
525
+ param_guard(action, flag, args: args)
526
+ flag = args.shift.to_sym
527
+ end
528
+ stash flag, args
509
529
  end
510
530
  when 'log', 'diff', 'range-diff'
511
531
  case flag
@@ -531,7 +551,7 @@ module Squared
531
551
  end
532
552
  args = args.drop(index.size)
533
553
  end
534
- log!(flag, args, index: index)
554
+ log_(flag, args, index: index)
535
555
  end
536
556
  next
537
557
  end
@@ -549,7 +569,7 @@ module Squared
549
569
  opts.concat(refs.shellsplit) if refs
550
570
  end
551
571
  if action == 'log'
552
- log!(flag, opts, range: range)
572
+ log_(flag, opts, range: range)
553
573
  else
554
574
  diff(flag, opts, range: range, from: action.to_sym)
555
575
  end
@@ -613,7 +633,7 @@ module Squared
613
633
  end
614
634
  end
615
635
  param_guard(action, flag, args: grep)
616
- log!(flag, opts, grep: grep)
636
+ log_(flag, opts, grep: grep)
617
637
  end
618
638
  end
619
639
  when 'checkout'
@@ -794,10 +814,10 @@ module Squared
794
814
  end
795
815
  success?(reset(flag, args, commit: commit))
796
816
  end
797
- when :index, :undo
798
- format_desc(action, flag, ('opts*,pathspec*' if flag == :index))
817
+ when :index
818
+ format_desc action, flag, 'opts*,pathspec*'
799
819
  task flag do |_, args|
800
- reset(flag, flag == :index ? args.to_a : [])
820
+ reset(flag, args.to_a, refs: [])
801
821
  end
802
822
  when :mode
803
823
  format_desc action, flag, 'mode,ref/:'
@@ -812,6 +832,11 @@ module Squared
812
832
  ref = commithead args.ref
813
833
  reset(flag, refs: args.extras, ref: !ref || ref == ':' ? choice_commit(reflog: false) : ref)
814
834
  end
835
+ when :undo
836
+ format_desc action, flag
837
+ task flag do
838
+ reset flag
839
+ end
815
840
  end
816
841
  when 'show'
817
842
  case flag
@@ -976,6 +1001,51 @@ module Squared
976
1001
  task flag do |_, args|
977
1002
  sparse_checkout flag, args.to_a
978
1003
  end
1004
+ when 'remote'
1005
+ if flag == :add
1006
+ format_desc action, flag, 'name,url,opts*'
1007
+ task flag, [:name, :url] do |_, args|
1008
+ name = param_guard(action, flag, args: args, key: :name)
1009
+ url = param_guard(action, flag, args: args, key: :url)
1010
+ raise ArgumentError, message('invalid url', hint: url) unless url.match?(GIT_PROTO)
1011
+
1012
+ git(:remote, args.extras, extras: [name, url])
1013
+ end
1014
+ else
1015
+ format_desc action, flag, "#{OPT_GIT[:remote].keys.join('|')},opts*" if TASK_METADATA
1016
+ task flag, [:command] do |_, args|
1017
+ command = param_guard(action, flag, args: args, key: :command)
1018
+ cmd, opts = git_session('remote', opts: args.extras)
1019
+ param = case command
1020
+ when 'rename'
1021
+ 'New name'
1022
+ when 'set-branches'
1023
+ 'Branches'
1024
+ when 'set-head'
1025
+ 'Branch' if opts.empty?
1026
+ when 'set-url'
1027
+ 'URL'
1028
+ end
1029
+ remote, target = choice_remote(force: true, values: param ? [[param, true]] : [])
1030
+ cmd << '-v' if has_value!(opts, 'v', 'verbose') && command == 'show'
1031
+ cmd << command << remote
1032
+ op = OptionPartition.new(opts, OPT_GIT[:remote].fetch(command.to_sym, []), cmd,
1033
+ project: self, strict: strict?)
1034
+ case command
1035
+ when 'remove'
1036
+ exit 1 unless confirm_basic('Remove?', remote, 'N')
1037
+ when 'get-url', 'show', 'prune'
1038
+ status = false
1039
+ else
1040
+ raise ArgumentError, message('unrecognized command', command, hint: action) unless param
1041
+ raise ArgumentError, message('missing target', command, hint: action) unless target
1042
+
1043
+ op << target
1044
+ end
1045
+ op.clear
1046
+ success?(source(banner: !quiet?), status)
1047
+ end
1048
+ end
979
1049
  when 'git'
980
1050
  before = case flag
981
1051
  when :blame
@@ -1056,7 +1126,8 @@ module Squared
1056
1126
  cur ||= line.delete_prefix!('* ')
1057
1127
  heads << line if matchany?(reg, line)
1058
1128
  end
1059
- raise_error 'head not found', hint: 'for-each-ref' unless cur
1129
+ raise message('head not found', hint: 'for-each-ref') unless cur
1130
+
1060
1131
  opts << 'ff-only' if opts.empty? && option('ff-only', notequals: '0')
1061
1132
  (heads.dup << cur).each_with_index do |branch, i|
1062
1133
  next unless (i < heads.size && cur != branch) || i == heads.size
@@ -1219,18 +1290,17 @@ module Squared
1219
1290
  else
1220
1291
  op << out
1221
1292
  end
1222
- elsif !op.empty?
1223
- op.add_first(prefix: ':')
1224
1293
  elsif flag == :branch
1225
- raise ArgumentError, 'no branch name'
1294
+ op.add_first(prefix: ':', expect: 'no branch name')
1226
1295
  end
1227
- op.clear
1296
+ op.add_first
1297
+ .clear
1228
1298
  when :clear
1229
1299
  n = sub_style file.read.lines.size, theme[:inline]
1230
1300
  s = sub_style name, theme[:active]
1231
1301
  source(stdout: true) if confirm("Remove #{n} stash entries from #{s}?", 'N')
1232
1302
  return
1233
- when :list
1303
+ else
1234
1304
  op.clear
1235
1305
  out, banner, from = source(io: true)
1236
1306
  print_item banner
@@ -1321,6 +1391,7 @@ module Squared
1321
1391
 
1322
1392
  sync = kwargs.fetch(:sync) { invoked_sync?('revbuild', flag) }
1323
1393
  kwargs = kwargs.key?(:include) || kwargs.key?(:exclude) ? kw.call : @revbuild || { pass: true }
1394
+ from = symjoin 'git', 'revbuild'
1324
1395
  case flag
1325
1396
  when :build
1326
1397
  op = OptionPartition.new(opts, VAL_GIT[:revbuild].map { |key| "#{key}=b?" }, project: self, strict: strict?)
@@ -1333,7 +1404,7 @@ module Squared
1333
1404
  .compact
1334
1405
  OptionPartition.uniq!(args)
1335
1406
  end
1336
- run_p(*Array(kwargs[:before]), sync: sync, from: :revbuild) if kwargs[:before]
1407
+ run_p(*Array(kwargs[:before]), sync: sync, from: symjoin(from, 'before')) if kwargs[:before]
1337
1408
  force = if (force = env('REVBUILD_FORCE', strict: true))
1338
1409
  force != '0' && force != 'false'
1339
1410
  elsif (force = ENV['GIT_FORCE'] || ENV['REVBUILD_FORCE'])
@@ -1348,7 +1419,7 @@ module Squared
1348
1419
  end
1349
1420
  end
1350
1421
  start = time_epoch
1351
- ret = build(*@output, sync: sync, from: symjoin('git', 'revbuild'))
1422
+ ret = build(*@output, sync: sync, from: from)
1352
1423
  rescue => e
1353
1424
  print_error(e, pass: true)
1354
1425
  else
@@ -1356,7 +1427,7 @@ module Squared
1356
1427
  print_status('revbuild', subject: name, start: start, loglevel: Logger::WARN, from: :failed)
1357
1428
  workspace.rev_timeutc name, 'build'
1358
1429
  else
1359
- run_p(*Array(kwargs[:after]), sync: sync, from: :revbuild) if kwargs[:after] && ret != false
1430
+ run_p(*Array(kwargs[:after]), sync: sync, from: symjoin(from, 'after')) if kwargs[:after] && ret != false
1360
1431
  print_status('revbuild', subject: name, start: start)
1361
1432
  workspace.rev_write(name, { 'revision' => sha, 'files' => status_digest(*args, **kwargs) },
1362
1433
  sync: sync, utc: 'build')
@@ -1463,7 +1534,7 @@ module Squared
1463
1534
  ret
1464
1535
  end
1465
1536
 
1466
- def log!(flag, opts = [], range: [], index: [], grep: [])
1537
+ def log_(flag, opts = [], range: [], index: [], grep: [])
1467
1538
  cmd, opts = git_session('log', opts: opts)
1468
1539
  op = OptionPartition.new(opts, collect_hash(OPT_GIT[:log]), cmd, project: self, strict: strict?,
1469
1540
  no: collect_hash(OPT_GIT[:no][:log]),
@@ -1480,7 +1551,6 @@ module Squared
1480
1551
  append_pathspec op.extras
1481
1552
  source(exception: false)
1482
1553
  end
1483
- alias log_ log!
1484
1554
 
1485
1555
  def diff(flag, opts = [], refs: [], branch: nil, range: [], index: [], from: :diff)
1486
1556
  cmd, opts = git_session(from, opts: opts)
@@ -1513,7 +1583,8 @@ module Squared
1513
1583
  op.add_quote(branch) if branch
1514
1584
  if !index.empty?
1515
1585
  if op.arg?('cached')
1516
- raise_error "single commit: #{index.join(', ')}", hint: 'cached' unless index.size == 1
1586
+ raise message('single commit only', index.join(', '), hint: flag) unless index.size == 1
1587
+
1517
1588
  op << index.first
1518
1589
  else
1519
1590
  op.merge(index)
@@ -1576,9 +1647,9 @@ module Squared
1576
1647
  next if line.empty?
1577
1648
 
1578
1649
  branch, origin, hint = line.split('...')
1579
- if hint && !hint.match?(/^\[(\D+0,\D+0)\]$/)
1580
- raise_error 'work tree is not usable', hint: hint[1..-2]
1581
- elsif (!origin || origin.empty?) && !dryrun?
1650
+ raise message('work tree is not usable', hint: hint[1..-2]) if hint && !hint.match?(/^\[(\D+0,\D+0)\]$/)
1651
+
1652
+ if (!origin || origin.empty?) && !dryrun?
1582
1653
  return nil if pass
1583
1654
 
1584
1655
  unless (origin = option('upstream', prefix: 'git', ignore: false))
@@ -1681,7 +1752,8 @@ module Squared
1681
1752
  cmd << shell_quote(target)
1682
1753
  cmd << shell_quote(ref) if ref
1683
1754
  when :track
1684
- raise_error 'invalid upstream', hint: ref unless ref.include?('/')
1755
+ raise message('invalid upstream', hint: ref) unless ref.include?('/')
1756
+
1685
1757
  if ref.delete_prefix!('^')
1686
1758
  cmd << '--unset-upstream' << shell_quote(ref)
1687
1759
  remote = false
@@ -1739,15 +1811,15 @@ module Squared
1739
1811
  data = line.sub(/^\*?\s+/, '').split(/\s+/, 3)
1740
1812
  a = sub_style(data[0], theme[:inline], styles: (:underline if !first && line.start_with?('*')))
1741
1813
  b = commitstyle data[1]
1742
- r = /\A(?:\[((?~\]\s))\]\s)?(.+)\z/m.match(data[2])
1743
- if (r1 = r[1]) && r1 =~ /^(.+):(?: ([a-z]+) (\d+),)? ([a-z]+) (\d+)$/
1814
+ md = /\A(?:\[((?~\]\s))\]\s)?(.+)\z/m.match(data[2])
1815
+ if (r1 = md[1]) && r1 =~ /^(.+):(?: ([a-z]+) (\d+),)? ([a-z]+) (\d+)$/
1744
1816
  write = ->(s1, s2) { "#{s1.capitalize.rjust(7)}: #{sub_style(s2, theme[:warn])}" }
1745
1817
  r1 = $1
1746
1818
  r2 = $2 && write.call($2, $3)
1747
1819
  r3 = write.call($4, $5)
1748
1820
  end
1749
1821
  r1 = nil if r1 == "origin/#{data[0]}"
1750
- ["#{"\n" unless index == 0} Branch: #{a.subhint(r1)}", r2, r3, " Commit: #{b}", "Message: #{r[2]}"]
1822
+ ["#{"\n" unless index == 0} Branch: #{a.subhint(r1)}", r2, r3, " Commit: #{b}", "Message: #{md[2]}"]
1751
1823
  .compact
1752
1824
  .join("\n")
1753
1825
  end
@@ -1903,9 +1975,9 @@ module Squared
1903
1975
  list_result(ret, 'files', grep: op.extras, from: from)
1904
1976
  end
1905
1977
 
1906
- def git(flag, opts = [])
1978
+ def git(flag, opts = [], extras: [])
1907
1979
  cmd, opts = git_session(flag, opts: opts)
1908
- list = OPT_GIT[:git].fetch(flag, []) + OPT_GIT.fetch(flag, [])
1980
+ list = OPT_GIT[:git].fetch(flag, []) + OPT_GIT.fetch(flag, []).yield_self { |a| a.is_a?(Array) ? a : [] }
1909
1981
  case flag
1910
1982
  when :add
1911
1983
  list.concat(OPT_GIT[:log][:diff_context])
@@ -1954,6 +2026,12 @@ module Squared
1954
2026
  end
1955
2027
  end
1956
2028
  return source(git_session('status -s'), banner: false) unless append_pathspec(op.extras)
2029
+ when :remote
2030
+ op.unshift(*extras)
2031
+ op.adjoin('add')
2032
+ .add_first(expect: 'missing name')
2033
+ .add_first(expect: 'missing URL', quote: true)
2034
+ .clear
1957
2035
  when :mv
1958
2036
  refs = projectmap op.extras
1959
2037
  raise 'no source/destination' unless refs.size > 1
@@ -1982,7 +2060,7 @@ module Squared
1982
2060
  when :revert, :mv, :rm
1983
2061
  source(sync: false, stderr: true)
1984
2062
  else
1985
- success?(source, flag == :'sparse-checkout' || (flag == :add && !op.arg?('verbose')))
2063
+ success?(source, %i[remote sparse-checkout].include?(flag) || (flag == :add && !op.arg?('verbose')))
1986
2064
  end
1987
2065
  end
1988
2066
 
@@ -1994,7 +2072,7 @@ module Squared
1994
2072
  build? && !!workspace.revfile
1995
2073
  end
1996
2074
 
1997
- def enabled?(*, **kwargs)
2075
+ def enabled?(**kwargs)
1998
2076
  super || (kwargs[:base] == false && !!clone?)
1999
2077
  end
2000
2078
 
@@ -2075,7 +2153,7 @@ module Squared
2075
2153
  elsif stdout?
2076
2154
  styles = theme.fetch(:banner, []).reject { |val| val.to_s.end_with?('!') }
2077
2155
  styles << :bold if styles.size <= 1
2078
- puts print_footer("#{size} #{size == 1 ? type.sub(/(?:(?<!l)e)?s\z/, '') : type}",
2156
+ puts print_footer("#{size} #{size == 1 ? type.sub(/((?<!l)e)?s\z/, '') : type}",
2079
2157
  sub: opt_style(styles, /^(\d+)(.+)$/))
2080
2158
  end
2081
2159
  on :last, from
@@ -2282,11 +2360,21 @@ module Squared
2282
2360
  stdout: stdout, banner: banner, **kwargs)
2283
2361
  end
2284
2362
 
2285
- def dryrun?(*, target: @session, prefix: target&.first)
2363
+ def git_grep(cmd, *grep, equals: true, **kwargs)
2364
+ ret = []
2365
+ git_spawn(cmd, **kwargs).lines(chomp: true).each do |line|
2366
+ if grep.any? { |val| val.is_a?(Regexp) ? val.match?(line) : equals ? line == val : line.include?(val) }
2367
+ ret << line
2368
+ end
2369
+ end
2370
+ ret
2371
+ end
2372
+
2373
+ def dryrun?(target: @session, prefix: target&.first, **)
2286
2374
  Array(target).include?('--dry-run') || !option('dry-run', target: target, prefix: prefix).nil?
2287
2375
  end
2288
2376
 
2289
- def quiet?(*, target: @session, **)
2377
+ def quiet?(target: @session, **)
2290
2378
  return silent? unless target
2291
2379
 
2292
2380
  silent? || target.include?('--quiet') || (target.include?('-q') && target.first.stripext == 'git')
@@ -2298,7 +2386,8 @@ module Squared
2298
2386
 
2299
2387
  def repotrack(origin, branch, quote: true)
2300
2388
  n = origin&.index('/')
2301
- raise_error "missing #{origin ? 'branch' : 'remote'} name", hint: origin unless n && branch
2389
+ raise message("missing #{origin ? 'branch' : 'remote'} name", hint: origin) unless n && branch
2390
+
2302
2391
  branch = "#{branch}:#{origin[n.succ..-1]}" unless origin.end_with?("/#{branch}")
2303
2392
  ret = [origin[0..n.pred], branch]
2304
2393
  ret.quote! if quote