squared 0.4.18 → 0.5.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.
@@ -3,8 +3,8 @@
3
3
  module Squared
4
4
  module Workspace
5
5
  module Git
6
- GIT_REPO = Support.hashobj
7
- GIT_PROTO = %r{\A(https?|ssh|git|file)://}i.freeze
6
+ GIT_REPO = {}
7
+ GIT_PROTO = %r{^(?:https?|ssh|git|file)://}i.freeze
8
8
  private_constant :GIT_REPO, :GIT_PROTO
9
9
 
10
10
  attr_reader :revfile
@@ -27,7 +27,7 @@ module Squared
27
27
  base = name
28
28
  @project.each_value { |proj| repo << proj if !proj.parent && check.call(proj) }
29
29
  else
30
- warn log_message(Logger::WARN, name, subject: 'git', hint: 'invalid') if warning
30
+ warn log_message(Logger::WARN, name, subject: 'git', hint: 'invalid', pass: true) if warning
31
31
  return self
32
32
  end
33
33
  if base
@@ -58,8 +58,8 @@ module Squared
58
58
  end
59
59
  end
60
60
  key = task_name key
61
- GIT_REPO[main][key] = [uri.to_s, opts]
62
- @kind[key] << Project::Git
61
+ (GIT_REPO[main] ||= {})[key] = [uri.to_s, opts]
62
+ (@kind[key] ||= []) << Project::Git
63
63
  end
64
64
  if cache == true
65
65
  revbuild
@@ -74,7 +74,7 @@ module Squared
74
74
  @revdoc = JSON.parse(@revfile.read) if @revfile.exist?
75
75
  rescue StandardError => e
76
76
  @revfile = nil
77
- warn log_message(Logger::WARN, e, pass: true)
77
+ warn log_message(Logger::WARN, e, pass: true) if @warning
78
78
  self
79
79
  else
80
80
  @revdoc = {} unless @revdoc.is_a?(Hash)
@@ -91,13 +91,13 @@ module Squared
91
91
 
92
92
  data = @revdoc
93
93
  last = keys.pop
94
- for key in keys
94
+ keys.each do |key|
95
95
  if data[key].is_a?(Hash)
96
96
  data = data[key]
97
97
  elsif create
98
98
  data = data[key] = {}
99
99
  else
100
- return
100
+ return nil
101
101
  end
102
102
  end
103
103
  data[last] = val
@@ -157,13 +157,14 @@ module Squared
157
157
  class Git < Base
158
158
  OPT_GIT = {
159
159
  common: %w[c=q bare glob-pathspecs icase-pathspecs literal-pathspecs no-optional-locks no-pager
160
- no-replace-objects noglob-pathspecs paginate config-env=q exec-path=p namespace=p].freeze,
160
+ no-replace-objects noglob-pathspecs paginate attr-source=b config-env=q exec-path=p
161
+ namespace=p].freeze,
161
162
  add: %w[A|all e|edit f|force ignore-errors ignore-missing ignore-removal i|interactive no-all
162
163
  no-ignore-removal n|dry-run p|patch pathspec-file-nul renormalize sparse u|update v|verbose
163
164
  chmod=b pathspec-from-file=p].freeze,
164
- branch: %w[a|all create-reflog i|ignore-case q|quiet r|remotes v|verbose vv abbrev=i color=b column=b
165
- contains=b format=q merged=b no-contains=b no-merged=b points-at=b u|set-upstream-to=b sort=q
166
- t|track=b].freeze,
165
+ branch: %w[a|all create-reflog i|ignore-case omit-empty q|quiet r|remotes v|verbose vv abbrev=i color=b
166
+ column=b contains=b format=q merged=b no-contains=b no-merged=b points-at=b u|set-upstream-to=b
167
+ sort=q t|track=b].freeze,
167
168
  checkout: %w[l d|detach f|force ignore-other-worktrees ignore-skip-worktree-bits m|merge p|patch
168
169
  pathspec-file-nul q|quiet ours theirs conflict=b orphan=b pathspec-from-file=p t|track=b].freeze,
169
170
  diff: {
@@ -171,7 +172,8 @@ module Squared
171
172
  show: %w[s exit-code histogram].freeze
172
173
  }.freeze,
173
174
  fetch: {
174
- base: %w[multiple progress P|prune-tags refetch stdin u|update-head-ok recurse-submodules-default=b].freeze,
175
+ base: %w[multiple porcelain progress P|prune-tags refetch stdin u|update-head-ok
176
+ recurse-submodules-default=b].freeze,
175
177
  pull: %w[4 6 n t a|append atomic dry-run f|force k|keep negotiate-only prefetch p|prune q|quiet
176
178
  set-upstream unshallow update-shallow v|verbose deepen=i depth=i j|jobs=i negotiation-tip=q
177
179
  recurse-submodules=v refmap=q o|server-option=q shallow-exclude=b shallow-since=v
@@ -179,9 +181,6 @@ module Squared
179
181
  }.freeze,
180
182
  git: {
181
183
  add: %w[N|intent-to-add refresh].freeze,
182
- blame: %w[b c l s t w C=im? L=q M=im? S=p color-by-age color-lines first-parent incremental line-porcelain
183
- p|porcelain root score-debug f|show-name e|show-email n|show-number show-stats abbrev=i
184
- contents=p date=q encoding=b ignore-rev=b ignore-revs-file=p reverse=q].freeze,
185
184
  clean: %w[d x X f|force n|dry-run i|interactive q|quiet e|exclude=q].freeze,
186
185
  mv: %w[k f|force n|dry-run v|verbose].freeze,
187
186
  revert: %w[e S=bm? abort continue n|no-commit quit reference skip cleanup=b gpg-sign=b? m|mainline=i
@@ -199,9 +198,9 @@ module Squared
199
198
  branches=q? committer=q decorate=b decorate-refs=q decorate-refs-exclude=q exclude=q
200
199
  exclude-hidden=b glob=q grep=q grep-reflog=q n|max-count=i max-parents=i min-parents=i no-walk=b?
201
200
  remotes=q? since=q since-as-filter=q skip=i tags=q? until=q].freeze,
202
- format: %w[t children combined-all-paths oneline left-right no-diff-merges parents relative-date
203
- show-signature date=q diff-merges=b encoding=b expand-tabs=i format=q notes=b pretty=q?
204
- show-linear-break=q?].freeze,
201
+ format: %w[t children combined-all-paths dd oneline left-right no-diff-merges parents relative-date
202
+ show-notes-by-default show-signature date=q diff-merges=b encoding=b expand-tabs=i format=q
203
+ notes=b pretty=q? show-linear-break=q?].freeze,
205
204
  diff: %w[p R u z B=bm? C=bm? l=im G=qm I=qm M=bm? O=qm S=qm U=im binary check compact-summary cumulative
206
205
  find-copies-harder full-index W|function-context w|ignore-all-space ignore-blank-lines
207
206
  ignore-cr-at-eol ignore-space-at-eol b|ignore-space-change D|irreversible-delete graph
@@ -231,12 +230,13 @@ module Squared
231
230
  theirs W|worktree conflict=b pathspec-from-file=p s|source=b].freeze,
232
231
  rev_parse: %w[absolute-git-dir all git-common-dir git-dir is-bare-repository is-inside-git-dir
233
232
  is-inside-work-tree is-shallow-repository local-env-vars no-revs not q|quiet revs-only
234
- shared-index-path show-cdup show-prefix show-superproject-working-tree show-toplevel sq sq-quote
235
- symbolic symbolic-full-name verify abbrev-ref=b? after=q before=q branches=q? default=q
236
- disambiguate=b exclude=q exclude-hidden=b git-path=p glob=q path-format=b? prefix=q remotes=q?
237
- resolve-git-dir=p short=i? show-object-format=b? since=q tags=q? until=q].freeze,
238
- show: %w[t combined-all-paths no-diff-merges remerge-diff show-signature diff-merges=b encoding=b
239
- expand-tabs=i notes=q show-notes=q?].freeze,
233
+ shared-index-path show-cdup show-prefix show-ref-format show-superproject-working-tree
234
+ show-toplevel sq sq-quote symbolic symbolic-full-name verify abbrev-ref=b? after=q before=q
235
+ branches=q? default=q disambiguate=b exclude=q exclude-hidden=b git-path=p glob=q
236
+ output-object-format=b path-format=b? prefix=q remotes=q? resolve-git-dir=p short=i?
237
+ show-object-format=b? since=q tags=q? until=q].freeze,
238
+ show: %w[t combined-all-paths no-diff-merges remerge-diff show-notes-by-default show-signature diff-merges=b
239
+ encoding=b expand-tabs=i notes=q show-notes=q?].freeze,
240
240
  stash: {
241
241
  common: %w[q|quiet].freeze,
242
242
  push: %w[a|all u|include-untracked k|keep-index no-keep-index no-include-untracked pathspec-file-nul
@@ -244,21 +244,10 @@ module Squared
244
244
  pop: %w[index].freeze,
245
245
  apply: %w[index].freeze
246
246
  }.freeze,
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,
258
- tag: %w[n=im cleanup=b create-reflog i|ignore-case color=b? column=b contains=b? format=q merged=b?
259
- no-contains=b? no-merged=b? points-at=q sort=q].freeze,
247
+ status: %w[u|ignore-submodules=bm? ignored=b? untracked-files=b?],
248
+ tag: %w[n=im cleanup=b create-reflog i|ignore-case omit-empty color=b? column=b contains=b? format=q merged=b?
249
+ no-contains=b? no-merged=b? points-at=q sort=q trailer=q].freeze,
260
250
  no: {
261
- blame: %w[progress].freeze,
262
251
  branch: %w[color color-moved column track].freeze,
263
252
  checkout: %w[overwrite-ignore guess overlay progress recurse-submodules track].freeze,
264
253
  fetch: {
@@ -280,8 +269,6 @@ module Squared
280
269
  rev_parse: %w[flags].freeze,
281
270
  revert: %w[edit gpg-sign rerere-autoupdate].freeze,
282
271
  show: %w[standard-notes].freeze,
283
- status: %w[ahead-behind column renames].freeze,
284
- switch: %w[guess progress recurse-submodules track].freeze,
285
272
  tag: %w[column].freeze
286
273
  }.freeze
287
274
  }.freeze
@@ -301,10 +288,12 @@ module Squared
301
288
  include Rake::DSL
302
289
 
303
290
  def populate(ws, **)
304
- return if ws.series.exclude?(:pull, true) || ws.size == 1
291
+ return if ws.series.exclude?(:pull, true)
292
+
293
+ namespace(name = ws.task_name('git')) do
294
+ all = ws.task_join(name, 'all')
305
295
 
306
- namespace ws.task_name('git') do |ns|
307
- ws.format_desc(all = ws.task_join(ns.scope.path, 'all'), 'stash|rebase|autostash?,depend?')
296
+ ws.format_desc(all, 'stash|rebase|autostash?,depend?')
308
297
  task 'all' do |_, args|
309
298
  args = args.to_a
310
299
  cmd = if args.include?('stash')
@@ -321,7 +310,6 @@ module Squared
321
310
  cmd << ws.task_sync('build')
322
311
  Common::Utils.task_invoke(*cmd, **ws.invokeargs)
323
312
  end
324
-
325
313
  ws.series.sync << all
326
314
  ws.series.multiple << all
327
315
  end
@@ -343,27 +331,25 @@ module Squared
343
331
  'checkout' => %i[commit branch track detach path].freeze,
344
332
  'commit' => %i[add all amend amend-orig fixup].freeze,
345
333
  'diff' => %i[head branch files view between contain].freeze,
346
- 'fetch' => %i[origin remote all].freeze,
334
+ 'fetch' => %i[origin remote].freeze,
347
335
  'files' => %i[cached modified deleted others].freeze,
348
- 'git' => %i[add blame clean mv revert rm status].freeze,
336
+ 'git' => %i[add clean mv revert rm].freeze,
349
337
  'log' => %i[view between contain].freeze,
350
338
  'merge' => %i[commit no-commit send].freeze,
351
- 'pull' => %i[origin remote all].freeze,
339
+ 'pull' => %i[origin remote].freeze,
352
340
  'rebase' => %i[branch onto send].freeze,
353
341
  'refs' => %i[heads tags remote].freeze,
354
- 'reset' => %i[commit index patch mode undo].freeze,
342
+ 'reset' => %i[commit index patch mode].freeze,
355
343
  'restore' => %i[source staged worktree].freeze,
356
344
  'rev' => %i[commit build output].freeze,
357
345
  'show' => %i[format oneline textconv].freeze,
358
- 'stash' => %i[push pop apply branch drop clear list].freeze,
359
- 'submodule' => %i[status update branch url sync].freeze,
360
- 'switch' => %i[branch create detach].freeze,
346
+ 'stash' => %i[push pop apply drop clear list].freeze,
347
+ 'switch' => %i[create detach merge].freeze,
361
348
  'tag' => %i[add sign delete list].freeze
362
349
  })
363
350
 
364
351
  def initialize(*, **)
365
352
  super
366
- @submodule = basepath('.gitmodules').exist?
367
353
  initialize_ref Git.ref if gitpath.exist?
368
354
  end
369
355
 
@@ -373,56 +359,33 @@ module Squared
373
359
 
374
360
  def populate(*, **)
375
361
  super
376
- return unless ref?(Git.ref) || @only
362
+ return unless ref?(Git.ref)
377
363
 
378
364
  namespace name do
379
365
  Git.subtasks do |action, flags|
380
- next if task_pass?(action)
366
+ next if @pass.include?(action)
381
367
 
382
368
  namespace action do
383
369
  flags.each do |flag|
384
370
  case action
385
371
  when 'pull', 'fetch'
386
372
  if flag == :remote
387
- format_desc action, flag, 'remote?,opts*'
373
+ format_desc action, flag, 'remote,opts*'
388
374
  task flag, [:remote] do |_, args|
389
375
  if (remote = args.remote)
390
376
  args = args.extras
391
377
  else
392
378
  remote = choice_remote
393
- args = args.to_a
379
+ args = args.to_a.drop(1)
394
380
  end
395
381
  __send__(action, flag, args, remote: remote)
396
382
  end
397
383
  else
398
- format_desc(action, flag, 'opts*', after: flag == :all && action == 'pull' ? 'pattern*' : nil)
384
+ format_desc action, flag, 'opts*'
399
385
  task flag do |_, args|
400
386
  __send__ action, flag, args.to_a
401
387
  end
402
388
  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
426
389
  when 'commit'
427
390
  case flag
428
391
  when :all
@@ -436,12 +399,12 @@ module Squared
436
399
  if flag == :fixup
437
400
  ref, squash, pick = choice_commit(accept: [['Auto squash?', true]], reflog: false,
438
401
  values: ['Pick [amend|reword]'])
439
- pick &&= case pick.downcase
440
- when 'a', 'amend'
441
- 'amend'
442
- when 'r', 'reword'
443
- 'reword'
444
- end
402
+ pick = case pick&.downcase
403
+ when 'a', 'amend'
404
+ 'amend'
405
+ when 'r', 'reword'
406
+ 'reword'
407
+ end
445
408
  if squash
446
409
  found = false
447
410
  git_spawn(git_output('log --format=%h'), stdout: false).each do |val|
@@ -453,14 +416,12 @@ module Squared
453
416
  end
454
417
  end
455
418
  end
456
- opts = []
457
- refs = []
458
- unless pick == 'reword'
459
- if flag == :add
460
- opts = param_guard(action, flag, args: args.to_a)
461
- elsif (refs = args.to_a).empty?
462
- refs = readline('Enter file patterns', force: true).shellsplit
463
- end
419
+ refs = pick == 'reword' ? [] : param_guard(action, flag, args: args.to_a)
420
+ if flag == :add
421
+ opts = refs
422
+ refs = []
423
+ else
424
+ opts = []
464
425
  end
465
426
  commit(flag, opts, refs: refs, ref: ref, squash: squash, pick: pick)
466
427
  end
@@ -501,7 +462,6 @@ module Squared
501
462
  when 'stash'
502
463
  format_desc(action, flag, 'opts*', after: case flag
503
464
  when :push then 'pathspec*'
504
- when :branch then 'name,stash?|:'
505
465
  when :clear, :list then nil
506
466
  else 'stash?|:' end)
507
467
  task flag do |_, args|
@@ -623,12 +583,12 @@ module Squared
623
583
  format_desc action, flag, 'ref,opts*'
624
584
  task flag, [:commit] do |_, args|
625
585
  commit = commithead args.commit
626
- args = if commit
627
- args.extras
628
- else
629
- commit, opts = choice_commit(values: ['Options'])
630
- OptionPartition.strip(opts)
631
- end
586
+ if commit
587
+ args = args.extras
588
+ else
589
+ commit, opts = choice_commit(values: ['Options'])
590
+ args = OptionPartition.strip(opts)
591
+ end
632
592
  checkout(flag, args, commit: commit)
633
593
  end
634
594
  when :detach
@@ -718,10 +678,10 @@ module Squared
718
678
  when :create
719
679
  format_desc action, flag, '(^)name,ref?=HEAD|:'
720
680
  task flag, [:name, :commit] do |_, args|
721
- branch = param_guard(action, flag, args: args, key: :name)
681
+ target = param_guard(action, flag, args: args, key: :name)
722
682
  commit = commithead args.commit
723
683
  commit, track = choice_commit(values: ['Track? [Y|n]'], force: false) if commit == ':'
724
- switch(flag, branch: branch, commit: commit, track: track)
684
+ switch(flag, target: target, commit: commit, track: track)
725
685
  end
726
686
  when :detach
727
687
  format_desc action, flag, 'ref?=HEAD'
@@ -729,16 +689,11 @@ module Squared
729
689
  commit = commithead(args.commit) || choice_commit(force: false)
730
690
  switch(flag, commit: commit)
731
691
  end
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'))
692
+ when :merge
693
+ format_desc action, flag, 'branch?'
694
+ task flag, [:branch] do |_, args|
695
+ commit = args.branch || choice_refs('Choose a branch')
696
+ switch(flag, commit: commit)
742
697
  end
743
698
  end
744
699
  when 'reset'
@@ -759,10 +714,10 @@ module Squared
759
714
  end
760
715
  print_success if success?(reset(flag, args, commit: commit))
761
716
  end
762
- when :index, :undo
763
- format_desc(action, flag, flag == :index ? 'opts*,pathspec*' : nil)
717
+ when :index
718
+ format_desc action, flag, 'opts*,pathspec*'
764
719
  task flag do |_, args|
765
- reset(flag, flag == :index ? args.to_a : [])
720
+ reset flag, args.to_a
766
721
  end
767
722
  when :mode
768
723
  format_desc action, flag, 'mode,ref?=HEAD|:'
@@ -804,28 +759,28 @@ module Squared
804
759
  when :branch
805
760
  format_desc action, flag, 'upstream,branch?=HEAD,opts*'
806
761
  task flag, [:upstream] do |_, args|
807
- args = if (upstream = args.upstream)
808
- args.extras
809
- else
810
- upstream, opts = choice_refs('Choose upstream branch', values: ['Options'])
811
- OptionPartition.strip(opts)
812
- end
762
+ if (upstream = args.upstream)
763
+ args = args.extras
764
+ else
765
+ upstream, opts = choice_refs('Choose upstream branch', values: ['Options'])
766
+ args = OptionPartition.strip(opts)
767
+ end
813
768
  rebase(flag, args, upstream: upstream)
814
769
  end
815
770
  when :onto
816
771
  format_desc action, flag, 'ref,upstream,branch?=HEAD'
817
772
  task flag, [:commit, :upstream, :branch] do |_, args|
818
773
  commit = commithead args.commit
819
- args = if commit
820
- upstream = param_guard(action, flag, args: args, key: :upstream)
821
- branch = args.branch
822
- []
823
- else
824
- commit = choice_refs 'Choose "onto" branch'
825
- target, opts = choice_commit(multiple: 2, values: ['Options'], reflog: false)
826
- branch, upstream = target
827
- OptionPartition.strip(opts)
828
- end
774
+ if commit
775
+ upstream = param_guard(action, flag, args: args, key: :upstream)
776
+ branch = args.branch
777
+ args = []
778
+ else
779
+ commit = choice_refs 'Choose "onto" branch'
780
+ target, opts = choice_commit(multiple: 2, values: ['Options'], reflog: false)
781
+ branch, upstream = target
782
+ args = OptionPartition.strip(opts)
783
+ end
829
784
  rebase(flag, args, commit: commit, upstream: upstream, branch: branch)
830
785
  end
831
786
  when :commit, :'no-commit'
@@ -861,7 +816,7 @@ module Squared
861
816
  rev_parse(flag, ref: ref, size: size)
862
817
  end
863
818
  when :build
864
- format_desc action, flag, 'opts*'
819
+ format_desc action, flag, OPT_GIT[:status]
865
820
  task flag do |_, args|
866
821
  revbuild flag, args.to_a
867
822
  end
@@ -878,7 +833,7 @@ module Squared
878
833
  ls_remote(flag, args.extras, remote: args.remote)
879
834
  end
880
835
  else
881
- format_desc(action, flag, 'opts*,pattern*', after: action == 'files' ? 'pathspec*' : nil)
836
+ format_desc action, flag, 'opts*,pattern*'
882
837
  task flag do |_, args|
883
838
  __send__(action == 'refs' ? :ls_remote : :ls_files, flag, args.to_a)
884
839
  end
@@ -889,13 +844,13 @@ module Squared
889
844
  format_desc action, flag, 'ref,opts*,pathspec*'
890
845
  task flag, [:commit] do |_, args|
891
846
  commit = commithead args.commit
892
- args = if commit
893
- args.extras
894
- else
895
- commit, opts, files = choice_commit(values: ['Options', ['Pathspec', true]])
896
- files = files&.shellsplit
897
- OptionPartition.strip(opts)
898
- end
847
+ if commit
848
+ args = args.extras
849
+ else
850
+ commit, opts, files = choice_commit(values: ['Options', ['Pathspec', true]])
851
+ args = OptionPartition.strip(opts)
852
+ files = files&.shellsplit
853
+ end
899
854
  restore(flag, args, commit: commit, files: files)
900
855
  end
901
856
  when :staged, :worktree
@@ -904,14 +859,19 @@ module Squared
904
859
  args = args.to_a
905
860
  if args.empty? || args.last == ':'
906
861
  files = []
907
- status_data.each { |row| files << row[0] if row[flag == :staged ? 2 : 1].match?(/[AMDRTC]/) }
862
+ status_data.each do |line|
863
+ case (flag == :staged ? line[2] : line[1])
864
+ when /[AMDRTC]/
865
+ files << line[0]
866
+ end
867
+ end
908
868
  unless files.empty?
909
869
  files = choice_index('Select a file', files, multiple: true, force: false,
910
870
  accept: 'Restore?')
911
871
  end
912
872
  args.pop
913
873
  args, glob = args.partition { |val| val.match?(/^(?:[a-z-]+=|[^*]+$)/) }
914
- files.concat(glob)
874
+ (files ||= []).concat(glob)
915
875
  next if args.empty? && files.empty?
916
876
  end
917
877
  restore(flag, args, files: files)
@@ -919,17 +879,13 @@ module Squared
919
879
  end
920
880
  when 'git'
921
881
  before = case flag
922
- when :blame then 'file'
923
882
  when :mv then 'source+,destination'
924
883
  when :revert then 'commit+' end
925
884
  format_desc(action, flag, 'opts*', before: before, after: case flag
926
- when :add
927
- 'pathspec*,pattern*'
928
- when :clean, :rm, :status
929
- 'pathspec*'
930
- end)
885
+ when :add then 'pathspec*|:pattern:*'
886
+ when :clean, :rm then 'pathspec*' end)
931
887
  task flag do |_, args|
932
- __send__(flag == :status ? :status : :git, flag, args.to_a)
888
+ git flag, args.to_a
933
889
  end
934
890
  end
935
891
  end
@@ -953,13 +909,14 @@ module Squared
953
909
  super
954
910
  end
955
911
 
956
- def pull(flag = nil, opts = [], sync: invoked_sync?('pull', flag), remote: nil, hint: nil)
912
+ def pull(flag = nil, opts = [], sync: invoked_sync?('pull', flag), remote: nil)
957
913
  cmd, opts = git_session('pull', opts: opts)
958
- cmd << '--autostash' if option('autostash')
959
914
  if flag == :rebase
960
915
  cmd << '--rebase'
916
+ cmd << '--autostash' if option('autostash')
961
917
  else
962
- if (val = option('rebase', ignore: false))
918
+ cmd << '--autostash' if flag == :autostash
919
+ option('rebase', ignore: false) do |val|
963
920
  cmd << case val
964
921
  when '0', 'false'
965
922
  '--no-rebase'
@@ -967,51 +924,15 @@ module Squared
967
924
  VAL_GIT[:rebase][:value].include?(val) ? basic_option('rebase', val) : '--rebase'
968
925
  end
969
926
  end
970
- case flag
971
- when :all
972
- unless git_spawn('status -s -z --untracked-files=all').empty?
973
- if confirm('Stash local changes? [Y/n] ', 'Y')
974
- git_spawn 'stash push --keep-index --quiet'
975
- elsif !(force = confirm('Force checkout? [y/N] ', 'N'))
976
- return
977
- end
978
- end
979
- op = OptionPartition.new(opts, OPT_GIT[:pull], cmd, project: self, no: OPT_GIT[:no][:pull])
980
- reg = if op.empty?
981
- []
982
- else
983
- opts = op.uniq(opts)
984
- matchmap op
985
- end
986
- session_done op.target
987
- heads = []
988
- cur = nil
989
- foreachref('heads', format: '%(if)%(HEAD)%(then)* %(end)%(refname:short)').each do |line|
990
- line.chomp!
991
- cur ||= line.delete_prefix!('* ')
992
- heads << line if matchany?(line, reg)
993
- end
994
- raise_error('head not found', hint: 'for-each-ref') unless cur
995
- opts << 'ff-only' if opts.empty? && !option('ff-only', equals: '0')
996
- (heads.dup << cur).each_with_index do |branch, index|
997
- next unless (index < heads.size && cur != branch) || index == heads.size
998
-
999
- git_spawn 'switch --quiet', force && '--force', shell_quote(branch)
1000
- pull(nil, opts, sync: false, hint: branch) if heads.include?(branch)
1001
- end
1002
- return
1003
- when :autostash
1004
- cmd << '--autostash'
1005
- end
1006
927
  end
1007
928
  append_pull(opts, OPT_GIT[:pull] + OPT_GIT[:fetch][:pull],
1008
929
  no: OPT_GIT[:no][:pull] + OPT_GIT[:no][:fetch][:pull], remote: remote, flag: flag, from: :pull)
1009
- source(sync: sync, sub: if stdout?
930
+ source(sync: sync, sub: if verbose
1010
931
  [
1011
932
  { pat: /^(.+)(\|\s+\d+\s+)([^-]*)(-+)(.*)$/, styles: color(:red), index: 4 },
1012
- { pat: /^(.+)(\|\s+\d+\s+)(\++)(.*)$/, styles: color(:green), index: 3 }
933
+ { pat: /^(.+)(\|\s+\d+\s+)(\++)(-*)(.*)$/, styles: color(:green), index: 3 }
1013
934
  ]
1014
- end, hint: hint, **threadargs)
935
+ end, **threadargs)
1015
936
  end
1016
937
 
1017
938
  def rebase(flag = nil, opts = [], sync: invoked_sync?('rebase', flag), commit: nil, upstream: nil, branch: nil,
@@ -1024,7 +945,7 @@ module Squared
1024
945
  return unless upstream
1025
946
 
1026
947
  op = OptionPartition.new(opts, OPT_GIT[:rebase], cmd, project: self, no: OPT_GIT[:no][:rebase])
1027
- cmd << upstream
948
+ cmd << shell_escape(upstream)
1028
949
  append_head op.shift
1029
950
  op.clear(pass: false)
1030
951
  when :onto
@@ -1032,13 +953,9 @@ module Squared
1032
953
 
1033
954
  cmd << '--interactive' if option('interactive', 'i')
1034
955
  cmd << shell_option('onto', commit) if commit
1035
- cmd << upstream
956
+ cmd << shell_escape(upstream)
1036
957
  append_head branch
1037
958
  else
1038
- unless gitpath('REBASE_HEAD').exist?
1039
- puts log_message(Logger::INFO, name, 'no rebase in progress', hint: command) if stdout?
1040
- return
1041
- end
1042
959
  return unless VAL_GIT[:rebase][:send].include?(command)
1043
960
 
1044
961
  cmd << "--#{command}"
@@ -1052,7 +969,6 @@ module Squared
1052
969
 
1053
970
  def fetch(flag = nil, opts = [], sync: invoked_sync?('fetch', flag), remote: nil)
1054
971
  opts = git_session('fetch', opts: opts).last
1055
- opts << 'all' if flag == :all || option('all')
1056
972
  append_pull(opts, collect_hash(OPT_GIT[:fetch]), no: collect_hash(OPT_GIT[:no][:fetch]),
1057
973
  remote: remote, flag: flag, from: :fetch)
1058
974
  source(sync: sync, **threadargs)
@@ -1063,23 +979,26 @@ module Squared
1063
979
 
1064
980
  cmd = git_session('clone', worktree: false)
1065
981
  opts = data[1].dup
1066
- if (val = option('depth', ignore: false))
982
+ option('depth', ignore: false) do |val|
1067
983
  if (n = val.to_i) > 0
1068
984
  opts[:depth] = n
1069
985
  else
1070
986
  opts.delete(:depth)
1071
987
  end
1072
988
  end
1073
- opts[:origin] = val if (val = option('origin', ignore: false))
1074
- if (val = option('branch', strict: true))
1075
- opts[:branch] = val
989
+ option('origin', ignore: false) { |val| opts[:origin] = val }
990
+ if (branch = option('branch', strict: true))
991
+ opts[:branch] = branch
1076
992
  opts.delete(:revision)
1077
- elsif (val = option('revision', strict: true))
1078
- opts[:revision] = val
1079
- opts.delete(:branch)
1080
- opts.delete(:mirror)
993
+ else
994
+ option('revision', strict: true) do |val|
995
+ opts[:revision] = val
996
+ opts.delete(:branch)
997
+ opts.delete(:mirror)
998
+ end
1081
999
  end
1082
- opts[:local] = val != '0' if (val = option('local', strict: true))
1000
+ option('local', strict: true) { |val| opts[:local] = val != '0' }
1001
+ option('bare') { |val| opts[:bare] = val }
1083
1002
  opts.delete(:'recurse-submodules') || opts.delete(:'no-recurse-submodules') if append_submodules(from: :clone)
1084
1003
  append_hash opts
1085
1004
  cmd << '--quiet' unless verbose
@@ -1092,33 +1011,19 @@ module Squared
1092
1011
  cmd, opts = git_session('stash', flag, opts: opts)
1093
1012
  list = OPT_GIT[:stash][:common] + OPT_GIT[:stash].fetch(flag, [])
1094
1013
  if flag == :list
1095
- list += collect_hash OPT_GIT[:log]
1096
- no = collect_hash OPT_GIT[:no][:log]
1014
+ list += collect_hash(OPT_GIT[:log])
1015
+ no = collect_hash(OPT_GIT[:no][:log])
1097
1016
  end
1098
1017
  op = OptionPartition.new(opts, list, cmd, project: self, no: no, first: flag == :push ? matchpathspec : nil)
1099
1018
  case flag
1100
1019
  when :push
1101
1020
  append_pathspec op.extras
1102
- when :pop, :apply, :drop, :branch
1103
- if op.remove(':')
1104
- if flag == :branch
1105
- if op.empty?
1106
- values = [['Branch name', true]]
1107
- else
1108
- op << op.pop
1109
- end
1110
- end
1111
- out = choice_index('Choose a stash', git_spawn('stash list', stdout: false),
1112
- values: values, column: /^[^@]+@\{(\d+)\}/, force: true)
1113
- if values
1114
- op.merge(out.reverse)
1115
- else
1116
- op << out
1117
- end
1021
+ when :pop, :apply, :drop
1022
+ if op.extras.delete(':')
1023
+ op << choice_index('Choose a stash', git_spawn('stash list', stdout: false),
1024
+ column: /^[^@]+@\{(\d+)\}/, force: true)
1118
1025
  elsif !op.empty?
1119
- op << op.pop
1120
- elsif flag == :branch
1121
- raise_error 'no branch name'
1026
+ op << shell_escape(op.pop)
1122
1027
  end
1123
1028
  op.clear
1124
1029
  when :clear
@@ -1135,35 +1040,30 @@ module Squared
1135
1040
  end
1136
1041
  else
1137
1042
  git_session('stash', 'push', opts: opts)
1138
- append_option(OptionPartition.select(OPT_GIT[:stash][:push], no: false), no: true, ignore: false)
1043
+ append_option(OPT_GIT[:stash][:push].grep_v(/[=|]/), no: true, ignore: false)
1139
1044
  append_message
1140
1045
  end
1141
1046
  source(banner: !quiet?, sync: sync, **threadargs)
1142
1047
  end
1143
1048
 
1144
- def status(flag = nil, opts = [])
1145
- cmd, opts = git_session('status', opts: opts)
1146
- if flag
1147
- op = OptionPartition.new(opts, OPT_GIT[:status], cmd, project: self, no: OPT_GIT[:no][:status])
1148
- append_pathspec op.extras
1149
- else
1150
- cmd << (option('long') ? '--long' : '--short')
1151
- cmd << '--branch' if option('branch')
1152
- if (val = option('ignore-submodules', ignore: false))
1153
- cmd << basic_option('ignore-submodules', case val
1154
- when '0', 'none'
1155
- 'none'
1156
- when '1', 'untracked'
1157
- 'untracked'
1158
- when '2', 'dirty'
1159
- 'dirty'
1160
- else
1161
- 'all'
1162
- end)
1163
- end
1164
- append_pathspec
1049
+ def status(*)
1050
+ cmd = git_session 'status'
1051
+ cmd << (option('long') ? '--long' : '--short')
1052
+ cmd << '--branch' if option('branch')
1053
+ option('ignore-submodules', ignore: false) do |val|
1054
+ cmd << basic_option('ignore-submodules', case val
1055
+ when '0', 'none'
1056
+ 'none'
1057
+ when '1', 'untracked'
1058
+ 'untracked'
1059
+ when '2', 'dirty'
1060
+ 'dirty'
1061
+ else
1062
+ 'all'
1063
+ end)
1165
1064
  end
1166
- if stdout?
1065
+ append_pathspec
1066
+ if verbose
1167
1067
  r = color(:red)
1168
1068
  g = color(:green)
1169
1069
  sub = if session_arg?('short')
@@ -1171,7 +1071,7 @@ module Squared
1171
1071
  { pat: /^(.)([A-Z?!])(.+)$/, styles: r, index: 2 },
1172
1072
  { pat: /^([A-Z?!])(.+)$/, styles: g },
1173
1073
  { pat: /^(\?\?)(.+)$/, styles: r },
1174
- { pat: /^(## )(.+?)(\.{3})(.+)$/, styles: [nil, g, nil, r], index: -1 }
1074
+ { pat: /^(## )((?~\.{3}))(\.{3})(.+)$/, styles: [nil, g, nil, r], index: -1 }
1175
1075
  ]
1176
1076
  else
1177
1077
  [pat: /^(\t+)([a-z]+: +.+)$/, styles: r, index: 2]
@@ -1185,8 +1085,8 @@ module Squared
1185
1085
  def revbuild(flag = nil, opts = [], sync: nil, **kwargs)
1186
1086
  statusargs = lambda do
1187
1087
  {
1188
- include: relativepath(Array(kwargs[:include]), all: true),
1189
- exclude: relativepath(Array(kwargs[:exclude]), all: true)
1088
+ include: relativepath(as_a(kwargs[:include]), all: true),
1089
+ exclude: relativepath(as_a(kwargs[:exclude]), all: true)
1190
1090
  }
1191
1091
  end
1192
1092
  unless workspace.closed
@@ -1204,19 +1104,19 @@ module Squared
1204
1104
  kwargs = kwargs.key?(:include) || kwargs.key?(:exclude) ? statusargs.call : @revbuild || {}
1205
1105
  case flag
1206
1106
  when :build
1207
- op = OptionPartition.new(opts, %w[ignore-submodules=b? ignored=b? untracked-files=b?], project: self)
1107
+ op = OptionPartition.new(opts, OPT_GIT[:status], project: self)
1208
1108
  op.clear(append: true)
1209
1109
  args = op.to_a
1210
1110
  else
1211
1111
  args = []
1212
- args << basic_option('untracked-files', flag) if (flag = option('untracked-files', prefix: 'git'))
1213
- args << basic_option('ignore-submodules', flag) if (flag = option('ignore-submodules', prefix: 'git'))
1214
- args << basic_option('ignored', flag) if (flag = option('ignored', prefix: 'git'))
1112
+ option('untracked-files', prefix: 'git') { |val| args << basic_option('untracked-files', val) }
1113
+ option('ignore-submodules', prefix: 'git') { |val| args << basic_option('ignore-submodules', val) }
1114
+ option('ignored', prefix: 'git') { |val| args << basic_option('ignored', val) }
1215
1115
  end
1216
1116
  if (cur = workspace.rev_entry(name)) && cur['revision'] == sha && !env('REVBUILD_FORCE')
1217
1117
  files = status_digest(*args, **kwargs)
1218
1118
  if cur['files'].size == files.size && cur['files'].find { |key, val| files[key] != val }.nil?
1219
- if stdout?
1119
+ if verbose
1220
1120
  if (since = workspace.rev_timesince(name, 'build'))
1221
1121
  puts log_message(Logger::INFO, name, 'no changes', subject: 'revbuild', hint: "#{since} ago")
1222
1122
  else
@@ -1231,7 +1131,10 @@ module Squared
1231
1131
  rescue StandardError => e
1232
1132
  warn log_message(Logger::WARN, e, pass: true) if warning?
1233
1133
  else
1234
- print_status(name, subject: 'revbuild', start: start, from: :completed)
1134
+ if verbose
1135
+ msg = sub_style('completed', styles: theme[:active])
1136
+ puts log_message(Logger::INFO, name, msg, subject: 'revbuild', hint: time_format(epochtime - start))
1137
+ end
1235
1138
  workspace.rev_write(name, { 'revision' => sha, 'files' => status_digest(*args, **kwargs) },
1236
1139
  sync: sync, utc: 'build')
1237
1140
  end
@@ -1261,9 +1164,6 @@ module Squared
1261
1164
  when :patch
1262
1165
  cmd << '--patch'
1263
1166
  append_pathspec(refs, pass: false)
1264
- when :undo
1265
- cmd << '--hard HEAD@{1}'
1266
- ref = false
1267
1167
  end
1268
1168
  unless ref == false
1269
1169
  append_commit(ref, head: true)
@@ -1274,7 +1174,7 @@ module Squared
1274
1174
 
1275
1175
  def checkout(flag, opts = [], branch: nil, origin: nil, create: nil, commit: nil, detach: nil, merge: false)
1276
1176
  cmd, opts = git_session('checkout', opts: opts)
1277
- append_option 'force', 'f', 'merge'
1177
+ append_option 'force', 'merge'
1278
1178
  case flag
1279
1179
  when :branch
1280
1180
  cmd << '--detach' if detach == 'd' || option('detach')
@@ -1295,14 +1195,13 @@ module Squared
1295
1195
  else
1296
1196
  op = OptionPartition.new(opts, OPT_GIT[:checkout], cmd, project: self, no: OPT_GIT[:no][:checkout],
1297
1197
  first: flag == :path ? matchpathspec : nil)
1298
- if flag == :path
1198
+ if flag == :commit
1199
+ op.append(commit)
1200
+ .clear(pass: false)
1201
+ else
1299
1202
  append_head
1300
1203
  append_pathspec(op.extras, pass: false)
1301
- print_success if success?(source)
1302
- return
1303
1204
  end
1304
- op.append(commit)
1305
- .clear(pass: false)
1306
1205
  end
1307
1206
  source
1308
1207
  end
@@ -1316,7 +1215,7 @@ module Squared
1316
1215
  elsif !session_arg?('s', 'sign', 'u', 'local-user')
1317
1216
  cmd << '--annotate'
1318
1217
  end
1319
- cmd << '--force' if option('force', 'f')
1218
+ cmd << '--force' if option('force')
1320
1219
  if !commit && message && (sha = commithash(message))
1321
1220
  commit = sha
1322
1221
  message = nil
@@ -1335,19 +1234,19 @@ module Squared
1335
1234
  list_result(ret, 'tags', from: from, grep: op.extras)
1336
1235
  return
1337
1236
  end
1338
- remote ||= option('remote')
1237
+ remote ||= option 'remote'
1339
1238
  source
1340
1239
  git_spawn('push', flag == :delete ? '-d' : nil, remote, *refs.map { |val| shell_quote(val) }) if remote
1341
1240
  end
1342
1241
 
1343
1242
  def log!(flag, opts = [], range: [], index: [])
1344
1243
  cmd, opts = git_session('log', opts: opts)
1345
- op = OptionPartition.new(opts, collect_hash(OPT_GIT[:log]), cmd, project: self,
1346
- no: collect_hash(OPT_GIT[:no][:log]),
1347
- first: matchpathspec)
1244
+ op = OptionPartition.new(opts, collect_hash(OPT_GIT[:log]), cmd,
1245
+ project: self, no: collect_hash(OPT_GIT[:no][:log]),
1246
+ first: matchpathspec)
1348
1247
  case flag
1349
1248
  when :between, :contain
1350
- op.add_quote(range.join(flag == :between ? '..' : '...'))
1249
+ op << shell_quote(range.join(flag == :between ? '..' : '...'))
1351
1250
  else
1352
1251
  op.merge(index)
1353
1252
  end
@@ -1370,18 +1269,19 @@ module Squared
1370
1269
  op << '--no-index'
1371
1270
  append_pathspec(refs, parent: true)
1372
1271
  else
1373
- op << '--merge-base' if option('merge-base')
1374
1272
  case flag
1375
1273
  when :view
1274
+ op << '--merge-base' if option('merge-base')
1376
1275
  op.merge(range)
1377
1276
  when :between, :contain
1378
1277
  op.delete('--merge-base')
1379
- op.add_quote(range.join(flag == :between ? '..' : '...'))
1278
+ op << shell_quote(range.join(flag == :between ? '..' : '...'))
1380
1279
  else
1381
- op.add_quote(branch) if branch
1280
+ op << '--merge-base' if option('merge-base')
1281
+ op << shell_quote(branch) if branch
1382
1282
  if !index.empty?
1383
1283
  if op.arg?('cached')
1384
- raise_error("one commit only: #{index.join(', ')}", hint: 'cached') if index.size > 1
1284
+ raise_error("one commit only: #{index.join(', ')}", hint: '--cached') if index.size > 1
1385
1285
  op << index.first
1386
1286
  else
1387
1287
  op.merge(index)
@@ -1433,16 +1333,16 @@ module Squared
1433
1333
  branch, origin, hint = line.split('...')
1434
1334
  if hint && !hint.match?(/^\[(\D+0,\D+0)\]$/)
1435
1335
  raise_error('work tree is not usable', hint: hint[1..-2])
1436
- elsif !origin || origin.empty?
1336
+ elsif origin.empty?
1437
1337
  return nil if pass
1438
1338
  break if dryrun
1439
1339
 
1440
1340
  unless (origin = option('upstream', prefix: 'git', ignore: false))
1441
1341
  if (origin = choice_refs('Choose an upstream', 'remotes', attempts: 1, force: false))
1442
1342
  git_spawn 'branch', quote_option('set-upstream-to', origin)
1443
- break
1343
+ else
1344
+ origin = readline('Enter an upstream', force: true)
1444
1345
  end
1445
- origin = readline('Enter an upstream', force: true)
1446
1346
  end
1447
1347
  raise_error('missing remote name', hint: origin) unless origin.include?('/')
1448
1348
  upstream = true
@@ -1489,10 +1389,6 @@ module Squared
1489
1389
  append_commit(*op.extras)
1490
1390
  end
1491
1391
  else
1492
- unless gitpath('MERGE_HEAD').exist?
1493
- puts log_message(Logger::INFO, name, 'no merge in progress', hint: command) if stdout?
1494
- return
1495
- end
1496
1392
  return unless VAL_GIT[:merge][:send].include?(command)
1497
1393
 
1498
1394
  cmd << "--#{command}"
@@ -1505,17 +1401,17 @@ module Squared
1505
1401
  stdout = false
1506
1402
  case flag
1507
1403
  when :create
1508
- if (arg = option('track', ignore: false))
1509
- cmd << case arg
1404
+ option('track', ignore: false) do |val|
1405
+ cmd << case val
1510
1406
  when '0', 'false'
1511
1407
  '--no-track'
1512
1408
  when 'direct', 'inherit'
1513
- basic_option('track', arg)
1409
+ basic_option 'track', val
1514
1410
  else
1515
1411
  '--track'
1516
1412
  end
1517
1413
  end
1518
- cmd << '--force' if option('force', 'f')
1414
+ cmd << '--force' if option('force')
1519
1415
  cmd << shell_quote(target)
1520
1416
  cmd << shell_quote(ref) if ref
1521
1417
  when :track
@@ -1530,12 +1426,14 @@ module Squared
1530
1426
  end
1531
1427
  when :delete
1532
1428
  remote&.each do |val|
1533
- source git_output('push --delete', *val.split('/', 2).map { |s| shell_quote(s) })
1429
+ source git_output('push', '--delete', *val.split('/', 2).map { |s| shell_quote(s) })
1534
1430
  end
1535
- force, list = refs.partition { |val| val.match?(/^[~^]/) }
1431
+ force, list = refs.partition { |val| val.start_with?(/[~^]/) }
1536
1432
  force.each do |val|
1537
- r = '-r' if val.delete!('~')
1538
- source git_output('branch', val.delete!('^') ? '-D' : '-d', r, shell_quote(val))
1433
+ dr = val[0, 2]
1434
+ d = dr.include?('^') ? '-D' : '-d'
1435
+ r = '-r' if dr.include?('~')
1436
+ source git_output('branch', d, r, shell_quote(val.sub(/^[~^]+/, '')))
1539
1437
  end
1540
1438
  return if list.empty?
1541
1439
 
@@ -1543,9 +1441,8 @@ module Squared
1543
1441
  append_value list
1544
1442
  remote = nil
1545
1443
  when :move, :copy
1546
- s = "-#{flag.to_s[0]}"
1547
- s.upcase! if option('force', 'f')
1548
- cmd << s
1444
+ flag = "-#{flag.to_s[0]}"
1445
+ cmd << (option('force') ? flag.upcase : flag)
1549
1446
  refs.compact.each { |val| cmd << shell_quote(val) }
1550
1447
  stdout = true
1551
1448
  when :current
@@ -1555,7 +1452,7 @@ module Squared
1555
1452
  when :list
1556
1453
  op = OptionPartition.new(opts, OPT_GIT[:branch], cmd << '--list',
1557
1454
  project: self, no: OPT_GIT[:no][:branch], single: /\Av+\z/)
1558
- op.each { |val| op.add_quote(val) }
1455
+ op.each { |val| op << shell_quote(val) }
1559
1456
  out, banner, from = source(io: true)
1560
1457
  print_item banner
1561
1458
  ret = write_lines(out, sub: [
@@ -1565,19 +1462,19 @@ module Squared
1565
1462
  list_result(ret, 'branches', from: from)
1566
1463
  return
1567
1464
  else
1568
- if (head = git_spawn('rev-parse --abbrev-ref HEAD').chomp).empty?
1465
+ head = git_spawn('rev-parse --abbrev-ref HEAD').chomp
1466
+ if head.empty?
1569
1467
  ret = 0
1570
1468
  else
1571
1469
  git_spawn 'fetch --all --prune --quiet' if option('sync')
1572
- cmd << '-vv --no-abbrev --list'
1573
- out, banner, from = source(io: true)
1470
+ out, banner, from = source(cmd << '-vv --no-abbrev --list', io: true)
1574
1471
  ret = write_lines(out, grep: [/^\*\s+#{Regexp.escape(head)}\s/], banner: banner, first: true) do |line|
1575
1472
  next line if stdin?
1576
1473
 
1577
1474
  data = line.sub(/^\*\s+/, '').split(/\s+/)
1578
1475
  a = sub_style(data[0], styles: theme[:inline])
1579
1476
  b = sub_style(data[1], styles: theme[:extra])
1580
- r = /\A(?:\[(.+?)(?=\]\s)\]\s)?(.+)\z/m.match(data[2..-1].join(' '))
1477
+ r = /\A(?:\[((?~\]\s))\]\s)?(.+)\z/m.match(data[2..-1].join(' '))
1581
1478
  if (r1 = r[1]) && r1 =~ /^(.+):(?: ([a-z]+) (\d+),)? ([a-z]+) (\d+)$/
1582
1479
  write = ->(s1, s2) { "#{s1.capitalize.rjust(7)}: #{sub_style(s2, styles: theme[:warn])}" }
1583
1480
  r1 = $1
@@ -1601,55 +1498,33 @@ module Squared
1601
1498
  if !ref
1602
1499
  print_success if flag == :create
1603
1500
  elsif remote && target
1604
- source git_output('push -u', shell_quote(ref.split('/', 2).first), shell_quote(target))
1605
- end
1606
- end
1607
-
1608
- def switch(flag, opts = [], branch: nil, commit: nil, track: nil)
1609
- cmd, opts = git_session('switch', opts: opts)
1610
- cmd << '--force' if option('force', 'f')
1611
- if flag == :branch
1612
- op = OptionPartition.new(opts, OPT_GIT[:switch], cmd, project: self, no: OPT_GIT[:no][:switch])
1613
- op.add_quote(branch)
1614
- else
1615
- case flag
1616
- when :create
1617
- c = 'c'
1618
- if target.start_with?('^')
1619
- target = target[1..-1]
1620
- c = c.upcase
1621
- end
1622
- cmd << quote_option(c, target)
1623
- cmd << case (track ||= option('track', ignore: false))
1624
- when 'n', 'N', '0', 'false'
1625
- '--no-track'
1626
- when 'y', 'Y', '1', 'true'
1627
- '--track'
1628
- when 'direct', 'inherit'
1629
- basic_option 'track', track
1630
- end
1631
- when :detach
1632
- cmd << "--#{flag}"
1633
- end
1634
- append_head commit
1501
+ source git_output('push', '-u', shell_quote(ref.split('/', 2).first), shell_quote(target))
1635
1502
  end
1636
- source
1637
1503
  end
1638
1504
 
1639
- def submodule(flag, opts = [], path: nil, url: nil)
1640
- cmd, opts = git_session('submodule', opts: opts)
1641
- op = OptionPartition.new(opts, OPT_GIT[:submodule].fetch(flag, []), cmd, project: self)
1505
+ def switch(flag, opts = [], target: nil, commit: nil, track: nil)
1506
+ cmd = git_session('switch', opts: opts).first
1642
1507
  case flag
1643
- when :branch, :url
1644
- op.adjoin("set-#{flag}")
1645
- op << '--'
1646
- op.add_path(path) if path
1647
- op.add_quote(url) if url
1648
- else
1649
- op.adjoin(flag)
1650
- op << '--recursive' if option('recursive', 'r')
1651
- op.splice(path: true)
1508
+ when :create
1509
+ c = 'c'
1510
+ if target.start_with?('^')
1511
+ target = target[1..-1]
1512
+ c = c.upcase
1513
+ end
1514
+ cmd << quote_option(c, target)
1515
+ cmd << case (track ||= option('track', ignore: false))
1516
+ when 'n', 'N', '0', 'false'
1517
+ '--no-track'
1518
+ when 'y', 'Y', '1', 'true'
1519
+ '--track'
1520
+ when 'direct', 'inherit'
1521
+ basic_option 'track', track
1522
+ end
1523
+ when :detach, :merge
1524
+ cmd << "--#{flag}"
1652
1525
  end
1526
+ cmd << '--force' if option('force')
1527
+ append_head commit
1653
1528
  source
1654
1529
  end
1655
1530
 
@@ -1672,15 +1547,19 @@ module Squared
1672
1547
  source(banner: false)
1673
1548
  return
1674
1549
  when :oneline
1675
- format = flag.to_s
1550
+ format = 'oneline'
1676
1551
  end
1677
- case format
1678
- when 'oneline', 'short', 'medium', 'full', 'fuller', 'reference', 'email', 'raw'
1679
- cmd << basic_option('format', format)
1680
- when /(?:^t?format:|%)/
1681
- cmd << quote_option('pretty', format)
1682
- else
1683
- opts << format if format
1552
+ if format
1553
+ case (val = format.downcase)
1554
+ when 'oneline', 'short', 'medium', 'full', 'fuller', 'reference', 'email', 'raw'
1555
+ cmd << basic_option('format', val)
1556
+ else
1557
+ if format.start_with?(/t?format:/) || format.include?('%')
1558
+ cmd << quote_option('pretty', format)
1559
+ else
1560
+ opts << format
1561
+ end
1562
+ end
1684
1563
  end
1685
1564
  op = OptionPartition.new(opts, OPT_GIT[:show] + OPT_GIT[:diff][:show] + OPT_GIT[:log][:diff], cmd,
1686
1565
  project: self,
@@ -1714,7 +1593,7 @@ module Squared
1714
1593
  cmd, opts = git_session('ls-remote', '--refs', opts: opts)
1715
1594
  cmd << "--#{flag}" unless flag == :remote
1716
1595
  op = OptionPartition.new(opts, OPT_GIT[:ls_remote], cmd, project: self)
1717
- op.add_quote(remote) if remote
1596
+ op << shell_quote(remote) if remote
1718
1597
  out, banner, from = source(io: true)
1719
1598
  print_item banner
1720
1599
  ret = write_lines(out, grep: op.extras, prefix: "refs/#{flag}/")
@@ -1724,7 +1603,6 @@ module Squared
1724
1603
  def ls_files(flag, opts = [])
1725
1604
  cmd, opts = git_session('ls-files', "--#{flag}", opts: opts)
1726
1605
  op = OptionPartition.new(opts, OPT_GIT[:ls_files], cmd, project: self)
1727
- op.splice(path: true, pattern: true)
1728
1606
  out, banner, from = source(io: true)
1729
1607
  print_item banner
1730
1608
  ret = write_lines(out, grep: op.extras)
@@ -1733,16 +1611,10 @@ module Squared
1733
1611
 
1734
1612
  def git(flag, opts = [])
1735
1613
  cmd, opts = git_session(flag, opts: opts)
1736
- op = OptionPartition.new(opts, OPT_GIT[:git].fetch(flag, []) + OPT_GIT.fetch(flag, []), cmd,
1737
- project: self, no: OPT_GIT[:no][flag], first: case flag
1738
- when :blame, :revert
1739
- nil
1740
- else matchpathspec end)
1614
+ list = OPT_GIT[:git].fetch(flag, []) + OPT_GIT.fetch(flag, [])
1615
+ op = OptionPartition.new(opts, list, cmd, project: self, no: OPT_GIT[:no][flag],
1616
+ first: flag == :revert ? nil : matchpathspec)
1741
1617
  case flag
1742
- when :blame
1743
- raise_error 'no file found' unless (n = op.index { |s| (path + s).file? })
1744
- op << '--' << shell_quote(path + op.delete_at(n))
1745
- op.clear
1746
1618
  when :revert
1747
1619
  if VAL_GIT[:rebase][:send].any? { |val| op.arg?(val) }
1748
1620
  op.clear
@@ -1753,25 +1625,24 @@ module Squared
1753
1625
  end
1754
1626
  when :add, :clean
1755
1627
  if flag == :add && !op.arg?('pathspec-from-file')
1756
- grep, list = op.partition { |val| OptionPartition.pattern?(val) }
1757
- unless grep.empty? && !list.empty?
1628
+ grep, list = op.extras.partition { |val| val.start_with?(':') && val.end_with?(':') }
1629
+ if list.empty? || !grep.empty?
1630
+ red = color(:red)
1758
1631
  grep.map! { |val| Regexp.new(val[1..-2]) }
1759
- files = [].tap do |out|
1760
- status_data.each do |a, b|
1761
- next if b.strip.empty? || (!grep.empty? && grep.none? { |pat| pat.match?(a) })
1632
+ files = status_data.map! do |a, b|
1633
+ next unless grep.empty? || grep.any? { |pat| pat.match?(a) }
1762
1634
 
1763
- out << "#{sub_style(b, styles: color(:red))} #{a}"
1764
- end
1635
+ "#{sub_style(b, styles: red)} #{a}"
1765
1636
  end
1637
+ .compact
1766
1638
  unless files.empty?
1767
1639
  files = choice_index('Select files', files, multiple: true, force: true, trim: /^\S+\s/,
1768
- accept: [['Add?', false, true]])
1640
+ accept: 'Add?')
1769
1641
  end
1770
1642
  op.swap(list + files)
1771
1643
  end
1772
1644
  end
1773
- return source(git_session('status -s'), banner: false) unless append_pathspec(op.extras)
1774
-
1645
+ append_pathspec op.extras
1775
1646
  verbose = flag == :add && !op.arg?('verbose')
1776
1647
  print_success if success?(source) && verbose
1777
1648
  return
@@ -1800,37 +1671,30 @@ module Squared
1800
1671
  private
1801
1672
 
1802
1673
  def source(cmd = @session, exception: true, io: false, sync: true, stdout: false, stderr: false, banner: true,
1803
- multiple: false, hint: nil, from: nil, send: :system, **kwargs)
1674
+ multiple: false, **kwargs)
1804
1675
  cmd = cmd.target if cmd.is_a?(OptionPartition)
1805
- if io && banner == false
1806
- from = nil
1807
- banner = nil
1808
- else
1809
- banner = nil if banner && (multiple || !banner?)
1810
- if cmd.respond_to?(:done)
1811
- if from.nil? && (from = cmd.drop(1).find { |val| val.match?(/\A[a-z]{1,2}[a-z\-]*\z/) })
1812
- from = :"git:#{from}"
1813
- end
1814
- banner &&= cmd.temp { |val| val.start_with?('--work-tree') || val.start_with?('--git-dir') }
1676
+ banner = nil if banner && (multiple || !banner?)
1677
+ if cmd.respond_to?(:done)
1678
+ if io && banner == false
1679
+ from = nil
1680
+ elsif !from && (from = cmd.drop(1).find { |val| val.match?(/\A[a-z]{1,2}[a-z\-]*\z/) })
1681
+ from = :"git:#{from}"
1815
1682
  end
1816
- from = nil if from == false
1683
+ banner &&= cmd.temp { |val| val.start_with?('--work-tree') || val.start_with?('--git-dir') }
1817
1684
  end
1818
1685
  cmd = session_done cmd
1819
1686
  log&.info cmd
1687
+ on :first, from
1820
1688
  banner = if banner
1821
- banner = (banner.is_a?(String) ? banner : cmd).gsub(File.join(path, ''), '')
1822
- format_banner(hint ? "#{banner} (#{hint})" : banner, banner: true)
1689
+ format_banner((banner.is_a?(String) ? banner : cmd).gsub(File.join(path, ''), ''), banner: true)
1823
1690
  end
1824
- on :first, from
1825
1691
  begin
1826
1692
  if io
1827
- ret = if stdout
1828
- `#{cmd}`
1829
- else
1830
- banner ? [IO.popen(cmd), banner, from] : IO.popen(cmd)
1831
- end
1832
- return ret
1833
- elsif stdin? ? sync : stdout
1693
+ return `#{cmd}` if stdout
1694
+
1695
+ return banner ? [IO.popen(cmd), banner, from] : IO.popen(cmd)
1696
+ end
1697
+ if stdin? ? sync : stdout
1834
1698
  print_item banner unless multiple
1835
1699
  ret = `#{cmd}`
1836
1700
  if !ret.empty?
@@ -1838,9 +1702,9 @@ module Squared
1838
1702
  elsif success?(!banner.nil?)
1839
1703
  print_success
1840
1704
  end
1841
- elsif !kwargs[:sub] && (sync || (!exception && !stderr))
1705
+ elsif sync || (!exception && !stderr)
1842
1706
  print_item banner unless multiple
1843
- ret = shell(cmd, name: send, exception: exception)
1707
+ ret = shell(cmd, exception: exception)
1844
1708
  else
1845
1709
  require 'open3'
1846
1710
  if stderr
@@ -1858,7 +1722,11 @@ module Squared
1858
1722
  end
1859
1723
  end
1860
1724
  rescue StandardError => e
1861
- on_error(e, from, pass: true)
1725
+ log&.error e
1726
+ ret = on(:error, from, e)
1727
+ raise if exception && ret != true
1728
+
1729
+ warn log_message(Logger::WARN, e, pass: true) if warning?
1862
1730
  nil
1863
1731
  else
1864
1732
  on :last, from
@@ -1867,7 +1735,16 @@ module Squared
1867
1735
  end
1868
1736
 
1869
1737
  def write_lines(data, grep: [], prefix: nil, sub: nil, banner: nil, loglevel: nil, pass: false, first: false)
1870
- grep = grep.empty? ? nil : matchmap(grep, prefix)
1738
+ grep = unless grep.empty?
1739
+ grep.map do |val|
1740
+ if val.is_a?(Regexp)
1741
+ val
1742
+ else
1743
+ val = ".*#{val}" if prefix && !val.sub!(/\A(\^|\\A)/, '')
1744
+ Regexp.new("#{prefix}#{val == '*' ? '.+' : val}")
1745
+ end
1746
+ end
1747
+ end
1871
1748
  sub = nil if stdin?
1872
1749
  ret = 0
1873
1750
  out = []
@@ -1898,7 +1775,7 @@ module Squared
1898
1775
  def list_result(size, type, grep: [], action: 'found', from: nil)
1899
1776
  if size == 0
1900
1777
  puts empty_status("No #{type} were #{action}", 'grep', grep.join(', '))
1901
- elsif stdout?
1778
+ elsif verbose
1902
1779
  styles = theme.fetch(:banner, []).reject { |s| s.to_s.end_with?('!') }
1903
1780
  styles << :bold if styles.size <= 1
1904
1781
  puts print_footer("#{size} #{size == 1 ? type.sub(/(?:(?<!l)e)?s\z/, '') : type}",
@@ -1941,7 +1818,8 @@ module Squared
1941
1818
  glob = kwargs.fetch(:include, [])
1942
1819
  pass = kwargs.fetch(:exclude, [])
1943
1820
  ret = {}
1944
- status_data(*args).each do |file,|
1821
+ status_data(*args).each do |line|
1822
+ file = line.first
1945
1823
  next if !glob.empty? && glob.none? { |val| File.fnmatch?(val, file, File::FNM_DOTMATCH) }
1946
1824
  next if !pass.empty? && pass.any? { |val| File.fnmatch?(val, file, File::FNM_DOTMATCH) }
1947
1825
 
@@ -1951,17 +1829,17 @@ module Squared
1951
1829
  end
1952
1830
 
1953
1831
  def status_data(*args)
1954
- [].tap do |ret|
1955
- git_spawn('status -z -uall', *args).split("\x0").each do |line|
1956
- next unless line =~ /^(.)(.) (.+)$/
1832
+ ret = []
1833
+ git_spawn('status -z -uall', *args).split("\x0").each do |line|
1834
+ next unless line =~ /^(.)(.) (.+)$/
1957
1835
 
1958
- ret << [$3, $2, $1]
1959
- end
1836
+ ret << [$3, $2, $1]
1960
1837
  end
1838
+ ret
1961
1839
  end
1962
1840
 
1963
1841
  def append_pull(opts, list, target: @session, flag: nil, no: nil, remote: nil, from: nil)
1964
- target << '--force' if option('force', 'f', target: target)
1842
+ target << '--force' if option('force', target: target)
1965
1843
  append_submodules(target: target, from: from)
1966
1844
  return if !remote && opts.empty?
1967
1845
 
@@ -1973,9 +1851,11 @@ module Squared
1973
1851
  when 'rebase'
1974
1852
  op << basic_option($1, $2) if VAL_GIT[:rebase][:value].include?($2)
1975
1853
  when 'shallow-since'
1976
- op.append?($1) { Date.parse($2)&.strftime('%F %T') }
1854
+ next unless (val = Date.parse($2))
1855
+
1856
+ op << quote_option($1, val.strftime('%F %T'))
1977
1857
  when 'recurse-submodules'
1978
- op.append?($1, $2, type: :basic)
1858
+ op << basic_option($1, $2) unless op.arg?('recurse-submodules')
1979
1859
  when 'refspec'
1980
1860
  refspec << shell_escape($2, quote: true)
1981
1861
  end
@@ -1985,7 +1865,7 @@ module Squared
1985
1865
  op.errors << opt
1986
1866
  end
1987
1867
  end
1988
- op << '--verbose' if (flag || from == :fetch) && stdout? && !op.arg?('quiet')
1868
+ op << '--verbose' if (flag || from == :fetch) && verbose && !op.arg?('quiet')
1989
1869
  if remote
1990
1870
  op.append(remote, delim: true)
1991
1871
  if (val = option('refspec', target: target, strict: true))
@@ -1998,7 +1878,7 @@ module Squared
1998
1878
  op.swap.merge(op.map! { |opt| shell_escape(opt, quote: true) })
1999
1879
  return
2000
1880
  elsif option('all')
2001
- op << '--all'
1881
+ cmd << '--all'
2002
1882
  end
2003
1883
  op.clear(errors: true, subject: flag.to_s) if flag
2004
1884
  end
@@ -2015,19 +1895,13 @@ module Squared
2015
1895
  def append_pathspec(files = [], target: @session, expect: false, parent: false, pass: true)
2016
1896
  if session_arg?('pathspec-from-file', target: target)
2017
1897
  option_clear files
2018
- true
2019
1898
  else
2020
- if files.empty? && (val = option('pathspec', target: target))
2021
- files = split_escape val
2022
- end
1899
+ option('pathspec', target: target) { |val| files = split_escape val } if files.empty?
2023
1900
  files = projectmap(files, parent: parent, pass: pass)
2024
1901
  if !files.empty?
2025
1902
  target << '--' << files.join(' ')
2026
- true
2027
1903
  elsif expect
2028
1904
  raise_error(parent ? 'pathspec not present' : 'pathspec not within worktree')
2029
- else
2030
- false
2031
1905
  end
2032
1906
  end
2033
1907
  end
@@ -2044,61 +1918,51 @@ module Squared
2044
1918
  end
2045
1919
 
2046
1920
  def append_submodules(target: @session, from: nil)
2047
- return unless (val = option('recurse-submodules', target: target, ignore: false))
2048
-
2049
- if from == :clone
2050
- case val
2051
- when '0', 'false'
2052
- target << '--no-recurse-submodules'
2053
- when '1', 'true'
2054
- target << '--recurse-submodules'
2055
- else
2056
- projectmap(split_escape(val)).each do |path|
2057
- target << basic_option('recurse-submodules', path)
1921
+ option('recurse-submodules', target: target, ignore: false) do |val|
1922
+ if from == :clone
1923
+ case val
1924
+ when '0', 'false'
1925
+ target << '--no-recurse-submodules'
1926
+ when '1', 'true'
1927
+ target << '--recurse-submodules'
1928
+ else
1929
+ projectmap(split_escape(val)).each do |path|
1930
+ target << basic_option('recurse-submodules', path)
1931
+ end
2058
1932
  end
1933
+ else
1934
+ target << case val
1935
+ when 'no', '0', 'false'
1936
+ '--no-recurse-submodules'
1937
+ when 'yes', 'on-demand'
1938
+ "--recurse-submodules=#{val}"
1939
+ else
1940
+ '--recurse-submodules'
1941
+ end
2059
1942
  end
2060
- else
2061
- target << case val
2062
- when 'no', '0', 'false'
2063
- '--no-recurse-submodules'
2064
- when 'yes', 'on-demand'
2065
- "--recurse-submodules=#{val}"
2066
- else
2067
- '--recurse-submodules'
2068
- end
2069
1943
  end
2070
1944
  end
2071
1945
 
2072
1946
  def foreachref(path, *args, format: nil)
2073
- path = Array(path).map! { |val| "refs/#{val}" }
2074
- format &&= quote_option('format', format)
2075
- ret = git_spawn('for-each-ref', format, *args, *path, stdout: workspace.windows?)
2076
- ret.is_a?(String) ? ret.lines : ret
1947
+ path = as_a(path).map! { |val| "refs/#{val}" }
1948
+ git_spawn('for-each-ref', format && quote_option('format', format), *args, *path, stdout: false)
2077
1949
  end
2078
1950
 
2079
1951
  def git_session(*cmd, opts: nil, worktree: true, **kwargs)
2080
- dir = worktree ? [quote_option('work-tree', path), quote_option('git-dir', gitpath)] : []
1952
+ dir = worktree ? ["--work-tree=#{shell_quote(path)}", "--git-dir=#{shell_quote(gitpath)}"] : []
2081
1953
  return session('git', *dir, *cmd, **kwargs) unless opts
2082
1954
 
2083
1955
  op = OptionPartition.new(opts, OPT_GIT[:common], dir, project: self)
2084
- [session('git', *op.to_a, *cmd, **kwargs), op.extras]
1956
+ ret = session('git', *op.to_a, *cmd, **kwargs)
1957
+ [ret, op.extras]
2085
1958
  end
2086
1959
 
2087
1960
  def git_output(*cmd, **kwargs)
2088
1961
  git_session(*cmd, main: false, options: false, **kwargs)
2089
1962
  end
2090
1963
 
2091
- def git_spawn(*cmd, exception: true, io: true, sync: true, stdout: true, banner: false, **kwargs)
2092
- kwargs[:send] = if sync
2093
- :system
2094
- else
2095
- exception = false
2096
- io = false
2097
- stdout = false
2098
- :spawn
2099
- end
2100
- source(cmd.first.is_a?(Set) ? cmd.first : git_output(*cmd), exception: exception, io: io, sync: sync,
2101
- stdout: stdout, banner: banner, **kwargs)
1964
+ def git_spawn(*cmd, stdout: true)
1965
+ source(cmd.first.is_a?(Set) ? cmd.first : git_output(*cmd), io: true, banner: false, stdout: stdout)
2102
1966
  end
2103
1967
 
2104
1968
  def dryrun?(*, target: @session, **)
@@ -2113,29 +1977,28 @@ module Squared
2113
1977
  target.include?('--quiet') || (target.include?('-q') && stripext(target.first) == 'git')
2114
1978
  end
2115
1979
 
2116
- def gitpath(*args)
2117
- path.join('.git', *args)
1980
+ def gitpath
1981
+ path + '.git'
2118
1982
  end
2119
1983
 
2120
1984
  def repotrack(origin, branch, quote: true)
2121
1985
  i = origin.index('/')
2122
- branch = "#{branch}:#{origin[(i + 1)..-1]}" unless origin.end_with?("/#{branch}")
2123
- ret = [origin[0..(i - 1)], branch]
2124
- quote ? ret.map! { |val| shell_quote(val) } : ret
1986
+ branch = "#{branch}:#{origin[i + 1..-1]}" unless origin.end_with?("/#{branch}")
1987
+ [origin[0..i - 1], branch].tap { |ret| ret.map! { |val| shell_quote(val) } if quote }
2125
1988
  end
2126
1989
 
2127
1990
  def commithash(val)
2128
- val[/\A:(\h{5,40})\z/, 1] || val[/\A#\{(\h{5,40})\}\z/, 1]
1991
+ val[/\A:(\h{5,40})\z/, 1]
2129
1992
  end
2130
1993
 
2131
1994
  def commithead(val)
2132
1995
  return val unless (s = matchhead(val))
2133
1996
 
2134
- s.match?(/^\d/) ? "@~#{s}" : "@#{s}"
1997
+ s.start_with?(/\d/) ? "@~#{s}" : "@#{s}"
2135
1998
  end
2136
1999
 
2137
2000
  def matchhead(val)
2138
- val && val =~ /^(?:(?:HEAD|@)([~^]\d*)?|H(\d+))$/ ? $2 || $1 || '' : nil
2001
+ val =~ /^(?:(?:HEAD|@)([~^]\d*)?|H(\d+))$/ ? $2 || $1 || '' : nil
2139
2002
  end
2140
2003
 
2141
2004
  def matchpathspec