squared 0.6.8 → 0.7.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.
@@ -84,7 +84,7 @@ module Squared
84
84
  def revbuild(file: nil)
85
85
  @revfile = @home.join(file || "#{@main}.revb")
86
86
  @revdoc = JSON.parse(@revfile.read) if @revfile.exist?
87
- rescue StandardError => e
87
+ rescue => e
88
88
  @revfile = nil
89
89
  warn log_warn(e, pass: true)
90
90
  self
@@ -117,7 +117,7 @@ module Squared
117
117
 
118
118
  def rev_timesince(*keys, clock: false)
119
119
  epoch = time_epoch - rev_entry(*keys).to_i
120
- rescue StandardError
120
+ rescue
121
121
  nil
122
122
  else
123
123
  time_format(epoch, clock: clock)
@@ -140,7 +140,7 @@ module Squared
140
140
  rev_timeutc(name, utc) if utc
141
141
  end
142
142
  File.write(@revfile, JSON.pretty_generate(@revdoc))
143
- rescue StandardError => e
143
+ rescue => e
144
144
  log&.debug e
145
145
  warn log_warn(e, pass: true) if warning
146
146
  ensure
@@ -190,7 +190,8 @@ module Squared
190
190
  revert: %w[e S=bm? n|no-commit reference cleanup=b gpg-sign=b? m|mainline=i s|signoff strategy=b
191
191
  X|strategy-option=b].freeze,
192
192
  rm: %w[r cached f|force n|dry-run ignore-unmatch pathspec-file-nul q|quiet sparse
193
- pathspec-from-file=p].freeze
193
+ pathspec-from-file=p].freeze,
194
+ 'sparse-checkout': %w[cone sparse-index no-cone no-sparse-index skip-checks].freeze
194
195
  }.freeze,
195
196
  log: {
196
197
  base: %w[L=qm all all-match alternate-refs author-date-order basic-regexp bisect boundary cherry cherry-mark
@@ -242,6 +243,10 @@ module Squared
242
243
  show-object-format=b? since=q tags=q? until=q].freeze,
243
244
  show: %w[t combined-all-paths no-diff-merges remerge-diff show-notes-by-default show-signature diff-merges=b
244
245
  encoding=b expand-tabs=i notes=q show-notes=q?].freeze,
246
+ sparse_checkout: {
247
+ add: %w[skip-checks].freeze,
248
+ clean: %w[n f v].freeze
249
+ }.freeze,
245
250
  stash: {
246
251
  common: %w[q|quiet].freeze,
247
252
  push: %w[a|all u|include-untracked k|keep-index no-keep-index no-include-untracked pathspec-file-nul p|patch
@@ -287,6 +292,10 @@ module Squared
287
292
  rev_parse: %w[flags].freeze,
288
293
  revert: %w[edit gpg-sign rerere-autoupdate].freeze,
289
294
  show: %w[standard-notes].freeze,
295
+ sparse_checkout: {
296
+ reapply: %w[cone sparse-index].freeze,
297
+ clean: %w[dry-run force verbose].freeze
298
+ }.freeze,
290
299
  status: %w[ahead-behind column renames].freeze,
291
300
  switch: %w[guess progress recurse-submodules track].freeze,
292
301
  tag: %w[column].freeze
@@ -326,7 +335,7 @@ module Squared
326
335
  'diff' => %i[head branch files view between contain].freeze,
327
336
  'fetch' => %i[origin remote all].freeze,
328
337
  'files' => %i[cached modified deleted others].freeze,
329
- 'git' => %i[add blame clean grep mv revert rm status].freeze,
338
+ 'git' => %i[add blame clean grep mv revert rm sparse-checkout status].freeze,
330
339
  'log' => %i[view grep between contain].freeze,
331
340
  'merge' => %i[commit no-commit send].freeze,
332
341
  'pull' => %i[origin remote all].freeze,
@@ -334,8 +343,9 @@ module Squared
334
343
  'refs' => %i[heads tags remote].freeze,
335
344
  'reset' => %i[commit index patch mode undo].freeze,
336
345
  'restore' => %i[source staged worktree].freeze,
337
- 'rev' => %i[commit build output].freeze,
346
+ 'rev' => %i[commit branch build output].freeze,
338
347
  'show' => %i[format oneline textconv].freeze,
348
+ 'sparse-checkout' => %i[add reapply list clean disable].freeze,
339
349
  'stash' => %i[push pop apply branch drop clear list all staged worktree].freeze,
340
350
  'submodule' => %i[status update branch url sync].freeze,
341
351
  'switch' => %i[branch create detach].freeze,
@@ -479,9 +489,8 @@ module Squared
479
489
  ])
480
490
  choice_remote
481
491
  end
482
- tag(flag, refs: [name], message: message, commit: commit, remote: remote).tap do |ret|
483
- success?(ret, !remote)
484
- end
492
+ ret = tag(flag, refs: [name], message: message, commit: commit, remote: remote)
493
+ success?(ret, !remote)
485
494
  end
486
495
  end
487
496
  when 'stash'
@@ -505,16 +514,15 @@ module Squared
505
514
  args.shift
506
515
  index = choice_commit(multiple: true)
507
516
  else
508
- index = []
509
- args.each do |val|
517
+ index = args.each_with_object([]) do |val, out|
510
518
  if matchhead(val)
511
- index << commithead(val)
519
+ out << commithead(val)
512
520
  elsif (sha = commithash(val))
513
- index << sha
521
+ out << sha
514
522
  elsif val.start_with?('^')
515
- index << shell_quote(val)
523
+ out << shell_quote(val)
516
524
  else
517
- break
525
+ break out
518
526
  end
519
527
  end
520
528
  args = args.drop(index.size)
@@ -547,11 +555,10 @@ module Squared
547
555
  args.shift
548
556
  index = choice_commit(multiple: true)
549
557
  else
550
- index = []
551
- args.each do |val|
552
- break unless (sha = commithead(val) || commithash(val))
558
+ index = args.each_with_object([]) do |val, out|
559
+ break out unless (sha = commithead(val) || commithash(val))
553
560
 
554
- index << sha
561
+ out << sha
555
562
  end
556
563
  args = args.drop(index.size)
557
564
  end
@@ -697,12 +704,12 @@ module Squared
697
704
  format_desc action, flag, '[^~]name*,:?'
698
705
  task flag do |_, args|
699
706
  refs = args.to_a
700
- if refs.empty? || (r = refs.last == ':')
707
+ if refs.empty? || (i = refs.last == ':')
701
708
  accept = ['Delete?']
702
- accept << accept_b('Force?') unless r
703
- remote = choice_refs('Choose a branch', r ? 'remotes' : 'heads', multiple: true,
709
+ accept << accept_b('Force?') unless i
710
+ remote = choice_refs('Choose a branch', i ? 'remotes' : 'heads', multiple: true,
704
711
  accept: accept)
705
- if r
712
+ if i
706
713
  refs.pop
707
714
  else
708
715
  refs = remote.first
@@ -882,6 +889,12 @@ module Squared
882
889
  end
883
890
  rev_parse(flag, ref: ref, size: size)
884
891
  end
892
+ when :branch
893
+ format_desc action, flag, 'ref?=HEAD|upstream|push'
894
+ task flag, [:ref] do |_, args|
895
+ ret = rev_parse(flag, ref: args.ref || 'upstream')
896
+ puts ret unless ret.empty?
897
+ end
885
898
  when :build
886
899
  next unless build?
887
900
 
@@ -927,20 +940,28 @@ module Squared
927
940
  task flag do |_, args|
928
941
  args = args.to_a
929
942
  if args.empty? || args.last == ':'
930
- files = []
931
- status_data.each { |row| files << row[0] if row[flag == :staged ? 2 : 1].match?(/[AMDRTC]/) }
943
+ files = status_data.each_with_object([]) do |row, out|
944
+ out << row[0] if row[flag == :staged ? 2 : 1].match?(/[AMDRTC]/)
945
+ end
932
946
  unless files.empty?
933
947
  files = choice_index('Select a file', files, multiple: true, force: false,
934
948
  accept: 'Restore?')
935
949
  end
936
950
  args.pop
937
- args, glob = args.partition { |val| val.match?(/^(?:[a-z-]+=|[^*]+$)/) }
951
+ args, glob = args.partition { |val| val.match?(/^([a-z-]+=|[^*]+$)/) }
938
952
  files.concat(glob)
939
953
  next if args.empty? && files.empty?
940
954
  end
941
955
  restore(flag, args, files: files)
942
956
  end
943
957
  end
958
+ when 'sparse-checkout'
959
+ break unless matchfile(gitpath('config.worktree'), /\bsparseCheckout\s+=\s+true/)
960
+
961
+ format_desc(action, flag, 'opts*', after: ('dir+' if flag == :add))
962
+ task flag do |_, args|
963
+ sparse_checkout flag, args.to_a
964
+ end
944
965
  when 'git'
945
966
  before = case flag
946
967
  when :blame
@@ -955,6 +976,8 @@ module Squared
955
976
  'pathspec*,pattern*'
956
977
  when :grep
957
978
  'tree*,pathspec*'
979
+ when :'sparse-checkout'
980
+ 'dir+'
958
981
  when :clean, :rm, :status
959
982
  'pathspec*'
960
983
  end
@@ -1019,12 +1042,11 @@ module Squared
1019
1042
  heads = []
1020
1043
  cur = nil
1021
1044
  foreachref('heads', format: '%(if)%(HEAD)%(then)* %(end)%(refname:short)').each do |line|
1022
- line.chomp!
1023
1045
  cur ||= line.delete_prefix!('* ')
1024
- heads << line if matchany?(line, reg)
1046
+ heads << line if matchany?(reg, line)
1025
1047
  end
1026
1048
  raise_error 'head not found', hint: 'for-each-ref' unless cur
1027
- opts << 'ff-only' if opts.empty? && !option('ff-only', equals: '0')
1049
+ opts << 'ff-only' if opts.empty? && option('ff-only', notequals: '0')
1028
1050
  (heads.dup << cur).each_with_index do |branch, i|
1029
1051
  next unless (i < heads.size && cur != branch) || i == heads.size
1030
1052
 
@@ -1038,12 +1060,16 @@ module Squared
1038
1060
  end
1039
1061
  append_pull(opts, OPT_GIT[:pull] + OPT_GIT[:fetch][:pull],
1040
1062
  flag: flag, from: :pull, remote: remote, no: OPT_GIT[:no][:pull] + OPT_GIT[:no][:fetch][:pull])
1041
- source(sync: sync, sub: if stdout?
1042
- [
1043
- opt_style(color(:red), /^(.+)(\|\s+\d+\s+)([^-]*)(-+)(.*)$/, 4),
1044
- opt_style(color(:green), /^(.+)(\|\s+\d+\s+)(\++)(.*)$/, 3)
1045
- ]
1046
- end, hint: hint, **threadargs)
1063
+ if sync
1064
+ source(hint: hint)
1065
+ else
1066
+ source(sync: false, sub: if stdout?
1067
+ [
1068
+ opt_style(color(:red), /^(.+)(\|\s+\d+\s+)([^-]*)(-+)(.*)$/, 4),
1069
+ opt_style(color(:green), /^(.+)(\|\s+\d+\s+)(\++)(.*)$/, 3)
1070
+ ]
1071
+ end, hint: hint, **threadargs)
1072
+ end
1047
1073
  end
1048
1074
 
1049
1075
  def rebase(flag = nil, opts = [], sync: invoked_sync?('rebase', flag), commit: nil, upstream: nil, branch: nil,
@@ -1075,7 +1101,7 @@ module Squared
1075
1101
 
1076
1102
  cmd << "--#{command}"
1077
1103
  end
1078
- source
1104
+ source(sync: sync)
1079
1105
  end
1080
1106
 
1081
1107
  def autostash(*, sync: invoked_sync?('autostash'), **)
@@ -1087,7 +1113,11 @@ module Squared
1087
1113
  opts << 'all' if flag == :all || option('all')
1088
1114
  append_pull(opts, collect_hash(OPT_GIT[:fetch]), flag: flag, from: :fetch, remote: remote,
1089
1115
  no: collect_hash(OPT_GIT[:no][:fetch]))
1090
- source(sync: sync, **threadargs)
1116
+ if sync
1117
+ source
1118
+ else
1119
+ source(sync: false, **threadargs)
1120
+ end
1091
1121
  end
1092
1122
 
1093
1123
  def clone(*, sync: invoked_sync?('clone'), **)
@@ -1128,7 +1158,7 @@ module Squared
1128
1158
  append_hash opts
1129
1159
  cmd << '--quiet' if option('quiet') || !verbose
1130
1160
  append_value(data[0], path, delim: true)
1131
- source(banner: sync && !quiet?, multiple: !sync || quiet?)
1161
+ source(sync: sync, banner: sync && !quiet?, multiple: !sync || quiet?)
1132
1162
  end
1133
1163
 
1134
1164
  def stash(flag = nil, opts = [], sync: invoked_sync?('stash', flag))
@@ -1195,10 +1225,14 @@ module Squared
1195
1225
  end
1196
1226
  else
1197
1227
  git_session('stash', 'push', opts: opts)
1198
- append_option(OptionPartition.select(OPT_GIT[:stash][:push], no: false), no: true, ignore: false)
1228
+ append_option(*OptionPartition.select(OPT_GIT[:stash][:push], no: false), no: true, ignore: false)
1199
1229
  append_message
1200
1230
  end
1201
- source(sync: sync, banner: !quiet?, **threadargs)
1231
+ if sync
1232
+ source(banner: !quiet?)
1233
+ else
1234
+ source(sync: false, banner: !quiet?, **threadargs)
1235
+ end
1202
1236
  end
1203
1237
 
1204
1238
  def status(flag = nil, opts = [])
@@ -1211,12 +1245,10 @@ module Squared
1211
1245
  cmd << '--branch' if option('branch')
1212
1246
  option('ignore-submodules', ignore: false) do |val|
1213
1247
  cmd << basic_option('ignore-submodules', case val
1214
- when '0', 'none'
1248
+ when 'none', '0', 'false'
1215
1249
  'none'
1216
- when '1', 'untracked'
1217
- 'untracked'
1218
- when '2', 'dirty'
1219
- 'dirty'
1250
+ when 'untracked', 'dirty'
1251
+ val
1220
1252
  else
1221
1253
  'all'
1222
1254
  end)
@@ -1242,7 +1274,7 @@ module Squared
1242
1274
  list_result(ret, 'files', action: 'modified', from: from)
1243
1275
  end
1244
1276
 
1245
- def revbuild(flag = nil, opts = [], sync: nil, **kwargs)
1277
+ def revbuild(flag = nil, opts = [], **kwargs)
1246
1278
  kw = lambda do
1247
1279
  {
1248
1280
  include: relativepath(*Array(kwargs[:include]), all: true),
@@ -1260,7 +1292,7 @@ module Squared
1260
1292
  sha = git_spawn('rev-parse --verify HEAD').chomp
1261
1293
  return if sha.empty?
1262
1294
 
1263
- sync = invoked_sync?('revbuild', flag) if sync.nil?
1295
+ sync = kwargs.fetch(:sync) { invoked_sync?('revbuild', flag) }
1264
1296
  kwargs = kwargs.key?(:include) || kwargs.key?(:exclude) ? kw.call : @revbuild || {}
1265
1297
  case flag
1266
1298
  when :build
@@ -1283,11 +1315,11 @@ module Squared
1283
1315
  end
1284
1316
  end
1285
1317
  start = time_epoch
1286
- build(*@output, sync: sync, from: :'git:revbuild')
1287
- rescue StandardError => e
1318
+ build(*@output, sync: sync, from: symjoin('git', 'revbuild'))
1319
+ rescue => e
1288
1320
  print_error(e, pass: true)
1289
1321
  else
1290
- print_status('revbuild', subject: name, start: start, from: :completed)
1322
+ print_status('revbuild', subject: name, start: start)
1291
1323
  workspace.rev_write(name, { 'revision' => sha, 'files' => status_digest(*args, **kwargs) },
1292
1324
  sync: sync, utc: 'build')
1293
1325
  end
@@ -1341,9 +1373,9 @@ module Squared
1341
1373
  cmd << '-m' if merge
1342
1374
  cmd << '--detach' << commit
1343
1375
  else
1344
- list = OPT_GIT[:checkout] + OPT_GIT[:log][:diff_context]
1345
- op = OptionPartition.new(opts, list, cmd, project: self, no: OPT_GIT[:no][:checkout],
1346
- first: (matchpathspec if flag == :path))
1376
+ op = OptionPartition.new(opts, OPT_GIT[:checkout] + OPT_GIT[:log][:diff_context], cmd,
1377
+ project: self, no: OPT_GIT[:no][:checkout],
1378
+ first: (matchpathspec if flag == :path))
1347
1379
  if flag == :path
1348
1380
  append_head
1349
1381
  append_pathspec(op.extras, pass: false)
@@ -1383,8 +1415,10 @@ module Squared
1383
1415
  list_result(ret, 'tags', grep: op.extras, from: from)
1384
1416
  return
1385
1417
  end
1386
- remote ||= option('remote')
1387
- source.tap { |ret| git_spawn('push', ('-d' if flag == :delete), remote, *refs.quote!) if ret && remote }
1418
+ if (ret = source) && (remote ||= option('remote'))
1419
+ git_spawn('push', ('-d' if flag == :delete), remote, *refs.quote!)
1420
+ end
1421
+ ret
1388
1422
  end
1389
1423
 
1390
1424
  def log!(flag, opts = [], range: [], index: [], grep: [])
@@ -1488,7 +1522,7 @@ module Squared
1488
1522
  format = '%(if)%(HEAD)%(then)%(refname:short)...%(upstream:short)...%(upstream:track)%(end)'
1489
1523
  git_spawn 'fetch --no-tags --quiet'
1490
1524
  foreachref('heads', format: format).each do |line|
1491
- next if (line = line.chomp).empty?
1525
+ next if line.empty?
1492
1526
 
1493
1527
  branch, origin, hint = line.split('...')
1494
1528
  if hint && !hint.match?(/^\[(\D+0,\D+0)\]$/)
@@ -1533,7 +1567,7 @@ module Squared
1533
1567
  cached = git_spawn 'diff --cached --name-only --no-color'
1534
1568
  if amend || !cached.empty? || dryrun?
1535
1569
  if adding.empty? && !cached.empty? && banner?
1536
- puts(cached.lines(chomp: true).map! { |val| "cached #{shell_quote(val)}" })
1570
+ puts(cached.lines(chomp: true).map { |val| "cached #{shell_quote(val)}" })
1537
1571
  end
1538
1572
  source co
1539
1573
  source pu
@@ -1605,7 +1639,7 @@ module Squared
1605
1639
  end
1606
1640
  when :delete
1607
1641
  remote&.each { |val| source git_output('push --delete', *val.split('/', 2).quote!) }
1608
- force, list = refs.partition { |val| val.start_with?(/[~^]/) }
1642
+ force, list = refs.partition { |val| val.start_with?('~', '^') }
1609
1643
  force.each do |val|
1610
1644
  r = '-r' if val.delete!('~')
1611
1645
  source git_output('branch', val.delete!('^') ? '-D' : '-d', r, shell_quote(val))
@@ -1712,7 +1746,7 @@ module Squared
1712
1746
  op << '--recursive' if option('r', 'recursive')
1713
1747
  op.splice(path: true)
1714
1748
  end
1715
- source.tap { |ret| success?(ret, flag == :branch) }
1749
+ success?(source, flag == :branch)
1716
1750
  end
1717
1751
 
1718
1752
  def restore(flag, opts = [], commit: nil, files: nil)
@@ -1732,7 +1766,7 @@ module Squared
1732
1766
  cmd << '--textconv'
1733
1767
  append_value(files.flat_map { |val| Dir[val] }
1734
1768
  .select { |val| projectpath?(val) }
1735
- .map! { |val| shell_quote("HEAD:#{val}") })
1769
+ .map { |val| shell_quote("HEAD:#{val}") })
1736
1770
  source(banner: false)
1737
1771
  return
1738
1772
  when :oneline
@@ -1760,8 +1794,13 @@ module Squared
1760
1794
  cmd << (size.to_i.zero? ? '--verify' : basic_option('short', [size.to_i, 5].max))
1761
1795
  append_commit(ref, head: true)
1762
1796
  when :branch
1763
- cmd << '--abbrev-ref'
1764
- append_commit(ref, head: true)
1797
+ cmd << '--abbrev-ref' << '--symbolic-full-name' << shell_quote(case ref
1798
+ when 'upstream', 'push'
1799
+ "@{#{ref}}"
1800
+ else
1801
+ ref
1802
+ end)
1803
+ return git_spawn(cmd, exception: false).chomp
1765
1804
  when :output
1766
1805
  if opts.delete('sq-quote')
1767
1806
  cmd << '--sq-quote'
@@ -1773,6 +1812,20 @@ module Squared
1773
1812
  source(banner: verbose?)
1774
1813
  end
1775
1814
 
1815
+ def sparse_checkout(flag, opts = [])
1816
+ cmd, opts = git_session('sparse-checkout', flag, opts: opts)
1817
+ op = OptionPartition.new(opts, OPT_GIT[:sparse_checkout].fetch(flag, []), cmd,
1818
+ project: self, no: OPT_GIT[:no][:sparse_checkout][flag])
1819
+ case flag
1820
+ when :add
1821
+ append_pathspec(op.detach, expect: true, resolve: false, pass: false)
1822
+ when :clean
1823
+ op << '--dry-run' unless op.arg?('n', 'f', 'force')
1824
+ end
1825
+ op.clear
1826
+ success?(source, flag != :list)
1827
+ end
1828
+
1776
1829
  def ls_remote(flag, opts = [], remote: nil)
1777
1830
  cmd, opts = git_session('ls-remote --refs', opts: opts)
1778
1831
  cmd << "--#{flag}" unless flag == :remote
@@ -1802,10 +1855,12 @@ module Squared
1802
1855
  list.concat(OPT_GIT[:log][:diff_context])
1803
1856
  when :revert
1804
1857
  list.concat(VAL_GIT[:rebase][:send])
1858
+ when :'sparse-checkout'
1859
+ cmd << 'set'
1805
1860
  end
1806
1861
  op = OptionPartition.new(opts, list, cmd, project: self, no: OPT_GIT[:no][flag], single: /\A\d+\z/,
1807
1862
  first: case flag
1808
- when :blame, :revert then nil
1863
+ when :blame, :revert, :'sparse-checkout' then nil
1809
1864
  else matchpathspec
1810
1865
  end)
1811
1866
  case flag
@@ -1826,12 +1881,12 @@ module Squared
1826
1881
  grep, pathspec = op.partition { |val| OptionPartition.pattern?(val) }
1827
1882
  unless grep.empty? && !pathspec.empty?
1828
1883
  grep.map! { |val| Regexp.new(val[1..-2]) }
1829
- files = []
1830
- status_data.each do |a, b|
1884
+ files = status_data.map do |a, b|
1831
1885
  next if b.strip.empty? || (!grep.empty? && grep.none? { |pat| pat.match?(a) })
1832
1886
 
1833
- files << "#{sub_style(b, color(:red))} #{a}"
1887
+ "#{sub_style(b, color(:red))} #{a}"
1834
1888
  end
1889
+ .compact
1835
1890
  unless files.empty?
1836
1891
  files = choice_index('Select files', files, multiple: true, trim: /^\S+\s/,
1837
1892
  accept: [accept_y('Add?')])
@@ -1840,13 +1895,12 @@ module Squared
1840
1895
  end
1841
1896
  end
1842
1897
  return source(git_session('status -s'), banner: false) unless append_pathspec(op.extras)
1843
- return success?(source) if flag == :add && !op.arg?('verbose')
1844
1898
  when :mv
1845
1899
  refs = projectmap op.extras
1846
1900
  raise_error 'no source/destination' unless refs.size > 1
1847
1901
  op.merge(refs)
1848
- when :rm, :clean
1849
- append_pathspec(op.extras, expect: flag == :rm)
1902
+ when :rm, :clean, :'sparse-checkout'
1903
+ append_pathspec(op.extras, expect: flag != :clean, resolve: flag != :'sparse-checkout')
1850
1904
  when :grep
1851
1905
  op.each do |val|
1852
1906
  if op.include?('--')
@@ -1863,7 +1917,7 @@ module Squared
1863
1917
  when :revert, :mv, :rm
1864
1918
  source(sync: false, stderr: true)
1865
1919
  else
1866
- source
1920
+ success?(source, flag == :'sparse-checkout' || (flag == :add && !op.arg?('verbose')))
1867
1921
  end
1868
1922
  end
1869
1923
 
@@ -1882,7 +1936,7 @@ module Squared
1882
1936
  private
1883
1937
 
1884
1938
  def source(cmd = @session, exception: true, io: false, sync: true, stdout: false, stderr: false, banner: true,
1885
- multiple: false, hint: nil, from: nil, send: :system, **kwargs)
1939
+ multiple: false, hint: nil, from: nil, timeout: nil, send: :system, **kwargs)
1886
1940
  cmd = cmd.target if cmd.is_a?(OptionPartition)
1887
1941
  if io && banner == false
1888
1942
  from = nil
@@ -1892,16 +1946,20 @@ module Squared
1892
1946
  banner = nil unless banner? && !multiple
1893
1947
  args = true
1894
1948
  end
1895
- if from.nil? && (from = cmd.drop(1).find { |val| val.match?(/\A[a-z]{1,2}[a-z-]*\z/) })
1896
- from = :"git:#{from}"
1897
- elsif from == false
1949
+ if from == false
1898
1950
  from = nil
1951
+ elsif !from && cmd.respond_to?(:drop)
1952
+ from = cmd.drop(1).find { |val| val.match?(/\A[a-z]{1,2}[a-z-]*\z/) }
1953
+ from &&= symjoin 'git', from
1899
1954
  end
1900
- banner &&= cmd.temp { |val| val.start_with?(/--(?:work-tree|git-dir)/) } if cmd.respond_to?(:temp)
1955
+ banner &&= cmd.temp { |val| val.start_with?(/--(work-tree|git-dir)/) } if cmd.respond_to?(:temp)
1901
1956
  end
1957
+ timeout = session_timeout cmd if timeout.nil?
1902
1958
  cmd = session_done cmd
1903
1959
  log&.info cmd
1904
- banner = format_banner(banner.is_a?(String) ? banner : cmd, hint: hint, strip: true) if banner
1960
+ banner = if banner
1961
+ format_banner(banner.is_a?(String) ? banner : cmd, hint: hint, strip: true)
1962
+ end
1905
1963
  on :first, from
1906
1964
  begin
1907
1965
  if io
@@ -1910,35 +1968,31 @@ module Squared
1910
1968
  return args ? [IO.popen(cmd), banner || '', from] : IO.popen(cmd)
1911
1969
  elsif stdin? ? sync : stdout
1912
1970
  print_item banner unless multiple
1913
- ret = `#{cmd}`.chomp
1971
+ ret = if stdin? && timeout.to_f > 0
1972
+ Timeout.timeout(timeout) { `#{cmd}` }
1973
+ else
1974
+ `#{cmd}`
1975
+ end.chomp
1914
1976
  raise(ret.empty? ? $?.to_s : ret) unless $?.success?
1915
1977
 
1916
1978
  if ret.empty?
1917
- success?(nil, !banner.nil?)
1979
+ success?(true, !banner.nil?)
1918
1980
  else
1919
1981
  puts ret
1920
1982
  end
1921
1983
  elsif !kwargs[:sub] && (sync || (!exception && !stderr))
1922
1984
  print_item banner unless multiple
1923
- ret = shell(cmd, name: send, exception: exception)
1985
+ ret = shell_t(cmd, name: send, exception: exception, timeout: timeout)
1986
+ elsif timeout.to_f > 0
1987
+ Timeout.timeout(timeout) { run_e(cmd, stderr: stderr, banner: banner, **kwargs) }
1924
1988
  else
1925
- require 'open3'
1926
- if stderr
1927
- Open3.popen3(cmd) do |_, out, err|
1928
- n = write_lines(out, banner: banner, pass: true, **kwargs)
1929
- if n == 0
1930
- n = write_lines(err, banner: banner)
1931
- success?(nil, n == 0 && !banner.nil?)
1932
- else
1933
- write_lines(err, loglevel: Logger::DEBUG)
1934
- end
1935
- end
1936
- else
1937
- Open3.popen2e(cmd) { |_, out| write_lines(out, banner: banner, **kwargs) }
1938
- end
1989
+ run_e(cmd, stderr: stderr, banner: banner, **kwargs)
1939
1990
  end
1940
- rescue StandardError => e
1941
- on_error(e, from, pass: true)
1991
+ rescue Timeout::Error => e
1992
+ print_error(Logger::ERROR, cmd, subject: name, hint: e)
1993
+ exit 1 unless exception?(Logger::DEBUG, Logger::INFO)
1994
+ rescue => e
1995
+ on_error(e, pass: true)
1942
1996
  nil
1943
1997
  else
1944
1998
  on :last, from
@@ -1946,32 +2000,6 @@ module Squared
1946
2000
  end
1947
2001
  end
1948
2002
 
1949
- def write_lines(data, grep: [], prefix: nil, sub: nil, banner: nil, loglevel: nil, pass: false, first: false)
1950
- grep = (matchmap(grep, prefix) unless grep.empty?)
1951
- sub = (as_a sub unless stdin?)
1952
- ret = 0
1953
- out = []
1954
- data.each do |line|
1955
- next if grep&.none? { |pat| pat.match?(line) }
1956
- next if block_given? && !(line = yield(line, ret))
1957
-
1958
- if loglevel
1959
- log&.add loglevel, line
1960
- else
1961
- sub&.each { |h| sub_style!(line, **h) }
1962
- if banner
1963
- out << line
1964
- else
1965
- puts line
1966
- end
1967
- end
1968
- ret += 1
1969
- break if first
1970
- end
1971
- print_item banner, out if banner && (ret > 0 || (!pass && !first))
1972
- ret
1973
- end
1974
-
1975
2003
  def list_result(size, type, action: 'found', grep: [], from: nil)
1976
2004
  if size == 0
1977
2005
  puts empty_status("No #{type} were #{action}", 'grep', grep.join(', '))
@@ -2007,7 +2035,7 @@ module Squared
2007
2035
  ret = choice_index('Choose a commit', git_spawn(cmd, stdout: false), column: /^(\S+)/, force: force, **kwargs)
2008
2036
  case ret
2009
2037
  when Array
2010
- ret.map!(&:stripstyle)
2038
+ ret.map(&:stripstyle)
2011
2039
  when String
2012
2040
  ret.stripstyle
2013
2041
  else
@@ -2025,24 +2053,18 @@ module Squared
2025
2053
  algorithm ||= Digest::SHA256
2026
2054
  glob = kwargs.fetch(:include, [])
2027
2055
  pass = kwargs.fetch(:exclude, [])
2028
- ret = {}
2029
- status_data(*args).each do |file,|
2056
+ status_data(*args).map(&:first).each_with_object({}) do |file, out|
2030
2057
  next if !glob.empty? && glob.none? { |val| File.fnmatch?(val, file, File::FNM_DOTMATCH) }
2031
2058
  next if pass.any? { |val| File.fnmatch?(val, file, File::FNM_DOTMATCH) }
2032
2059
 
2033
- ret[file] = algorithm.hexdigest(File.read(basepath(file)))
2060
+ out[file] = algorithm.hexdigest(File.read(basepath(file)))
2034
2061
  end
2035
- ret
2036
2062
  end
2037
2063
 
2038
2064
  def status_data(*args)
2039
- ret = []
2040
- git_spawn('status -z -uall', *args).split("\x0").each do |line|
2041
- next unless line =~ /^(.)(.) (.+)$/
2042
-
2043
- ret << [$3, $2, $1]
2065
+ git_spawn('status -z -uall', *args).split("\x0").each_with_object([]) do |line, out|
2066
+ out << [$3, $2, $1] if line =~ /^(.)(.) (.+)$/
2044
2067
  end
2045
- ret
2046
2068
  end
2047
2069
 
2048
2070
  def append_pull(opts, list, flag:, from:, target: @session, no: nil, remote: nil)
@@ -2050,9 +2072,8 @@ module Squared
2050
2072
  append_submodules(target: target, from: from)
2051
2073
  return if !remote && opts.empty?
2052
2074
 
2053
- refspec = []
2054
2075
  op = OptionPartition.new(opts, remote ? list + ['refspec=v'] : list, target, project: self, no: no)
2055
- op.each do |opt|
2076
+ refspec = op.each_with_object([]) do |opt, out|
2056
2077
  if opt =~ op.values
2057
2078
  case $1
2058
2079
  when 'rebase'
@@ -2063,7 +2084,7 @@ module Squared
2063
2084
  when 'recurse-submodules'
2064
2085
  op.append?($1, $2, type: :basic)
2065
2086
  when 'refspec'
2066
- refspec << shell_quote($2)
2087
+ out << shell_quote($2)
2067
2088
  end
2068
2089
  elsif op.arg?('multiple')
2069
2090
  op.found << opt
@@ -2075,7 +2096,7 @@ module Squared
2075
2096
  if remote
2076
2097
  op.append(remote, delim: true)
2077
2098
  if (val = option('refspec', target: target, strict: true))
2078
- op.append(*split_escape(val))
2099
+ op.append(split_escape(val))
2079
2100
  else
2080
2101
  op.merge(refspec)
2081
2102
  end
@@ -2089,27 +2110,31 @@ module Squared
2089
2110
  op.clear(errors: true, subject: flag) if flag
2090
2111
  end
2091
2112
 
2092
- def append_commit(*val, target: @session, head: false)
2093
- val.compact!
2094
- if !val.empty?
2095
- val.each { |ref| target << (commithash(ref) || shell_quote(ref)) }
2113
+ def append_commit(*refs, target: @session, head: false)
2114
+ refs.compact!
2115
+ if !refs.empty?
2116
+ target.merge(refs.map { |val| commithash(val) || shell_quote(val) })
2096
2117
  elsif head
2097
2118
  target << (append_head(target: target) || 'HEAD')
2098
2119
  end
2099
2120
  end
2100
2121
 
2101
- def append_pathspec(files = [], target: @session, expect: false, parent: false, pass: true)
2122
+ def append_pathspec(files = [], target: @session, expect: false, **kwargs)
2102
2123
  if session_arg?('pathspec-from-file', target: target)
2103
- option_clear files
2124
+ OptionPartition.clear(target, files, styles: theme[:inline])
2104
2125
  true
2105
2126
  else
2106
2127
  option('pathspec', target: target) { |val| files = split_escape(val) } if files.empty?
2107
- files = projectmap(files, parent: parent, pass: pass)
2128
+ files = projectmap(files, **kwargs)
2108
2129
  if !files.empty?
2109
2130
  target << '--' << files.join(' ')
2110
2131
  true
2111
2132
  elsif expect
2112
- raise_error(parent ? 'pathspec not present' : 'pathspec not within worktree')
2133
+ raise_error(if expect.is_a?(String)
2134
+ expect
2135
+ else
2136
+ kwargs[:parent] ? 'pathspec not present' : 'pathspec not within worktree'
2137
+ end)
2113
2138
  else
2114
2139
  false
2115
2140
  end
@@ -2153,11 +2178,15 @@ module Squared
2153
2178
  end
2154
2179
  end
2155
2180
 
2156
- def foreachref(path, *args, format: nil)
2157
- path = Array(path).map! { |val| "refs/#{val}" }
2181
+ def foreachref(path, *args, format: nil, chomp: true)
2182
+ path = Array(path).map { |val| "refs/#{val}" }
2158
2183
  format &&= quote_option('format', format)
2159
2184
  ret = git_spawn('for-each-ref', format, *args, *path, stdout: workspace.windows?)
2160
- ret.is_a?(String) ? ret.lines : ret
2185
+ if ret.is_a?(String)
2186
+ ret.lines(chomp: chomp)
2187
+ else
2188
+ chomp ? ret.readlines(chomp: chomp) : ret
2189
+ end
2161
2190
  end
2162
2191
 
2163
2192
  def git_session(*cmd, opts: nil, worktree: true, **kwargs)
@@ -2204,7 +2233,9 @@ module Squared
2204
2233
  raise_error(ArgumentError, "missing #{origin ? 'branch' : 'remote'} name", hint: origin)
2205
2234
  end
2206
2235
  branch = "#{branch}:#{origin[i.succ..-1]}" unless origin.end_with?("/#{branch}")
2207
- [origin[0..i.pred], branch].tap { |ret| ret.quote! if quote }
2236
+ ret = [origin[0..i.pred], branch]
2237
+ ret.quote! if quote
2238
+ ret
2208
2239
  end
2209
2240
 
2210
2241
  def commithash(val)
@@ -2229,16 +2260,22 @@ module Squared
2229
2260
  [/\A[^a-z\d-]+/i, %r{\A[^=\\/*]*[\\/*]}, /\A--\z/]
2230
2261
  end
2231
2262
 
2263
+ def matchfile(file, pat)
2264
+ file.read[pat, 0] if file.exist?
2265
+ rescue
2266
+ nil
2267
+ end
2268
+
2232
2269
  def messageopt
2233
2270
  option('message', 'm', prefix: 'git', ignore: false)
2234
2271
  end
2235
2272
 
2236
2273
  def threadargs
2237
- { stderr: true, exception: exception || !workspace.series.multiple? }
2274
+ { stderr: true, exception: exception? || !workspace.series.multiple? }
2238
2275
  end
2239
2276
  end
2240
2277
 
2241
- Application.implement Git
2278
+ Application.implement Git, base: 1
2242
2279
  end
2243
2280
  end
2244
2281
  end