squared 0.7.13 → 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
 
@@ -141,6 +143,7 @@ module Squared
141
143
  end
142
144
  File.write(@revfile, JSON.pretty_generate(@revdoc))
143
145
  rescue => e
146
+ log&.debug e
144
147
  warn log_warn(e, pass: true) if warning
145
148
  ensure
146
149
  @revlock = false
@@ -169,10 +172,9 @@ module Squared
169
172
  fetch: {
170
173
  base: %w[multiple porcelain progress P|prune-tags refetch stdin u|update-head-ok
171
174
  recurse-submodules-default=b?].freeze,
172
- pull: %w[n t 4|ipv4 6|ipv6 a|append atomic dry-run f|force k|keep negotiate-only prefetch p|prune q|quiet
173
- set-upstream unshallow update-shallow v|verbose deepen=i depth=i j|jobs=i negotiation-tip=q
174
- recurse-submodules=v refmap=q o|server-option=q shallow-exclude=b shallow-since=v
175
- upload-pack=q].freeze
175
+ pull: %w[4 6 n t a|append atomic dry-run f|force k|keep negotiate-only prefetch p|prune q|quiet set-upstream
176
+ unshallow update-shallow v|verbose deepen=i depth=i j|jobs=i negotiation-tip=q recurse-submodules=v
177
+ refmap=q o|server-option=q shallow-exclude=b shallow-since=v upload-pack=q].freeze
176
178
  }.freeze,
177
179
  git: {
178
180
  add: %w[N|intent-to-add].freeze,
@@ -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
@@ -212,7 +215,7 @@ module Squared
212
215
  graph ita-invisible-in-index minimal name-only name-status no-color-moved-ws no-prefix no-renames
213
216
  numstat patch-with-raw patch-with-stat patience pickaxe-all pickaxe-regex raw shortstat summary
214
217
  a|text abbrev=i? anchored=q break-rewrites=b? color=b color-moved=b color-moved-ws=b color-words=q?
215
- diff-algorithm=b diff-filter=q? X|dirstat=b? dirstat-by-file=b? dst-prefix=q find-copies=i?
218
+ diff-algorithm=b diff-filter=e? X|dirstat=b? dirstat-by-file=b? dst-prefix=q find-copies=i?
216
219
  find-object=b find-renames=b? ignore-matching-lines=q ignore-submodules=b? line-prefix=q
217
220
  max-depth=n output=p output-indicator-context=q output-indicator-new=q output-indicator-old=q
218
221
  relative=p rotate-to=p skip-to=p src-prefix=q stat=b? stat-count=i stat-width=i stat-name-width=i
@@ -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,
@@ -277,7 +290,7 @@ module Squared
277
290
  checkout: %w[overwrite-ignore guess overlay progress recurse-submodules track].freeze,
278
291
  fetch: {
279
292
  base: %w[auto-gc auto-maintenance write-commit-graph write-fetch-head].freeze,
280
- pull: %w[all recurse-submodules show-forced-updates tags].freeze
293
+ pull: %w[all ipv4 ipv6 recurse-submodules show-forced-updates tags].freeze
281
294
  },
282
295
  grep: %w[color exclude-standard recursive textconv].freeze,
283
296
  log: {
@@ -310,7 +323,7 @@ module Squared
310
323
  }.freeze,
311
324
  rebase: {
312
325
  send: %w[continue skip abort quit].freeze,
313
- value: %w[false true merges interactive].freeze
326
+ value: %w[true false merges interactive].freeze
314
327
  }.freeze,
315
328
  reset: %w[soft mixed hard merge keep recurse-submodules no-recurse-submodules].freeze,
316
329
  revbuild: %w[untracked-files ignore-submodules ignored].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
@@ -601,10 +621,10 @@ module Squared
601
621
  when 'i', 'E', 'F', 'P'
602
622
  opts << last
603
623
  else
604
- if last =~ /^f(?:-ormat)?=(.+)$/
624
+ if last =~ /^(f(-ormat)?)=(.+)$/
605
625
  opts.shift
606
626
  opts << "format=#{$1}"
607
- elsif last =~ /^max(?:-count)?=(\d+)$/
627
+ elsif last =~ /^(max(-count)?)=(\d+)$/
608
628
  opts << "max-count=#{$1}"
609
629
  else
610
630
  grep << last
@@ -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
@@ -1201,11 +1272,7 @@ module Squared
1201
1272
  first: (matchpathspec if flag == :push))
1202
1273
  case flag
1203
1274
  when :push
1204
- if op.remove(':')
1205
- op.readline('Enter message', option: 'message', quote: true)
1206
- else
1207
- append_message
1208
- end
1275
+ op.readline('Enter message', option: 'message', quote: true) if op.remove(':')
1209
1276
  append_pathspec op.extras
1210
1277
  when :pop, :apply, :drop, :branch
1211
1278
  if op.remove(':')
@@ -1223,22 +1290,21 @@ module Squared
1223
1290
  else
1224
1291
  op << out
1225
1292
  end
1226
- elsif !op.empty?
1227
- op.add_first(prefix: ':')
1228
1293
  elsif flag == :branch
1229
- raise ArgumentError, 'no branch name'
1294
+ op.add_first(prefix: ':', expect: 'no branch name')
1230
1295
  end
1231
- op.clear
1296
+ op.add_first
1297
+ .clear
1232
1298
  when :clear
1233
1299
  n = sub_style file.read.lines.size, theme[:inline]
1234
1300
  s = sub_style name, theme[:active]
1235
1301
  source(stdout: true) if confirm("Remove #{n} stash entries from #{s}?", 'N')
1236
1302
  return
1237
- when :list
1303
+ else
1238
1304
  op.clear
1239
1305
  out, banner, from = source(io: true)
1240
1306
  print_item banner
1241
- list_result(write_lines(out), 'objects', banner: banner, from: from)
1307
+ list_result(write_lines(out), 'objects', from: from)
1242
1308
  return
1243
1309
  end
1244
1310
  else
@@ -1290,7 +1356,7 @@ module Squared
1290
1356
  end
1291
1357
  out, banner, from = source(io: true)
1292
1358
  ret = write_lines(out, banner: banner, sub: sub)
1293
- list_result(ret, 'files', action: 'modified', banner: banner, from: from)
1359
+ list_result(ret, 'files', action: 'modified', from: from)
1294
1360
  end
1295
1361
 
1296
1362
  def revbuild(flag = nil, opts = [], **kwargs)
@@ -1325,6 +1391,7 @@ module Squared
1325
1391
 
1326
1392
  sync = kwargs.fetch(:sync) { invoked_sync?('revbuild', flag) }
1327
1393
  kwargs = kwargs.key?(:include) || kwargs.key?(:exclude) ? kw.call : @revbuild || { pass: true }
1394
+ from = symjoin 'git', 'revbuild'
1328
1395
  case flag
1329
1396
  when :build
1330
1397
  op = OptionPartition.new(opts, VAL_GIT[:revbuild].map { |key| "#{key}=b?" }, project: self, strict: strict?)
@@ -1337,7 +1404,7 @@ module Squared
1337
1404
  .compact
1338
1405
  OptionPartition.uniq!(args)
1339
1406
  end
1340
- 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]
1341
1408
  force = if (force = env('REVBUILD_FORCE', strict: true))
1342
1409
  force != '0' && force != 'false'
1343
1410
  elsif (force = ENV['GIT_FORCE'] || ENV['REVBUILD_FORCE'])
@@ -1352,7 +1419,7 @@ module Squared
1352
1419
  end
1353
1420
  end
1354
1421
  start = time_epoch
1355
- ret = build(*@output, sync: sync, from: symjoin('git', 'revbuild'))
1422
+ ret = build(*@output, sync: sync, from: from)
1356
1423
  rescue => e
1357
1424
  print_error(e, pass: true)
1358
1425
  else
@@ -1360,7 +1427,7 @@ module Squared
1360
1427
  print_status('revbuild', subject: name, start: start, loglevel: Logger::WARN, from: :failed)
1361
1428
  workspace.rev_timeutc name, 'build'
1362
1429
  else
1363
- 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
1364
1431
  print_status('revbuild', subject: name, start: start)
1365
1432
  workspace.rev_write(name, { 'revision' => sha, 'files' => status_digest(*args, **kwargs) },
1366
1433
  sync: sync, utc: 'build')
@@ -1458,7 +1525,7 @@ module Squared
1458
1525
  out, banner, from = source(io: true)
1459
1526
  print_item banner
1460
1527
  ret = write_lines(out, grep: op.extras)
1461
- list_result(ret, 'tags', grep: op.extras, banner: banner, from: from)
1528
+ list_result(ret, 'tags', grep: op.extras, from: from)
1462
1529
  return
1463
1530
  end
1464
1531
  if (ret = source) && (remote ||= option('remote'))
@@ -1467,11 +1534,11 @@ module Squared
1467
1534
  ret
1468
1535
  end
1469
1536
 
1470
- def log!(flag, opts = [], range: [], index: [], grep: [])
1537
+ def log_(flag, opts = [], range: [], index: [], grep: [])
1471
1538
  cmd, opts = git_session('log', opts: opts)
1472
- op = OptionPartition.new(opts, collect_hash(OPT_GIT[:log]), cmd,
1473
- project: self, strict: strict?, no: collect_hash(OPT_GIT[:no][:log]),
1474
- 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)
1475
1542
  case flag
1476
1543
  when :between, :contain
1477
1544
  op.add_quote(range.join(flag == :between ? '..' : '...'))
@@ -1484,7 +1551,6 @@ module Squared
1484
1551
  append_pathspec op.extras
1485
1552
  source(exception: false)
1486
1553
  end
1487
- alias log_ log!
1488
1554
 
1489
1555
  def diff(flag, opts = [], refs: [], branch: nil, range: [], index: [], from: :diff)
1490
1556
  cmd, opts = git_session(from, opts: opts)
@@ -1517,7 +1583,8 @@ module Squared
1517
1583
  op.add_quote(branch) if branch
1518
1584
  if !index.empty?
1519
1585
  if op.arg?('cached')
1520
- 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
+
1521
1588
  op << index.first
1522
1589
  else
1523
1590
  op.merge(index)
@@ -1528,7 +1595,7 @@ module Squared
1528
1595
  end
1529
1596
  if (n = op.index('>'))
1530
1597
  patch = op.slice!(n, 2)[1]
1531
- elsif !op.last.include?('*') && !op.exist?(op.last)
1598
+ elsif !op.exist?(op.last)
1532
1599
  patch ||= op.pop
1533
1600
  end
1534
1601
  append_pathspec op.extras
@@ -1580,9 +1647,9 @@ module Squared
1580
1647
  next if line.empty?
1581
1648
 
1582
1649
  branch, origin, hint = line.split('...')
1583
- if hint && !hint.match?(/^\[(\D+0,\D+0)\]$/)
1584
- raise_error 'work tree is not usable', hint: hint[1..-2]
1585
- 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?
1586
1653
  return nil if pass
1587
1654
 
1588
1655
  unless (origin = option('upstream', prefix: 'git', ignore: false))
@@ -1685,7 +1752,8 @@ module Squared
1685
1752
  cmd << shell_quote(target)
1686
1753
  cmd << shell_quote(ref) if ref
1687
1754
  when :track
1688
- raise_error 'invalid upstream', hint: ref unless ref.include?('/')
1755
+ raise message('invalid upstream', hint: ref) unless ref.include?('/')
1756
+
1689
1757
  if ref.delete_prefix!('^')
1690
1758
  cmd << '--unset-upstream' << shell_quote(ref)
1691
1759
  remote = false
@@ -1726,7 +1794,7 @@ module Squared
1726
1794
  opt_style(color(:green), /^(\*\s+)(\S+)(.*)$/, 2),
1727
1795
  opt_style(color(:red), %r{^(\s*)(remotes/\S+)(.*)$}, 2)
1728
1796
  ])
1729
- list_result(ret, 'branches', banner: banner, from: from)
1797
+ list_result(ret, 'branches', from: from)
1730
1798
  return
1731
1799
  else
1732
1800
  if (head = git_spawn('rev-parse --abbrev-ref HEAD').chomp).empty?
@@ -1735,7 +1803,7 @@ module Squared
1735
1803
  git_spawn 'fetch --all --prune --quiet' if option('sync')
1736
1804
  cmd << '-vv --no-abbrev --list'
1737
1805
  out, banner, from = source(io: true)
1738
- first = from_base?('branch')
1806
+ first = workspace.size > 1
1739
1807
  grep = first ? [/^\*\s+#{Regexp.escape(head)}\s/] : []
1740
1808
  ret = write_lines(out, grep: grep, banner: banner, first: first) do |line, index|
1741
1809
  next line if stdin?
@@ -1743,15 +1811,15 @@ module Squared
1743
1811
  data = line.sub(/^\*?\s+/, '').split(/\s+/, 3)
1744
1812
  a = sub_style(data[0], theme[:inline], styles: (:underline if !first && line.start_with?('*')))
1745
1813
  b = commitstyle data[1]
1746
- r = /\A(?:\[((?~\]\s))\]\s)?(.+)\z/m.match(data[2])
1747
- 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+)$/
1748
1816
  write = ->(s1, s2) { "#{s1.capitalize.rjust(7)}: #{sub_style(s2, theme[:warn])}" }
1749
1817
  r1 = $1
1750
1818
  r2 = $2 && write.call($2, $3)
1751
1819
  r3 = write.call($4, $5)
1752
1820
  end
1753
1821
  r1 = nil if r1 == "origin/#{data[0]}"
1754
- ["#{"\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]}"]
1755
1823
  .compact
1756
1824
  .join("\n")
1757
1825
  end
@@ -1808,7 +1876,9 @@ module Squared
1808
1876
 
1809
1877
  def restore(flag, opts = [], commit: nil, files: nil)
1810
1878
  cmd, opts = git_session('restore', shell_option(flag, commit, escape: false, force: false), opts: opts)
1811
- 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,
1812
1882
  project: self, strict: strict?, no: OPT_GIT[:no][:restore], first: matchpathspec)
1813
1883
  append_pathspec(op.extras + (files || []), pass: false)
1814
1884
  source(sync: false, stderr: true)
@@ -1821,7 +1891,7 @@ module Squared
1821
1891
  cmd << '--textconv'
1822
1892
  append_value(files.flat_map { |val| Dir[val] }
1823
1893
  .select { |val| projectpath?(val) }
1824
- .map { |val| "HEAD:#{val}" })
1894
+ .map { |val| shell_quote("HEAD:#{val}") })
1825
1895
  source(banner: false)
1826
1896
  return
1827
1897
  when :oneline
@@ -1835,11 +1905,12 @@ module Squared
1835
1905
  else
1836
1906
  opts << format if format
1837
1907
  end
1838
- list = OPT_GIT[:show] + OPT_GIT[:diff][:show] + collect_hash(OPT_GIT[:log], pass: [:base])
1908
+ list = OPT_GIT[:show] + OPT_GIT[:diff][:show] + OPT_GIT[:log][:diff] + OPT_GIT[:log][:diff_context]
1839
1909
  op = OptionPartition.new(opts, list, cmd,
1840
- project: self, strict: strict?,
1910
+ project: self,
1911
+ strict: strict?,
1841
1912
  no: OPT_GIT[:no][:show] + collect_hash(OPT_GIT[:no][:log], pass: [:base]))
1842
- op.append
1913
+ op.append(delim: true)
1843
1914
  source(exception: false, banner: flag != :oneline)
1844
1915
  end
1845
1916
 
@@ -1862,8 +1933,8 @@ module Squared
1862
1933
  cmd << '--sq-quote'
1863
1934
  args = true
1864
1935
  end
1865
- OptionPartition.new(opts, OPT_GIT[:rev_parse], cmd, project: self, strict: strict?, args: args,
1866
- 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)
1867
1938
  .append(escape: args)
1868
1939
  end
1869
1940
  source(banner: verbose?)
@@ -1890,8 +1961,8 @@ module Squared
1890
1961
  op.add_quote(remote) if remote
1891
1962
  out, banner, from = source(io: true)
1892
1963
  print_item banner
1893
- ret = write_lines(out, grep: op.extras)
1894
- list_result(ret, flag.to_s, grep: op.extras, banner: banner, from: from)
1964
+ ret = write_lines(out, grep: op.extras, prefix: "refs/#{flag}/")
1965
+ list_result(ret, flag.to_s, grep: op.extras, from: from)
1895
1966
  end
1896
1967
 
1897
1968
  def ls_files(flag, opts = [])
@@ -1901,12 +1972,12 @@ module Squared
1901
1972
  out, banner, from = source(io: true)
1902
1973
  print_item banner
1903
1974
  ret = write_lines(out, grep: op.extras)
1904
- list_result(ret, 'files', grep: op.extras, banner: banner, from: from)
1975
+ list_result(ret, 'files', grep: op.extras, from: from)
1905
1976
  end
1906
1977
 
1907
- def git(flag, opts = [])
1978
+ def git(flag, opts = [], extras: [])
1908
1979
  cmd, opts = git_session(flag, opts: opts)
1909
- 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 : [] }
1910
1981
  case flag
1911
1982
  when :add
1912
1983
  list.concat(OPT_GIT[:log][:diff_context])
@@ -1915,7 +1986,8 @@ module Squared
1915
1986
  when :'sparse-checkout'
1916
1987
  cmd << 'set'
1917
1988
  end
1918
- op = OptionPartition.new(opts, list, cmd, project: self, strict: strict?, no: OPT_GIT[:no][flag],
1989
+ op = OptionPartition.new(opts, list, cmd, project: self, strict: strict?,
1990
+ no: OPT_GIT[:no][flag], single: /\A\d+\z/,
1919
1991
  first: case flag
1920
1992
  when :blame, :revert, :'sparse-checkout' then nil
1921
1993
  else matchpathspec
@@ -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
 
@@ -2069,13 +2147,13 @@ module Squared
2069
2147
  end
2070
2148
  end
2071
2149
 
2072
- def list_result(size, type, action: 'found', grep: [], banner: nil, from: nil)
2150
+ def list_result(size, type, action: 'found', grep: [], from: nil)
2073
2151
  if size == 0
2074
2152
  puts empty_status("No #{type} were #{action}", 'grep', grep.join(', '))
2075
- elsif stdout? && (banner.nil? || (banner.is_a?(String) && !banner.empty?))
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
@@ -2104,7 +2182,7 @@ module Squared
2104
2182
  ret = choice_index('Choose a commit', git_spawn(cmd, stdout: false), column: /^(\S+)/, force: force, **kwargs)
2105
2183
  case ret
2106
2184
  when Array
2107
- ret.map { |val| val.is_a?(Array) ? val.map { |s| s&.stripstyle } : val&.stripstyle }
2185
+ ret.map(&:stripstyle)
2108
2186
  when String
2109
2187
  ret.stripstyle
2110
2188
  else
@@ -2141,8 +2219,8 @@ module Squared
2141
2219
  append_submodules(target: target, from: from)
2142
2220
  return if !remote && opts.empty?
2143
2221
 
2144
- op = OptionPartition.new(opts, remote ? list + ['refspec=v'] : list, target,
2145
- project: self, strict: strict?, no: no)
2222
+ op = OptionPartition.new(opts, remote ? list + ['refspec=v'] : list, target, project: self, strict: strict?,
2223
+ no: no)
2146
2224
  refspec = op.each_with_object([]) do |opt, out|
2147
2225
  if opt =~ op.values
2148
2226
  case $1
@@ -2189,9 +2267,9 @@ module Squared
2189
2267
  end
2190
2268
  end
2191
2269
 
2192
- def append_pathspec(files = [], target: @session, expect: false, strict: strict?, **kwargs)
2270
+ def append_pathspec(files = [], target: @session, expect: false, **kwargs)
2193
2271
  if session_arg?('pathspec-from-file', target: target)
2194
- OptionPartition.clear(target, files, strict: strict, styles: theme[:inline])
2272
+ OptionPartition.clear(target, files, styles: theme[:inline])
2195
2273
  true
2196
2274
  else
2197
2275
  option('pathspec', target: target) { |val| files = split_escape(val) } if files.empty?
@@ -2282,7 +2360,21 @@ module Squared
2282
2360
  stdout: stdout, banner: banner, **kwargs)
2283
2361
  end
2284
2362
 
2285
- def quiet?(*, target: @session, **)
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, **)
2374
+ Array(target).include?('--dry-run') || !option('dry-run', target: target, prefix: prefix).nil?
2375
+ end
2376
+
2377
+ def quiet?(target: @session, **)
2286
2378
  return silent? unless target
2287
2379
 
2288
2380
  silent? || target.include?('--quiet') || (target.include?('-q') && target.first.stripext == 'git')
@@ -2294,7 +2386,8 @@ module Squared
2294
2386
 
2295
2387
  def repotrack(origin, branch, quote: true)
2296
2388
  n = origin&.index('/')
2297
- 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
+
2298
2391
  branch = "#{branch}:#{origin[n.succ..-1]}" unless origin.end_with?("/#{branch}")
2299
2392
  ret = [origin[0..n.pred], branch]
2300
2393
  ret.quote! if quote