squared 0.4.40 → 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
@@ -133,7 +133,8 @@ module Squared
133
133
  end
134
134
  File.write(@revfile, JSON.pretty_generate(@revdoc))
135
135
  rescue StandardError => e
136
- warn log_message(Logger::WARN, e, pass: true) if warning
136
+ log&.debug e
137
+ warn log_message(Logger::WARN, e, pass: true) if warning?
137
138
  ensure
138
139
  @revlock = false
139
140
  end
@@ -156,13 +157,14 @@ module Squared
156
157
  class Git < Base
157
158
  OPT_GIT = {
158
159
  common: %w[c=q bare glob-pathspecs icase-pathspecs literal-pathspecs no-optional-locks no-pager
159
- 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,
160
162
  add: %w[A|all e|edit f|force ignore-errors ignore-missing ignore-removal i|interactive no-all
161
163
  no-ignore-removal n|dry-run p|patch pathspec-file-nul renormalize sparse u|update v|verbose
162
164
  chmod=b pathspec-from-file=p].freeze,
163
- branch: %w[a|all create-reflog i|ignore-case q|quiet r|remotes v|verbose abbrev=i color=b column=b
164
- contains=b format=q merged=b no-contains=b no-merged=b points-at=b u|set-upstream-to=b sort=q
165
- 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,
166
168
  checkout: %w[l d|detach f|force ignore-other-worktrees ignore-skip-worktree-bits m|merge p|patch
167
169
  pathspec-file-nul q|quiet ours theirs conflict=b orphan=b pathspec-from-file=p t|track=b].freeze,
168
170
  diff: {
@@ -170,7 +172,8 @@ module Squared
170
172
  show: %w[s exit-code histogram].freeze
171
173
  }.freeze,
172
174
  fetch: {
173
- 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,
174
177
  pull: %w[4 6 n t a|append atomic dry-run f|force k|keep negotiate-only prefetch p|prune q|quiet
175
178
  set-upstream unshallow update-shallow v|verbose deepen=i depth=i j|jobs=i negotiation-tip=q
176
179
  recurse-submodules=v refmap=q o|server-option=q shallow-exclude=b shallow-since=v
@@ -178,9 +181,6 @@ module Squared
178
181
  }.freeze,
179
182
  git: {
180
183
  add: %w[N|intent-to-add refresh].freeze,
181
- 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
182
- p|porcelain root score-debug f|show-name e|show-email n|show-number show-stats abbrev=i
183
- contents=p date=q encoding=b ignore-rev=b ignore-revs-file=p reverse=q].freeze,
184
184
  clean: %w[d x X f|force n|dry-run i|interactive q|quiet e|exclude=q].freeze,
185
185
  mv: %w[k f|force n|dry-run v|verbose].freeze,
186
186
  revert: %w[e S=bm? abort continue n|no-commit quit reference skip cleanup=b gpg-sign=b? m|mainline=i
@@ -198,9 +198,9 @@ module Squared
198
198
  branches=q? committer=q decorate=b decorate-refs=q decorate-refs-exclude=q exclude=q
199
199
  exclude-hidden=b glob=q grep=q grep-reflog=q n|max-count=i max-parents=i min-parents=i no-walk=b?
200
200
  remotes=q? since=q since-as-filter=q skip=i tags=q? until=q].freeze,
201
- format: %w[t children combined-all-paths oneline left-right no-diff-merges parents relative-date
202
- show-signature date=q diff-merges=b encoding=b expand-tabs=i format=q notes=b pretty=q?
203
- 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,
204
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
205
205
  find-copies-harder full-index W|function-context w|ignore-all-space ignore-blank-lines
206
206
  ignore-cr-at-eol ignore-space-at-eol b|ignore-space-change D|irreversible-delete graph
@@ -230,12 +230,13 @@ module Squared
230
230
  theirs W|worktree conflict=b pathspec-from-file=p s|source=b].freeze,
231
231
  rev_parse: %w[absolute-git-dir all git-common-dir git-dir is-bare-repository is-inside-git-dir
232
232
  is-inside-work-tree is-shallow-repository local-env-vars no-revs not q|quiet revs-only
233
- shared-index-path show-cdup show-prefix show-superproject-working-tree show-toplevel sq sq-quote
234
- symbolic symbolic-full-name verify abbrev-ref=b? after=q before=q branches=q? default=q
235
- disambiguate=b exclude=q exclude-hidden=b git-path=p glob=q path-format=b? prefix=q remotes=q?
236
- resolve-git-dir=p short=i? show-object-format=b? since=q tags=q? until=q].freeze,
237
- show: %w[t combined-all-paths no-diff-merges remerge-diff show-signature diff-merges=b encoding=b
238
- 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,
239
240
  stash: {
240
241
  common: %w[q|quiet].freeze,
241
242
  push: %w[a|all u|include-untracked k|keep-index no-keep-index no-include-untracked pathspec-file-nul
@@ -243,21 +244,10 @@ module Squared
243
244
  pop: %w[index].freeze,
244
245
  apply: %w[index].freeze
245
246
  }.freeze,
246
- status: %w[z u=bm? b|branch long s|short show-stash v|verbose column=b find-renames=i? ignore-submodules=b?
247
- ignored=b? porcelain=b? untracked-files=b?].freeze,
248
- submodule: {
249
- status: %w[cached recursive].freeze,
250
- update: %w[checkout f|force init merge N|no-fetch no-recommend-shallow no-single-branch recommend-shallow
251
- rebase recursive remote single-branch depth=i filter=q jobs=i reference=b ref-format=q].freeze,
252
- branch: %w[b|branch d|default].freeze,
253
- sync: %w[recursive].freeze
254
- }.freeze,
255
- switch: %w[d|detach discard-changes f|force ignore-other-worktrees m|merge q|quiet conflict=b c|create=q
256
- C|force-create=q orphan=q t|track=b].freeze,
257
- tag: %w[n=im cleanup=b create-reflog i|ignore-case color=b? column=b contains=b? format=q merged=b?
258
- 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,
259
250
  no: {
260
- blame: %w[progress].freeze,
261
251
  branch: %w[color color-moved column track].freeze,
262
252
  checkout: %w[overwrite-ignore guess overlay progress recurse-submodules track].freeze,
263
253
  fetch: {
@@ -279,8 +269,6 @@ module Squared
279
269
  rev_parse: %w[flags].freeze,
280
270
  revert: %w[edit gpg-sign rerere-autoupdate].freeze,
281
271
  show: %w[standard-notes].freeze,
282
- status: %w[ahead-behind column renames].freeze,
283
- switch: %w[guess progress recurse-submodules track].freeze,
284
272
  tag: %w[column].freeze
285
273
  }.freeze
286
274
  }.freeze
@@ -300,10 +288,12 @@ module Squared
300
288
  include Rake::DSL
301
289
 
302
290
  def populate(ws, **)
303
- 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')
304
295
 
305
- namespace ws.task_name('git') do |ns|
306
- ws.format_desc(all = ws.task_join(ns.scope.path, 'all'), 'stash|rebase|autostash?,depend?')
296
+ ws.format_desc(all, 'stash|rebase|autostash?,depend?')
307
297
  task 'all' do |_, args|
308
298
  args = args.to_a
309
299
  cmd = if args.include?('stash')
@@ -320,7 +310,6 @@ module Squared
320
310
  cmd << ws.task_sync('build')
321
311
  Common::Utils.task_invoke(*cmd, **ws.invokeargs)
322
312
  end
323
-
324
313
  ws.series.sync << all
325
314
  ws.series.multiple << all
326
315
  end
@@ -338,31 +327,29 @@ module Squared
338
327
  end
339
328
 
340
329
  subtasks({
341
- 'branch' => %i[create track delete move copy list all current].freeze,
330
+ 'branch' => %i[create track delete move copy list current].freeze,
342
331
  'checkout' => %i[commit branch track detach path].freeze,
343
332
  'commit' => %i[add all amend amend-orig fixup].freeze,
344
333
  'diff' => %i[head branch files view between contain].freeze,
345
- 'fetch' => %i[origin remote all].freeze,
334
+ 'fetch' => %i[origin remote].freeze,
346
335
  'files' => %i[cached modified deleted others].freeze,
347
- 'git' => %i[add blame clean mv revert rm status].freeze,
336
+ 'git' => %i[add clean mv revert rm].freeze,
348
337
  'log' => %i[view between contain].freeze,
349
338
  'merge' => %i[commit no-commit send].freeze,
350
- 'pull' => %i[origin remote all].freeze,
339
+ 'pull' => %i[origin remote].freeze,
351
340
  'rebase' => %i[branch onto send].freeze,
352
341
  'refs' => %i[heads tags remote].freeze,
353
- 'reset' => %i[commit index patch mode undo].freeze,
342
+ 'reset' => %i[commit index patch mode].freeze,
354
343
  'restore' => %i[source staged worktree].freeze,
355
344
  'rev' => %i[commit build output].freeze,
356
345
  'show' => %i[format oneline textconv].freeze,
357
- 'stash' => %i[push pop apply branch drop clear list].freeze,
358
- 'submodule' => %i[status update branch url sync].freeze,
359
- 'switch' => %i[branch create detach].freeze,
346
+ 'stash' => %i[push pop apply drop clear list].freeze,
347
+ 'switch' => %i[create detach merge].freeze,
360
348
  'tag' => %i[add sign delete list].freeze
361
349
  })
362
350
 
363
351
  def initialize(*, **)
364
352
  super
365
- @submodule = basepath('.gitmodules').exist?
366
353
  initialize_ref Git.ref if gitpath.exist?
367
354
  end
368
355
 
@@ -372,56 +359,33 @@ module Squared
372
359
 
373
360
  def populate(*, **)
374
361
  super
375
- return unless ref?(Git.ref) || @only
362
+ return unless ref?(Git.ref)
376
363
 
377
364
  namespace name do
378
365
  Git.subtasks do |action, flags|
379
- next if task_pass?(action)
366
+ next if @pass.include?(action)
380
367
 
381
368
  namespace action do
382
369
  flags.each do |flag|
383
370
  case action
384
371
  when 'pull', 'fetch'
385
372
  if flag == :remote
386
- format_desc action, flag, 'remote?,opts*'
373
+ format_desc action, flag, 'remote,opts*'
387
374
  task flag, [:remote] do |_, args|
388
375
  if (remote = args.remote)
389
376
  args = args.extras
390
377
  else
391
378
  remote = choice_remote
392
- args = args.to_a
379
+ args = args.to_a.drop(1)
393
380
  end
394
381
  __send__(action, flag, args, remote: remote)
395
382
  end
396
383
  else
397
- format_desc(action, flag, 'opts*', after: flag == :all && action == 'pull' ? 'pattern*' : nil)
384
+ format_desc action, flag, 'opts*'
398
385
  task flag do |_, args|
399
386
  __send__ action, flag, args.to_a
400
387
  end
401
388
  end
402
- when 'submodule'
403
- break unless @submodule
404
-
405
- case flag
406
- when :branch
407
- format_desc action, flag, 'path,opts*'
408
- task flag, [:path] do |_, args|
409
- path = param_guard(action, flag, args: args, key: :path)
410
- submodule(flag, args.extras, path: path)
411
- end
412
- when :url
413
- format_desc action, flag, 'path,url,opts*'
414
- task flag, [:path, :url] do |_, args|
415
- path = param_guard(action, flag, args: args, key: :path)
416
- url = param_guard(action, flag, args: args, key: :url)
417
- submodule(flag, args.extras, path: path, url: url)
418
- end
419
- else
420
- format_desc action, flag, 'opts*,path*'
421
- task flag do |_, args|
422
- submodule flag, args.to_a
423
- end
424
- end
425
389
  when 'commit'
426
390
  case flag
427
391
  when :all
@@ -435,12 +399,12 @@ module Squared
435
399
  if flag == :fixup
436
400
  ref, squash, pick = choice_commit(accept: [['Auto squash?', true]], reflog: false,
437
401
  values: ['Pick [amend|reword]'])
438
- pick &&= case pick.downcase
439
- when 'a', 'amend'
440
- 'amend'
441
- when 'r', 'reword'
442
- 'reword'
443
- end
402
+ pick = case pick&.downcase
403
+ when 'a', 'amend'
404
+ 'amend'
405
+ when 'r', 'reword'
406
+ 'reword'
407
+ end
444
408
  if squash
445
409
  found = false
446
410
  git_spawn(git_output('log --format=%h'), stdout: false).each do |val|
@@ -452,14 +416,12 @@ module Squared
452
416
  end
453
417
  end
454
418
  end
455
- opts = []
456
- refs = []
457
- unless pick == 'reword'
458
- if flag == :add
459
- opts = param_guard(action, flag, args: args.to_a)
460
- elsif (refs = args.to_a).empty?
461
- refs = readline('Enter file patterns', force: true).shellsplit
462
- 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 = []
463
425
  end
464
426
  commit(flag, opts, refs: refs, ref: ref, squash: squash, pick: pick)
465
427
  end
@@ -499,11 +461,9 @@ module Squared
499
461
  end
500
462
  when 'stash'
501
463
  format_desc(action, flag, 'opts*', after: case flag
502
- when :push then 'pathspec*,:'
503
- when :branch then 'name,stash?|:'
464
+ when :push then 'pathspec*'
504
465
  when :clear, :list then nil
505
- else 'stash?|:'
506
- end)
466
+ else 'stash?|:' end)
507
467
  task flag do |_, args|
508
468
  stash flag, args.to_a
509
469
  end
@@ -541,7 +501,7 @@ module Squared
541
501
  commit1 = commithead args.commit1
542
502
  if commit1
543
503
  commit2 = commithead param_guard(action, flag, args: args, key: :commit2)
544
- args = args.extras
504
+ args = args.extras.to_a
545
505
  range = [commit1, commit2]
546
506
  else
547
507
  range, opts, refs = choice_commit(multiple: view ? true : 2, values: %w[Options Pathspec])
@@ -603,7 +563,7 @@ module Squared
603
563
  detach = args.detach
604
564
  commit = commithead args.commit
605
565
  end
606
- param_guard(action, flag, args: { create: create }, key: :create, pat: /\A[Bb]\z/) if create
566
+ param_guard(action, flag, args: { create: create }, key: :create, pat: /\Ab\z/i) if create
607
567
  else
608
568
  branch = choice_refs 'Choose a branch to switch'
609
569
  end
@@ -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
@@ -636,7 +596,7 @@ module Squared
636
596
  task flag, [:commit] do |_, args|
637
597
  commit = commithead args.commit
638
598
  unless commit
639
- commit, merge = choice_commit(values: ['Merge? [y/N]'])
599
+ commit, merge = choice_commit(values: ['Merge? [y|N]'])
640
600
  merge = merge&.upcase == 'Y'
641
601
  end
642
602
  checkout(flag, commit: commit, merge: merge)
@@ -673,7 +633,7 @@ module Squared
673
633
  branch(flag, target: target, ref: ref, remote: remote)
674
634
  end
675
635
  when :delete
676
- format_desc action, flag, '[^~]name*,:?'
636
+ format_desc action, flag, '(^~)name*,:?'
677
637
  task flag do |_, args|
678
638
  refs = args.to_a
679
639
  if refs.empty? || (r = refs.last == ':')
@@ -696,7 +656,12 @@ module Squared
696
656
  task flag do |_, args|
697
657
  branch flag, args.to_a
698
658
  end
699
- when :move, :copy
659
+ when :current
660
+ format_desc action, flag
661
+ task flag do
662
+ branch flag
663
+ end
664
+ else
700
665
  format_desc action, flag, 'branch,oldbranch?'
701
666
  task flag, [:branch, :oldbranch] do |_, args|
702
667
  if (branch = args.branch)
@@ -707,21 +672,16 @@ module Squared
707
672
  end
708
673
  branch(flag, refs: [oldbranch, branch])
709
674
  end
710
- else
711
- format_desc action, flag
712
- task flag do
713
- branch flag
714
- end
715
675
  end
716
676
  when 'switch'
717
677
  case flag
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
- commit, track = choice_commit(values: ['Track? [Y/n]'], force: false) if commit == ':'
724
- switch(flag, branch: branch, commit: commit, track: track)
683
+ commit, track = choice_commit(values: ['Track? [Y|n]'], force: false) if commit == ':'
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'
@@ -751,19 +706,18 @@ module Squared
751
706
  args = args.extras
752
707
  else
753
708
  commit, mode = choice_commit(values: ['Mode [mixed|soft|hard|N]'])
754
- args = args.extras.concat(case mode&.downcase
755
- when 'h', 'hard' then ['hard']
756
- when 's', 'soft' then ['soft']
757
- when 'n', 'N' then ['mixed', 'N']
758
- else ['mixed']
759
- end)
709
+ args = args.extras.to_a.concat(case mode&.downcase
710
+ when 'h', 'hard' then ['hard']
711
+ when 's', 'soft' then ['soft']
712
+ when 'n', 'N' then ['mixed', 'N']
713
+ else ['mixed'] end)
760
714
  end
761
715
  print_success if success?(reset(flag, args, commit: commit))
762
716
  end
763
- when :index, :undo
764
- format_desc(action, flag, flag == :index ? 'opts*,pathspec*' : nil)
717
+ when :index
718
+ format_desc action, flag, 'opts*,pathspec*'
765
719
  task flag do |_, args|
766
- reset(flag, flag == :index ? args.to_a : [])
720
+ reset flag, args.to_a
767
721
  end
768
722
  when :mode
769
723
  format_desc action, flag, 'mode,ref?=HEAD|:'
@@ -805,28 +759,28 @@ module Squared
805
759
  when :branch
806
760
  format_desc action, flag, 'upstream,branch?=HEAD,opts*'
807
761
  task flag, [:upstream] do |_, args|
808
- args = if (upstream = args.upstream)
809
- args.extras
810
- else
811
- upstream, opts = choice_refs('Choose upstream branch', values: ['Options'])
812
- OptionPartition.strip(opts)
813
- 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
814
768
  rebase(flag, args, upstream: upstream)
815
769
  end
816
770
  when :onto
817
771
  format_desc action, flag, 'ref,upstream,branch?=HEAD'
818
772
  task flag, [:commit, :upstream, :branch] do |_, args|
819
773
  commit = commithead args.commit
820
- args = if commit
821
- upstream = param_guard(action, flag, args: args, key: :upstream)
822
- branch = args.branch
823
- []
824
- else
825
- commit = choice_refs 'Choose "onto" branch'
826
- target, opts = choice_commit(reflog: false, multiple: 2, values: ['Options'])
827
- branch, upstream = target
828
- OptionPartition.strip(opts)
829
- 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
830
784
  rebase(flag, args, commit: commit, upstream: upstream, branch: branch)
831
785
  end
832
786
  when :commit, :'no-commit'
@@ -862,9 +816,7 @@ module Squared
862
816
  rev_parse(flag, ref: ref, size: size)
863
817
  end
864
818
  when :build
865
- next unless build?
866
-
867
- format_desc action, flag, 'opts*'
819
+ format_desc action, flag, OPT_GIT[:status]
868
820
  task flag do |_, args|
869
821
  revbuild flag, args.to_a
870
822
  end
@@ -881,7 +833,7 @@ module Squared
881
833
  ls_remote(flag, args.extras, remote: args.remote)
882
834
  end
883
835
  else
884
- format_desc(action, flag, 'opts*,pattern*', after: action == 'files' ? 'pathspec*' : nil)
836
+ format_desc action, flag, 'opts*,pattern*'
885
837
  task flag do |_, args|
886
838
  __send__(action == 'refs' ? :ls_remote : :ls_files, flag, args.to_a)
887
839
  end
@@ -892,13 +844,13 @@ module Squared
892
844
  format_desc action, flag, 'ref,opts*,pathspec*'
893
845
  task flag, [:commit] do |_, args|
894
846
  commit = commithead args.commit
895
- args = if commit
896
- args.extras
897
- else
898
- commit, opts, files = choice_commit(values: ['Options', ['Pathspec', true]])
899
- files = files&.shellsplit
900
- OptionPartition.strip(opts)
901
- 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
902
854
  restore(flag, args, commit: commit, files: files)
903
855
  end
904
856
  when :staged, :worktree
@@ -907,14 +859,19 @@ module Squared
907
859
  args = args.to_a
908
860
  if args.empty? || args.last == ':'
909
861
  files = []
910
- 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
911
868
  unless files.empty?
912
869
  files = choice_index('Select a file', files, multiple: true, force: false,
913
870
  accept: 'Restore?')
914
871
  end
915
872
  args.pop
916
873
  args, glob = args.partition { |val| val.match?(/^(?:[a-z-]+=|[^*]+$)/) }
917
- files.concat(glob)
874
+ (files ||= []).concat(glob)
918
875
  next if args.empty? && files.empty?
919
876
  end
920
877
  restore(flag, args, files: files)
@@ -922,18 +879,13 @@ module Squared
922
879
  end
923
880
  when 'git'
924
881
  before = case flag
925
- when :blame then 'file'
926
882
  when :mv then 'source+,destination'
927
- when :revert then 'commit+'
928
- end
883
+ when :revert then 'commit+' end
929
884
  format_desc(action, flag, 'opts*', before: before, after: case flag
930
- when :add
931
- 'pathspec*,pattern*'
932
- when :clean, :rm, :status
933
- 'pathspec*'
934
- end)
885
+ when :add then 'pathspec*|:pattern:*'
886
+ when :clean, :rm then 'pathspec*' end)
935
887
  task flag do |_, args|
936
- __send__(flag == :status ? :status : :git, flag, args.to_a)
888
+ git flag, args.to_a
937
889
  end
938
890
  end
939
891
  end
@@ -957,13 +909,14 @@ module Squared
957
909
  super
958
910
  end
959
911
 
960
- 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)
961
913
  cmd, opts = git_session('pull', opts: opts)
962
- cmd << '--autostash' if option('autostash')
963
914
  if flag == :rebase
964
915
  cmd << '--rebase'
916
+ cmd << '--autostash' if option('autostash')
965
917
  else
966
- if (val = option('rebase', ignore: false))
918
+ cmd << '--autostash' if flag == :autostash
919
+ option('rebase', ignore: false) do |val|
967
920
  cmd << case val
968
921
  when '0', 'false'
969
922
  '--no-rebase'
@@ -971,48 +924,15 @@ module Squared
971
924
  VAL_GIT[:rebase][:value].include?(val) ? basic_option('rebase', val) : '--rebase'
972
925
  end
973
926
  end
974
- case flag
975
- when :all
976
- unless git_spawn('status -s -z --untracked-files=all').empty?
977
- if confirm('Stash local changes? [Y/n] ', 'Y')
978
- git_spawn 'stash push --keep-index --quiet'
979
- elsif !(force = confirm('Force checkout? [y/N] ', 'N'))
980
- return
981
- end
982
- printsucc
983
- end
984
- op = OptionPartition.new(opts, OPT_GIT[:pull], cmd, project: self, no: OPT_GIT[:no][:pull])
985
- opts -= op.extras
986
- reg = matchmap op
987
- session_done op.target
988
- heads = []
989
- cur = nil
990
- foreachref('heads', format: '%(if)%(HEAD)%(then)* %(end)%(refname:short)').each do |line|
991
- line.chomp!
992
- cur ||= line.sub!(/\A\* /, '')
993
- heads << line if matchany?(line, reg)
994
- end
995
- raise_error('head not found', hint: 'for-each-ref') unless cur
996
- opts << 'ff-only' if opts.empty? && !option('ff-only', equals: '0')
997
- (heads.dup << cur).each_with_index do |branch, index|
998
- next unless (index < heads.size && cur != branch) || index == heads.size
999
-
1000
- git_spawn 'switch --quiet', force && '--force', shell_quote(branch)
1001
- pull(nil, opts, sync: false, hint: branch) if heads.include?(branch)
1002
- end
1003
- return
1004
- when :autostash
1005
- cmd << '--autostash'
1006
- end
1007
927
  end
1008
928
  append_pull(opts, OPT_GIT[:pull] + OPT_GIT[:fetch][:pull],
1009
929
  no: OPT_GIT[:no][:pull] + OPT_GIT[:no][:fetch][:pull], remote: remote, flag: flag, from: :pull)
1010
- source(sync: sync, sub: if stdout?
930
+ source(sync: sync, sub: if verbose
1011
931
  [
1012
932
  { pat: /^(.+)(\|\s+\d+\s+)([^-]*)(-+)(.*)$/, styles: color(:red), index: 4 },
1013
- { pat: /^(.+)(\|\s+\d+\s+)(\++)(.*)$/, styles: color(:green), index: 3 }
933
+ { pat: /^(.+)(\|\s+\d+\s+)(\++)(-*)(.*)$/, styles: color(:green), index: 3 }
1014
934
  ]
1015
- end, hint: hint, **threadargs)
935
+ end, **threadargs)
1016
936
  end
1017
937
 
1018
938
  def rebase(flag = nil, opts = [], sync: invoked_sync?('rebase', flag), commit: nil, upstream: nil, branch: nil,
@@ -1025,7 +945,7 @@ module Squared
1025
945
  return unless upstream
1026
946
 
1027
947
  op = OptionPartition.new(opts, OPT_GIT[:rebase], cmd, project: self, no: OPT_GIT[:no][:rebase])
1028
- op << upstream
948
+ cmd << shell_escape(upstream)
1029
949
  append_head op.shift
1030
950
  op.clear(pass: false)
1031
951
  when :onto
@@ -1033,18 +953,14 @@ module Squared
1033
953
 
1034
954
  cmd << '--interactive' if option('interactive', 'i')
1035
955
  cmd << shell_option('onto', commit) if commit
1036
- cmd << upstream
956
+ cmd << shell_escape(upstream)
1037
957
  append_head branch
1038
958
  else
1039
- unless gitpath('REBASE_HEAD').exist?
1040
- puts log_message(Logger::INFO, name, 'no rebase in progress', hint: command) if stdout?
1041
- return
1042
- end
1043
959
  return unless VAL_GIT[:rebase][:send].include?(command)
1044
960
 
1045
961
  cmd << "--#{command}"
1046
962
  end
1047
- source(sync: sync)
963
+ source
1048
964
  end
1049
965
 
1050
966
  def autostash(*, sync: invoked_sync?('autostash'), **)
@@ -1053,7 +969,6 @@ module Squared
1053
969
 
1054
970
  def fetch(flag = nil, opts = [], sync: invoked_sync?('fetch', flag), remote: nil)
1055
971
  opts = git_session('fetch', opts: opts).last
1056
- opts << 'all' if flag == :all || option('all')
1057
972
  append_pull(opts, collect_hash(OPT_GIT[:fetch]), no: collect_hash(OPT_GIT[:no][:fetch]),
1058
973
  remote: remote, flag: flag, from: :fetch)
1059
974
  source(sync: sync, **threadargs)
@@ -1064,28 +979,31 @@ module Squared
1064
979
 
1065
980
  cmd = git_session('clone', worktree: false)
1066
981
  opts = data[1].dup
1067
- if (val = option('depth', ignore: false))
982
+ option('depth', ignore: false) do |val|
1068
983
  if (n = val.to_i) > 0
1069
984
  opts[:depth] = n
1070
985
  else
1071
986
  opts.delete(:depth)
1072
987
  end
1073
988
  end
1074
- opts[:origin] = val if (val = option('origin', ignore: false))
1075
- if (val = option('branch', strict: true))
1076
- opts[:branch] = val
989
+ option('origin', ignore: false) { |val| opts[:origin] = val }
990
+ if (branch = option('branch', strict: true))
991
+ opts[:branch] = branch
1077
992
  opts.delete(:revision)
1078
- elsif (val = option('revision', strict: true))
1079
- opts[:revision] = val
1080
- opts.delete(:branch)
1081
- 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
1082
999
  end
1083
- 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 }
1084
1002
  opts.delete(:'recurse-submodules') || opts.delete(:'no-recurse-submodules') if append_submodules(from: :clone)
1085
1003
  append_hash opts
1086
1004
  cmd << '--quiet' unless verbose
1087
1005
  append_value(data[0], path, delim: true)
1088
- source(sync: sync, banner: sync && !quiet?, multiple: !sync || quiet?)
1006
+ source(banner: sync && !quiet?, multiple: !sync || quiet?)
1089
1007
  end
1090
1008
 
1091
1009
  def stash(flag = nil, opts = [], sync: invoked_sync?('stash', flag))
@@ -1093,34 +1011,19 @@ module Squared
1093
1011
  cmd, opts = git_session('stash', flag, opts: opts)
1094
1012
  list = OPT_GIT[:stash][:common] + OPT_GIT[:stash].fetch(flag, [])
1095
1013
  if flag == :list
1096
- list += collect_hash OPT_GIT[:log]
1097
- no = collect_hash OPT_GIT[:no][:log]
1014
+ list += collect_hash(OPT_GIT[:log])
1015
+ no = collect_hash(OPT_GIT[:no][:log])
1098
1016
  end
1099
1017
  op = OptionPartition.new(opts, list, cmd, project: self, no: no, first: flag == :push ? matchpathspec : nil)
1100
1018
  case flag
1101
1019
  when :push
1102
- op.append?('message', readline('Enter message', force: true), force: true) if op.remove(':')
1103
1020
  append_pathspec op.extras
1104
- when :pop, :apply, :drop, :branch
1105
- if op.remove(':')
1106
- if flag == :branch
1107
- if op.empty?
1108
- values = [['Branch name', true]]
1109
- else
1110
- op << op.shift
1111
- end
1112
- end
1113
- out = choice_index('Choose a stash', git_spawn('stash list', stdout: false),
1114
- values: values, column: /^[^@]+@\{(\d+)\}/)
1115
- if values
1116
- op.merge(out.reverse)
1117
- else
1118
- op << out
1119
- 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)
1120
1025
  elsif !op.empty?
1121
- op << op.shift
1122
- elsif flag == :branch
1123
- raise_error 'no branch name'
1026
+ op << shell_escape(op.pop)
1124
1027
  end
1125
1028
  op.clear
1126
1029
  when :clear
@@ -1132,40 +1035,35 @@ module Squared
1132
1035
  op.clear
1133
1036
  out, banner, from = source(io: true)
1134
1037
  print_item banner
1135
- list_result(write_lines(out), 'objects', banner: banner, from: from)
1038
+ list_result(write_lines(out), 'objects', from: from)
1136
1039
  return
1137
1040
  end
1138
1041
  else
1139
1042
  git_session('stash', 'push', opts: opts)
1140
- 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)
1141
1044
  append_message
1142
1045
  end
1143
1046
  source(banner: !quiet?, sync: sync, **threadargs)
1144
1047
  end
1145
1048
 
1146
- def status(flag = nil, opts = [])
1147
- cmd, opts = git_session('status', opts: opts)
1148
- if flag
1149
- op = OptionPartition.new(opts, OPT_GIT[:status], cmd, project: self, no: OPT_GIT[:no][:status])
1150
- append_pathspec op.extras
1151
- else
1152
- cmd << (option('long') ? '--long' : '--short')
1153
- cmd << '--branch' if option('branch')
1154
- if (val = option('ignore-submodules', ignore: false))
1155
- cmd << basic_option('ignore-submodules', case val
1156
- when '0', 'none'
1157
- 'none'
1158
- when '1', 'untracked'
1159
- 'untracked'
1160
- when '2', 'dirty'
1161
- 'dirty'
1162
- else
1163
- 'all'
1164
- end)
1165
- end
1166
- 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)
1167
1064
  end
1168
- if stdout?
1065
+ append_pathspec
1066
+ if verbose
1169
1067
  r = color(:red)
1170
1068
  g = color(:green)
1171
1069
  sub = if session_arg?('short')
@@ -1173,22 +1071,22 @@ module Squared
1173
1071
  { pat: /^(.)([A-Z?!])(.+)$/, styles: r, index: 2 },
1174
1072
  { pat: /^([A-Z?!])(.+)$/, styles: g },
1175
1073
  { pat: /^(\?\?)(.+)$/, styles: r },
1176
- { pat: /^(## )(.+?)(\.{3})(.+)$/, styles: [nil, g, nil, r], index: -1 }
1074
+ { pat: /^(## )((?~\.{3}))(\.{3})(.+)$/, styles: [nil, g, nil, r], index: -1 }
1177
1075
  ]
1178
1076
  else
1179
- [{ pat: /^(\t+)([a-z]+: +.+)$/, styles: r, index: 2 }]
1077
+ [pat: /^(\t+)([a-z]+: +.+)$/, styles: r, index: 2]
1180
1078
  end
1181
1079
  end
1182
1080
  out, banner, from = source(io: true)
1183
1081
  ret = write_lines(out, banner: banner, sub: sub)
1184
- list_result(ret, 'files', action: 'modified', banner: banner, from: from)
1082
+ list_result(ret, 'files', from: from, action: 'modified')
1185
1083
  end
1186
1084
 
1187
1085
  def revbuild(flag = nil, opts = [], sync: nil, **kwargs)
1188
1086
  statusargs = lambda do
1189
1087
  {
1190
- include: relativepath(Array(kwargs[:include]), all: true),
1191
- 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)
1192
1090
  }
1193
1091
  end
1194
1092
  unless workspace.closed
@@ -1206,19 +1104,19 @@ module Squared
1206
1104
  kwargs = kwargs.key?(:include) || kwargs.key?(:exclude) ? statusargs.call : @revbuild || {}
1207
1105
  case flag
1208
1106
  when :build
1209
- 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)
1210
1108
  op.clear(append: true)
1211
1109
  args = op.to_a
1212
1110
  else
1213
1111
  args = []
1214
- args << basic_option('untracked-files', flag) if (flag = option('untracked-files', prefix: 'git'))
1215
- args << basic_option('ignore-submodules', flag) if (flag = option('ignore-submodules', prefix: 'git'))
1216
- 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) }
1217
1115
  end
1218
1116
  if (cur = workspace.rev_entry(name)) && cur['revision'] == sha && !env('REVBUILD_FORCE')
1219
1117
  files = status_digest(*args, **kwargs)
1220
1118
  if cur['files'].size == files.size && cur['files'].find { |key, val| files[key] != val }.nil?
1221
- if stdout?
1119
+ if verbose
1222
1120
  if (since = workspace.rev_timesince(name, 'build'))
1223
1121
  puts log_message(Logger::INFO, name, 'no changes', subject: 'revbuild', hint: "#{since} ago")
1224
1122
  else
@@ -1229,11 +1127,14 @@ module Squared
1229
1127
  end
1230
1128
  end
1231
1129
  start = epochtime
1232
- build(*@output, sync: sync, from: :'git:revbuild')
1130
+ build(@output, sync: sync, from: :'git:revbuild')
1233
1131
  rescue StandardError => e
1234
- print_error(e, pass: true)
1132
+ warn log_message(Logger::WARN, e, pass: true) if warning?
1235
1133
  else
1236
- 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
1237
1138
  workspace.rev_write(name, { 'revision' => sha, 'files' => status_digest(*args, **kwargs) },
1238
1139
  sync: sync, utc: 'build')
1239
1140
  end
@@ -1262,9 +1163,7 @@ module Squared
1262
1163
  end
1263
1164
  when :patch
1264
1165
  cmd << '--patch'
1265
- when :undo
1266
- cmd << '--hard HEAD@{1}'
1267
- ref = false
1166
+ append_pathspec(refs, pass: false)
1268
1167
  end
1269
1168
  unless ref == false
1270
1169
  append_commit(ref, head: true)
@@ -1275,7 +1174,7 @@ module Squared
1275
1174
 
1276
1175
  def checkout(flag, opts = [], branch: nil, origin: nil, create: nil, commit: nil, detach: nil, merge: false)
1277
1176
  cmd, opts = git_session('checkout', opts: opts)
1278
- append_option 'force', 'f', 'merge'
1177
+ append_option 'force', 'merge'
1279
1178
  case flag
1280
1179
  when :branch
1281
1180
  cmd << '--detach' if detach == 'd' || option('detach')
@@ -1296,14 +1195,13 @@ module Squared
1296
1195
  else
1297
1196
  op = OptionPartition.new(opts, OPT_GIT[:checkout], cmd, project: self, no: OPT_GIT[:no][:checkout],
1298
1197
  first: flag == :path ? matchpathspec : nil)
1299
- if flag == :path
1198
+ if flag == :commit
1199
+ op.append(commit)
1200
+ .clear(pass: false)
1201
+ else
1300
1202
  append_head
1301
1203
  append_pathspec(op.extras, pass: false)
1302
- print_success if success?(source)
1303
- return
1304
1204
  end
1305
- op.append(commit)
1306
- .clear(pass: false)
1307
1205
  end
1308
1206
  source
1309
1207
  end
@@ -1317,7 +1215,7 @@ module Squared
1317
1215
  elsif !session_arg?('s', 'sign', 'u', 'local-user')
1318
1216
  cmd << '--annotate'
1319
1217
  end
1320
- cmd << '--force' if option('force', 'f')
1218
+ cmd << '--force' if option('force')
1321
1219
  if !commit && message && (sha = commithash(message))
1322
1220
  commit = sha
1323
1221
  message = nil
@@ -1333,22 +1231,22 @@ module Squared
1333
1231
  out, banner, from = source(io: true)
1334
1232
  print_item banner
1335
1233
  ret = write_lines(out, grep: op.extras)
1336
- list_result(ret, 'tags', grep: op.extras, banner: banner, from: from)
1234
+ list_result(ret, 'tags', from: from, grep: op.extras)
1337
1235
  return
1338
1236
  end
1339
- remote ||= option('remote')
1237
+ remote ||= option 'remote'
1340
1238
  source
1341
1239
  git_spawn('push', flag == :delete ? '-d' : nil, remote, *refs.map { |val| shell_quote(val) }) if remote
1342
1240
  end
1343
1241
 
1344
1242
  def log!(flag, opts = [], range: [], index: [])
1345
1243
  cmd, opts = git_session('log', opts: opts)
1346
- op = OptionPartition.new(opts, collect_hash(OPT_GIT[:log]), cmd, project: self,
1347
- no: collect_hash(OPT_GIT[:no][:log]),
1348
- 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)
1349
1247
  case flag
1350
1248
  when :between, :contain
1351
- op.add_quote(range.join(flag == :between ? '..' : '...'))
1249
+ op << shell_quote(range.join(flag == :between ? '..' : '...'))
1352
1250
  else
1353
1251
  op.merge(index)
1354
1252
  end
@@ -1356,7 +1254,6 @@ module Squared
1356
1254
  append_pathspec op.extras
1357
1255
  source(exception: false)
1358
1256
  end
1359
- alias log_ log!
1360
1257
 
1361
1258
  def diff(flag, opts = [], refs: [], branch: nil, range: [], index: [])
1362
1259
  cmd, opts = git_session('diff', opts: opts)
@@ -1372,18 +1269,19 @@ module Squared
1372
1269
  op << '--no-index'
1373
1270
  append_pathspec(refs, parent: true)
1374
1271
  else
1375
- op << '--merge-base' if option('merge-base')
1376
1272
  case flag
1377
1273
  when :view
1274
+ op << '--merge-base' if option('merge-base')
1378
1275
  op.merge(range)
1379
1276
  when :between, :contain
1380
1277
  op.delete('--merge-base')
1381
- op.add_quote(range.join(flag == :between ? '..' : '...'))
1278
+ op << shell_quote(range.join(flag == :between ? '..' : '...'))
1382
1279
  else
1383
- op.add_quote(branch) if branch
1280
+ op << '--merge-base' if option('merge-base')
1281
+ op << shell_quote(branch) if branch
1384
1282
  if !index.empty?
1385
1283
  if op.arg?('cached')
1386
- 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
1387
1285
  op << index.first
1388
1286
  else
1389
1287
  op.merge(index)
@@ -1435,17 +1333,18 @@ module Squared
1435
1333
  branch, origin, hint = line.split('...')
1436
1334
  if hint && !hint.match?(/^\[(\D+0,\D+0)\]$/)
1437
1335
  raise_error('work tree is not usable', hint: hint[1..-2])
1438
- elsif !origin || origin.empty?
1336
+ elsif origin.empty?
1439
1337
  return nil if pass
1440
1338
  break if dryrun
1441
1339
 
1442
1340
  unless (origin = option('upstream', prefix: 'git', ignore: false))
1443
1341
  if (origin = choice_refs('Choose an upstream', 'remotes', attempts: 1, force: false))
1444
1342
  git_spawn 'branch', quote_option('set-upstream-to', origin)
1445
- break
1343
+ else
1344
+ origin = readline('Enter an upstream', force: true)
1446
1345
  end
1447
- origin = readline('Enter an upstream', force: true)
1448
1346
  end
1347
+ raise_error('missing remote name', hint: origin) unless origin.include?('/')
1449
1348
  upstream = true
1450
1349
  end
1451
1350
  break
@@ -1458,7 +1357,7 @@ module Squared
1458
1357
  co = git_session('commit', options: false)
1459
1358
  pu = git_output 'push', upstream && '--set-upstream'
1460
1359
  if dryrun
1461
- op.adjoin('--dry-run')
1360
+ op.delete('--dry-run')
1462
1361
  co << '--dry-run'
1463
1362
  pu << '--dry-run'
1464
1363
  end
@@ -1470,6 +1369,7 @@ module Squared
1470
1369
  end
1471
1370
  pu << '--force-with-lease' if amend
1472
1371
  pu.merge(repotrack(origin, branch))
1372
+ puts if pass
1473
1373
  source op
1474
1374
  source co
1475
1375
  source pu
@@ -1477,29 +1377,23 @@ module Squared
1477
1377
 
1478
1378
  def merge(flag, opts = [], command: nil, branch: nil)
1479
1379
  cmd, opts = git_session('merge', opts: opts)
1480
- display = false
1481
1380
  case flag
1482
1381
  when :commit, :'no-commit'
1483
1382
  op = OptionPartition.new(opts, OPT_GIT[:merge], cmd, project: self, no: OPT_GIT[:no][:merge])
1383
+ raise_error 'no branch/commit' if op.empty?
1484
1384
  op << "--#{flag}" << '--'
1485
1385
  if branch
1486
1386
  op << branch
1487
1387
  op.clear(pass: false)
1488
1388
  else
1489
- raise_error 'no branch/commit' if op.empty?
1490
1389
  append_commit(*op.extras)
1491
1390
  end
1492
1391
  else
1493
- unless gitpath('MERGE_HEAD').exist?
1494
- puts log_message(Logger::INFO, name, 'no merge in progress', hint: command) if stdout?
1495
- return
1496
- end
1497
1392
  return unless VAL_GIT[:merge][:send].include?(command)
1498
1393
 
1499
1394
  cmd << "--#{command}"
1500
- display = command == 'abort'
1501
1395
  end
1502
- print_success if success?(source, display)
1396
+ source
1503
1397
  end
1504
1398
 
1505
1399
  def branch(flag = nil, opts = [], refs: [], ref: nil, target: nil, remote: nil)
@@ -1507,17 +1401,17 @@ module Squared
1507
1401
  stdout = false
1508
1402
  case flag
1509
1403
  when :create
1510
- if (arg = option('track', ignore: false))
1511
- cmd << case arg
1404
+ option('track', ignore: false) do |val|
1405
+ cmd << case val
1512
1406
  when '0', 'false'
1513
1407
  '--no-track'
1514
1408
  when 'direct', 'inherit'
1515
- basic_option('track', arg)
1409
+ basic_option 'track', val
1516
1410
  else
1517
1411
  '--track'
1518
1412
  end
1519
1413
  end
1520
- cmd << '--force' if option('force', 'f')
1414
+ cmd << '--force' if option('force')
1521
1415
  cmd << shell_quote(target)
1522
1416
  cmd << shell_quote(ref) if ref
1523
1417
  when :track
@@ -1532,12 +1426,14 @@ module Squared
1532
1426
  end
1533
1427
  when :delete
1534
1428
  remote&.each do |val|
1535
- 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) })
1536
1430
  end
1537
- force, list = refs.partition { |val| val.match?(/^[~^]/) }
1431
+ force, list = refs.partition { |val| val.start_with?(/[~^]/) }
1538
1432
  force.each do |val|
1539
- r = '-r' if val.delete!('~')
1540
- 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(/^[~^]+/, '')))
1541
1437
  end
1542
1438
  return if list.empty?
1543
1439
 
@@ -1545,9 +1441,8 @@ module Squared
1545
1441
  append_value list
1546
1442
  remote = nil
1547
1443
  when :move, :copy
1548
- s = +"-#{flag.to_s[0]}"
1549
- s.upcase! if option('force', 'f')
1550
- cmd << s
1444
+ flag = "-#{flag.to_s[0]}"
1445
+ cmd << (option('force') ? flag.upcase : flag)
1551
1446
  refs.compact.each { |val| cmd << shell_quote(val) }
1552
1447
  stdout = true
1553
1448
  when :current
@@ -1557,29 +1452,29 @@ module Squared
1557
1452
  when :list
1558
1453
  op = OptionPartition.new(opts, OPT_GIT[:branch], cmd << '--list',
1559
1454
  project: self, no: OPT_GIT[:no][:branch], single: /\Av+\z/)
1560
- op.each { |val| op.add_quote(val) }
1455
+ op.each { |val| op << shell_quote(val) }
1561
1456
  out, banner, from = source(io: true)
1562
1457
  print_item banner
1563
1458
  ret = write_lines(out, sub: [
1564
1459
  { pat: /^(\*\s+)(\S+)(.*)$/, styles: color(:green), index: 2 },
1565
1460
  { pat: %r{^(\s*)(remotes/\S+)(.*)$}, styles: color(:red), index: 2 }
1566
1461
  ])
1567
- list_result(ret, 'branches', banner: banner, from: from)
1462
+ list_result(ret, 'branches', from: from)
1568
1463
  return
1569
1464
  else
1570
- 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?
1571
1467
  ret = 0
1572
1468
  else
1573
1469
  git_spawn 'fetch --all --prune --quiet' if option('sync')
1574
- cmd << '-vv --no-abbrev --list'
1575
- out, banner, from = source(io: true)
1470
+ out, banner, from = source(cmd << '-vv --no-abbrev --list', io: true)
1576
1471
  ret = write_lines(out, grep: [/^\*\s+#{Regexp.escape(head)}\s/], banner: banner, first: true) do |line|
1577
1472
  next line if stdin?
1578
1473
 
1579
1474
  data = line.sub(/^\*\s+/, '').split(/\s+/)
1580
1475
  a = sub_style(data[0], styles: theme[:inline])
1581
1476
  b = sub_style(data[1], styles: theme[:extra])
1582
- r = /\A(?:\[(.+?)(?=\]\s)\]\s)?(.+)\z/m.match(data[2..-1].join(' '))
1477
+ r = /\A(?:\[((?~\]\s))\]\s)?(.+)\z/m.match(data[2..-1].join(' '))
1583
1478
  if (r1 = r[1]) && r1 =~ /^(.+):(?: ([a-z]+) (\d+),)? ([a-z]+) (\d+)$/
1584
1479
  write = ->(s1, s2) { "#{s1.capitalize.rjust(7)}: #{sub_style(s2, styles: theme[:warn])}" }
1585
1480
  r1 = $1
@@ -1593,7 +1488,9 @@ module Squared
1593
1488
  end
1594
1489
  on :last, from
1595
1490
  end
1596
- print_error(name, 'no ref found', subject: 'branch', hint: 'head', pass: true) if ret == 0
1491
+ if ret == 0
1492
+ warn log_message(Logger::WARN, name, 'no ref found', subject: 'branch', hint: 'head', pass: true)
1493
+ end
1597
1494
  return
1598
1495
  end
1599
1496
  return unless success?(source(stdout: stdout))
@@ -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))
1501
+ source git_output('push', '-u', shell_quote(ref.split('/', 2).first), shell_quote(target))
1605
1502
  end
1606
1503
  end
1607
1504
 
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
1635
- end
1636
- source
1637
- end
1638
-
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,
@@ -1693,7 +1572,7 @@ module Squared
1693
1572
  cmd, opts = git_session('rev-parse', opts: opts)
1694
1573
  case flag
1695
1574
  when :commit
1696
- cmd << (size.to_i.zero? ? '--verify' : basic_option('short', [size.to_i, 5].max))
1575
+ cmd << ((n = size.to_i) > 0 ? basic_option('short', [n, 5].max) : '--verify')
1697
1576
  append_commit(ref, head: true)
1698
1577
  when :branch
1699
1578
  cmd << '--abbrev-ref'
@@ -1714,36 +1593,28 @@ 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}/")
1721
- list_result(ret, flag.to_s, grep: op.extras, banner: banner, from: from)
1600
+ list_result(ret, flag.to_s, from: from, grep: op.extras)
1722
1601
  end
1723
1602
 
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)
1731
- list_result(ret, 'files', grep: op.extras, banner: banner, from: from)
1609
+ list_result(ret, 'files', from: from, grep: op.extras)
1732
1610
  end
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
1741
- 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)
1742
1617
  case flag
1743
- when :blame
1744
- raise_error 'no file found' unless (n = op.index { |s| basepath(s).file? })
1745
- op << '--' << shell_quote(basepath(op.delete_at(n)))
1746
- op.clear
1747
1618
  when :revert
1748
1619
  if VAL_GIT[:rebase][:send].any? { |val| op.arg?(val) }
1749
1620
  op.clear
@@ -1752,34 +1623,35 @@ module Squared
1752
1623
  else
1753
1624
  append_commit(*op.extras)
1754
1625
  end
1755
- when :add
1626
+ when :add, :clean
1756
1627
  if flag == :add && !op.arg?('pathspec-from-file')
1757
- grep, list = op.partition { |val| OptionPartition.pattern?(val) }
1758
- 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)
1759
1631
  grep.map! { |val| Regexp.new(val[1..-2]) }
1760
- files = []
1761
- status_data.each do |a, b|
1762
- 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) }
1763
1634
 
1764
- files << "#{sub_style(b, styles: color(:red))} #{a}"
1635
+ "#{sub_style(b, styles: red)} #{a}"
1765
1636
  end
1637
+ .compact
1766
1638
  unless files.empty?
1767
- files = choice_index('Select files', files, multiple: true, trim: /^\S+\s/,
1768
- accept: [['Add?', false, true]])
1639
+ files = choice_index('Select files', files, multiple: true, force: true, trim: /^\S+\s/,
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
-
1775
- print_success if success?(source, flag == :add && !op.arg?('verbose'))
1645
+ append_pathspec op.extras
1646
+ verbose = flag == :add && !op.arg?('verbose')
1647
+ print_success if success?(source) && verbose
1776
1648
  return
1777
1649
  when :mv
1778
1650
  refs = projectmap op.extras
1779
1651
  raise_error 'no source/destination' unless refs.size > 1
1780
1652
  op.merge(refs)
1781
- when :rm, :clean
1782
- append_pathspec(op.extras, expect: flag == :rm)
1653
+ when :rm
1654
+ append_pathspec(op.extras, expect: true)
1783
1655
  end
1784
1656
  source(sync: false, stderr: true)
1785
1657
  end
@@ -1799,51 +1671,40 @@ module Squared
1799
1671
  private
1800
1672
 
1801
1673
  def source(cmd = @session, exception: true, io: false, sync: true, stdout: false, stderr: false, banner: true,
1802
- multiple: false, hint: nil, from: nil, send: :system, **kwargs)
1803
- unless cmd
1804
- print_error('no git session started', subject: project, hint: from, pass: true)
1805
- return
1806
- end
1674
+ multiple: false, **kwargs)
1807
1675
  cmd = cmd.target if cmd.is_a?(OptionPartition)
1808
- if io && banner == false
1809
- from = nil
1810
- banner = nil
1811
- else
1812
- if banner
1813
- banner = nil unless banner? && !multiple
1814
- args = true
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
- if cmd.respond_to?(:done)
1817
- if from.nil? && (from = cmd.drop(1).find { |val| val.match?(/\A[a-z]{1,2}[a-z-]*\z/) })
1818
- from = :"git:#{from}"
1819
- end
1820
- banner &&= cmd.temp { |val| val.start_with?('--work-tree') || val.start_with?('--git-dir') }
1821
- end
1822
- from = nil if from == false
1683
+ banner &&= cmd.temp { |val| val.start_with?('--work-tree') || val.start_with?('--git-dir') }
1823
1684
  end
1824
1685
  cmd = session_done cmd
1825
1686
  log&.info cmd
1687
+ on :first, from
1826
1688
  banner = if banner
1827
- banner = (banner.is_a?(String) ? banner : cmd).gsub(File.join(path, ''), '')
1828
- format_banner(hint ? "#{banner} (#{hint})" : banner)
1689
+ format_banner((banner.is_a?(String) ? banner : cmd).gsub(File.join(path, ''), ''), banner: true)
1829
1690
  end
1830
- on :first, from
1831
1691
  begin
1832
1692
  if io
1833
1693
  return `#{cmd}` if stdout
1834
1694
 
1835
- return args ? [IO.popen(cmd), banner || '', from] : IO.popen(cmd)
1836
- elsif stdin? ? sync : stdout
1695
+ return banner ? [IO.popen(cmd), banner, from] : IO.popen(cmd)
1696
+ end
1697
+ if stdin? ? sync : stdout
1837
1698
  print_item banner unless multiple
1838
- ret = `#{cmd}`.chomp
1699
+ ret = `#{cmd}`
1839
1700
  if !ret.empty?
1840
1701
  puts ret
1841
1702
  elsif success?(!banner.nil?)
1842
1703
  print_success
1843
1704
  end
1844
- elsif !kwargs[:sub] && (sync || (!exception && !stderr))
1705
+ elsif sync || (!exception && !stderr)
1845
1706
  print_item banner unless multiple
1846
- ret = shell(cmd, name: send, exception: exception)
1707
+ ret = shell(cmd, exception: exception)
1847
1708
  else
1848
1709
  require 'open3'
1849
1710
  if stderr
@@ -1861,7 +1722,11 @@ module Squared
1861
1722
  end
1862
1723
  end
1863
1724
  rescue StandardError => e
1864
- 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?
1865
1730
  nil
1866
1731
  else
1867
1732
  on :last, from
@@ -1870,7 +1735,16 @@ module Squared
1870
1735
  end
1871
1736
 
1872
1737
  def write_lines(data, grep: [], prefix: nil, sub: nil, banner: nil, loglevel: nil, pass: false, first: false)
1873
- 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
1874
1748
  sub = nil if stdin?
1875
1749
  ret = 0
1876
1750
  out = []
@@ -1894,24 +1768,18 @@ module Squared
1894
1768
  ret += 1
1895
1769
  break if first
1896
1770
  end
1897
- if banner && (ret > 0 || (!pass && !first))
1898
- if banner.empty?
1899
- print_item(*out)
1900
- else
1901
- print_item(banner, *out)
1902
- end
1903
- end
1771
+ print_item banner, out if banner && (ret > 0 || (!pass && !first))
1904
1772
  ret
1905
1773
  end
1906
1774
 
1907
- def list_result(size, type, action: 'found', grep: [], banner: nil, from: nil)
1775
+ def list_result(size, type, grep: [], action: 'found', from: nil)
1908
1776
  if size == 0
1909
1777
  puts empty_status("No #{type} were #{action}", 'grep', grep.join(', '))
1910
- elsif stdout? && (banner.nil? || (banner.is_a?(String) && !banner.empty?))
1778
+ elsif verbose
1911
1779
  styles = theme.fetch(:banner, []).reject { |s| s.to_s.end_with?('!') }
1912
1780
  styles << :bold if styles.size <= 1
1913
1781
  puts print_footer("#{size} #{size == 1 ? type.sub(/(?:(?<!l)e)?s\z/, '') : type}",
1914
- sub: [{ pat: /^(\d+)(.+)$/, styles: styles }])
1782
+ sub: [pat: /^(\d+)(.+)$/, styles: styles])
1915
1783
  end
1916
1784
  on :last, from
1917
1785
  end
@@ -1927,7 +1795,7 @@ module Squared
1927
1795
  end
1928
1796
  args = []
1929
1797
  args << quote_option('sort', sort) if sort
1930
- args << basic_option('count', env('GIT_COUNT', ARG[:CHOICE])) if count
1798
+ args << shell_option('count', env('GIT_COUNT', ARG[:CHOICE])) if count
1931
1799
  choice_index(msg, foreachref(type, *args, format: format), trim: trim, **kwargs)
1932
1800
  end
1933
1801
 
@@ -1950,11 +1818,12 @@ module Squared
1950
1818
  glob = kwargs.fetch(:include, [])
1951
1819
  pass = kwargs.fetch(:exclude, [])
1952
1820
  ret = {}
1953
- status_data(*args).each do |file,|
1821
+ status_data(*args).each do |line|
1822
+ file = line.first
1954
1823
  next if !glob.empty? && glob.none? { |val| File.fnmatch?(val, file, File::FNM_DOTMATCH) }
1955
1824
  next if !pass.empty? && pass.any? { |val| File.fnmatch?(val, file, File::FNM_DOTMATCH) }
1956
1825
 
1957
- ret[file] = algorithm.hexdigest(File.read(basepath(file)))
1826
+ ret[file] = algorithm.hexdigest(File.read(path + file))
1958
1827
  end
1959
1828
  ret
1960
1829
  end
@@ -1970,7 +1839,7 @@ module Squared
1970
1839
  end
1971
1840
 
1972
1841
  def append_pull(opts, list, target: @session, flag: nil, no: nil, remote: nil, from: nil)
1973
- target << '--force' if option('force', 'f', target: target)
1842
+ target << '--force' if option('force', target: target)
1974
1843
  append_submodules(target: target, from: from)
1975
1844
  return if !remote && opts.empty?
1976
1845
 
@@ -1982,11 +1851,13 @@ module Squared
1982
1851
  when 'rebase'
1983
1852
  op << basic_option($1, $2) if VAL_GIT[:rebase][:value].include?($2)
1984
1853
  when 'shallow-since'
1985
- 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'))
1986
1857
  when 'recurse-submodules'
1987
- op.append?($1, $2, type: :basic)
1858
+ op << basic_option($1, $2) unless op.arg?('recurse-submodules')
1988
1859
  when 'refspec'
1989
- refspec << shell_quote($2)
1860
+ refspec << shell_escape($2, quote: true)
1990
1861
  end
1991
1862
  elsif op.arg?('multiple')
1992
1863
  op.found << opt
@@ -1994,9 +1865,9 @@ module Squared
1994
1865
  op.errors << opt
1995
1866
  end
1996
1867
  end
1997
- op << '--verbose' if (flag || from == :fetch) && stdout? && !op.arg?('quiet')
1868
+ op << '--verbose' if (flag || from == :fetch) && verbose && !op.arg?('quiet')
1998
1869
  if remote
1999
- op.append(remote)
1870
+ op.append(remote, delim: true)
2000
1871
  if (val = option('refspec', target: target, strict: true))
2001
1872
  op.append(*split_escape(val))
2002
1873
  else
@@ -2004,12 +1875,12 @@ module Squared
2004
1875
  end
2005
1876
  op.delete('--all')
2006
1877
  elsif op.arg?('multiple')
2007
- op.add_quote(*op.found)
1878
+ op.swap.merge(op.map! { |opt| shell_escape(opt, quote: true) })
2008
1879
  return
2009
1880
  elsif option('all')
2010
- op << '--all'
1881
+ cmd << '--all'
2011
1882
  end
2012
- op.clear(errors: true, subject: flag) if flag
1883
+ op.clear(errors: true, subject: flag.to_s) if flag
2013
1884
  end
2014
1885
 
2015
1886
  def append_commit(*val, target: @session, head: false)
@@ -2024,28 +1895,20 @@ module Squared
2024
1895
  def append_pathspec(files = [], target: @session, expect: false, parent: false, pass: true)
2025
1896
  if session_arg?('pathspec-from-file', target: target)
2026
1897
  option_clear files
2027
- true
2028
1898
  else
2029
- if files.empty? && (val = option('pathspec', target: target))
2030
- files = split_escape val
2031
- end
1899
+ option('pathspec', target: target) { |val| files = split_escape val } if files.empty?
2032
1900
  files = projectmap(files, parent: parent, pass: pass)
2033
1901
  if !files.empty?
2034
1902
  target << '--' << files.join(' ')
2035
- true
2036
1903
  elsif expect
2037
1904
  raise_error(parent ? 'pathspec not present' : 'pathspec not within worktree')
2038
- else
2039
- false
2040
1905
  end
2041
1906
  end
2042
1907
  end
2043
1908
 
2044
1909
  def append_message(val = nil, target: @session)
2045
1910
  val = messageopt if val.to_s.empty?
2046
- return unless val
2047
-
2048
- target << quote_option('message', val)
1911
+ target << quote_option('message', val) if val
2049
1912
  end
2050
1913
 
2051
1914
  def append_head(val = nil, target: @session)
@@ -2055,61 +1918,51 @@ module Squared
2055
1918
  end
2056
1919
 
2057
1920
  def append_submodules(target: @session, from: nil)
2058
- return unless (val = option('recurse-submodules', target: target, ignore: false))
2059
-
2060
- if from == :clone
2061
- case val
2062
- when '0', 'false'
2063
- target << '--no-recurse-submodules'
2064
- when '1', 'true'
2065
- target << '--recurse-submodules'
2066
- else
2067
- projectmap(split_escape(val)).each do |path|
2068
- 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
2069
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
2070
1942
  end
2071
- else
2072
- target << case val
2073
- when 'no', '0', 'false'
2074
- '--no-recurse-submodules'
2075
- when 'yes', 'on-demand'
2076
- "--recurse-submodules=#{val}"
2077
- else
2078
- '--recurse-submodules'
2079
- end
2080
1943
  end
2081
1944
  end
2082
1945
 
2083
1946
  def foreachref(path, *args, format: nil)
2084
- path = Array(path).map! { |val| "refs/#{val}" }
2085
- format &&= quote_option('format', format)
2086
- ret = git_spawn('for-each-ref', format, *args, *path, stdout: workspace.windows?)
2087
- 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)
2088
1949
  end
2089
1950
 
2090
1951
  def git_session(*cmd, opts: nil, worktree: true, **kwargs)
2091
- 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)}"] : []
2092
1953
  return session('git', *dir, *cmd, **kwargs) unless opts
2093
1954
 
2094
1955
  op = OptionPartition.new(opts, OPT_GIT[:common], dir, project: self)
2095
- [session('git', *op.to_a, *cmd, **kwargs), op.extras]
1956
+ ret = session('git', *op.to_a, *cmd, **kwargs)
1957
+ [ret, op.extras]
2096
1958
  end
2097
1959
 
2098
1960
  def git_output(*cmd, **kwargs)
2099
1961
  git_session(*cmd, main: false, options: false, **kwargs)
2100
1962
  end
2101
1963
 
2102
- def git_spawn(*cmd, exception: true, io: true, sync: true, stdout: true, banner: false, **kwargs)
2103
- kwargs[:send] = if sync
2104
- :system
2105
- else
2106
- exception = false
2107
- io = false
2108
- stdout = false
2109
- :spawn
2110
- end
2111
- source(cmd.first.is_a?(Set) ? cmd.first : git_output(*cmd), exception: exception, io: io, sync: sync,
2112
- 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)
2113
1966
  end
2114
1967
 
2115
1968
  def dryrun?(*, target: @session, **)
@@ -2124,35 +1977,32 @@ module Squared
2124
1977
  target.include?('--quiet') || (target.include?('-q') && stripext(target.first) == 'git')
2125
1978
  end
2126
1979
 
2127
- def gitpath(*args)
2128
- path.join('.git', *args)
1980
+ def gitpath
1981
+ path + '.git'
2129
1982
  end
2130
1983
 
2131
1984
  def repotrack(origin, branch, quote: true)
2132
- unless origin && branch && (i = origin.index('/'))
2133
- raise_error(ArgumentError, "missing #{origin ? 'branch' : 'remote'} name", hint: origin)
2134
- end
2135
- branch = "#{branch}:#{origin[(i + 1)..-1]}" unless origin.end_with?("/#{branch}")
2136
- ret = [origin[0..(i - 1)], branch]
2137
- quote ? ret.map! { |val| shell_quote(val) } : ret
1985
+ i = origin.index('/')
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 }
2138
1988
  end
2139
1989
 
2140
1990
  def commithash(val)
2141
- val[/\A:(\h{5,40})\z/, 1] || val[/\A#\{(\h{5,40})\}\z/, 1]
1991
+ val[/\A:(\h{5,40})\z/, 1]
2142
1992
  end
2143
1993
 
2144
1994
  def commithead(val)
2145
1995
  return val unless (s = matchhead(val))
2146
1996
 
2147
- s.match?(/^\d/) ? "@~#{s}" : "@#{s}"
1997
+ s.start_with?(/\d/) ? "@~#{s}" : "@#{s}"
2148
1998
  end
2149
1999
 
2150
2000
  def matchhead(val)
2151
- val && val =~ /^(?:(?:HEAD|@)([~^]\d*)?|H(\d+))$/ ? $2 || $1 || '' : nil
2001
+ val =~ /^(?:(?:HEAD|@)([~^]\d*)?|H(\d+))$/ ? $2 || $1 || '' : nil
2152
2002
  end
2153
2003
 
2154
2004
  def matchpathspec
2155
- [/\A[^a-z\d-]+/i, %r{\A[^=\\/*]*[\\/*]}, /\A--\z/]
2005
+ [/\A[^a-z\d-]+/i, %r{\A[^=\\/*]*[\\/*]}]
2156
2006
  end
2157
2007
 
2158
2008
  def messageopt