squared 0.4.16 → 0.4.17

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.
@@ -4,7 +4,7 @@ module Squared
4
4
  module Workspace
5
5
  module Git
6
6
  GIT_REPO = Support.hashobj
7
- GIT_PROTO = %r{^(?:https?|ssh|git|file)://}i.freeze
7
+ GIT_PROTO = %r{\A(https?|ssh|git|file)://}i.freeze
8
8
  private_constant :GIT_REPO, :GIT_PROTO
9
9
 
10
10
  attr_reader :revfile
@@ -244,7 +244,17 @@ module Squared
244
244
  pop: %w[index].freeze,
245
245
  apply: %w[index].freeze
246
246
  }.freeze,
247
- status: %w[u|ignore-submodules=bm? ignored=b? untracked-files=b?],
247
+ status: %w[z u=bm? b|branch long s|short show-stash v|verbose column=b find-renames=i? ignore-submodules=b?
248
+ ignored=b? porcelain=b? untracked-files=b?].freeze,
249
+ submodule: {
250
+ status: %w[cached recursive].freeze,
251
+ update: %w[checkout f|force init merge N|no-fetch no-recommend-shallow no-single-branch recommend-shallow
252
+ rebase recursive remote single-branch depth=i filter=q jobs=i reference=b ref-format=q].freeze,
253
+ branch: %w[b|branch d|default].freeze,
254
+ sync: %w[recursive].freeze
255
+ }.freeze,
256
+ switch: %w[d|detach discard-changes f|force ignore-other-worktrees m|merge q|quiet conflict=b c|create=q
257
+ C|force-create=q orphan=q t|track=b].freeze,
248
258
  tag: %w[n=im cleanup=b create-reflog i|ignore-case color=b? column=b contains=b? format=q merged=b?
249
259
  no-contains=b? no-merged=b? points-at=q sort=q].freeze,
250
260
  no: {
@@ -270,6 +280,8 @@ module Squared
270
280
  rev_parse: %w[flags].freeze,
271
281
  revert: %w[edit gpg-sign rerere-autoupdate].freeze,
272
282
  show: %w[standard-notes].freeze,
283
+ status: %w[ahead-behind column renames].freeze,
284
+ switch: %w[guess progress recurse-submodules track].freeze,
273
285
  tag: %w[column].freeze
274
286
  }.freeze
275
287
  }.freeze
@@ -291,10 +303,8 @@ module Squared
291
303
  def populate(ws, **)
292
304
  return if ws.series.exclude?(:pull, true)
293
305
 
294
- namespace(name = ws.task_name('git')) do
295
- all = ws.task_join(name, 'all')
296
-
297
- ws.format_desc(all, 'stash|rebase|autostash?,depend?')
306
+ namespace(ws.task_name('git')) do |ns|
307
+ ws.format_desc(all = ws.task_join(ns.scope.path, 'all'), 'stash|rebase|autostash?,depend?')
298
308
  task 'all' do |_, args|
299
309
  args = args.to_a
300
310
  cmd = if args.include?('stash')
@@ -311,6 +321,7 @@ module Squared
311
321
  cmd << ws.task_sync('build')
312
322
  Common::Utils.task_invoke(*cmd, **ws.invokeargs)
313
323
  end
324
+
314
325
  ws.series.sync << all
315
326
  ws.series.multiple << all
316
327
  end
@@ -332,25 +343,27 @@ module Squared
332
343
  'checkout' => %i[commit branch track detach path].freeze,
333
344
  'commit' => %i[add all amend amend-orig fixup].freeze,
334
345
  'diff' => %i[head branch files view between contain].freeze,
335
- 'fetch' => %i[origin remote].freeze,
346
+ 'fetch' => %i[origin remote all].freeze,
336
347
  'files' => %i[cached modified deleted others].freeze,
337
- 'git' => %i[add blame clean mv revert rm].freeze,
348
+ 'git' => %i[add blame clean mv revert rm status].freeze,
338
349
  'log' => %i[view between contain].freeze,
339
350
  'merge' => %i[commit no-commit send].freeze,
340
- 'pull' => %i[origin remote].freeze,
351
+ 'pull' => %i[origin remote all].freeze,
341
352
  'rebase' => %i[branch onto send].freeze,
342
353
  'refs' => %i[heads tags remote].freeze,
343
- 'reset' => %i[commit index patch mode].freeze,
354
+ 'reset' => %i[commit index patch mode undo].freeze,
344
355
  'restore' => %i[source staged worktree].freeze,
345
356
  'rev' => %i[commit build output].freeze,
346
357
  'show' => %i[format oneline textconv].freeze,
347
358
  'stash' => %i[push pop apply branch drop clear list].freeze,
348
- 'switch' => %i[create detach merge].freeze,
359
+ 'submodule' => %i[status update branch url sync].freeze,
360
+ 'switch' => %i[branch create detach].freeze,
349
361
  'tag' => %i[add sign delete list].freeze
350
362
  })
351
363
 
352
364
  def initialize(*, **)
353
365
  super
366
+ @submodule = basepath('.gitmodules').exist?
354
367
  initialize_ref Git.ref if gitpath.exist?
355
368
  end
356
369
 
@@ -360,7 +373,7 @@ module Squared
360
373
 
361
374
  def populate(*, **)
362
375
  super
363
- return unless ref?(Git.ref)
376
+ return unless ref?(Git.ref) || @only
364
377
 
365
378
  namespace name do
366
379
  Git.subtasks do |action, flags|
@@ -382,11 +395,34 @@ module Squared
382
395
  __send__(action, flag, args, remote: remote)
383
396
  end
384
397
  else
385
- format_desc action, flag, 'opts*'
398
+ format_desc(action, flag, 'opts*', after: flag == :all && action == 'pull' ? 'pattern*' : nil)
386
399
  task flag do |_, args|
387
400
  __send__ action, flag, args.to_a
388
401
  end
389
402
  end
403
+ when 'submodule'
404
+ break unless @submodule
405
+
406
+ case flag
407
+ when :branch
408
+ format_desc action, flag, 'path,opts*'
409
+ task flag, [:path] do |_, args|
410
+ path = param_guard(action, flag, args: args, key: :path)
411
+ submodule(flag, args.extras, path: path)
412
+ end
413
+ when :url
414
+ format_desc action, flag, 'path,url,opts*'
415
+ task flag, [:path, :url] do |_, args|
416
+ path = param_guard(action, flag, args: args, key: :path)
417
+ url = param_guard(action, flag, args: args, key: :url)
418
+ submodule(flag, args.extras, path: path, url: url)
419
+ end
420
+ else
421
+ format_desc action, flag, 'opts*,path*'
422
+ task flag do |_, args|
423
+ submodule flag, args.to_a
424
+ end
425
+ end
390
426
  when 'commit'
391
427
  case flag
392
428
  when :all
@@ -682,10 +718,10 @@ module Squared
682
718
  when :create
683
719
  format_desc action, flag, '(^)name,ref?=HEAD|:'
684
720
  task flag, [:name, :commit] do |_, args|
685
- target = param_guard(action, flag, args: args, key: :name)
721
+ branch = param_guard(action, flag, args: args, key: :name)
686
722
  commit = commithead args.commit
687
723
  commit, track = choice_commit(values: ['Track? [Y|n]'], force: false) if commit == ':'
688
- switch(flag, target: target, commit: commit, track: track)
724
+ switch(flag, branch: branch, commit: commit, track: track)
689
725
  end
690
726
  when :detach
691
727
  format_desc action, flag, 'ref?=HEAD'
@@ -693,11 +729,16 @@ module Squared
693
729
  commit = commithead(args.commit) || choice_commit(force: false)
694
730
  switch(flag, commit: commit)
695
731
  end
696
- when :merge
697
- format_desc action, flag, 'branch?'
698
- task flag, [:branch] do |_, args|
699
- commit = args.branch || choice_refs('Choose a branch')
700
- switch(flag, commit: commit)
732
+ when :branch
733
+ format_desc action, flag, 'name|:,opts*'
734
+ task flag, [:name] do |_, args|
735
+ if (branch = args.name)
736
+ args = args.extras
737
+ branch = nil if branch == ':'
738
+ else
739
+ args = []
740
+ end
741
+ switch(flag, args, branch: branch || choice_refs('Choose a branch'))
701
742
  end
702
743
  end
703
744
  when 'reset'
@@ -718,10 +759,10 @@ module Squared
718
759
  end
719
760
  print_success if success?(reset(flag, args, commit: commit))
720
761
  end
721
- when :index
722
- format_desc action, flag, 'opts*,pathspec*'
762
+ when :index, :undo
763
+ format_desc(action, flag, flag == :index ? 'opts*,pathspec*' : nil)
723
764
  task flag do |_, args|
724
- reset flag, args.to_a
765
+ reset(flag, flag == :index ? args.to_a : [])
725
766
  end
726
767
  when :mode
727
768
  format_desc action, flag, 'mode,ref?=HEAD|:'
@@ -820,7 +861,7 @@ module Squared
820
861
  rev_parse(flag, ref: ref, size: size)
821
862
  end
822
863
  when :build
823
- format_desc action, flag, OPT_GIT[:status]
864
+ format_desc action, flag, 'opts*'
824
865
  task flag do |_, args|
825
866
  revbuild flag, args.to_a
826
867
  end
@@ -837,7 +878,7 @@ module Squared
837
878
  ls_remote(flag, args.extras, remote: args.remote)
838
879
  end
839
880
  else
840
- format_desc action, flag, 'opts*,pattern*'
881
+ format_desc(action, flag, 'opts*,pattern*', after: action == 'files' ? 'pathspec*' : nil)
841
882
  task flag do |_, args|
842
883
  __send__(action == 'refs' ? :ls_remote : :ls_files, flag, args.to_a)
843
884
  end
@@ -882,10 +923,13 @@ module Squared
882
923
  when :mv then 'source+,destination'
883
924
  when :revert then 'commit+' end
884
925
  format_desc(action, flag, 'opts*', before: before, after: case flag
885
- when :add then 'pathspec*,:pattern:*'
886
- when :clean, :rm then 'pathspec*' end)
926
+ when :add
927
+ 'pathspec*,pattern*'
928
+ when :clean, :rm, :status
929
+ 'pathspec*'
930
+ end)
887
931
  task flag do |_, args|
888
- git flag, args.to_a
932
+ __send__(flag == :status ? :status : :git, flag, args.to_a)
889
933
  end
890
934
  end
891
935
  end
@@ -909,11 +953,44 @@ module Squared
909
953
  super
910
954
  end
911
955
 
912
- def pull(flag = nil, opts = [], sync: invoked_sync?('pull', flag), remote: nil)
956
+ def pull(flag = nil, opts = [], sync: invoked_sync?('pull', flag), remote: nil, hint: nil)
913
957
  cmd, opts = git_session('pull', opts: opts)
914
- if flag == :rebase
958
+ cmd << '--autostash' if option('autostash')
959
+ case flag
960
+ when :rebase
915
961
  cmd << '--rebase'
916
- cmd << '--autostash' if option('autostash')
962
+ when :all
963
+ unless git_spawn('status -s -z --untracked-files=all').empty?
964
+ if confirm('Stash local changes? [Y/n] ', 'Y')
965
+ git_spawn 'stash push --keep-index --quiet'
966
+ elsif !(force = confirm('Force checkout? [y/N] ', 'N'))
967
+ return
968
+ end
969
+ end
970
+ op = OptionPartition.new(opts, OPT_GIT[:pull], cmd, project: self, no: OPT_GIT[:no][:pull])
971
+ reg = if op.empty?
972
+ []
973
+ else
974
+ opts = opts.reject { |val| op.extras.include?(val) }
975
+ matchmap op
976
+ end
977
+ session_done op.target
978
+ heads = []
979
+ cur = nil
980
+ foreachref('heads', format: '%(if)%(HEAD)%(then)* %(end)%(refname:short)').each do |line|
981
+ line.chomp!
982
+ cur ||= line.delete_prefix!('* ')
983
+ heads << line if matchany?(line, reg)
984
+ end
985
+ raise_error('head not found', hint: 'for-each-ref') unless cur
986
+ opts << 'ff-only' if opts.empty? && !option('ff-only', equals: '0')
987
+ (heads.dup << cur).each_with_index do |branch, index|
988
+ next unless (index < heads.size && cur != branch) || index == heads.size
989
+
990
+ git_spawn 'switch --quiet', force && '--force', shell_quote(branch)
991
+ pull(nil, opts, sync: false, hint: branch) if heads.include?(branch)
992
+ end
993
+ return
917
994
  else
918
995
  cmd << '--autostash' if flag == :autostash
919
996
  if (val = option('rebase', ignore: false))
@@ -932,7 +1009,7 @@ module Squared
932
1009
  { pat: /^(.+)(\|\s+\d+\s+)([^-]*)(-+)(.*)$/, styles: color(:red), index: 4 },
933
1010
  { pat: /^(.+)(\|\s+\d+\s+)(\++)(.*)$/, styles: color(:green), index: 3 }
934
1011
  ]
935
- end, **threadargs)
1012
+ end, hint: hint, **threadargs)
936
1013
  end
937
1014
 
938
1015
  def rebase(flag = nil, opts = [], sync: invoked_sync?('rebase', flag), commit: nil, upstream: nil, branch: nil,
@@ -969,6 +1046,7 @@ module Squared
969
1046
 
970
1047
  def fetch(flag = nil, opts = [], sync: invoked_sync?('fetch', flag), remote: nil)
971
1048
  opts = git_session('fetch', opts: opts).last
1049
+ opts << 'all' if flag == :all || option('all')
972
1050
  append_pull(opts, collect_hash(OPT_GIT[:fetch]), no: collect_hash(OPT_GIT[:no][:fetch]),
973
1051
  remote: remote, flag: flag, from: :fetch)
974
1052
  source(sync: sync, **threadargs)
@@ -1008,15 +1086,15 @@ module Squared
1008
1086
  cmd, opts = git_session('stash', flag, opts: opts)
1009
1087
  list = OPT_GIT[:stash][:common] + OPT_GIT[:stash].fetch(flag, [])
1010
1088
  if flag == :list
1011
- list += collect_hash(OPT_GIT[:log])
1012
- no = collect_hash(OPT_GIT[:no][:log])
1089
+ list += collect_hash OPT_GIT[:log]
1090
+ no = collect_hash OPT_GIT[:no][:log]
1013
1091
  end
1014
1092
  op = OptionPartition.new(opts, list, cmd, project: self, no: no, first: flag == :push ? matchpathspec : nil)
1015
1093
  case flag
1016
1094
  when :push
1017
1095
  append_pathspec op.extras
1018
1096
  when :pop, :apply, :drop, :branch
1019
- if op.extras.delete(':')
1097
+ if op.remove(':')
1020
1098
  if flag == :branch
1021
1099
  if op.empty?
1022
1100
  values = [['Branch name', true]]
@@ -1051,29 +1129,34 @@ module Squared
1051
1129
  end
1052
1130
  else
1053
1131
  git_session('stash', 'push', opts: opts)
1054
- append_option(OPT_GIT[:stash][:push].grep_v(/[=|]/), no: true, ignore: false)
1132
+ append_option(OptionPartition.select(OPT_GIT[:stash][:push], no: false), no: true, ignore: false)
1055
1133
  append_message
1056
1134
  end
1057
1135
  source(banner: !quiet?, sync: sync, **threadargs)
1058
1136
  end
1059
1137
 
1060
- def status(*)
1061
- cmd = git_session 'status'
1062
- cmd << (option('long') ? '--long' : '--short')
1063
- cmd << '--branch' if option('branch')
1064
- if (val = option('ignore-submodules', ignore: false))
1065
- cmd << basic_option('ignore-submodules', case val
1066
- when '0', 'none'
1067
- 'none'
1068
- when '1', 'untracked'
1069
- 'untracked'
1070
- when '2', 'dirty'
1071
- 'dirty'
1072
- else
1073
- 'all'
1074
- end)
1138
+ def status(flag = nil, opts = [])
1139
+ cmd, opts = git_session('status', opts: opts)
1140
+ if flag
1141
+ op = OptionPartition.new(opts, OPT_GIT[:status], cmd, project: self, no: OPT_GIT[:no][:status])
1142
+ append_pathspec op.extras
1143
+ else
1144
+ cmd << (option('long') ? '--long' : '--short')
1145
+ cmd << '--branch' if option('branch')
1146
+ if (val = option('ignore-submodules', ignore: false))
1147
+ cmd << basic_option('ignore-submodules', case val
1148
+ when '0', 'none'
1149
+ 'none'
1150
+ when '1', 'untracked'
1151
+ 'untracked'
1152
+ when '2', 'dirty'
1153
+ 'dirty'
1154
+ else
1155
+ 'all'
1156
+ end)
1157
+ end
1158
+ append_pathspec
1075
1159
  end
1076
- append_pathspec
1077
1160
  if verbose
1078
1161
  r = color(:red)
1079
1162
  g = color(:green)
@@ -1115,7 +1198,7 @@ module Squared
1115
1198
  kwargs = kwargs.key?(:include) || kwargs.key?(:exclude) ? statusargs.call : @revbuild || {}
1116
1199
  case flag
1117
1200
  when :build
1118
- op = OptionPartition.new(opts, OPT_GIT[:status], project: self)
1201
+ op = OptionPartition.new(opts, %w[ignore-submodules=b? ignored=b? untracked-files=b?], project: self)
1119
1202
  op.clear(append: true)
1120
1203
  args = op.to_a
1121
1204
  else
@@ -1172,6 +1255,9 @@ module Squared
1172
1255
  when :patch
1173
1256
  cmd << '--patch'
1174
1257
  append_pathspec(refs, pass: false)
1258
+ when :undo
1259
+ cmd << '--hard HEAD@{1}'
1260
+ ref = false
1175
1261
  end
1176
1262
  unless ref == false
1177
1263
  append_commit(ref, head: true)
@@ -1182,7 +1268,7 @@ module Squared
1182
1268
 
1183
1269
  def checkout(flag, opts = [], branch: nil, origin: nil, create: nil, commit: nil, detach: nil, merge: false)
1184
1270
  cmd, opts = git_session('checkout', opts: opts)
1185
- append_option 'force', 'merge'
1271
+ append_option 'force', 'f', 'merge'
1186
1272
  case flag
1187
1273
  when :branch
1188
1274
  cmd << '--detach' if detach == 'd' || option('detach')
@@ -1224,7 +1310,7 @@ module Squared
1224
1310
  elsif !session_arg?('s', 'sign', 'u', 'local-user')
1225
1311
  cmd << '--annotate'
1226
1312
  end
1227
- cmd << '--force' if option('force')
1313
+ cmd << '--force' if option('force', 'f')
1228
1314
  if !commit && message && (sha = commithash(message))
1229
1315
  commit = sha
1230
1316
  message = nil
@@ -1255,7 +1341,7 @@ module Squared
1255
1341
  first: matchpathspec)
1256
1342
  case flag
1257
1343
  when :between, :contain
1258
- op << shell_quote(range.join(flag == :between ? '..' : '...'))
1344
+ op.add_quote(range.join(flag == :between ? '..' : '...'))
1259
1345
  else
1260
1346
  op.merge(index)
1261
1347
  end
@@ -1284,13 +1370,13 @@ module Squared
1284
1370
  op.merge(range)
1285
1371
  when :between, :contain
1286
1372
  op.delete('--merge-base')
1287
- op << shell_quote(range.join(flag == :between ? '..' : '...'))
1373
+ op.add_quote(range.join(flag == :between ? '..' : '...'))
1288
1374
  else
1289
1375
  op << '--merge-base' if option('merge-base')
1290
- op << shell_quote(branch) if branch
1376
+ op.add_quote(branch) if branch
1291
1377
  if !index.empty?
1292
1378
  if op.arg?('cached')
1293
- raise_error("one commit only: #{index.join(', ')}", hint: '--cached') if index.size > 1
1379
+ raise_error("one commit only: #{index.join(', ')}", hint: 'cached') if index.size > 1
1294
1380
  op << index.first
1295
1381
  else
1296
1382
  op.merge(index)
@@ -1420,7 +1506,7 @@ module Squared
1420
1506
  '--track'
1421
1507
  end
1422
1508
  end
1423
- cmd << '--force' if option('force')
1509
+ cmd << '--force' if option('force', 'f')
1424
1510
  cmd << shell_quote(target)
1425
1511
  cmd << shell_quote(ref) if ref
1426
1512
  when :track
@@ -1435,7 +1521,7 @@ module Squared
1435
1521
  end
1436
1522
  when :delete
1437
1523
  remote&.each do |val|
1438
- source git_output('push', '--delete', *val.split('/', 2).map { |s| shell_quote(s) })
1524
+ source git_output('push --delete', *val.split('/', 2).map { |s| shell_quote(s) })
1439
1525
  end
1440
1526
  force, list = refs.partition { |val| val.match?(/^[~^]/) }
1441
1527
  force.each do |val|
@@ -1451,7 +1537,7 @@ module Squared
1451
1537
  remote = nil
1452
1538
  when :move, :copy
1453
1539
  flag = "-#{flag.to_s[0]}"
1454
- cmd << (option('force') ? flag.upcase : flag)
1540
+ cmd << (option('force', 'f') ? flag.upcase : flag)
1455
1541
  refs.compact.each { |val| cmd << shell_quote(val) }
1456
1542
  stdout = true
1457
1543
  when :current
@@ -1461,7 +1547,7 @@ module Squared
1461
1547
  when :list
1462
1548
  op = OptionPartition.new(opts, OPT_GIT[:branch], cmd << '--list',
1463
1549
  project: self, no: OPT_GIT[:no][:branch], single: /\Av+\z/)
1464
- op.each { |val| op << shell_quote(val) }
1550
+ op.each { |val| op.add_quote(val) }
1465
1551
  out, banner, from = source(io: true)
1466
1552
  print_item banner
1467
1553
  ret = write_lines(out, sub: [
@@ -1507,33 +1593,52 @@ module Squared
1507
1593
  if !ref
1508
1594
  print_success if flag == :create
1509
1595
  elsif remote && target
1510
- source git_output('push', '-u', shell_quote(ref.split('/', 2).first), shell_quote(target))
1596
+ source git_output('push -u', shell_quote(ref.split('/', 2).first), shell_quote(target))
1511
1597
  end
1512
1598
  end
1513
1599
 
1514
- def switch(flag, opts = [], target: nil, commit: nil, track: nil)
1515
- cmd = git_session('switch', opts: opts).first
1516
- case flag
1517
- when :create
1518
- c = 'c'
1519
- if target.start_with?('^')
1520
- target = target[1..-1]
1521
- c = c.upcase
1600
+ def switch(flag, opts = [], branch: nil, commit: nil, track: nil)
1601
+ cmd, opts = git_session('switch', opts: opts)
1602
+ cmd << '--force' if option('force', 'f')
1603
+ if flag == :branch
1604
+ op = OptionPartition.new(opts, OPT_GIT[:switch], cmd, project: self, no: OPT_GIT[:no][:switch])
1605
+ op.add_quote(branch)
1606
+ else
1607
+ case flag
1608
+ when :create
1609
+ c = 'c'
1610
+ if target.start_with?('^')
1611
+ target = target[1..-1]
1612
+ c = c.upcase
1613
+ end
1614
+ cmd << quote_option(c, target)
1615
+ cmd << case (track ||= option('track', ignore: false))
1616
+ when 'n', 'N', '0', 'false'
1617
+ '--no-track'
1618
+ when 'y', 'Y', '1', 'true'
1619
+ '--track'
1620
+ when 'direct', 'inherit'
1621
+ basic_option 'track', track
1622
+ end
1623
+ when :detach
1624
+ cmd << "--#{flag}"
1522
1625
  end
1523
- cmd << quote_option(c, target)
1524
- cmd << case (track ||= option('track', ignore: false))
1525
- when 'n', 'N', '0', 'false'
1526
- '--no-track'
1527
- when 'y', 'Y', '1', 'true'
1528
- '--track'
1529
- when 'direct', 'inherit'
1530
- basic_option('track', track)
1531
- end
1532
- when :detach, :merge
1533
- cmd << "--#{flag}"
1626
+ append_head commit
1627
+ end
1628
+ source
1629
+ end
1630
+
1631
+ def submodule(flag, opts = [], path: nil, url: nil)
1632
+ cmd, opts = git_session('submodule', opts: opts)
1633
+ op = OptionPartition.new(opts, OPT_GIT[:submodule].fetch(flag, []), cmd, project: self)
1634
+ case flag
1635
+ when :branch, :url
1636
+ op << '--'
1637
+ op.add_path(path) if path
1638
+ op.add_quote(url) if url
1639
+ else
1640
+ op.splice(path: true)
1534
1641
  end
1535
- cmd << '--force' if option('force')
1536
- append_head commit
1537
1642
  source
1538
1643
  end
1539
1644
 
@@ -1602,7 +1707,7 @@ module Squared
1602
1707
  cmd, opts = git_session('ls-remote', '--refs', opts: opts)
1603
1708
  cmd << "--#{flag}" unless flag == :remote
1604
1709
  op = OptionPartition.new(opts, OPT_GIT[:ls_remote], cmd, project: self)
1605
- op << shell_quote(remote) if remote
1710
+ op.add_quote(remote) if remote
1606
1711
  out, banner, from = source(io: true)
1607
1712
  print_item banner
1608
1713
  ret = write_lines(out, grep: op.extras, prefix: "refs/#{flag}/")
@@ -1612,6 +1717,7 @@ module Squared
1612
1717
  def ls_files(flag, opts = [])
1613
1718
  cmd, opts = git_session('ls-files', "--#{flag}", opts: opts)
1614
1719
  op = OptionPartition.new(opts, OPT_GIT[:ls_files], cmd, project: self)
1720
+ op.splice(path: true, pattern: true)
1615
1721
  out, banner, from = source(io: true)
1616
1722
  print_item banner
1617
1723
  ret = write_lines(out, grep: op.extras)
@@ -1640,15 +1746,16 @@ module Squared
1640
1746
  end
1641
1747
  when :add, :clean
1642
1748
  if flag == :add && !op.arg?('pathspec-from-file')
1643
- grep, list = op.partition { |val| val.start_with?(':') && val.end_with?(':') }
1749
+ grep, list = op.partition { |val| OptionPartition.pattern?(val) }
1644
1750
  unless grep.empty? && !list.empty?
1645
1751
  grep.map! { |val| Regexp.new(val[1..-2]) }
1646
- files = status_data.map! do |a, b|
1647
- next if b.strip.empty? || (!grep.empty? && grep.none? { |pat| pat.match?(a) })
1752
+ files = [].tap do |out|
1753
+ status_data.each do |a, b|
1754
+ next if b.strip.empty? || (!grep.empty? && grep.none? { |pat| pat.match?(a) })
1648
1755
 
1649
- "#{sub_style(b, styles: color(:red))} #{a}"
1756
+ out << "#{sub_style(b, styles: color(:red))} #{a}"
1757
+ end
1650
1758
  end
1651
- .compact
1652
1759
  unless files.empty?
1653
1760
  files = choice_index('Select files', files, multiple: true, force: true, trim: /^\S+\s/,
1654
1761
  accept: [['Add?', false, true]])
@@ -1656,7 +1763,7 @@ module Squared
1656
1763
  op.swap(list + files)
1657
1764
  end
1658
1765
  end
1659
- return source(git_session('status', '-s'), banner: false) unless append_pathspec(op.extras)
1766
+ return source(git_session('status -s'), banner: false) unless append_pathspec(op.extras)
1660
1767
 
1661
1768
  verbose = flag == :add && !op.arg?('verbose')
1662
1769
  print_success if success?(source) && verbose
@@ -1686,7 +1793,7 @@ module Squared
1686
1793
  private
1687
1794
 
1688
1795
  def source(cmd = @session, exception: true, io: false, sync: true, stdout: false, stderr: false, banner: true,
1689
- multiple: false, from: nil, **kwargs)
1796
+ multiple: false, hint: nil, from: nil, **kwargs)
1690
1797
  cmd = cmd.target if cmd.is_a?(OptionPartition)
1691
1798
  if io && banner == false
1692
1799
  from = nil
@@ -1704,7 +1811,8 @@ module Squared
1704
1811
  cmd = session_done cmd
1705
1812
  log&.info cmd
1706
1813
  banner = if banner
1707
- format_banner((banner.is_a?(String) ? banner : cmd).gsub(File.join(path, ''), ''), banner: true)
1814
+ banner = (banner.is_a?(String) ? banner : cmd).gsub(File.join(path, ''), '')
1815
+ format_banner(hint ? "#{banner} (#{hint})" : banner, banner: true)
1708
1816
  end
1709
1817
  on :first, from
1710
1818
  begin
@@ -1752,16 +1860,7 @@ module Squared
1752
1860
  end
1753
1861
 
1754
1862
  def write_lines(data, grep: [], prefix: nil, sub: nil, banner: nil, loglevel: nil, pass: false, first: false)
1755
- grep = unless grep.empty?
1756
- grep.map do |val|
1757
- if val.is_a?(Regexp)
1758
- val
1759
- else
1760
- val = ".*#{val}" if prefix && !val.sub!(/\A(\^|\\A)/, '')
1761
- Regexp.new("#{prefix}#{val == '*' ? '.+' : val}")
1762
- end
1763
- end
1764
- end
1863
+ grep = grep.empty? ? nil : matchmap(grep, prefix)
1765
1864
  sub = nil if stdin?
1766
1865
  ret = 0
1767
1866
  out = []
@@ -1835,8 +1934,7 @@ module Squared
1835
1934
  glob = kwargs.fetch(:include, [])
1836
1935
  pass = kwargs.fetch(:exclude, [])
1837
1936
  ret = {}
1838
- status_data(*args).each do |line|
1839
- file = line.first
1937
+ status_data(*args).each do |file,|
1840
1938
  next if !glob.empty? && glob.none? { |val| File.fnmatch?(val, file, File::FNM_DOTMATCH) }
1841
1939
  next if !pass.empty? && pass.any? { |val| File.fnmatch?(val, file, File::FNM_DOTMATCH) }
1842
1940
 
@@ -1846,17 +1944,17 @@ module Squared
1846
1944
  end
1847
1945
 
1848
1946
  def status_data(*args)
1849
- ret = []
1850
- git_spawn('status -z -uall', *args).split("\x0").each do |line|
1851
- next unless line =~ /^(.)(.) (.+)$/
1947
+ [].tap do |ret|
1948
+ git_spawn('status -z -uall', *args).split("\x0").each do |line|
1949
+ next unless line =~ /^(.)(.) (.+)$/
1852
1950
 
1853
- ret << [$3, $2, $1]
1951
+ ret << [$3, $2, $1]
1952
+ end
1854
1953
  end
1855
- ret
1856
1954
  end
1857
1955
 
1858
1956
  def append_pull(opts, list, target: @session, flag: nil, no: nil, remote: nil, from: nil)
1859
- target << '--force' if option('force', target: target)
1957
+ target << '--force' if option('force', 'f', target: target)
1860
1958
  append_submodules(target: target, from: from)
1861
1959
  return if !remote && opts.empty?
1862
1960
 
@@ -1974,12 +2072,11 @@ module Squared
1974
2072
  end
1975
2073
 
1976
2074
  def git_session(*cmd, opts: nil, worktree: true, **kwargs)
1977
- dir = worktree ? ["--work-tree=#{shell_quote(path)}", "--git-dir=#{shell_quote(gitpath)}"] : []
2075
+ dir = worktree ? [quote_option('work-tree', path), quote_option('git-dir', gitpath)] : []
1978
2076
  return session('git', *dir, *cmd, **kwargs) unless opts
1979
2077
 
1980
2078
  op = OptionPartition.new(opts, OPT_GIT[:common], dir, project: self)
1981
- ret = session('git', *op.to_a, *cmd, **kwargs)
1982
- [ret, op.extras]
2079
+ [session('git', *op.to_a, *cmd, **kwargs), op.extras]
1983
2080
  end
1984
2081
 
1985
2082
  def git_output(*cmd, **kwargs)