squared 0.7.9 → 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.
@@ -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
@@ -1048,15 +1118,16 @@ module Squared
1048
1118
  op = OptionPartition.new(opts, OPT_GIT[:pull], cmd, project: self, strict: strict?,
1049
1119
  no: OPT_GIT[:no][:pull])
1050
1120
  opts -= op.extras
1051
- reg, neg = matchmap(op, negate: true)
1121
+ reg = matchmap op
1052
1122
  session_done op.target
1053
1123
  heads = []
1054
1124
  cur = nil
1055
1125
  foreachref('heads', format: '%(if)%(HEAD)%(then)* %(end)%(refname:short)').each do |line|
1056
1126
  cur ||= line.delete_prefix!('* ')
1057
- heads << line if matchany?(reg, line) && !matchany?(neg, line, empty: false)
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,22 +1290,21 @@ 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
1237
- list_result(write_lines(out), 'objects', banner: banner, from: from)
1307
+ list_result(write_lines(out), 'objects', from: from)
1238
1308
  return
1239
1309
  end
1240
1310
  else
@@ -1286,7 +1356,7 @@ module Squared
1286
1356
  end
1287
1357
  out, banner, from = source(io: true)
1288
1358
  ret = write_lines(out, banner: banner, sub: sub)
1289
- list_result(ret, 'files', action: 'modified', banner: banner, from: from)
1359
+ list_result(ret, 'files', action: 'modified', from: from)
1290
1360
  end
1291
1361
 
1292
1362
  def revbuild(flag = nil, opts = [], **kwargs)
@@ -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')
@@ -1454,7 +1525,7 @@ module Squared
1454
1525
  out, banner, from = source(io: true)
1455
1526
  print_item banner
1456
1527
  ret = write_lines(out, grep: op.extras)
1457
- list_result(ret, 'tags', grep: op.extras, banner: banner, from: from)
1528
+ list_result(ret, 'tags', grep: op.extras, from: from)
1458
1529
  return
1459
1530
  end
1460
1531
  if (ret = source) && (remote ||= option('remote'))
@@ -1463,11 +1534,11 @@ 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
- op = OptionPartition.new(opts, collect_hash(OPT_GIT[:log]), cmd,
1469
- project: self, strict: strict?, no: collect_hash(OPT_GIT[:no][:log]),
1470
- first: matchpathspec)
1539
+ op = OptionPartition.new(opts, collect_hash(OPT_GIT[:log]), cmd, project: self, strict: strict?,
1540
+ no: collect_hash(OPT_GIT[:no][:log]),
1541
+ first: matchpathspec)
1471
1542
  case flag
1472
1543
  when :between, :contain
1473
1544
  op.add_quote(range.join(flag == :between ? '..' : '...'))
@@ -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
@@ -1722,7 +1794,7 @@ module Squared
1722
1794
  opt_style(color(:green), /^(\*\s+)(\S+)(.*)$/, 2),
1723
1795
  opt_style(color(:red), %r{^(\s*)(remotes/\S+)(.*)$}, 2)
1724
1796
  ])
1725
- list_result(ret, 'branches', banner: banner, from: from)
1797
+ list_result(ret, 'branches', from: from)
1726
1798
  return
1727
1799
  else
1728
1800
  if (head = git_spawn('rev-parse --abbrev-ref HEAD').chomp).empty?
@@ -1731,7 +1803,7 @@ module Squared
1731
1803
  git_spawn 'fetch --all --prune --quiet' if option('sync')
1732
1804
  cmd << '-vv --no-abbrev --list'
1733
1805
  out, banner, from = source(io: true)
1734
- first = from_base?('branch')
1806
+ first = workspace.size > 1
1735
1807
  grep = first ? [/^\*\s+#{Regexp.escape(head)}\s/] : []
1736
1808
  ret = write_lines(out, grep: grep, banner: banner, first: first) do |line, index|
1737
1809
  next line if stdin?
@@ -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
@@ -1804,7 +1876,9 @@ module Squared
1804
1876
 
1805
1877
  def restore(flag, opts = [], commit: nil, files: nil)
1806
1878
  cmd, opts = git_session('restore', shell_option(flag, commit, escape: false, force: false), opts: opts)
1807
- op = OptionPartition.new(opts, OPT_GIT[:restore] + OPT_GIT[:log][:diff_context], cmd,
1879
+ op = OptionPartition.new(opts,
1880
+ OPT_GIT[:restore] + OPT_GIT[:log][:diff_context],
1881
+ cmd,
1808
1882
  project: self, strict: strict?, no: OPT_GIT[:no][:restore], first: matchpathspec)
1809
1883
  append_pathspec(op.extras + (files || []), pass: false)
1810
1884
  source(sync: false, stderr: true)
@@ -1833,7 +1907,8 @@ module Squared
1833
1907
  end
1834
1908
  list = OPT_GIT[:show] + OPT_GIT[:diff][:show] + OPT_GIT[:log][:diff] + OPT_GIT[:log][:diff_context]
1835
1909
  op = OptionPartition.new(opts, list, cmd,
1836
- project: self, strict: strict?,
1910
+ project: self,
1911
+ strict: strict?,
1837
1912
  no: OPT_GIT[:no][:show] + collect_hash(OPT_GIT[:no][:log], pass: [:base]))
1838
1913
  op.append(delim: true)
1839
1914
  source(exception: false, banner: flag != :oneline)
@@ -1858,8 +1933,8 @@ module Squared
1858
1933
  cmd << '--sq-quote'
1859
1934
  args = true
1860
1935
  end
1861
- OptionPartition.new(opts, OPT_GIT[:rev_parse], cmd, project: self, strict: strict?, args: args,
1862
- no: OPT_GIT[:no][:rev_parse])
1936
+ OptionPartition.new(opts, OPT_GIT[:rev_parse], cmd,
1937
+ project: self, strict: strict?, no: OPT_GIT[:no][:rev_parse], args: args)
1863
1938
  .append(escape: args)
1864
1939
  end
1865
1940
  source(banner: verbose?)
@@ -1887,7 +1962,7 @@ module Squared
1887
1962
  out, banner, from = source(io: true)
1888
1963
  print_item banner
1889
1964
  ret = write_lines(out, grep: op.extras, prefix: "refs/#{flag}/")
1890
- list_result(ret, flag.to_s, grep: op.extras, banner: banner, from: from)
1965
+ list_result(ret, flag.to_s, grep: op.extras, from: from)
1891
1966
  end
1892
1967
 
1893
1968
  def ls_files(flag, opts = [])
@@ -1897,12 +1972,12 @@ module Squared
1897
1972
  out, banner, from = source(io: true)
1898
1973
  print_item banner
1899
1974
  ret = write_lines(out, grep: op.extras)
1900
- list_result(ret, 'files', grep: op.extras, banner: banner, from: from)
1975
+ list_result(ret, 'files', grep: op.extras, from: from)
1901
1976
  end
1902
1977
 
1903
- def git(flag, opts = [])
1978
+ def git(flag, opts = [], extras: [])
1904
1979
  cmd, opts = git_session(flag, opts: opts)
1905
- 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 : [] }
1906
1981
  case flag
1907
1982
  when :add
1908
1983
  list.concat(OPT_GIT[:log][:diff_context])
@@ -1951,6 +2026,12 @@ module Squared
1951
2026
  end
1952
2027
  end
1953
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
1954
2035
  when :mv
1955
2036
  refs = projectmap op.extras
1956
2037
  raise 'no source/destination' unless refs.size > 1
@@ -1979,7 +2060,7 @@ module Squared
1979
2060
  when :revert, :mv, :rm
1980
2061
  source(sync: false, stderr: true)
1981
2062
  else
1982
- success?(source, flag == :'sparse-checkout' || (flag == :add && !op.arg?('verbose')))
2063
+ success?(source, %i[remote sparse-checkout].include?(flag) || (flag == :add && !op.arg?('verbose')))
1983
2064
  end
1984
2065
  end
1985
2066
 
@@ -1991,7 +2072,7 @@ module Squared
1991
2072
  build? && !!workspace.revfile
1992
2073
  end
1993
2074
 
1994
- def enabled?(*, **kwargs)
2075
+ def enabled?(**kwargs)
1995
2076
  super || (kwargs[:base] == false && !!clone?)
1996
2077
  end
1997
2078
 
@@ -2066,13 +2147,13 @@ module Squared
2066
2147
  end
2067
2148
  end
2068
2149
 
2069
- def list_result(size, type, action: 'found', grep: [], banner: nil, from: nil)
2150
+ def list_result(size, type, action: 'found', grep: [], from: nil)
2070
2151
  if size == 0
2071
2152
  puts empty_status("No #{type} were #{action}", 'grep', grep.join(', '))
2072
- elsif stdout? && (banner.nil? || (banner.is_a?(String) && !banner.empty?))
2153
+ elsif stdout?
2073
2154
  styles = theme.fetch(:banner, []).reject { |val| val.to_s.end_with?('!') }
2074
2155
  styles << :bold if styles.size <= 1
2075
- 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}",
2076
2157
  sub: opt_style(styles, /^(\d+)(.+)$/))
2077
2158
  end
2078
2159
  on :last, from
@@ -2138,8 +2219,8 @@ module Squared
2138
2219
  append_submodules(target: target, from: from)
2139
2220
  return if !remote && opts.empty?
2140
2221
 
2141
- op = OptionPartition.new(opts, remote ? list + ['refspec=v'] : list, target,
2142
- project: self, strict: strict?, no: no)
2222
+ op = OptionPartition.new(opts, remote ? list + ['refspec=v'] : list, target, project: self, strict: strict?,
2223
+ no: no)
2143
2224
  refspec = op.each_with_object([]) do |opt, out|
2144
2225
  if opt =~ op.values
2145
2226
  case $1
@@ -2279,11 +2360,21 @@ module Squared
2279
2360
  stdout: stdout, banner: banner, **kwargs)
2280
2361
  end
2281
2362
 
2282
- 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, **)
2283
2374
  Array(target).include?('--dry-run') || !option('dry-run', target: target, prefix: prefix).nil?
2284
2375
  end
2285
2376
 
2286
- def quiet?(*, target: @session, **)
2377
+ def quiet?(target: @session, **)
2287
2378
  return silent? unless target
2288
2379
 
2289
2380
  silent? || target.include?('--quiet') || (target.include?('-q') && target.first.stripext == 'git')
@@ -2295,7 +2386,8 @@ module Squared
2295
2386
 
2296
2387
  def repotrack(origin, branch, quote: true)
2297
2388
  n = origin&.index('/')
2298
- 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
+
2299
2391
  branch = "#{branch}:#{origin[n.succ..-1]}" unless origin.end_with?("/#{branch}")
2300
2392
  ret = [origin[0..n.pred], branch]
2301
2393
  ret.quote! if quote