squared 0.4.12 → 0.4.14

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.
@@ -21,7 +21,8 @@ module Squared
21
21
  compose: {
22
22
  common: %w[all-resources compatibility dry-run ansi|b env-file=p f|file=p parallel=b profile=b progress=b
23
23
  project-directory=p p|project-name=e].freeze,
24
- build: %w[no-cache pull push with-dependencies q|quiet build-arg=qq builder=b m|memory=b ssh=qq].freeze,
24
+ build: %w[check no-cache pull push with-dependencies q|quiet build-arg=qq builder=b m|memory=b
25
+ ssh=qq].freeze,
25
26
  exec: %w[dry-run privileged d|detach e|env=qq index=i T|no-TTY=b? user=e w|workdir=q].freeze,
26
27
  run: %w[build dry-run no-deps quiet-pull remove-orphans rm P|service-ports use-aliases cap-add=b cap-drop=b
27
28
  d|detach entrypoint=q e|env=qq i|interactive=b? l|label=q name=b T|no-TTY=b? p|publish=e pull=b
@@ -32,22 +33,24 @@ module Squared
32
33
  no-attach=b pull=b scale=i t|timeout=i wait-timeout=i].freeze
33
34
  }.freeze,
34
35
  container: {
35
- run: %w[d|detach init i|interactive no-healthcheck oom-kill-disable privileged P|publish-all q|quiet
36
- read-only rm runtime t|tty add-host=q annotation=q a|attach=b blkio-weight-device=i cap-add=b
37
- cap-drop=b cgroup-parent=b cgroupns=b cidfile=p detach-keys=q device=q device-cgroup-rule=q
38
- device-read-bps=q device-read-iops=q device-write-bps=q device-write-iops=q disable-content-trust=b?
39
- dns=e dns-option=e dns-search=e domainname=b entrypoint=q e|env=qq env-file=p expose=e gpus=q
40
- group-add=b health-cmd=q health-interval=b health-retries=i health-start-interval=b
41
- health-start-period=b health-timeout=b h|hostname=e io-maxbandwidth=b io-maxiops=b ip=b ip6=e ipc=b
42
- isolation=b kernel-memory=b l|label=q label-file=p link=b link-local-ip=b log-driver=b log-opt=q
43
- mac-address=e memory-swappiness=b mount=qq name=b network=b network-alias=b oom-score-adj=b pid=b
44
- platform=b p|publish=e pull=b restart=b runtime=b security-opt=q shm-size=b sig-proxy=b?
45
- stop-signal=b stop-timeout=i storage-opt=q sysctl=q tmpfs=q ulimit=q user=e userns=b uts=b
46
- v|volume=q volume-driver=b volumes-from=b w|workdir=q].freeze,
36
+ create: %w[init i|interactive no-healthcheck oom-kill-disable privileged P|publish-all q|quiet read-only
37
+ rm runtime t|tty use-api-socket io-maxbandwidth=b io-maxiops=b add-host=q annotation=q
38
+ a|attach=b blkio-weight=i blkio-weight-device=i cap-add=b cap-drop=b cgroup-parent=b cgroupns=b
39
+ cidfile=p device=q device-cgroup-rule=q device-read-bps=q device-read-iops=q device-write-bps=q
40
+ device-write-iops=q disable-content-trust=b? dns=e dns-option=e dns-search=e domainname=b
41
+ entrypoint=q e|env=qq env-file=p expose=e gpus=q group-add=b health-cmd=q health-interval=b
42
+ health-retries=i health-start-interval=b health-start-period=b health-timeout=b h|hostname=e ip=b
43
+ ip6=e ipc=b isolation=b kernel-memory=b l|label=q label-file=p link=b link-local-ip=b
44
+ log-driver=b log-opt=q mac-address=e m|memory=b memory-reservation=b memory-swap=n
45
+ memory-swappiness=n mount=qq name=b network=b network-alias=b oom-score-adj=b pid=b pids-limit=n
46
+ platform=b p|publish=e pull=b restart=b runtime=b security-opt=q shm-size=b stop-signal=b
47
+ stop-timeout=i storage-opt=q sysctl=q tmpfs=q ulimit=q u|user=b userns=b uts=b v|volume=q
48
+ volume-driver=b volumes-from=b w|workdir=q].freeze,
49
+ run: %w[d|detach detach-keys=q sig-proxy=b?].freeze,
47
50
  exec: %w[d|detach i|interactive privileged t|tty detach-keys=q e|env=qq env-file=p user=e
48
51
  w|workdir=q].freeze,
49
52
  update: %w[blkio-weight=i cpu-period=i cpu-quota=i cpu-rt-period=i cpu-rt-runtime=i c|cpu-shares=i cpus=f
50
- cpuset-cpus=b cpuset-mems=b m|memory=b memory-reservation=b memory-swap=b pids-limit=b
53
+ cpuset-cpus=b cpuset-mems=b m|memory=b memory-reservation=b memory-swap=b pids-limit=n
51
54
  restart=q].freeze,
52
55
  commit: %w[a|author=q c|change=q m|message=q pause=b?].freeze,
53
56
  inspect: %w[s|size f|format=q].freeze,
@@ -60,7 +63,8 @@ module Squared
60
63
  image: {
61
64
  list: %w[a|all digests no-trunc f|filter=q format=q].freeze,
62
65
  push: %w[a|all-tags disable-content-trust=b? platform=b q|quiet].freeze,
63
- rm: %w[f|force no-prune].freeze
66
+ rm: %w[f|force no-prune].freeze,
67
+ save: %w[o|output=p platform=b].freeze
64
68
  }.freeze,
65
69
  network: {
66
70
  connect: %w[alias=b driver-opt=q gw-priority=n ip=b ip6=b link=b link-local-ip=b].freeze,
@@ -90,8 +94,8 @@ module Squared
90
94
  subtasks({
91
95
  'build' => %i[tag context bake].freeze,
92
96
  'compose' => %i[build run exec up].freeze,
93
- 'image' => %i[list rm push].freeze,
94
- 'container' => %i[run exec update commit inspect diff start stop restart pause unpause top stats kill
97
+ 'image' => %i[list rm push tag save].freeze,
98
+ 'container' => %i[run create exec update commit inspect diff start stop restart pause unpause top stats kill
95
99
  rm].freeze,
96
100
  'network' => %i[connect disconnect].freeze
97
101
  })
@@ -107,7 +111,7 @@ module Squared
107
111
  @tag = tag || tagname("#{@project}:#{@version || 'latest'}")
108
112
  @mounts = mounts
109
113
  @secrets = secrets
110
- @registry = [registry, kwargs[:username]].compact.join('/')
114
+ @registry = tagjoin registry, kwargs[:username]
111
115
  initialize_ref Docker.ref
112
116
  initialize_logger(**kwargs)
113
117
  initialize_env(**kwargs)
@@ -156,7 +160,7 @@ module Squared
156
160
  compose! flag, args.to_a
157
161
  end
158
162
  when :exec, :run
159
- format_desc action, flag, "service,command#{flag == :exec ? '' : '?'},args*,opts*"
163
+ format_desc action, flag, "service,command#{flag == :exec ? '' : '?'}|:,args*,opts*"
160
164
  task flag, [:service] do |_, args|
161
165
  service = param_guard(action, flag, args: args, key: :service)
162
166
  compose!(flag, args.extras, service: service)
@@ -174,7 +178,7 @@ module Squared
174
178
  container(flag, args.extras, id: id)
175
179
  end
176
180
  end
177
- when :run
181
+ when :run, :create
178
182
  format_desc action, flag, 'image,opts*,args*'
179
183
  task flag, [:image] do |_, args|
180
184
  if args.image
@@ -197,12 +201,15 @@ module Squared
197
201
  tag = param_guard(action, flag, args: args, key: :tag)
198
202
  image(flag, args.extras, id: tag)
199
203
  end
200
- when :list, :rm
201
- format_desc(action, flag, flag == :rm ? 'id*,opts*' : 'opts*,args*')
204
+ else
205
+ format_desc(action, flag, case flag
206
+ when :rm, :save then 'id*,opts*'
207
+ when :tag then 'version?'
208
+ else 'opts*,args*' end)
202
209
  task flag do |_, args|
203
210
  args = args.to_a
204
- if flag == :rm && args.empty?
205
- choice_command :rm
211
+ if args.empty? && flag != :list
212
+ choice_command flag
206
213
  else
207
214
  image flag, args
208
215
  end
@@ -273,12 +280,12 @@ module Squared
273
280
  ret << quote_option('secret', @secrets, double: true)
274
281
  when Hash
275
282
  append = lambda do |type|
276
- as_a(@secrets[type]).each { |arg| ret << quote_option('secret', "type=#{type},#{arg}", double: true) }
283
+ Array(@secrets[type]).each { |arg| ret << quote_option('secret', "type=#{type},#{arg}", double: true) }
277
284
  end
278
285
  append.call(:file)
279
286
  append.call(:env)
280
287
  else
281
- as_a(@secrets).each { |arg| ret << quote_option('secret', arg) }
288
+ Array(@secrets).each { |arg| ret << quote_option('secret', arg) }
282
289
  end
283
290
  if (val = option('tag', ignore: false))
284
291
  append_tag val
@@ -306,7 +313,7 @@ module Squared
306
313
  when :bake
307
314
  unless op.empty?
308
315
  args = op.dup
309
- op.extras.clear
316
+ op.reset
310
317
  if Dir.exist?(args.last)
311
318
  if projectpath?(val = args.pop)
312
319
  context = val
@@ -340,18 +347,20 @@ module Squared
340
347
 
341
348
  def container(flag, opts = [], id: nil)
342
349
  cmd, opts = docker_session('container', flag, opts: opts)
350
+ rc = flag == :run || flag == :create
343
351
  list = OPT_DOCKER[:container].fetch(flag, [])
344
- list += OPT_DOCKER[:container][:update] if flag == :run
345
- op = OptionPartition.new(opts, list, cmd, project: self, args: flag == :run || flag == :exec)
352
+ list += OPT_DOCKER[:container][:create] if flag == :run
353
+ list += OPT_DOCKER[:container][:update] if rc
354
+ op = OptionPartition.new(opts, list, cmd, project: self, args: rc || flag == :exec)
346
355
  from = :"container:#{flag}"
347
356
  case flag
348
- when :run, :exec
349
- if flag == :run && !op.arg?('mount')
357
+ when :run, :create, :exec
358
+ if rc && !op.arg?('mount')
350
359
  run = VAL_DOCKER[:run]
351
360
  both = run[:bind] + run[:tmpfs]
352
361
  diff = run[:bind].reject { |val| run[:tmpfs].include?(val) }
353
362
  delim = Regexp.new(",\\s*(?=#{both.join('|')})")
354
- as_a(@mounts).each do |val|
363
+ Array(@mounts).each do |val|
355
364
  args = []
356
365
  tmpfs = true
357
366
  val.split(delim).each do |opt|
@@ -378,7 +387,7 @@ module Squared
378
387
  cmd << "--mount type=#{tmpfs ? 'tmpfs' : 'bind'},#{args.join(',')}"
379
388
  end
380
389
  end
381
- append_command(flag, id.to_s.empty? ? tagmain : id, op.extras, from: from)
390
+ append_command(flag, id || tagmain, op.extras, from: from)
382
391
  when :update
383
392
  raise_error('missing container', hint: from) if op.empty?
384
393
  op.append(escape: true)
@@ -442,7 +451,7 @@ module Squared
442
451
 
443
452
  def image(flag, opts = [], sync: true, id: nil, registry: nil)
444
453
  cmd, opts = docker_session('image', flag, opts: opts)
445
- op = OptionPartition.new(opts, OPT_DOCKER[:image][flag], cmd, project: self)
454
+ op = OptionPartition.new(opts, OPT_DOCKER[:image].fetch(flag, []), cmd, project: self)
446
455
  exception = @exception
447
456
  banner = true
448
457
  from = :"image:#{flag}"
@@ -473,7 +482,7 @@ module Squared
473
482
  end
474
483
  else
475
484
  if op.empty?
476
- list_image(flag, docker_output('image ls -a'), from: from) do |val|
485
+ list_image(:rm, docker_output('image ls -a'), from: from) do |val|
477
486
  image(:rm, opts, sync: sync, id: val)
478
487
  end
479
488
  else
@@ -481,6 +490,14 @@ module Squared
481
490
  end
482
491
  return
483
492
  end
493
+ when :tag, :save
494
+ list_image(flag, docker_output('image ls -a'), from: from) do |val|
495
+ op << val
496
+ if flag == :tag
497
+ op << tagname("#{@project}:#{op.extras.first}")
498
+ break
499
+ end
500
+ end
484
501
  when :push
485
502
  id ||= option('tag', ignore: false) || tagmain
486
503
  registry ||= op.shift || option('registry') || @registry
@@ -497,7 +514,8 @@ module Squared
497
514
  exception = true
498
515
  banner = false
499
516
  end
500
- run(cmd, sync: sync, exception: exception, banner: banner, from: from)
517
+ ret = run(cmd, sync: sync, exception: exception, banner: banner, from: from)
518
+ print_success if success?(ret) && (flag == :tag || flag == :save)
501
519
  end
502
520
 
503
521
  def network(flag, opts = [], target: nil)
@@ -548,7 +566,9 @@ module Squared
548
566
  end
549
567
 
550
568
  def append_command(flag, val, list, target: @session, from: nil)
551
- if (args = env('DOCKER_ARGS'))
569
+ if list.delete(':')
570
+ list << readline('Enter command [args]', force: true)
571
+ elsif (args = env('DOCKER_ARGS'))
552
572
  list << args
553
573
  end
554
574
  case flag
@@ -571,7 +591,7 @@ module Squared
571
591
  def append_file(type, target: @session)
572
592
  return unless type == 2 || type == 4 || @file.is_a?(Array)
573
593
 
574
- files = as_a(@file).map { |val| quote_option('file', path + val) }
594
+ files = Array(@file).map { |val| quote_option('file', path + val) }
575
595
  if target.is_a?(Set)
576
596
  target.merge(files)
577
597
  else
@@ -645,7 +665,7 @@ module Squared
645
665
  cols.each do |key|
646
666
  next if (key == 'Tag' && !dd) || (key == 'Size' && data[key] == '0B')
647
667
 
648
- puts "#{g + f} #{key}: #{as_a(data[key]).join(', ')}" unless data[key].to_s.empty?
668
+ puts "#{g + f} #{key}: #{Array(data[key]).join(', ')}" unless data[key].to_s.empty?
649
669
  end
650
670
  w = 9 + flag.to_s.size + 4 + ee.size
651
671
  puts g + sub_style(ARG[:BORDER][6] + (ARG[:BORDER][1] * w), styles: theme[:inline])
@@ -685,14 +705,14 @@ module Squared
685
705
 
686
706
  def choice_command(flag)
687
707
  msg, cmd, index = case flag
688
- when :run, :rm
689
- ['Choose an image', 'images -a', 2]
690
708
  when :exec
691
709
  ['Choose a container', 'ps -a', 0]
692
710
  when :bake
693
711
  ['Choose a target', 'buildx bake --list=type=targets', 0]
694
- else
712
+ when :connect, :disconnect
695
713
  ['Choose a network', 'network ls', 0]
714
+ else
715
+ ['Choose an image', 'images -a', 2]
696
716
  end
697
717
  lines = `#{docker_output(cmd)}`.lines
698
718
  header = lines.shift
@@ -702,25 +722,43 @@ module Squared
702
722
  puts " # #{header}"
703
723
  multiple = false
704
724
  parse = ->(val) { val.split(/\s+/)[index] }
725
+ ctx = flag.to_s
705
726
  case flag
706
727
  when :run, :exec
707
728
  values = [['Options', flag == :run], ['Arguments', flag == :exec]]
708
- cmd = flag.to_s
709
729
  when :rm, :bake
710
730
  values = ['Options']
711
731
  multiple = true
712
- cmd = flag == :rm ? 'image rm' : "buildx bake -f #{shell_quote(dockerfile)}"
713
- else
732
+ ctx = flag == :rm ? 'image rm' : "buildx bake -f #{shell_quote(dockerfile)}"
733
+ when :save
734
+ values = [['Output', true], 'Platform']
735
+ multiple = true
736
+ when :connect, :disconnect
714
737
  values = ['Options', ['Container', true]]
715
- cmd = "network #{flag}"
738
+ ctx = "network #{flag}"
716
739
  end
717
740
  out, opts, args = choice_index(msg, lines, multiple: multiple, values: values)
718
- ret = run docker_output(cmd, opts, '--', *(if out.is_a?(Array)
719
- out.map! { |val| parse.call(val) }
720
- else
721
- [parse.call(out)]
722
- end), args)
723
- print_success if success?(ret && cmd.start_with?('network'))
741
+ cmd = docker_output ctx
742
+ case flag
743
+ when :tag
744
+ args = tagjoin @registry, @tag
745
+ when :save
746
+ opts = "#{opts}.tar" unless opts.end_with?('.tar')
747
+ cmd << quote_option('output', File.expand_path(opts))
748
+ if args
749
+ cmd << basic_option('platform', args)
750
+ args = nil
751
+ end
752
+ else
753
+ cmd << opts << '--'
754
+ end
755
+ cmd.merge(if out.is_a?(Array)
756
+ out.map! { |val| parse.call(val) }
757
+ else
758
+ [parse.call(out)]
759
+ end)
760
+ cmd << args
761
+ print_success if success?(run(cmd)) && ctx.match?(/\A(?:network|tag|save)/)
724
762
  end
725
763
  end
726
764
 
@@ -743,6 +781,11 @@ module Squared
743
781
  val && projectpath?(val) ? shell_quote(path + val) : '.'
744
782
  end
745
783
 
784
+ def tagjoin(*args, char: '/')
785
+ args.compact!
786
+ args.join(char) unless args.empty?
787
+ end
788
+
746
789
  def tagname(val)
747
790
  val = val.split(':').map! { |s| charname(s.sub(/^\W+/, '')) }
748
791
  ret = val.join(':')
@@ -27,7 +27,7 @@ module Squared
27
27
  base = name
28
28
  @project.each_value { |proj| repo << proj if !proj.parent && check.call(proj) }
29
29
  else
30
- warn log_message(Logger::WARN, name, subject: 'git', hint: 'invalid', pass: true) if warning
30
+ warn log_message(Logger::WARN, name, subject: 'git', hint: 'invalid') if warning
31
31
  return self
32
32
  end
33
33
  if base
@@ -340,7 +340,7 @@ module Squared
340
340
  'restore' => %i[source staged worktree].freeze,
341
341
  'rev' => %i[commit build output].freeze,
342
342
  'show' => %i[format oneline textconv].freeze,
343
- 'stash' => %i[push pop apply drop clear list].freeze,
343
+ 'stash' => %i[push pop apply branch drop clear list].freeze,
344
344
  'switch' => %i[create detach merge].freeze,
345
345
  'tag' => %i[add sign delete list].freeze
346
346
  })
@@ -367,13 +367,13 @@ module Squared
367
367
  case action
368
368
  when 'pull', 'fetch'
369
369
  if flag == :remote
370
- format_desc action, flag, 'remote,opts*'
370
+ format_desc action, flag, 'remote?,opts*'
371
371
  task flag, [:remote] do |_, args|
372
372
  if (remote = args.remote)
373
373
  args = args.extras
374
374
  else
375
375
  remote = choice_remote
376
- args = args.to_a.drop(1)
376
+ args = args.to_a
377
377
  end
378
378
  __send__(action, flag, args, remote: remote)
379
379
  end
@@ -459,6 +459,7 @@ module Squared
459
459
  when 'stash'
460
460
  format_desc(action, flag, 'opts*', after: case flag
461
461
  when :push then 'pathspec*'
462
+ when :branch then 'name,stash?|:'
462
463
  when :clear, :list then nil
463
464
  else 'stash?|:' end)
464
465
  task flag do |_, args|
@@ -927,7 +928,7 @@ module Squared
927
928
  source(sync: sync, sub: if verbose
928
929
  [
929
930
  { pat: /^(.+)(\|\s+\d+\s+)([^-]*)(-+)(.*)$/, styles: color(:red), index: 4 },
930
- { pat: /^(.+)(\|\s+\d+\s+)(\++)(-*)(.*)$/, styles: color(:green), index: 3 }
931
+ { pat: /^(.+)(\|\s+\d+\s+)(\++)(.*)$/, styles: color(:green), index: 3 }
931
932
  ]
932
933
  end, **threadargs)
933
934
  end
@@ -942,7 +943,7 @@ module Squared
942
943
  return unless upstream
943
944
 
944
945
  op = OptionPartition.new(opts, OPT_GIT[:rebase], cmd, project: self, no: OPT_GIT[:no][:rebase])
945
- cmd << shell_escape(upstream)
946
+ cmd << upstream
946
947
  append_head op.shift
947
948
  op.clear(pass: false)
948
949
  when :onto
@@ -950,7 +951,7 @@ module Squared
950
951
 
951
952
  cmd << '--interactive' if option('interactive', 'i')
952
953
  cmd << shell_option('onto', commit) if commit
953
- cmd << shell_escape(upstream)
954
+ cmd << upstream
954
955
  append_head branch
955
956
  else
956
957
  return unless VAL_GIT[:rebase][:send].include?(command)
@@ -984,7 +985,14 @@ module Squared
984
985
  end
985
986
  end
986
987
  opts[:origin] = val if (val = option('origin', ignore: false))
987
- opts[:branch] = val if (val = option('branch', strict: true))
988
+ if (val = option('branch', strict: true))
989
+ opts[:branch] = val
990
+ opts.delete(:revision)
991
+ elsif (val = option('revision', strict: true))
992
+ opts[:revision] = val
993
+ opts.delete(:branch)
994
+ opts.delete(:mirror)
995
+ end
988
996
  opts[:local] = val != '0' if (val = option('local', strict: true))
989
997
  opts.delete(:'recurse-submodules') || opts.delete(:'no-recurse-submodules') if append_submodules(from: :clone)
990
998
  append_hash opts
@@ -1005,12 +1013,26 @@ module Squared
1005
1013
  case flag
1006
1014
  when :push
1007
1015
  append_pathspec op.extras
1008
- when :pop, :apply, :drop
1016
+ when :pop, :apply, :drop, :branch
1009
1017
  if op.extras.delete(':')
1010
- op << choice_index('Choose a stash', git_spawn('stash list', stdout: false),
1011
- column: /^[^@]+@\{(\d+)\}/, force: true)
1018
+ if flag == :branch
1019
+ if op.empty?
1020
+ values = [['Branch name', true]]
1021
+ else
1022
+ op << op.pop
1023
+ end
1024
+ end
1025
+ out = choice_index('Choose a stash', git_spawn('stash list', stdout: false),
1026
+ values: values, column: /^[^@]+@\{(\d+)\}/, force: true)
1027
+ if values
1028
+ op.merge(out.reverse)
1029
+ else
1030
+ op << out
1031
+ end
1012
1032
  elsif !op.empty?
1013
- op << shell_escape(op.pop)
1033
+ op << op.pop
1034
+ elsif flag == :branch
1035
+ raise_error 'no branch name'
1014
1036
  end
1015
1037
  op.clear
1016
1038
  when :clear
@@ -1072,8 +1094,8 @@ module Squared
1072
1094
  def revbuild(flag = nil, opts = [], sync: nil, **kwargs)
1073
1095
  statusargs = lambda do
1074
1096
  {
1075
- include: relativepath(as_a(kwargs[:include]), all: true),
1076
- exclude: relativepath(as_a(kwargs[:exclude]), all: true)
1097
+ include: relativepath(Array(kwargs[:include]), all: true),
1098
+ exclude: relativepath(Array(kwargs[:exclude]), all: true)
1077
1099
  }
1078
1100
  end
1079
1101
  unless workspace.closed
@@ -1182,13 +1204,14 @@ module Squared
1182
1204
  else
1183
1205
  op = OptionPartition.new(opts, OPT_GIT[:checkout], cmd, project: self, no: OPT_GIT[:no][:checkout],
1184
1206
  first: flag == :path ? matchpathspec : nil)
1185
- if flag == :commit
1186
- op.append(commit)
1187
- .clear(pass: false)
1188
- else
1207
+ if flag == :path
1189
1208
  append_head
1190
1209
  append_pathspec(op.extras, pass: false)
1210
+ print_success if success?(source)
1211
+ return
1191
1212
  end
1213
+ op.append(commit)
1214
+ .clear(pass: false)
1192
1215
  end
1193
1216
  source
1194
1217
  end
@@ -1228,9 +1251,9 @@ module Squared
1228
1251
 
1229
1252
  def log!(flag, opts = [], range: [], index: [])
1230
1253
  cmd, opts = git_session('log', opts: opts)
1231
- op = OptionPartition.new(opts, collect_hash(OPT_GIT[:log]), cmd,
1232
- project: self, no: collect_hash(OPT_GIT[:no][:log]),
1233
- first: matchpathspec)
1254
+ op = OptionPartition.new(opts, collect_hash(OPT_GIT[:log]), cmd, project: self,
1255
+ no: collect_hash(OPT_GIT[:no][:log]),
1256
+ first: matchpathspec)
1234
1257
  case flag
1235
1258
  when :between, :contain
1236
1259
  op << shell_quote(range.join(flag == :between ? '..' : '...'))
@@ -1617,19 +1640,20 @@ module Squared
1617
1640
  red = color(:red)
1618
1641
  grep.map! { |val| Regexp.new(val[1..-2]) }
1619
1642
  files = status_data.map! do |a, b|
1620
- next unless grep.empty? || grep.any? { |pat| pat.match?(a) }
1643
+ next if b.strip.empty? || (!grep.empty? && grep.none? { |pat| pat.match?(a) })
1621
1644
 
1622
1645
  "#{sub_style(b, styles: red)} #{a}"
1623
1646
  end
1624
1647
  .compact
1625
1648
  unless files.empty?
1626
1649
  files = choice_index('Select files', files, multiple: true, force: true, trim: /^\S+\s/,
1627
- accept: 'Add?')
1650
+ accept: [['Add?', false, true]])
1628
1651
  end
1629
1652
  op.swap(list + files)
1630
1653
  end
1631
1654
  end
1632
- append_pathspec(op.extras)
1655
+ return source(git_session('status', '-s'), banner: false) unless append_pathspec(op.extras)
1656
+
1633
1657
  verbose = flag == :add && !op.arg?('verbose')
1634
1658
  print_success if success?(source) && verbose
1635
1659
  return
@@ -1658,30 +1682,36 @@ module Squared
1658
1682
  private
1659
1683
 
1660
1684
  def source(cmd = @session, exception: true, io: false, sync: true, stdout: false, stderr: false, banner: true,
1661
- multiple: false, **kwargs)
1685
+ multiple: false, from: nil, **kwargs)
1662
1686
  cmd = cmd.target if cmd.is_a?(OptionPartition)
1663
- banner = nil if banner && (multiple || !banner?)
1664
- if cmd.respond_to?(:done)
1665
- if io && banner == false
1666
- from = nil
1667
- elsif !from && (from = cmd.drop(1).find { |val| val.match?(/\A[a-z]{1,2}[a-z\-]*\z/) })
1668
- from = :"git:#{from}"
1687
+ if io && banner == false
1688
+ from = nil
1689
+ banner = nil
1690
+ else
1691
+ banner = nil if banner && (multiple || !banner?)
1692
+ if cmd.respond_to?(:done)
1693
+ if from.nil? && (from = cmd.drop(1).find { |val| val.match?(/\A[a-z]{1,2}[a-z\-]*\z/) })
1694
+ from = :"git:#{from}"
1695
+ end
1696
+ banner &&= cmd.temp { |val| val.start_with?('--work-tree') || val.start_with?('--git-dir') }
1669
1697
  end
1670
- banner &&= cmd.temp { |val| val.start_with?('--work-tree') || val.start_with?('--git-dir') }
1698
+ from = nil if from == false
1671
1699
  end
1672
1700
  cmd = session_done cmd
1673
1701
  log&.info cmd
1674
- on :first, from
1675
1702
  banner = if banner
1676
1703
  format_banner((banner.is_a?(String) ? banner : cmd).gsub(File.join(path, ''), ''), banner: true)
1677
1704
  end
1705
+ on :first, from
1678
1706
  begin
1679
1707
  if io
1680
- return `#{cmd}` if stdout
1681
-
1682
- return banner ? [IO.popen(cmd), banner, from] : IO.popen(cmd)
1683
- end
1684
- if stdin? ? sync : stdout
1708
+ ret = if stdout
1709
+ `#{cmd}`
1710
+ else
1711
+ banner ? [IO.popen(cmd), banner, from] : IO.popen(cmd)
1712
+ end
1713
+ return ret
1714
+ elsif stdin? ? sync : stdout
1685
1715
  print_item banner unless multiple
1686
1716
  ret = `#{cmd}`
1687
1717
  if !ret.empty?
@@ -1689,7 +1719,7 @@ module Squared
1689
1719
  elsif success?(!banner.nil?)
1690
1720
  print_success
1691
1721
  end
1692
- elsif sync || (!exception && !stderr)
1722
+ elsif !kwargs[:sub] && (sync || (!exception && !stderr))
1693
1723
  print_item banner unless multiple
1694
1724
  ret = shell(cmd, exception: exception)
1695
1725
  else
@@ -1760,15 +1790,13 @@ module Squared
1760
1790
  end
1761
1791
 
1762
1792
  def list_result(size, type, grep: [], action: 'found', from: nil)
1763
- if verbose
1764
- if size > 0
1765
- styles = theme.fetch(:banner, []).reject { |s| s.to_s.end_with?('!') }
1766
- styles << :bold if styles.size <= 1
1767
- puts print_footer("#{size} #{size == 1 ? type.sub(/(?:(?<!l)e)?s\z/, '') : type}",
1768
- sub: [pat: /^(\d+)(.+)$/, styles: styles])
1769
- else
1770
- puts empty_status("No #{type} were #{action}", 'grep', grep.join(', '))
1771
- end
1793
+ if size == 0
1794
+ puts empty_status("No #{type} were #{action}", 'grep', grep.join(', '))
1795
+ elsif verbose
1796
+ styles = theme.fetch(:banner, []).reject { |s| s.to_s.end_with?('!') }
1797
+ styles << :bold if styles.size <= 1
1798
+ puts print_footer("#{size} #{size == 1 ? type.sub(/(?:(?<!l)e)?s\z/, '') : type}",
1799
+ sub: [pat: /^(\d+)(.+)$/, styles: styles])
1772
1800
  end
1773
1801
  on :last, from
1774
1802
  end
@@ -1884,6 +1912,7 @@ module Squared
1884
1912
  def append_pathspec(files = [], target: @session, expect: false, parent: false, pass: true)
1885
1913
  if session_arg?('pathspec-from-file', target: target)
1886
1914
  option_clear files
1915
+ true
1887
1916
  else
1888
1917
  if files.empty? && (val = option('pathspec', target: target))
1889
1918
  files = split_escape val
@@ -1891,8 +1920,11 @@ module Squared
1891
1920
  files = projectmap(files, parent: parent, pass: pass)
1892
1921
  if !files.empty?
1893
1922
  target << '--' << files.join(' ')
1923
+ true
1894
1924
  elsif expect
1895
1925
  raise_error(parent ? 'pathspec not present' : 'pathspec not within worktree')
1926
+ else
1927
+ false
1896
1928
  end
1897
1929
  end
1898
1930
  end
@@ -1935,8 +1967,10 @@ module Squared
1935
1967
  end
1936
1968
 
1937
1969
  def foreachref(path, *args, format: nil)
1938
- path = as_a(path).map! { |val| "refs/#{val}" }
1939
- git_spawn('for-each-ref', format && quote_option('format', format), *args, *path, stdout: false)
1970
+ path = Array(path).map! { |val| "refs/#{val}" }
1971
+ format &&= quote_option('format', format)
1972
+ ret = git_spawn('for-each-ref', format, *args, *path, stdout: workspace.windows?)
1973
+ ret.is_a?(String) ? ret.lines : ret
1940
1974
  end
1941
1975
 
1942
1976
  def git_session(*cmd, opts: nil, worktree: true, **kwargs)
@@ -1974,8 +2008,8 @@ module Squared
1974
2008
 
1975
2009
  def repotrack(origin, branch, quote: true)
1976
2010
  i = origin.index('/')
1977
- branch = "#{branch}:#{origin[i + 1..-1]}" unless origin.end_with?("/#{branch}")
1978
- ret = [origin[0..i - 1], branch]
2011
+ branch = "#{branch}:#{origin[(i + 1)..-1]}" unless origin.end_with?("/#{branch}")
2012
+ ret = [origin[0..(i - 1)], branch]
1979
2013
  quote ? ret.map! { |val| shell_quote(val) } : ret
1980
2014
  end
1981
2015