squared 0.4.38 → 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
@@ -134,7 +134,7 @@ module Squared
134
134
  File.write(@revfile, JSON.pretty_generate(@revdoc))
135
135
  rescue StandardError => e
136
136
  log&.debug e
137
- warn log_message(Logger::WARN, e, pass: true) if warning
137
+ warn log_message(Logger::WARN, e, pass: true) if warning?
138
138
  ensure
139
139
  @revlock = false
140
140
  end
@@ -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 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
@@ -339,31 +327,29 @@ module Squared
339
327
  end
340
328
 
341
329
  subtasks({
342
- 'branch' => %i[create track delete move copy list all current].freeze,
330
+ 'branch' => %i[create track delete move copy list current].freeze,
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
@@ -500,11 +461,9 @@ module Squared
500
461
  end
501
462
  when 'stash'
502
463
  format_desc(action, flag, 'opts*', after: case flag
503
- when :push then 'pathspec*,:'
504
- when :branch then 'name,stash?|:'
464
+ when :push then 'pathspec*'
505
465
  when :clear, :list then nil
506
- else 'stash?|:'
507
- end)
466
+ else 'stash?|:' end)
508
467
  task flag do |_, args|
509
468
  stash flag, args.to_a
510
469
  end
@@ -542,7 +501,7 @@ module Squared
542
501
  commit1 = commithead args.commit1
543
502
  if commit1
544
503
  commit2 = commithead param_guard(action, flag, args: args, key: :commit2)
545
- args = args.extras
504
+ args = args.extras.to_a
546
505
  range = [commit1, commit2]
547
506
  else
548
507
  range, opts, refs = choice_commit(multiple: view ? true : 2, values: %w[Options Pathspec])
@@ -604,7 +563,7 @@ module Squared
604
563
  detach = args.detach
605
564
  commit = commithead args.commit
606
565
  end
607
- 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
608
567
  else
609
568
  branch = choice_refs 'Choose a branch to switch'
610
569
  end
@@ -624,12 +583,12 @@ module Squared
624
583
  format_desc action, flag, 'ref,opts*'
625
584
  task flag, [:commit] do |_, args|
626
585
  commit = commithead args.commit
627
- args = if commit
628
- args.extras
629
- else
630
- commit, opts = choice_commit(values: ['Options'])
631
- OptionPartition.strip(opts)
632
- end
586
+ if commit
587
+ args = args.extras
588
+ else
589
+ commit, opts = choice_commit(values: ['Options'])
590
+ args = OptionPartition.strip(opts)
591
+ end
633
592
  checkout(flag, args, commit: commit)
634
593
  end
635
594
  when :detach
@@ -637,7 +596,7 @@ module Squared
637
596
  task flag, [:commit] do |_, args|
638
597
  commit = commithead args.commit
639
598
  unless commit
640
- commit, merge = choice_commit(values: ['Merge? [y/N]'])
599
+ commit, merge = choice_commit(values: ['Merge? [y|N]'])
641
600
  merge = merge&.upcase == 'Y'
642
601
  end
643
602
  checkout(flag, commit: commit, merge: merge)
@@ -674,7 +633,7 @@ module Squared
674
633
  branch(flag, target: target, ref: ref, remote: remote)
675
634
  end
676
635
  when :delete
677
- format_desc action, flag, '[^~]name*,:?'
636
+ format_desc action, flag, '(^~)name*,:?'
678
637
  task flag do |_, args|
679
638
  refs = args.to_a
680
639
  if refs.empty? || (r = refs.last == ':')
@@ -697,7 +656,12 @@ module Squared
697
656
  task flag do |_, args|
698
657
  branch flag, args.to_a
699
658
  end
700
- when :move, :copy
659
+ when :current
660
+ format_desc action, flag
661
+ task flag do
662
+ branch flag
663
+ end
664
+ else
701
665
  format_desc action, flag, 'branch,oldbranch?'
702
666
  task flag, [:branch, :oldbranch] do |_, args|
703
667
  if (branch = args.branch)
@@ -708,21 +672,16 @@ module Squared
708
672
  end
709
673
  branch(flag, refs: [oldbranch, branch])
710
674
  end
711
- else
712
- format_desc action, flag
713
- task flag do
714
- branch flag
715
- end
716
675
  end
717
676
  when 'switch'
718
677
  case flag
719
678
  when :create
720
679
  format_desc action, flag, '(^)name,ref?=HEAD|:'
721
680
  task flag, [:name, :commit] do |_, args|
722
- branch = param_guard(action, flag, args: args, key: :name)
681
+ target = param_guard(action, flag, args: args, key: :name)
723
682
  commit = commithead args.commit
724
- commit, track = choice_commit(values: ['Track? [Y/n]'], force: false) if commit == ':'
725
- 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)
726
685
  end
727
686
  when :detach
728
687
  format_desc action, flag, 'ref?=HEAD'
@@ -730,16 +689,11 @@ module Squared
730
689
  commit = commithead(args.commit) || choice_commit(force: false)
731
690
  switch(flag, commit: commit)
732
691
  end
733
- when :branch
734
- format_desc action, flag, 'name|:,opts*'
735
- task flag, [:name] do |_, args|
736
- if (branch = args.name)
737
- args = args.extras
738
- branch = nil if branch == ':'
739
- else
740
- args = []
741
- end
742
- 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)
743
697
  end
744
698
  end
745
699
  when 'reset'
@@ -752,19 +706,18 @@ module Squared
752
706
  args = args.extras
753
707
  else
754
708
  commit, mode = choice_commit(values: ['Mode [mixed|soft|hard|N]'])
755
- args = args.extras.concat(case mode&.downcase
756
- when 'h', 'hard' then ['hard']
757
- when 's', 'soft' then ['soft']
758
- when 'n', 'N' then ['mixed', 'N']
759
- else ['mixed']
760
- 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)
761
714
  end
762
715
  print_success if success?(reset(flag, args, commit: commit))
763
716
  end
764
- when :index, :undo
765
- format_desc(action, flag, flag == :index ? 'opts*,pathspec*' : nil)
717
+ when :index
718
+ format_desc action, flag, 'opts*,pathspec*'
766
719
  task flag do |_, args|
767
- reset(flag, flag == :index ? args.to_a : [])
720
+ reset flag, args.to_a
768
721
  end
769
722
  when :mode
770
723
  format_desc action, flag, 'mode,ref?=HEAD|:'
@@ -806,28 +759,28 @@ module Squared
806
759
  when :branch
807
760
  format_desc action, flag, 'upstream,branch?=HEAD,opts*'
808
761
  task flag, [:upstream] do |_, args|
809
- args = if (upstream = args.upstream)
810
- args.extras
811
- else
812
- upstream, opts = choice_refs('Choose upstream branch', values: ['Options'])
813
- OptionPartition.strip(opts)
814
- 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
815
768
  rebase(flag, args, upstream: upstream)
816
769
  end
817
770
  when :onto
818
771
  format_desc action, flag, 'ref,upstream,branch?=HEAD'
819
772
  task flag, [:commit, :upstream, :branch] do |_, args|
820
773
  commit = commithead args.commit
821
- args = if commit
822
- upstream = param_guard(action, flag, args: args, key: :upstream)
823
- branch = args.branch
824
- []
825
- else
826
- commit = choice_refs 'Choose "onto" branch'
827
- target, opts = choice_commit(reflog: false, multiple: 2, values: ['Options'])
828
- branch, upstream = target
829
- OptionPartition.strip(opts)
830
- 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
831
784
  rebase(flag, args, commit: commit, upstream: upstream, branch: branch)
832
785
  end
833
786
  when :commit, :'no-commit'
@@ -863,9 +816,7 @@ module Squared
863
816
  rev_parse(flag, ref: ref, size: size)
864
817
  end
865
818
  when :build
866
- next unless build?
867
-
868
- format_desc action, flag, 'opts*'
819
+ format_desc action, flag, OPT_GIT[:status]
869
820
  task flag do |_, args|
870
821
  revbuild flag, args.to_a
871
822
  end
@@ -882,7 +833,7 @@ module Squared
882
833
  ls_remote(flag, args.extras, remote: args.remote)
883
834
  end
884
835
  else
885
- format_desc(action, flag, 'opts*,pattern*', after: action == 'files' ? 'pathspec*' : nil)
836
+ format_desc action, flag, 'opts*,pattern*'
886
837
  task flag do |_, args|
887
838
  __send__(action == 'refs' ? :ls_remote : :ls_files, flag, args.to_a)
888
839
  end
@@ -893,13 +844,13 @@ module Squared
893
844
  format_desc action, flag, 'ref,opts*,pathspec*'
894
845
  task flag, [:commit] do |_, args|
895
846
  commit = commithead args.commit
896
- args = if commit
897
- args.extras
898
- else
899
- commit, opts, files = choice_commit(values: ['Options', ['Pathspec', true]])
900
- files = files&.shellsplit
901
- OptionPartition.strip(opts)
902
- 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
903
854
  restore(flag, args, commit: commit, files: files)
904
855
  end
905
856
  when :staged, :worktree
@@ -908,14 +859,19 @@ module Squared
908
859
  args = args.to_a
909
860
  if args.empty? || args.last == ':'
910
861
  files = []
911
- 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
912
868
  unless files.empty?
913
869
  files = choice_index('Select a file', files, multiple: true, force: false,
914
870
  accept: 'Restore?')
915
871
  end
916
872
  args.pop
917
873
  args, glob = args.partition { |val| val.match?(/^(?:[a-z-]+=|[^*]+$)/) }
918
- files.concat(glob)
874
+ (files ||= []).concat(glob)
919
875
  next if args.empty? && files.empty?
920
876
  end
921
877
  restore(flag, args, files: files)
@@ -923,18 +879,13 @@ module Squared
923
879
  end
924
880
  when 'git'
925
881
  before = case flag
926
- when :blame then 'file'
927
882
  when :mv then 'source+,destination'
928
- when :revert then 'commit+'
929
- end
883
+ when :revert then 'commit+' end
930
884
  format_desc(action, flag, 'opts*', before: before, after: case flag
931
- when :add
932
- 'pathspec*,pattern*'
933
- when :clean, :rm, :status
934
- 'pathspec*'
935
- end)
885
+ when :add then 'pathspec*|:pattern:*'
886
+ when :clean, :rm then 'pathspec*' end)
936
887
  task flag do |_, args|
937
- __send__(flag == :status ? :status : :git, flag, args.to_a)
888
+ git flag, args.to_a
938
889
  end
939
890
  end
940
891
  end
@@ -958,13 +909,14 @@ module Squared
958
909
  super
959
910
  end
960
911
 
961
- 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)
962
913
  cmd, opts = git_session('pull', opts: opts)
963
- cmd << '--autostash' if option('autostash')
964
914
  if flag == :rebase
965
915
  cmd << '--rebase'
916
+ cmd << '--autostash' if option('autostash')
966
917
  else
967
- if (val = option('rebase', ignore: false))
918
+ cmd << '--autostash' if flag == :autostash
919
+ option('rebase', ignore: false) do |val|
968
920
  cmd << case val
969
921
  when '0', 'false'
970
922
  '--no-rebase'
@@ -972,48 +924,15 @@ module Squared
972
924
  VAL_GIT[:rebase][:value].include?(val) ? basic_option('rebase', val) : '--rebase'
973
925
  end
974
926
  end
975
- case flag
976
- when :all
977
- unless git_spawn('status -s -z --untracked-files=all').empty?
978
- if confirm('Stash local changes? [Y/n] ', 'Y')
979
- git_spawn 'stash push --keep-index --quiet'
980
- elsif !(force = confirm('Force checkout? [y/N] ', 'N'))
981
- return
982
- end
983
- printsucc
984
- end
985
- op = OptionPartition.new(opts, OPT_GIT[:pull], cmd, project: self, no: OPT_GIT[:no][:pull])
986
- opts -= op.extras
987
- reg = matchmap op
988
- session_done op.target
989
- heads = []
990
- cur = nil
991
- foreachref('heads', format: '%(if)%(HEAD)%(then)* %(end)%(refname:short)').each do |line|
992
- line.chomp!
993
- cur ||= line.sub!(/\A\* /, '')
994
- heads << line if matchany?(line, reg)
995
- end
996
- raise_error('head not found', hint: 'for-each-ref') unless cur
997
- opts << 'ff-only' if opts.empty? && !option('ff-only', equals: '0')
998
- (heads.dup << cur).each_with_index do |branch, index|
999
- next unless (index < heads.size && cur != branch) || index == heads.size
1000
-
1001
- git_spawn 'switch --quiet', force && '--force', shell_quote(branch)
1002
- pull(nil, opts, sync: false, hint: branch) if heads.include?(branch)
1003
- end
1004
- return
1005
- when :autostash
1006
- cmd << '--autostash'
1007
- end
1008
927
  end
1009
928
  append_pull(opts, OPT_GIT[:pull] + OPT_GIT[:fetch][:pull],
1010
929
  no: OPT_GIT[:no][:pull] + OPT_GIT[:no][:fetch][:pull], remote: remote, flag: flag, from: :pull)
1011
- source(sync: sync, sub: if stdout?
930
+ source(sync: sync, sub: if verbose
1012
931
  [
1013
932
  { pat: /^(.+)(\|\s+\d+\s+)([^-]*)(-+)(.*)$/, styles: color(:red), index: 4 },
1014
- { pat: /^(.+)(\|\s+\d+\s+)(\++)(.*)$/, styles: color(:green), index: 3 }
933
+ { pat: /^(.+)(\|\s+\d+\s+)(\++)(-*)(.*)$/, styles: color(:green), index: 3 }
1015
934
  ]
1016
- end, hint: hint, **threadargs)
935
+ end, **threadargs)
1017
936
  end
1018
937
 
1019
938
  def rebase(flag = nil, opts = [], sync: invoked_sync?('rebase', flag), commit: nil, upstream: nil, branch: nil,
@@ -1026,7 +945,7 @@ module Squared
1026
945
  return unless upstream
1027
946
 
1028
947
  op = OptionPartition.new(opts, OPT_GIT[:rebase], cmd, project: self, no: OPT_GIT[:no][:rebase])
1029
- op << upstream
948
+ cmd << shell_escape(upstream)
1030
949
  append_head op.shift
1031
950
  op.clear(pass: false)
1032
951
  when :onto
@@ -1034,18 +953,14 @@ module Squared
1034
953
 
1035
954
  cmd << '--interactive' if option('interactive', 'i')
1036
955
  cmd << shell_option('onto', commit) if commit
1037
- cmd << upstream
956
+ cmd << shell_escape(upstream)
1038
957
  append_head branch
1039
958
  else
1040
- unless gitpath('REBASE_HEAD').exist?
1041
- puts log_message(Logger::INFO, name, 'no rebase in progress', hint: command) if stdout?
1042
- return
1043
- end
1044
959
  return unless VAL_GIT[:rebase][:send].include?(command)
1045
960
 
1046
961
  cmd << "--#{command}"
1047
962
  end
1048
- source(sync: sync)
963
+ source
1049
964
  end
1050
965
 
1051
966
  def autostash(*, sync: invoked_sync?('autostash'), **)
@@ -1054,7 +969,6 @@ module Squared
1054
969
 
1055
970
  def fetch(flag = nil, opts = [], sync: invoked_sync?('fetch', flag), remote: nil)
1056
971
  opts = git_session('fetch', opts: opts).last
1057
- opts << 'all' if flag == :all || option('all')
1058
972
  append_pull(opts, collect_hash(OPT_GIT[:fetch]), no: collect_hash(OPT_GIT[:no][:fetch]),
1059
973
  remote: remote, flag: flag, from: :fetch)
1060
974
  source(sync: sync, **threadargs)
@@ -1065,28 +979,31 @@ module Squared
1065
979
 
1066
980
  cmd = git_session('clone', worktree: false)
1067
981
  opts = data[1].dup
1068
- if (val = option('depth', ignore: false))
982
+ option('depth', ignore: false) do |val|
1069
983
  if (n = val.to_i) > 0
1070
984
  opts[:depth] = n
1071
985
  else
1072
986
  opts.delete(:depth)
1073
987
  end
1074
988
  end
1075
- opts[:origin] = val if (val = option('origin', ignore: false))
1076
- if (val = option('branch', strict: true))
1077
- opts[:branch] = val
989
+ option('origin', ignore: false) { |val| opts[:origin] = val }
990
+ if (branch = option('branch', strict: true))
991
+ opts[:branch] = branch
1078
992
  opts.delete(:revision)
1079
- elsif (val = option('revision', strict: true))
1080
- opts[:revision] = val
1081
- opts.delete(:branch)
1082
- 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
1083
999
  end
1084
- 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 }
1085
1002
  opts.delete(:'recurse-submodules') || opts.delete(:'no-recurse-submodules') if append_submodules(from: :clone)
1086
1003
  append_hash opts
1087
1004
  cmd << '--quiet' unless verbose
1088
1005
  append_value(data[0], path, delim: true)
1089
- source(sync: sync, banner: sync && !quiet?, multiple: !sync || quiet?)
1006
+ source(banner: sync && !quiet?, multiple: !sync || quiet?)
1090
1007
  end
1091
1008
 
1092
1009
  def stash(flag = nil, opts = [], sync: invoked_sync?('stash', flag))
@@ -1094,34 +1011,19 @@ module Squared
1094
1011
  cmd, opts = git_session('stash', flag, opts: opts)
1095
1012
  list = OPT_GIT[:stash][:common] + OPT_GIT[:stash].fetch(flag, [])
1096
1013
  if flag == :list
1097
- list += collect_hash OPT_GIT[:log]
1098
- no = collect_hash OPT_GIT[:no][:log]
1014
+ list += collect_hash(OPT_GIT[:log])
1015
+ no = collect_hash(OPT_GIT[:no][:log])
1099
1016
  end
1100
1017
  op = OptionPartition.new(opts, list, cmd, project: self, no: no, first: flag == :push ? matchpathspec : nil)
1101
1018
  case flag
1102
1019
  when :push
1103
- op.append?('message', readline('Enter message', force: true), force: true) if op.remove(':')
1104
1020
  append_pathspec op.extras
1105
- when :pop, :apply, :drop, :branch
1106
- if op.remove(':')
1107
- if flag == :branch
1108
- if op.empty?
1109
- values = [['Branch name', true]]
1110
- else
1111
- op << op.shift
1112
- end
1113
- end
1114
- out = choice_index('Choose a stash', git_spawn('stash list', stdout: false),
1115
- values: values, column: /^[^@]+@\{(\d+)\}/)
1116
- if values
1117
- op.merge(out.reverse)
1118
- else
1119
- op << out
1120
- 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)
1121
1025
  elsif !op.empty?
1122
- op << op.shift
1123
- elsif flag == :branch
1124
- raise_error 'no branch name'
1026
+ op << shell_escape(op.pop)
1125
1027
  end
1126
1028
  op.clear
1127
1029
  when :clear
@@ -1138,35 +1040,30 @@ module Squared
1138
1040
  end
1139
1041
  else
1140
1042
  git_session('stash', 'push', opts: opts)
1141
- 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)
1142
1044
  append_message
1143
1045
  end
1144
1046
  source(banner: !quiet?, sync: sync, **threadargs)
1145
1047
  end
1146
1048
 
1147
- def status(flag = nil, opts = [])
1148
- cmd, opts = git_session('status', opts: opts)
1149
- if flag
1150
- op = OptionPartition.new(opts, OPT_GIT[:status], cmd, project: self, no: OPT_GIT[:no][:status])
1151
- append_pathspec op.extras
1152
- else
1153
- cmd << (option('long') ? '--long' : '--short')
1154
- cmd << '--branch' if option('branch')
1155
- if (val = option('ignore-submodules', ignore: false))
1156
- cmd << basic_option('ignore-submodules', case val
1157
- when '0', 'none'
1158
- 'none'
1159
- when '1', 'untracked'
1160
- 'untracked'
1161
- when '2', 'dirty'
1162
- 'dirty'
1163
- else
1164
- 'all'
1165
- end)
1166
- end
1167
- 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)
1168
1064
  end
1169
- if stdout?
1065
+ append_pathspec
1066
+ if verbose
1170
1067
  r = color(:red)
1171
1068
  g = color(:green)
1172
1069
  sub = if session_arg?('short')
@@ -1174,10 +1071,10 @@ module Squared
1174
1071
  { pat: /^(.)([A-Z?!])(.+)$/, styles: r, index: 2 },
1175
1072
  { pat: /^([A-Z?!])(.+)$/, styles: g },
1176
1073
  { pat: /^(\?\?)(.+)$/, styles: r },
1177
- { pat: /^(## )(.+?)(\.{3})(.+)$/, styles: [nil, g, nil, r], index: -1 }
1074
+ { pat: /^(## )((?~\.{3}))(\.{3})(.+)$/, styles: [nil, g, nil, r], index: -1 }
1178
1075
  ]
1179
1076
  else
1180
- [{ pat: /^(\t+)([a-z]+: +.+)$/, styles: r, index: 2 }]
1077
+ [pat: /^(\t+)([a-z]+: +.+)$/, styles: r, index: 2]
1181
1078
  end
1182
1079
  end
1183
1080
  out, banner, from = source(io: true)
@@ -1188,8 +1085,8 @@ module Squared
1188
1085
  def revbuild(flag = nil, opts = [], sync: nil, **kwargs)
1189
1086
  statusargs = lambda do
1190
1087
  {
1191
- include: relativepath(Array(kwargs[:include]), all: true),
1192
- 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)
1193
1090
  }
1194
1091
  end
1195
1092
  unless workspace.closed
@@ -1207,19 +1104,19 @@ module Squared
1207
1104
  kwargs = kwargs.key?(:include) || kwargs.key?(:exclude) ? statusargs.call : @revbuild || {}
1208
1105
  case flag
1209
1106
  when :build
1210
- 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)
1211
1108
  op.clear(append: true)
1212
1109
  args = op.to_a
1213
1110
  else
1214
1111
  args = []
1215
- args << basic_option('untracked-files', flag) if (flag = option('untracked-files', prefix: 'git'))
1216
- args << basic_option('ignore-submodules', flag) if (flag = option('ignore-submodules', prefix: 'git'))
1217
- 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) }
1218
1115
  end
1219
1116
  if (cur = workspace.rev_entry(name)) && cur['revision'] == sha && !env('REVBUILD_FORCE')
1220
1117
  files = status_digest(*args, **kwargs)
1221
1118
  if cur['files'].size == files.size && cur['files'].find { |key, val| files[key] != val }.nil?
1222
- if stdout?
1119
+ if verbose
1223
1120
  if (since = workspace.rev_timesince(name, 'build'))
1224
1121
  puts log_message(Logger::INFO, name, 'no changes', subject: 'revbuild', hint: "#{since} ago")
1225
1122
  else
@@ -1230,11 +1127,14 @@ module Squared
1230
1127
  end
1231
1128
  end
1232
1129
  start = epochtime
1233
- build(*@output, sync: sync, from: :'git:revbuild')
1130
+ build(@output, sync: sync, from: :'git:revbuild')
1234
1131
  rescue StandardError => e
1235
- print_error(e, pass: true)
1132
+ warn log_message(Logger::WARN, e, pass: true) if warning?
1236
1133
  else
1237
- 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
1238
1138
  workspace.rev_write(name, { 'revision' => sha, 'files' => status_digest(*args, **kwargs) },
1239
1139
  sync: sync, utc: 'build')
1240
1140
  end
@@ -1263,9 +1163,7 @@ module Squared
1263
1163
  end
1264
1164
  when :patch
1265
1165
  cmd << '--patch'
1266
- when :undo
1267
- cmd << '--hard HEAD@{1}'
1268
- ref = false
1166
+ append_pathspec(refs, pass: false)
1269
1167
  end
1270
1168
  unless ref == false
1271
1169
  append_commit(ref, head: true)
@@ -1276,7 +1174,7 @@ module Squared
1276
1174
 
1277
1175
  def checkout(flag, opts = [], branch: nil, origin: nil, create: nil, commit: nil, detach: nil, merge: false)
1278
1176
  cmd, opts = git_session('checkout', opts: opts)
1279
- append_option 'force', 'f', 'merge'
1177
+ append_option 'force', 'merge'
1280
1178
  case flag
1281
1179
  when :branch
1282
1180
  cmd << '--detach' if detach == 'd' || option('detach')
@@ -1297,14 +1195,13 @@ module Squared
1297
1195
  else
1298
1196
  op = OptionPartition.new(opts, OPT_GIT[:checkout], cmd, project: self, no: OPT_GIT[:no][:checkout],
1299
1197
  first: flag == :path ? matchpathspec : nil)
1300
- if flag == :path
1198
+ if flag == :commit
1199
+ op.append(commit)
1200
+ .clear(pass: false)
1201
+ else
1301
1202
  append_head
1302
1203
  append_pathspec(op.extras, pass: false)
1303
- print_success if success?(source)
1304
- return
1305
1204
  end
1306
- op.append(commit)
1307
- .clear(pass: false)
1308
1205
  end
1309
1206
  source
1310
1207
  end
@@ -1318,7 +1215,7 @@ module Squared
1318
1215
  elsif !session_arg?('s', 'sign', 'u', 'local-user')
1319
1216
  cmd << '--annotate'
1320
1217
  end
1321
- cmd << '--force' if option('force', 'f')
1218
+ cmd << '--force' if option('force')
1322
1219
  if !commit && message && (sha = commithash(message))
1323
1220
  commit = sha
1324
1221
  message = nil
@@ -1337,19 +1234,19 @@ module Squared
1337
1234
  list_result(ret, 'tags', from: from, grep: op.extras)
1338
1235
  return
1339
1236
  end
1340
- remote ||= option('remote')
1237
+ remote ||= option 'remote'
1341
1238
  source
1342
1239
  git_spawn('push', flag == :delete ? '-d' : nil, remote, *refs.map { |val| shell_quote(val) }) if remote
1343
1240
  end
1344
1241
 
1345
1242
  def log!(flag, opts = [], range: [], index: [])
1346
1243
  cmd, opts = git_session('log', opts: opts)
1347
- op = OptionPartition.new(opts, collect_hash(OPT_GIT[:log]), cmd, project: self,
1348
- no: collect_hash(OPT_GIT[:no][:log]),
1349
- 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)
1350
1247
  case flag
1351
1248
  when :between, :contain
1352
- op.add_quote(range.join(flag == :between ? '..' : '...'))
1249
+ op << shell_quote(range.join(flag == :between ? '..' : '...'))
1353
1250
  else
1354
1251
  op.merge(index)
1355
1252
  end
@@ -1357,7 +1254,6 @@ module Squared
1357
1254
  append_pathspec op.extras
1358
1255
  source(exception: false)
1359
1256
  end
1360
- alias log_ log!
1361
1257
 
1362
1258
  def diff(flag, opts = [], refs: [], branch: nil, range: [], index: [])
1363
1259
  cmd, opts = git_session('diff', opts: opts)
@@ -1373,18 +1269,19 @@ module Squared
1373
1269
  op << '--no-index'
1374
1270
  append_pathspec(refs, parent: true)
1375
1271
  else
1376
- op << '--merge-base' if option('merge-base')
1377
1272
  case flag
1378
1273
  when :view
1274
+ op << '--merge-base' if option('merge-base')
1379
1275
  op.merge(range)
1380
1276
  when :between, :contain
1381
1277
  op.delete('--merge-base')
1382
- op.add_quote(range.join(flag == :between ? '..' : '...'))
1278
+ op << shell_quote(range.join(flag == :between ? '..' : '...'))
1383
1279
  else
1384
- op.add_quote(branch) if branch
1280
+ op << '--merge-base' if option('merge-base')
1281
+ op << shell_quote(branch) if branch
1385
1282
  if !index.empty?
1386
1283
  if op.arg?('cached')
1387
- 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
1388
1285
  op << index.first
1389
1286
  else
1390
1287
  op.merge(index)
@@ -1436,17 +1333,18 @@ module Squared
1436
1333
  branch, origin, hint = line.split('...')
1437
1334
  if hint && !hint.match?(/^\[(\D+0,\D+0)\]$/)
1438
1335
  raise_error('work tree is not usable', hint: hint[1..-2])
1439
- elsif !origin || origin.empty?
1336
+ elsif origin.empty?
1440
1337
  return nil if pass
1441
1338
  break if dryrun
1442
1339
 
1443
1340
  unless (origin = option('upstream', prefix: 'git', ignore: false))
1444
1341
  if (origin = choice_refs('Choose an upstream', 'remotes', attempts: 1, force: false))
1445
1342
  git_spawn 'branch', quote_option('set-upstream-to', origin)
1446
- break
1343
+ else
1344
+ origin = readline('Enter an upstream', force: true)
1447
1345
  end
1448
- origin = readline('Enter an upstream', force: true)
1449
1346
  end
1347
+ raise_error('missing remote name', hint: origin) unless origin.include?('/')
1450
1348
  upstream = true
1451
1349
  end
1452
1350
  break
@@ -1459,7 +1357,7 @@ module Squared
1459
1357
  co = git_session('commit', options: false)
1460
1358
  pu = git_output 'push', upstream && '--set-upstream'
1461
1359
  if dryrun
1462
- op.adjoin('--dry-run')
1360
+ op.delete('--dry-run')
1463
1361
  co << '--dry-run'
1464
1362
  pu << '--dry-run'
1465
1363
  end
@@ -1471,6 +1369,7 @@ module Squared
1471
1369
  end
1472
1370
  pu << '--force-with-lease' if amend
1473
1371
  pu.merge(repotrack(origin, branch))
1372
+ puts if pass
1474
1373
  source op
1475
1374
  source co
1476
1375
  source pu
@@ -1478,29 +1377,23 @@ module Squared
1478
1377
 
1479
1378
  def merge(flag, opts = [], command: nil, branch: nil)
1480
1379
  cmd, opts = git_session('merge', opts: opts)
1481
- display = false
1482
1380
  case flag
1483
1381
  when :commit, :'no-commit'
1484
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?
1485
1384
  op << "--#{flag}" << '--'
1486
1385
  if branch
1487
1386
  op << branch
1488
1387
  op.clear(pass: false)
1489
1388
  else
1490
- raise_error 'no branch/commit' if op.empty?
1491
1389
  append_commit(*op.extras)
1492
1390
  end
1493
1391
  else
1494
- unless gitpath('MERGE_HEAD').exist?
1495
- puts log_message(Logger::INFO, name, 'no merge in progress', hint: command) if stdout?
1496
- return
1497
- end
1498
1392
  return unless VAL_GIT[:merge][:send].include?(command)
1499
1393
 
1500
1394
  cmd << "--#{command}"
1501
- display = command == 'abort'
1502
1395
  end
1503
- print_success if success?(source, display)
1396
+ source
1504
1397
  end
1505
1398
 
1506
1399
  def branch(flag = nil, opts = [], refs: [], ref: nil, target: nil, remote: nil)
@@ -1508,17 +1401,17 @@ module Squared
1508
1401
  stdout = false
1509
1402
  case flag
1510
1403
  when :create
1511
- if (arg = option('track', ignore: false))
1512
- cmd << case arg
1404
+ option('track', ignore: false) do |val|
1405
+ cmd << case val
1513
1406
  when '0', 'false'
1514
1407
  '--no-track'
1515
1408
  when 'direct', 'inherit'
1516
- basic_option('track', arg)
1409
+ basic_option 'track', val
1517
1410
  else
1518
1411
  '--track'
1519
1412
  end
1520
1413
  end
1521
- cmd << '--force' if option('force', 'f')
1414
+ cmd << '--force' if option('force')
1522
1415
  cmd << shell_quote(target)
1523
1416
  cmd << shell_quote(ref) if ref
1524
1417
  when :track
@@ -1533,12 +1426,14 @@ module Squared
1533
1426
  end
1534
1427
  when :delete
1535
1428
  remote&.each do |val|
1536
- 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) })
1537
1430
  end
1538
- force, list = refs.partition { |val| val.match?(/^[~^]/) }
1431
+ force, list = refs.partition { |val| val.start_with?(/[~^]/) }
1539
1432
  force.each do |val|
1540
- r = '-r' if val.delete!('~')
1541
- 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(/^[~^]+/, '')))
1542
1437
  end
1543
1438
  return if list.empty?
1544
1439
 
@@ -1546,9 +1441,8 @@ module Squared
1546
1441
  append_value list
1547
1442
  remote = nil
1548
1443
  when :move, :copy
1549
- s = +"-#{flag.to_s[0]}"
1550
- s.upcase! if option('force', 'f')
1551
- cmd << s
1444
+ flag = "-#{flag.to_s[0]}"
1445
+ cmd << (option('force') ? flag.upcase : flag)
1552
1446
  refs.compact.each { |val| cmd << shell_quote(val) }
1553
1447
  stdout = true
1554
1448
  when :current
@@ -1558,7 +1452,7 @@ module Squared
1558
1452
  when :list
1559
1453
  op = OptionPartition.new(opts, OPT_GIT[:branch], cmd << '--list',
1560
1454
  project: self, no: OPT_GIT[:no][:branch], single: /\Av+\z/)
1561
- op.each { |val| op.add_quote(val) }
1455
+ op.each { |val| op << shell_quote(val) }
1562
1456
  out, banner, from = source(io: true)
1563
1457
  print_item banner
1564
1458
  ret = write_lines(out, sub: [
@@ -1568,19 +1462,19 @@ module Squared
1568
1462
  list_result(ret, 'branches', from: from)
1569
1463
  return
1570
1464
  else
1571
- 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?
1572
1467
  ret = 0
1573
1468
  else
1574
1469
  git_spawn 'fetch --all --prune --quiet' if option('sync')
1575
- cmd << '-vv --no-abbrev --list'
1576
- out, banner, from = source(io: true)
1470
+ out, banner, from = source(cmd << '-vv --no-abbrev --list', io: true)
1577
1471
  ret = write_lines(out, grep: [/^\*\s+#{Regexp.escape(head)}\s/], banner: banner, first: true) do |line|
1578
1472
  next line if stdin?
1579
1473
 
1580
1474
  data = line.sub(/^\*\s+/, '').split(/\s+/)
1581
1475
  a = sub_style(data[0], styles: theme[:inline])
1582
1476
  b = sub_style(data[1], styles: theme[:extra])
1583
- r = /\A(?:\[(.+?)(?=\]\s)\]\s)?(.+)\z/m.match(data[2..-1].join(' '))
1477
+ r = /\A(?:\[((?~\]\s))\]\s)?(.+)\z/m.match(data[2..-1].join(' '))
1584
1478
  if (r1 = r[1]) && r1 =~ /^(.+):(?: ([a-z]+) (\d+),)? ([a-z]+) (\d+)$/
1585
1479
  write = ->(s1, s2) { "#{s1.capitalize.rjust(7)}: #{sub_style(s2, styles: theme[:warn])}" }
1586
1480
  r1 = $1
@@ -1594,7 +1488,9 @@ module Squared
1594
1488
  end
1595
1489
  on :last, from
1596
1490
  end
1597
- 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
1598
1494
  return
1599
1495
  end
1600
1496
  return unless success?(source(stdout: stdout))
@@ -1602,55 +1498,33 @@ module Squared
1602
1498
  if !ref
1603
1499
  print_success if flag == :create
1604
1500
  elsif remote && target
1605
- source git_output('push -u', shell_quote(ref.split('/', 2).first), shell_quote(target))
1606
- end
1607
- end
1608
-
1609
- def switch(flag, opts = [], branch: nil, commit: nil, track: nil)
1610
- cmd, opts = git_session('switch', opts: opts)
1611
- cmd << '--force' if option('force', 'f')
1612
- if flag == :branch
1613
- op = OptionPartition.new(opts, OPT_GIT[:switch], cmd, project: self, no: OPT_GIT[:no][:switch])
1614
- op.add_quote(branch)
1615
- else
1616
- case flag
1617
- when :create
1618
- c = 'c'
1619
- if target.start_with?('^')
1620
- target = target[1..-1]
1621
- c = c.upcase
1622
- end
1623
- cmd << quote_option(c, target)
1624
- cmd << case (track ||= option('track', ignore: false))
1625
- when 'n', 'N', '0', 'false'
1626
- '--no-track'
1627
- when 'y', 'Y', '1', 'true'
1628
- '--track'
1629
- when 'direct', 'inherit'
1630
- basic_option 'track', track
1631
- end
1632
- when :detach
1633
- cmd << "--#{flag}"
1634
- end
1635
- append_head commit
1501
+ source git_output('push', '-u', shell_quote(ref.split('/', 2).first), shell_quote(target))
1636
1502
  end
1637
- source
1638
1503
  end
1639
1504
 
1640
- def submodule(flag, opts = [], path: nil, url: nil)
1641
- cmd, opts = git_session('submodule', opts: opts)
1642
- 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
1643
1507
  case flag
1644
- when :branch, :url
1645
- op.adjoin("set-#{flag}")
1646
- op << '--'
1647
- op.add_path(path) if path
1648
- op.add_quote(url) if url
1649
- else
1650
- op.adjoin(flag)
1651
- op << '--recursive' if option('recursive', 'r')
1652
- 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}"
1653
1525
  end
1526
+ cmd << '--force' if option('force')
1527
+ append_head commit
1654
1528
  source
1655
1529
  end
1656
1530
 
@@ -1673,15 +1547,19 @@ module Squared
1673
1547
  source(banner: false)
1674
1548
  return
1675
1549
  when :oneline
1676
- format = flag.to_s
1550
+ format = 'oneline'
1677
1551
  end
1678
- case format
1679
- when 'oneline', 'short', 'medium', 'full', 'fuller', 'reference', 'email', 'raw'
1680
- cmd << basic_option('format', format)
1681
- when /(?:^t?format:|%)/
1682
- cmd << quote_option('pretty', format)
1683
- else
1684
- 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
1685
1563
  end
1686
1564
  op = OptionPartition.new(opts, OPT_GIT[:show] + OPT_GIT[:diff][:show] + OPT_GIT[:log][:diff], cmd,
1687
1565
  project: self,
@@ -1694,7 +1572,7 @@ module Squared
1694
1572
  cmd, opts = git_session('rev-parse', opts: opts)
1695
1573
  case flag
1696
1574
  when :commit
1697
- 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')
1698
1576
  append_commit(ref, head: true)
1699
1577
  when :branch
1700
1578
  cmd << '--abbrev-ref'
@@ -1715,7 +1593,7 @@ module Squared
1715
1593
  cmd, opts = git_session('ls-remote', '--refs', opts: opts)
1716
1594
  cmd << "--#{flag}" unless flag == :remote
1717
1595
  op = OptionPartition.new(opts, OPT_GIT[:ls_remote], cmd, project: self)
1718
- op.add_quote(remote) if remote
1596
+ op << shell_quote(remote) if remote
1719
1597
  out, banner, from = source(io: true)
1720
1598
  print_item banner
1721
1599
  ret = write_lines(out, grep: op.extras, prefix: "refs/#{flag}/")
@@ -1725,7 +1603,6 @@ module Squared
1725
1603
  def ls_files(flag, opts = [])
1726
1604
  cmd, opts = git_session('ls-files', "--#{flag}", opts: opts)
1727
1605
  op = OptionPartition.new(opts, OPT_GIT[:ls_files], cmd, project: self)
1728
- op.splice(path: true, pattern: true)
1729
1606
  out, banner, from = source(io: true)
1730
1607
  print_item banner
1731
1608
  ret = write_lines(out, grep: op.extras)
@@ -1734,17 +1611,10 @@ module Squared
1734
1611
 
1735
1612
  def git(flag, opts = [])
1736
1613
  cmd, opts = git_session(flag, opts: opts)
1737
- op = OptionPartition.new(opts, OPT_GIT[:git].fetch(flag, []) + OPT_GIT.fetch(flag, []), cmd,
1738
- project: self, no: OPT_GIT[:no][flag], first: case flag
1739
- when :blame, :revert
1740
- nil
1741
- else matchpathspec
1742
- 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)
1743
1617
  case flag
1744
- when :blame
1745
- raise_error 'no file found' unless (n = op.index { |s| basepath(s).file? })
1746
- op << '--' << shell_quote(basepath(op.delete_at(n)))
1747
- op.clear
1748
1618
  when :revert
1749
1619
  if VAL_GIT[:rebase][:send].any? { |val| op.arg?(val) }
1750
1620
  op.clear
@@ -1753,34 +1623,35 @@ module Squared
1753
1623
  else
1754
1624
  append_commit(*op.extras)
1755
1625
  end
1756
- when :add
1626
+ when :add, :clean
1757
1627
  if flag == :add && !op.arg?('pathspec-from-file')
1758
- grep, list = op.partition { |val| OptionPartition.pattern?(val) }
1759
- 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)
1760
1631
  grep.map! { |val| Regexp.new(val[1..-2]) }
1761
- files = []
1762
- status_data.each do |a, b|
1763
- 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) }
1764
1634
 
1765
- files << "#{sub_style(b, styles: color(:red))} #{a}"
1635
+ "#{sub_style(b, styles: red)} #{a}"
1766
1636
  end
1637
+ .compact
1767
1638
  unless files.empty?
1768
- files = choice_index('Select files', files, multiple: true, trim: /^\S+\s/,
1769
- accept: [['Add?', false, true]])
1639
+ files = choice_index('Select files', files, multiple: true, force: true, trim: /^\S+\s/,
1640
+ accept: 'Add?')
1770
1641
  end
1771
1642
  op.swap(list + files)
1772
1643
  end
1773
1644
  end
1774
- return source(git_session('status -s'), banner: false) unless append_pathspec(op.extras)
1775
-
1776
- 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
1777
1648
  return
1778
1649
  when :mv
1779
1650
  refs = projectmap op.extras
1780
1651
  raise_error 'no source/destination' unless refs.size > 1
1781
1652
  op.merge(refs)
1782
- when :rm, :clean
1783
- append_pathspec(op.extras, expect: flag == :rm)
1653
+ when :rm
1654
+ append_pathspec(op.extras, expect: true)
1784
1655
  end
1785
1656
  source(sync: false, stderr: true)
1786
1657
  end
@@ -1800,51 +1671,40 @@ 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)
1804
- unless cmd
1805
- print_error('no git session started', subject: project, hint: from, pass: true)
1806
- return
1807
- end
1674
+ multiple: false, **kwargs)
1808
1675
  cmd = cmd.target if cmd.is_a?(OptionPartition)
1809
- if io && banner == false
1810
- from = nil
1811
- banner = nil
1812
- else
1813
- if banner
1814
- banner = nil unless banner? && !multiple
1815
- 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}"
1816
1682
  end
1817
- if cmd.respond_to?(:done)
1818
- if from.nil? && (from = cmd.drop(1).find { |val| val.match?(/\A[a-z]{1,2}[a-z-]*\z/) })
1819
- from = :"git:#{from}"
1820
- end
1821
- banner &&= cmd.temp { |val| val.start_with?('--work-tree') || val.start_with?('--git-dir') }
1822
- end
1823
- from = nil if from == false
1683
+ banner &&= cmd.temp { |val| val.start_with?('--work-tree') || val.start_with?('--git-dir') }
1824
1684
  end
1825
1685
  cmd = session_done cmd
1826
1686
  log&.info cmd
1687
+ on :first, from
1827
1688
  banner = if banner
1828
- banner = (banner.is_a?(String) ? banner : cmd).gsub(File.join(path, ''), '')
1829
- format_banner(hint ? "#{banner} (#{hint})" : banner)
1689
+ format_banner((banner.is_a?(String) ? banner : cmd).gsub(File.join(path, ''), ''), banner: true)
1830
1690
  end
1831
- on :first, from
1832
1691
  begin
1833
1692
  if io
1834
1693
  return `#{cmd}` if stdout
1835
1694
 
1836
- return args ? [IO.popen(cmd), banner || '', from] : IO.popen(cmd)
1837
- elsif stdin? ? sync : stdout
1695
+ return banner ? [IO.popen(cmd), banner, from] : IO.popen(cmd)
1696
+ end
1697
+ if stdin? ? sync : stdout
1838
1698
  print_item banner unless multiple
1839
- ret = `#{cmd}`.chomp
1699
+ ret = `#{cmd}`
1840
1700
  if !ret.empty?
1841
1701
  puts ret
1842
1702
  elsif success?(!banner.nil?)
1843
1703
  print_success
1844
1704
  end
1845
- elsif !kwargs[:sub] && (sync || (!exception && !stderr))
1705
+ elsif sync || (!exception && !stderr)
1846
1706
  print_item banner unless multiple
1847
- ret = shell(cmd, name: send, exception: exception)
1707
+ ret = shell(cmd, exception: exception)
1848
1708
  else
1849
1709
  require 'open3'
1850
1710
  if stderr
@@ -1862,7 +1722,11 @@ module Squared
1862
1722
  end
1863
1723
  end
1864
1724
  rescue StandardError => e
1865
- 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?
1866
1730
  nil
1867
1731
  else
1868
1732
  on :last, from
@@ -1871,7 +1735,16 @@ module Squared
1871
1735
  end
1872
1736
 
1873
1737
  def write_lines(data, grep: [], prefix: nil, sub: nil, banner: nil, loglevel: nil, pass: false, first: false)
1874
- 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
1875
1748
  sub = nil if stdin?
1876
1749
  ret = 0
1877
1750
  out = []
@@ -1902,11 +1775,11 @@ module Squared
1902
1775
  def list_result(size, type, grep: [], action: 'found', from: nil)
1903
1776
  if size == 0
1904
1777
  puts empty_status("No #{type} were #{action}", 'grep', grep.join(', '))
1905
- elsif stdout?
1778
+ elsif verbose
1906
1779
  styles = theme.fetch(:banner, []).reject { |s| s.to_s.end_with?('!') }
1907
1780
  styles << :bold if styles.size <= 1
1908
1781
  puts print_footer("#{size} #{size == 1 ? type.sub(/(?:(?<!l)e)?s\z/, '') : type}",
1909
- sub: [{ pat: /^(\d+)(.+)$/, styles: styles }])
1782
+ sub: [pat: /^(\d+)(.+)$/, styles: styles])
1910
1783
  end
1911
1784
  on :last, from
1912
1785
  end
@@ -1922,7 +1795,7 @@ module Squared
1922
1795
  end
1923
1796
  args = []
1924
1797
  args << quote_option('sort', sort) if sort
1925
- args << basic_option('count', env('GIT_COUNT', ARG[:CHOICE])) if count
1798
+ args << shell_option('count', env('GIT_COUNT', ARG[:CHOICE])) if count
1926
1799
  choice_index(msg, foreachref(type, *args, format: format), trim: trim, **kwargs)
1927
1800
  end
1928
1801
 
@@ -1945,11 +1818,12 @@ module Squared
1945
1818
  glob = kwargs.fetch(:include, [])
1946
1819
  pass = kwargs.fetch(:exclude, [])
1947
1820
  ret = {}
1948
- status_data(*args).each do |file,|
1821
+ status_data(*args).each do |line|
1822
+ file = line.first
1949
1823
  next if !glob.empty? && glob.none? { |val| File.fnmatch?(val, file, File::FNM_DOTMATCH) }
1950
1824
  next if !pass.empty? && pass.any? { |val| File.fnmatch?(val, file, File::FNM_DOTMATCH) }
1951
1825
 
1952
- ret[file] = algorithm.hexdigest(File.read(basepath(file)))
1826
+ ret[file] = algorithm.hexdigest(File.read(path + file))
1953
1827
  end
1954
1828
  ret
1955
1829
  end
@@ -1965,7 +1839,7 @@ module Squared
1965
1839
  end
1966
1840
 
1967
1841
  def append_pull(opts, list, target: @session, flag: nil, no: nil, remote: nil, from: nil)
1968
- target << '--force' if option('force', 'f', target: target)
1842
+ target << '--force' if option('force', target: target)
1969
1843
  append_submodules(target: target, from: from)
1970
1844
  return if !remote && opts.empty?
1971
1845
 
@@ -1977,11 +1851,13 @@ module Squared
1977
1851
  when 'rebase'
1978
1852
  op << basic_option($1, $2) if VAL_GIT[:rebase][:value].include?($2)
1979
1853
  when 'shallow-since'
1980
- 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'))
1981
1857
  when 'recurse-submodules'
1982
- op.append?($1, $2, type: :basic)
1858
+ op << basic_option($1, $2) unless op.arg?('recurse-submodules')
1983
1859
  when 'refspec'
1984
- refspec << shell_quote($2)
1860
+ refspec << shell_escape($2, quote: true)
1985
1861
  end
1986
1862
  elsif op.arg?('multiple')
1987
1863
  op.found << opt
@@ -1989,9 +1865,9 @@ module Squared
1989
1865
  op.errors << opt
1990
1866
  end
1991
1867
  end
1992
- op << '--verbose' if (flag || from == :fetch) && stdout? && !op.arg?('quiet')
1868
+ op << '--verbose' if (flag || from == :fetch) && verbose && !op.arg?('quiet')
1993
1869
  if remote
1994
- op.append(remote)
1870
+ op.append(remote, delim: true)
1995
1871
  if (val = option('refspec', target: target, strict: true))
1996
1872
  op.append(*split_escape(val))
1997
1873
  else
@@ -1999,12 +1875,12 @@ module Squared
1999
1875
  end
2000
1876
  op.delete('--all')
2001
1877
  elsif op.arg?('multiple')
2002
- op.add_quote(*op.found)
1878
+ op.swap.merge(op.map! { |opt| shell_escape(opt, quote: true) })
2003
1879
  return
2004
1880
  elsif option('all')
2005
- op << '--all'
1881
+ cmd << '--all'
2006
1882
  end
2007
- op.clear(errors: true, subject: flag) if flag
1883
+ op.clear(errors: true, subject: flag.to_s) if flag
2008
1884
  end
2009
1885
 
2010
1886
  def append_commit(*val, target: @session, head: false)
@@ -2019,28 +1895,20 @@ module Squared
2019
1895
  def append_pathspec(files = [], target: @session, expect: false, parent: false, pass: true)
2020
1896
  if session_arg?('pathspec-from-file', target: target)
2021
1897
  option_clear files
2022
- true
2023
1898
  else
2024
- if files.empty? && (val = option('pathspec', target: target))
2025
- files = split_escape val
2026
- end
1899
+ option('pathspec', target: target) { |val| files = split_escape val } if files.empty?
2027
1900
  files = projectmap(files, parent: parent, pass: pass)
2028
1901
  if !files.empty?
2029
1902
  target << '--' << files.join(' ')
2030
- true
2031
1903
  elsif expect
2032
1904
  raise_error(parent ? 'pathspec not present' : 'pathspec not within worktree')
2033
- else
2034
- false
2035
1905
  end
2036
1906
  end
2037
1907
  end
2038
1908
 
2039
1909
  def append_message(val = nil, target: @session)
2040
1910
  val = messageopt if val.to_s.empty?
2041
- return unless val
2042
-
2043
- target << quote_option('message', val)
1911
+ target << quote_option('message', val) if val
2044
1912
  end
2045
1913
 
2046
1914
  def append_head(val = nil, target: @session)
@@ -2050,61 +1918,51 @@ module Squared
2050
1918
  end
2051
1919
 
2052
1920
  def append_submodules(target: @session, from: nil)
2053
- return unless (val = option('recurse-submodules', target: target, ignore: false))
2054
-
2055
- if from == :clone
2056
- case val
2057
- when '0', 'false'
2058
- target << '--no-recurse-submodules'
2059
- when '1', 'true'
2060
- target << '--recurse-submodules'
2061
- else
2062
- projectmap(split_escape(val)).each do |path|
2063
- 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
2064
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
2065
1942
  end
2066
- else
2067
- target << case val
2068
- when 'no', '0', 'false'
2069
- '--no-recurse-submodules'
2070
- when 'yes', 'on-demand'
2071
- "--recurse-submodules=#{val}"
2072
- else
2073
- '--recurse-submodules'
2074
- end
2075
1943
  end
2076
1944
  end
2077
1945
 
2078
1946
  def foreachref(path, *args, format: nil)
2079
- path = Array(path).map! { |val| "refs/#{val}" }
2080
- format &&= quote_option('format', format)
2081
- ret = git_spawn('for-each-ref', format, *args, *path, stdout: workspace.windows?)
2082
- 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)
2083
1949
  end
2084
1950
 
2085
1951
  def git_session(*cmd, opts: nil, worktree: true, **kwargs)
2086
- 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)}"] : []
2087
1953
  return session('git', *dir, *cmd, **kwargs) unless opts
2088
1954
 
2089
1955
  op = OptionPartition.new(opts, OPT_GIT[:common], dir, project: self)
2090
- [session('git', *op.to_a, *cmd, **kwargs), op.extras]
1956
+ ret = session('git', *op.to_a, *cmd, **kwargs)
1957
+ [ret, op.extras]
2091
1958
  end
2092
1959
 
2093
1960
  def git_output(*cmd, **kwargs)
2094
1961
  git_session(*cmd, main: false, options: false, **kwargs)
2095
1962
  end
2096
1963
 
2097
- def git_spawn(*cmd, exception: true, io: true, sync: true, stdout: true, banner: false, **kwargs)
2098
- kwargs[:send] = if sync
2099
- :system
2100
- else
2101
- exception = false
2102
- io = false
2103
- stdout = false
2104
- :spawn
2105
- end
2106
- source(cmd.first.is_a?(Set) ? cmd.first : git_output(*cmd), exception: exception, io: io, sync: sync,
2107
- 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)
2108
1966
  end
2109
1967
 
2110
1968
  def dryrun?(*, target: @session, **)
@@ -2119,35 +1977,32 @@ module Squared
2119
1977
  target.include?('--quiet') || (target.include?('-q') && stripext(target.first) == 'git')
2120
1978
  end
2121
1979
 
2122
- def gitpath(*args)
2123
- path.join('.git', *args)
1980
+ def gitpath
1981
+ path + '.git'
2124
1982
  end
2125
1983
 
2126
1984
  def repotrack(origin, branch, quote: true)
2127
- unless origin && branch && (i = origin.index('/'))
2128
- raise_error(ArgumentError, "missing #{origin ? 'branch' : 'remote'} name", hint: origin)
2129
- end
2130
- branch = "#{branch}:#{origin[(i + 1)..-1]}" unless origin.end_with?("/#{branch}")
2131
- ret = [origin[0..(i - 1)], branch]
2132
- 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 }
2133
1988
  end
2134
1989
 
2135
1990
  def commithash(val)
2136
- val[/\A:(\h{5,40})\z/, 1] || val[/\A#\{(\h{5,40})\}\z/, 1]
1991
+ val[/\A:(\h{5,40})\z/, 1]
2137
1992
  end
2138
1993
 
2139
1994
  def commithead(val)
2140
1995
  return val unless (s = matchhead(val))
2141
1996
 
2142
- s.match?(/^\d/) ? "@~#{s}" : "@#{s}"
1997
+ s.start_with?(/\d/) ? "@~#{s}" : "@#{s}"
2143
1998
  end
2144
1999
 
2145
2000
  def matchhead(val)
2146
- val && val =~ /^(?:(?:HEAD|@)([~^]\d*)?|H(\d+))$/ ? $2 || $1 || '' : nil
2001
+ val =~ /^(?:(?:HEAD|@)([~^]\d*)?|H(\d+))$/ ? $2 || $1 || '' : nil
2147
2002
  end
2148
2003
 
2149
2004
  def matchpathspec
2150
- [/\A[^a-z\d-]+/i, %r{\A[^=\\/*]*[\\/*]}, /\A--\z/]
2005
+ [/\A[^a-z\d-]+/i, %r{\A[^=\\/*]*[\\/*]}]
2151
2006
  end
2152
2007
 
2153
2008
  def messageopt