squared 0.5.25 → 0.6.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.
@@ -41,13 +41,13 @@ module Squared
41
41
  end
42
42
  end
43
43
  data.each do |key, val|
44
- if val.is_a?(Hash)
45
- uri = val.fetch(:uri, '')
46
- opts = val.fetch(:options, {})
47
- else
48
- uri = val.is_a?(String) ? val : key.to_s
49
- opts = options
50
- end
44
+ uri = if val.is_a?(Hash)
45
+ opts = val.fetch(:options, {})
46
+ val.fetch(:uri, '')
47
+ else
48
+ opts = options
49
+ val.is_a?(String) ? val : key.to_s
50
+ end
51
51
  unless uri.match?(GIT_PROTO) || Pathname.new(uri).absolute?
52
52
  if uri.start_with?('.')
53
53
  uri = @root + uri
@@ -57,8 +57,7 @@ module Squared
57
57
  next
58
58
  end
59
59
  end
60
- key = task_name key
61
- GIT_REPO[main][key] = [uri.to_s, opts]
60
+ GIT_REPO[main][key = task_name(key)] = [uri.to_s, opts]
62
61
  @kind[key] << Project::Git
63
62
  end
64
63
  if cache == true
@@ -69,6 +68,16 @@ module Squared
69
68
  self
70
69
  end
71
70
 
71
+ def git_repo(name)
72
+ (ret = GIT_REPO[main]) && ret[name]
73
+ end
74
+
75
+ def git_clone?(path, name = nil)
76
+ return false if name && !git_repo(name)
77
+
78
+ !path.exist? || path.empty?
79
+ end
80
+
72
81
  def revbuild(file: nil)
73
82
  @revfile = @home.join(file || "#{@main}.revb")
74
83
  @revdoc = JSON.parse(@revfile.read) if @revfile.exist?
@@ -81,10 +90,6 @@ module Squared
81
90
  self
82
91
  end
83
92
 
84
- def git_repo(name)
85
- (ret = GIT_REPO[main]) && ret[name]
86
- end
87
-
88
93
  def rev_entry(*keys, val: nil, create: true)
89
94
  return unless @revdoc
90
95
  return @revdoc.dig(*keys) unless val
@@ -134,22 +139,10 @@ module Squared
134
139
  File.write(@revfile, JSON.pretty_generate(@revdoc))
135
140
  rescue StandardError => e
136
141
  log&.debug e
137
- warn log_message(Logger::WARN, e, pass: true) if warning
142
+ warn log_message(Logger::WARN, e, pass: true) if warning?
138
143
  ensure
139
144
  @revlock = false
140
145
  end
141
-
142
- def git_clone?(path, name = nil)
143
- return false if name && !git_repo(name)
144
-
145
- !path.exist? || path.empty?
146
- end
147
-
148
- private
149
-
150
- def rev_timenow
151
- Time.now.utc.strftime('%s%L').to_i
152
- end
153
146
  end
154
147
  Application.include Git
155
148
 
@@ -159,12 +152,11 @@ module Squared
159
152
  common: %w[c=q bare glob-pathspecs icase-pathspecs literal-pathspecs no-optional-locks no-pager
160
153
  no-replace-objects noglob-pathspecs paginate attr-source=b config-env=q exec-path=p
161
154
  namespace=p].freeze,
162
- add: %w[A|all e|edit f|force ignore-errors ignore-missing ignore-removal i|interactive no-all
163
- no-ignore-removal n|dry-run p|patch pathspec-file-nul renormalize sparse u|update v|verbose
164
- chmod=b pathspec-from-file=p].freeze,
165
- branch: %w[a|all create-reflog i|ignore-case omit-empty q|quiet r|remotes v|verbose 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,
155
+ add: %w[A e|edit f|force ignore-errors ignore-missing i|interactive n|dry-run p|patch pathspec-file-nul
156
+ refresh renormalize sparse u|update v|verbose chmod=b pathspec-from-file=p].freeze,
157
+ branch: %w[a|all create-reflog i|ignore-case omit-empty q|quiet r|remotes v|verbose abbrev=i color=b column=b
158
+ contains=b format=q merged=b no-contains=b no-merged=b points-at=b u|set-upstream-to=b sort=q
159
+ t|track=b].freeze,
168
160
  checkout: %w[l d|detach f|force ignore-other-worktrees ignore-skip-worktree-bits m|merge p|patch
169
161
  pathspec-file-nul q|quiet ours theirs conflict=b orphan=b pathspec-from-file=p t|track=b].freeze,
170
162
  diff: {
@@ -174,17 +166,22 @@ module Squared
174
166
  fetch: {
175
167
  base: %w[multiple porcelain progress P|prune-tags refetch stdin u|update-head-ok
176
168
  recurse-submodules-default=b].freeze,
177
- pull: %w[4 6 n t a|append atomic dry-run f|force k|keep negotiate-only prefetch p|prune q|quiet
178
- set-upstream unshallow update-shallow v|verbose deepen=i depth=i j|jobs=i negotiation-tip=q
179
- recurse-submodules=v refmap=q o|server-option=q shallow-exclude=b shallow-since=v
180
- upload-pack=q].freeze
169
+ pull: %w[4 6 n t a|append atomic dry-run f|force k|keep negotiate-only prefetch p|prune q|quiet set-upstream
170
+ unshallow update-shallow v|verbose deepen=i depth=i j|jobs=i negotiation-tip=q recurse-submodules=v
171
+ refmap=q o|server-option=q shallow-exclude=b shallow-since=v upload-pack=q].freeze
181
172
  }.freeze,
182
173
  git: {
183
174
  add: %w[N|intent-to-add refresh].freeze,
184
175
  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
185
- p|porcelain root score-debug f|show-name e|show-email n|show-number show-stats abbrev=i
186
- contents=p date=q encoding=b ignore-rev=b ignore-revs-file=p reverse=q].freeze,
176
+ p|porcelain root score-debug f|show-name e|show-email n|show-number show-stats abbrev=i contents=p
177
+ date=q encoding=b ignore-rev=b ignore-revs-file=p reverse=q].freeze,
187
178
  clean: %w[d x X f|force n|dry-run i|interactive q|quiet e|exclude=q].freeze,
179
+ grep: %w[e f=p h H I O=bm r all-match and G|basic-regexp break cached column c|count E|extended-regexp
180
+ l|files-with-matches L|files-without-match F|fixed-strings full-name W|function-context heading
181
+ i|ignore-case v|invert-match n|line-number name-only no-index not z|null o|only-matching or
182
+ P|perl-regexp q|quiet recurse-submodules p|show-function a|text untracked w|word-regexp
183
+ A|after-context=i B|before-context=i color=b C|context=i m|max-count=n max-depth=i
184
+ open-files-in-pager=b threads=n].freeze,
188
185
  mv: %w[k f|force n|dry-run v|verbose].freeze,
189
186
  revert: %w[e S=bm? abort continue n|no-commit quit reference skip cleanup=b gpg-sign=b? m|mainline=i
190
187
  s|signoff strategy=b X|strategy-option=b].freeze,
@@ -204,26 +201,27 @@ module Squared
204
201
  format: %w[t children combined-all-paths dd oneline left-right no-diff-merges parents relative-date
205
202
  show-notes-by-default show-signature date=q diff-merges=b encoding=b expand-tabs=i format=q
206
203
  notes=b pretty=q? show-linear-break=q?].freeze,
207
- 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
204
+ diff: %w[p R u z B=bm? C=bm? l=im G=qm I=qm M=bm? O=qm S=qm binary check compact-summary cumulative
208
205
  find-copies-harder full-index W|function-context w|ignore-all-space ignore-blank-lines
209
206
  ignore-cr-at-eol ignore-space-at-eol b|ignore-space-change D|irreversible-delete graph
210
207
  ita-invisible-in-index minimal name-only name-status no-color-moved-ws no-prefix no-renames numstat
211
208
  patch-with-raw patch-with-stat patience pickaxe-all pickaxe-regex raw shortstat summary a|text
212
209
  abbrev=i? anchored=q break-rewrites=b? color=b color-moved=b color-moved-ws=b color-words=q?
213
210
  diff-algorithm=b diff-filter=e? X|dirstat=b? dirstat-by-file=b? dst-prefix=q find-copies=i?
214
- find-object=b find-renames=b? ignore-matching-lines=q ignore-submodules=b? inter-hunk-context=i
215
- line-prefix=q output=p output-indicator-context=q output-indicator-new=q output-indicator-old=q
216
- relative=p rotate-to=p skip-to=p src-prefix=q stat=b? stat-count=i stat-width=i stat-name-width=i
217
- submodule=b? unified=i word-diff=b? word-diff-regex=q ws-error-highlight=b].freeze
211
+ find-object=b find-renames=b? ignore-matching-lines=q ignore-submodules=b? line-prefix=q output=p
212
+ output-indicator-context=q output-indicator-new=q output-indicator-old=q relative=p rotate-to=p
213
+ skip-to=p src-prefix=q stat=b? stat-count=i stat-width=i stat-name-width=i submodule=b?
214
+ word-diff=b? word-diff-regex=q ws-error-highlight=b].freeze,
215
+ diff_context: %w[U=im inter-hunk-context=i unified=i].freeze
218
216
  }.freeze,
219
217
  ls_files: %w[f t v z debug deduplicate directory eol error-unmatch exclude-standard full-name i|ignored
220
218
  k|killed no-empty-directory recurse-submodules sparse s|stage u|unmerged abbrev=i x|exclude=q
221
219
  X|exclude-from=p exclude-per-directory=p format=q with-tree=q].freeze,
222
220
  ls_remote: %w[exit-code get-url q|quiet symref o|server-option=q sort=q upload-pack=q].freeze,
223
- merge: %w[e n S=bm? allow-unrelated-histories ff-only m=q q|quiet v|verbose cleanup=b F|file=p gpg-sign=b?
224
- into-name=b log=i s|strategy=b X|strategy-option=b].freeze,
225
- pull: %w[e n S=bm? allow-unrelated-histories ff-only cleanup=b gpg-sign=b? log=i r|rebase=v? s|strategy=b
226
- X|strategy-option=b].freeze,
221
+ merge: %w[e n S=bm? allow-unrelated-histories compact-summary ff-only m=q q|quiet v|verbose cleanup=b F|file=p
222
+ gpg-sign=b? into-name=b log=i s|strategy=b X|strategy-option=b].freeze,
223
+ pull: %w[e n S=bm? allow-unrelated-histories compact-summary ff-only cleanup=b gpg-sign=b? log=i r|rebase=v?
224
+ s|strategy=b X|strategy-option=b].freeze,
227
225
  rebase: %w[n C=im S=bm? allow-empty-message apply committer-date-is-author-date edit-todo empty=b
228
226
  f|force-rebase ignore-date ignore-whitespace i|interactive keep-base m|merge no-ff q|quiet quit
229
227
  reset-author-date root show-current-patch signoff v|verbose empty=b x|exec=q gpg-sign=b? onto=b
@@ -242,8 +240,8 @@ module Squared
242
240
  encoding=b expand-tabs=i notes=q show-notes=q?].freeze,
243
241
  stash: {
244
242
  common: %w[q|quiet].freeze,
245
- push: %w[a|all u|include-untracked k|keep-index no-keep-index no-include-untracked pathspec-file-nul
246
- p|patch S|staged m|message=q pathspec-from-file=p].freeze,
243
+ push: %w[a|all u|include-untracked k|keep-index no-keep-index no-include-untracked pathspec-file-nul p|patch
244
+ S|staged m|message=q pathspec-from-file=p].freeze,
247
245
  pop: %w[index].freeze,
248
246
  apply: %w[index].freeze
249
247
  }.freeze,
@@ -261,6 +259,7 @@ module Squared
261
259
  tag: %w[n=im cleanup=b create-reflog i|ignore-case omit-empty color=b? column=b contains=b? format=q merged=b?
262
260
  no-contains=b? no-merged=b? points-at=q sort=q trailer=q].freeze,
263
261
  no: {
262
+ add: %w[all ignore-removal].freeze,
264
263
  blame: %w[progress].freeze,
265
264
  branch: %w[color color-moved column track].freeze,
266
265
  checkout: %w[overwrite-ignore guess overlay progress recurse-submodules track].freeze,
@@ -268,6 +267,7 @@ module Squared
268
267
  base: %w[auto-gc auto-maintenance write-commit-graph write-fetch-head].freeze,
269
268
  pull: %w[all ipv4 ipv6 recurse-submodules show-forced-updates tags].freeze
270
269
  },
270
+ grep: %w[color exclude-standard recursive textconv].freeze,
271
271
  log: {
272
272
  base: %w[decorate mailmap merges use-mailmap].freeze,
273
273
  diff: %w[color color-moved ext-diff indent-heuristic patch relative rename-empty textconv].freeze,
@@ -303,33 +303,6 @@ module Squared
303
303
  class << self
304
304
  include Rake::DSL
305
305
 
306
- def populate(ws, **)
307
- return if ws.series.exclude?(:pull, true) || ws.size == 1
308
-
309
- namespace ws.task_name('git') do |ns|
310
- ws.format_desc(all = ws.task_join(ns.scope.path, 'all'), 'stash|rebase|autostash?,depend?')
311
- task 'all' do |_, args|
312
- args = args.to_a
313
- cmd = if args.include?('stash')
314
- ['stash', 'pull']
315
- elsif args.include?('rebase')
316
- ['rebase']
317
- elsif args.include?('autostash')
318
- ['autostash']
319
- else
320
- ['pull']
321
- end
322
- cmd.map! { |val| ws.task_sync(val) }
323
- cmd << ws.task_sync('depend') if args.include?('depend') && !ws.series.exclude?(:depend, true)
324
- cmd << ws.task_sync('build')
325
- Common::Utils.task_invoke(*cmd, **ws.invokeargs)
326
- end
327
-
328
- ws.series.sync << all
329
- ws.series.multiple << all
330
- end
331
- end
332
-
333
306
  def tasks
334
307
  %i[pull rebase autostash fetch clone stash status branch revbuild].freeze
335
308
  end
@@ -342,13 +315,13 @@ module Squared
342
315
  end
343
316
 
344
317
  subtasks({
345
- 'branch' => %i[create track delete move copy list all current].freeze,
318
+ 'branch' => %i[create track delete move copy list current].freeze,
346
319
  'checkout' => %i[commit branch track detach path].freeze,
347
320
  'commit' => %i[add all amend amend-orig fixup].freeze,
348
321
  'diff' => %i[head branch files view between contain].freeze,
349
322
  'fetch' => %i[origin remote all].freeze,
350
323
  'files' => %i[cached modified deleted others].freeze,
351
- 'git' => %i[add blame clean mv revert rm status].freeze,
324
+ 'git' => %i[add blame clean grep mv revert rm status].freeze,
352
325
  'log' => %i[view between contain].freeze,
353
326
  'merge' => %i[commit no-commit send].freeze,
354
327
  'pull' => %i[origin remote all].freeze,
@@ -358,7 +331,7 @@ module Squared
358
331
  'restore' => %i[source staged worktree].freeze,
359
332
  'rev' => %i[commit build output].freeze,
360
333
  'show' => %i[format oneline textconv].freeze,
361
- 'stash' => %i[push pop apply branch drop clear list all].freeze,
334
+ 'stash' => %i[push pop apply branch drop clear list all staged worktree].freeze,
362
335
  'submodule' => %i[status update branch url sync].freeze,
363
336
  'switch' => %i[branch create detach].freeze,
364
337
  'tag' => %i[add sign delete list].freeze
@@ -366,7 +339,7 @@ module Squared
366
339
 
367
340
  def initialize(*, **)
368
341
  super
369
- @submodule = basepath('.gitmodules').exist?
342
+ @submodule = exist?('.gitmodules')
370
343
  initialize_ref Git.ref if gitpath.exist?
371
344
  end
372
345
 
@@ -389,16 +362,16 @@ module Squared
389
362
  if flag == :remote
390
363
  format_desc action, flag, 'remote?,opts*'
391
364
  task flag, [:remote] do |_, args|
392
- if (remote = args.remote)
393
- args = args.extras
394
- else
395
- remote = choice_remote
396
- args = args.to_a
397
- end
365
+ args = if (remote = args.remote)
366
+ args.extras
367
+ else
368
+ remote = choice_remote
369
+ args.to_a
370
+ end
398
371
  __send__(action, flag, args, remote: remote)
399
372
  end
400
373
  else
401
- format_desc(action, flag, 'opts*', after: flag == :all && action == 'pull' ? 'pattern*' : nil)
374
+ format_desc(action, flag, 'opts*', after: ('pattern*' if flag == :all && action == 'pull'))
402
375
  task flag do |_, args|
403
376
  __send__ action, flag, args.to_a
404
377
  end
@@ -435,11 +408,11 @@ module Squared
435
408
  commit(flag, message: args.message)
436
409
  end
437
410
  else
438
- format_desc(action, flag, 'pathspec+', before: flag == :add ? 'opts*' : nil)
411
+ format_desc(action, flag, 'pathspec+', before: ('opts*' if flag == :add))
439
412
  task flag do |_, args|
440
413
  if flag == :fixup
441
- ref, squash, pick = choice_commit(accept: [['Auto squash?', true]], reflog: false,
442
- values: ['Pick [amend|reword]'])
414
+ ref, squash, pick = choice_commit(reflog: false, accept: [accept_b('Auto squash?')],
415
+ values: 'Pick [amend|reword]')
443
416
  pick &&= case pick.downcase
444
417
  when 'a', 'amend'
445
418
  'amend'
@@ -481,7 +454,7 @@ module Squared
481
454
  task flag do |_, args|
482
455
  refs = args.to_a
483
456
  if refs.empty?
484
- refs = choice_refs('Choose a tag', 'tags', multiple: true, accept: 'Delete?', series: true)
457
+ refs = choice_refs('Choose a tag', 'tags', multiple: true, series: true, accept: 'Delete?')
485
458
  remote = choice_remote
486
459
  end
487
460
  tag(flag, refs: refs, remote: remote)
@@ -494,12 +467,13 @@ module Squared
494
467
  commit = commithead args.commit
495
468
  remote = args.remote
496
469
  else
497
- commit, name, message = choice_commit(values: [['Enter tag name', true], 'Enter message'],
498
- series: true, reflog: false)
470
+ commit, name, message = choice_commit(reflog: false, series: true,
471
+ values: [['Enter tag name', true], 'Enter message'])
499
472
  remote = choice_remote
500
473
  end
501
- ret = tag(flag, refs: [name], message: message, commit: commit, remote: remote)
502
- print_success if success?(ret, !remote)
474
+ tag(flag, refs: [name], message: message, commit: commit, remote: remote).tap do |ret|
475
+ success?(ret, !remote)
476
+ end
503
477
  end
504
478
  end
505
479
  when 'stash'
@@ -515,9 +489,8 @@ module Squared
515
489
  when 'log', 'diff'
516
490
  case flag
517
491
  when :view, :between, :contain
518
- view = flag == :view
519
- if view && action == 'log'
520
- format_desc action, flag, '(^)commit*|:,opts*,ref?,pathspec*'
492
+ if action == 'log' && flag == :view
493
+ format_desc action, flag, '(^)commit*|:,opts*,pathspec*'
521
494
  task flag do |_, args|
522
495
  args = args.to_a
523
496
  if args.first == ':'
@@ -544,16 +517,17 @@ module Squared
544
517
  format_desc action, flag, 'commit1,commit2,opts*,pathspec*'
545
518
  task flag, [:commit1, :commit2] do |_, args|
546
519
  commit1 = commithead args.commit1
547
- if commit1
548
- commit2 = commithead param_guard(action, flag, args: args, key: :commit2)
549
- args = args.extras
550
- range = [commit1, commit2]
551
- else
552
- range, opts, refs = choice_commit(multiple: view ? true : 2, values: %w[Options Pathspec])
553
- range = range.reverse
554
- args = OptionPartition.strip(opts)
555
- args.concat(refs.shellsplit) if refs
556
- end
520
+ range = if commit1
521
+ commit2 = commithead param_guard(action, flag, args: args, key: :commit2)
522
+ args = args.extras
523
+ [commit1, commit2]
524
+ else
525
+ range, opts, refs = choice_commit(multiple: flag == :view ? true : 2,
526
+ values: %w[Options Pathspec])
527
+ args = OptionPartition.strip(opts)
528
+ args.concat(refs.shellsplit) if refs
529
+ range.reverse
530
+ end
557
531
  __send__(action == 'log' ? :log! : :diff, flag, args, range: range)
558
532
  end
559
533
  end
@@ -566,7 +540,11 @@ module Squared
566
540
  index = choice_commit(multiple: true)
567
541
  else
568
542
  index = []
569
- args.each { |val| index << (commithead(val) || commithash(val) || break) }
543
+ args.each do |val|
544
+ break unless (sha = commithead(val) || commithash(val))
545
+
546
+ index << sha
547
+ end
570
548
  args = args.drop(index.size)
571
549
  end
572
550
  diff(flag, args, index: index)
@@ -578,36 +556,36 @@ module Squared
578
556
  diff(flag, args.extras, branch: branch)
579
557
  end
580
558
  when :files
581
- format_desc action, flag, 'path1,path2'
582
- task flag, [:path1, :path2] do |_, args|
559
+ format_desc action, flag, 'path1,path2,patch?'
560
+ task flag, [:path1, :path2, :patch] do |_, args|
583
561
  path1 = param_guard(action, flag, args: args, key: :path1)
584
562
  path2 = param_guard(action, flag, args: args, key: :path2)
585
- diff(flag, refs: [path1, path2])
563
+ diff(flag, refs: [path1, path2, args.patch])
586
564
  end
587
565
  end
588
566
  when 'checkout'
589
567
  case flag
590
568
  when :branch
591
- format_desc action, flag, 'name,create?=[bB],commit?,detach?=d'
569
+ format_desc action, flag, 'name,create?=[bB],commit?,d/etach?'
592
570
  task flag, [:name, :create, :commit, :detach] do |_, args|
593
571
  if (branch = args.name)
594
572
  branch = param_guard(action, flag, args: args, key: :name)
595
573
  create = args.create
596
- if args.commit == 'd'
597
- detach = 'd'
598
- commit = nil
599
- elsif create == 'd'
600
- create = nil
601
- commit = nil
602
- detach = 'd'
603
- elsif create && create.size > 1
604
- commit = commithead create
605
- create = nil
606
- detach = args.commit
607
- else
608
- detach = args.detach
609
- commit = commithead args.commit
610
- end
574
+ detach = if args.commit == 'd'
575
+ commit = nil
576
+ 'd'
577
+ elsif create == 'd'
578
+ create = nil
579
+ commit = nil
580
+ 'd'
581
+ elsif create && create.size > 1
582
+ commit = commithead create
583
+ create = nil
584
+ args.commit
585
+ else
586
+ commit = commithead args.commit
587
+ args.detach
588
+ end
611
589
  param_guard(action, flag, args: { create: create }, key: :create, pat: /\A[Bb]\z/) if create
612
590
  else
613
591
  branch = choice_refs 'Choose a branch to switch'
@@ -620,7 +598,7 @@ module Squared
620
598
  if (origin = args.origin)
621
599
  branch = args.name
622
600
  else
623
- origin, branch = choice_refs('Choose a remote', 'remotes', values: ['Enter branch name'])
601
+ origin, branch = choice_refs('Choose a remote', 'remotes', values: 'Enter branch name')
624
602
  end
625
603
  checkout(flag, branch: branch, origin: origin)
626
604
  end
@@ -631,7 +609,7 @@ module Squared
631
609
  args = if commit
632
610
  args.extras
633
611
  else
634
- commit, opts = choice_commit(values: ['Options'])
612
+ commit, opts = choice_commit(values: 'Options')
635
613
  OptionPartition.strip(opts)
636
614
  end
637
615
  checkout(flag, args, commit: commit)
@@ -641,7 +619,7 @@ module Squared
641
619
  task flag, [:commit] do |_, args|
642
620
  commit = commithead args.commit
643
621
  unless commit
644
- commit, merge = choice_commit(values: ['Merge? [y/N]'])
622
+ commit, merge = choice_commit(values: 'Merge? [y/N]')
645
623
  merge = merge&.upcase == 'Y'
646
624
  end
647
625
  checkout(flag, commit: commit, merge: merge)
@@ -655,11 +633,13 @@ module Squared
655
633
  when 'branch'
656
634
  case flag
657
635
  when :create
658
- format_desc action, flag, 'name,ref?=HEAD|:'
636
+ format_desc action, flag, 'name,ref?|:'
659
637
  task flag, [:name, :ref] do |_, args|
660
638
  target = param_guard(action, flag, args: args, key: :name)
661
639
  ref = commithead args.ref
662
- ref, remote = choice_refs('Choose a remote', 'remotes', accept: [['Push?', true]]) if ref == ':'
640
+ if ref == ':'
641
+ ref, remote = choice_refs('Choose a remote', 'remotes', accept: [accept_b('Push?')])
642
+ end
663
643
  branch(flag, target: target, ref: ref, remote: remote)
664
644
  end
665
645
  when :track
@@ -669,18 +649,18 @@ module Squared
669
649
  target = args.name
670
650
  remote = true if ref.delete_prefix!('~')
671
651
  else
672
- ref, remote, target = choice_refs('Choose a remote', 'remotes', accept: [['Push?', true]],
673
- values: ['Enter branch name'])
652
+ ref, remote, target = choice_refs('Choose a remote', 'remotes', accept: [accept_b('Push?')],
653
+ values: 'Enter branch name')
674
654
  end
675
655
  branch(flag, target: target, ref: ref, remote: remote)
676
656
  end
677
657
  when :delete
678
- format_desc action, flag, '[^~]name*,:?'
658
+ format_desc action, flag, '(^~)name*,:?'
679
659
  task flag do |_, args|
680
660
  refs = args.to_a
681
661
  if refs.empty? || (r = refs.last == ':')
682
662
  accept = ['Delete?']
683
- accept << ['Force?', true] unless r
663
+ accept << accept_b('Force?') unless r
684
664
  remote = choice_refs('Choose a branch', r ? 'remotes' : 'heads', multiple: true,
685
665
  accept: accept)
686
666
  if r
@@ -698,7 +678,12 @@ module Squared
698
678
  task flag do |_, args|
699
679
  branch flag, args.to_a
700
680
  end
701
- when :move, :copy
681
+ when :current
682
+ format_desc action, flag
683
+ task flag do
684
+ branch flag
685
+ end
686
+ else
702
687
  format_desc action, flag, 'branch,oldbranch?'
703
688
  task flag, [:branch, :oldbranch] do |_, args|
704
689
  if (branch = args.branch)
@@ -709,24 +694,19 @@ module Squared
709
694
  end
710
695
  branch(flag, refs: [oldbranch, branch])
711
696
  end
712
- else
713
- format_desc action, flag
714
- task flag do
715
- branch flag
716
- end
717
697
  end
718
698
  when 'switch'
719
699
  case flag
720
700
  when :create
721
- format_desc action, flag, '(^)name,ref?=HEAD|:'
701
+ format_desc action, flag, '(^)name,ref?|:'
722
702
  task flag, [:name, :commit] do |_, args|
723
703
  branch = param_guard(action, flag, args: args, key: :name)
724
704
  commit = commithead args.commit
725
- commit, track = choice_commit(values: ['Track? [Y/n]'], force: false) if commit == ':'
705
+ commit, track = choice_commit(force: false, values: 'Track? [Y/n]') if commit == ':'
726
706
  switch(flag, branch: branch, commit: commit, track: track)
727
707
  end
728
708
  when :detach
729
- format_desc action, flag, 'ref?=HEAD'
709
+ format_desc action, flag, 'ref?'
730
710
  task flag, [:commit] do |_, args|
731
711
  commit = commithead(args.commit) || choice_commit(force: false)
732
712
  switch(flag, commit: commit)
@@ -734,12 +714,12 @@ module Squared
734
714
  when :branch
735
715
  format_desc action, flag, 'name|:,opts*'
736
716
  task flag, [:name] do |_, args|
737
- if (branch = args.name)
738
- args = args.extras
739
- branch = nil if branch == ':'
740
- else
741
- args = []
742
- end
717
+ args = if (branch = args.name)
718
+ branch = nil if branch == ':'
719
+ args.extras
720
+ else
721
+ []
722
+ end
743
723
  switch(flag, args, branch: branch || choice_refs('Choose a branch'))
744
724
  end
745
725
  end
@@ -749,26 +729,26 @@ module Squared
749
729
  format_desc action, flag, 'ref|:,opts*'
750
730
  task flag, [:commit] do |_, args|
751
731
  commit = commithead args.commit
752
- if commit && commit != ':'
753
- args = args.extras
754
- else
755
- commit, mode = choice_commit(values: ['Mode [mixed|soft|hard|N]'])
756
- args = args.extras.concat(case mode&.downcase
732
+ args = if commit && commit != ':'
733
+ args.extras
734
+ else
735
+ commit, mode = choice_commit(values: ['Mode [mixed|soft|hard|N]'])
736
+ args.extras.concat(case mode&.downcase
757
737
  when 'h', 'hard' then ['hard']
758
738
  when 's', 'soft' then ['soft']
759
- when 'n', 'N' then ['mixed', 'N']
739
+ when 'n', 'N' then %w[mixed N]
760
740
  else ['mixed']
761
741
  end)
762
- end
763
- print_success if success?(reset(flag, args, commit: commit))
742
+ end
743
+ success?(reset(flag, args, commit: commit))
764
744
  end
765
745
  when :index, :undo
766
- format_desc(action, flag, flag == :index ? 'opts*,pathspec*' : nil)
746
+ format_desc(action, flag, ('opts*,pathspec*' if flag == :index))
767
747
  task flag do |_, args|
768
748
  reset(flag, flag == :index ? args.to_a : [])
769
749
  end
770
750
  when :mode
771
- format_desc action, flag, 'mode,ref?=HEAD|:'
751
+ format_desc action, flag, 'mode,ref?|:'
772
752
  task flag, [:mode, :ref] do |_, args|
773
753
  mode = param_guard(action, flag, args: args, key: :mode)
774
754
  ref = commithead args.ref
@@ -776,7 +756,7 @@ module Squared
776
756
  reset(flag, mode: mode, ref: ref)
777
757
  end
778
758
  when :patch
779
- format_desc action, flag, 'ref?=HEAD|:,pathspec*'
759
+ format_desc action, flag, 'ref?|:,pathspec*'
780
760
  task flag, [:ref] do |_, args|
781
761
  ref = commithead args.ref
782
762
  ref = choice_commit(reflog: false) unless ref && ref != ':'
@@ -805,18 +785,18 @@ module Squared
805
785
  when 'rebase', 'merge'
806
786
  case flag
807
787
  when :branch
808
- format_desc action, flag, 'upstream,branch?=HEAD,opts*'
788
+ format_desc action, flag, 'upstream,branch?,opts*'
809
789
  task flag, [:upstream] do |_, args|
810
790
  args = if (upstream = args.upstream)
811
791
  args.extras
812
792
  else
813
- upstream, opts = choice_refs('Choose upstream branch', values: ['Options'])
793
+ upstream, opts = choice_refs('Choose upstream branch', values: 'Options')
814
794
  OptionPartition.strip(opts)
815
795
  end
816
796
  rebase(flag, args, upstream: upstream)
817
797
  end
818
798
  when :onto
819
- format_desc action, flag, 'ref,upstream,branch?=HEAD'
799
+ format_desc action, flag, 'ref,upstream,branch?'
820
800
  task flag, [:commit, :upstream, :branch] do |_, args|
821
801
  commit = commithead args.commit
822
802
  args = if commit
@@ -825,7 +805,7 @@ module Squared
825
805
  []
826
806
  else
827
807
  commit = choice_refs 'Choose "onto" branch'
828
- target, opts = choice_commit(reflog: false, multiple: 2, values: ['Options'])
808
+ target, opts = choice_commit(reflog: false, multiple: 2, values: 'Options')
829
809
  branch, upstream = target
830
810
  OptionPartition.strip(opts)
831
811
  end
@@ -837,7 +817,7 @@ module Squared
837
817
  args = args.to_a
838
818
  if args.empty?
839
819
  accept = "Merge with #{`#{git_output('branch --show-current')}`.chomp}?"
840
- branch, opts = choice_refs('Choose a branch', values: ['Options'], accept: accept)
820
+ branch, opts = choice_refs('Choose a branch', values: 'Options', accept: accept)
841
821
  args = OptionPartition.strip(opts)
842
822
  end
843
823
  merge(flag, args, branch: branch)
@@ -853,7 +833,7 @@ module Squared
853
833
  when 'rev'
854
834
  case flag
855
835
  when :commit
856
- format_desc action, flag, 'ref?=HEAD,size?'
836
+ format_desc action, flag, 'ref?,size?'
857
837
  task flag, [:ref, :size] do |_, args|
858
838
  ref = commithead args.ref
859
839
  size = args.size
@@ -864,8 +844,6 @@ module Squared
864
844
  rev_parse(flag, ref: ref, size: size)
865
845
  end
866
846
  when :build
867
- next unless build?
868
-
869
847
  format_desc action, flag, 'opts*'
870
848
  task flag do |_, args|
871
849
  revbuild flag, args.to_a
@@ -883,7 +861,7 @@ module Squared
883
861
  ls_remote(flag, args.extras, remote: args.remote)
884
862
  end
885
863
  else
886
- format_desc(action, flag, 'opts*,pattern*', after: action == 'files' ? 'pathspec*' : nil)
864
+ format_desc(action, flag, 'opts*,pattern*', after: ('pathspec*' if action == 'files'))
887
865
  task flag do |_, args|
888
866
  __send__(action == 'refs' ? :ls_remote : :ls_files, flag, args.to_a)
889
867
  end
@@ -924,16 +902,22 @@ module Squared
924
902
  end
925
903
  when 'git'
926
904
  before = case flag
927
- when :blame then 'file'
928
- when :mv then 'source+,destination'
929
- when :revert then 'commit+'
905
+ when :blame
906
+ 'file'
907
+ when :mv
908
+ 'source+,destination'
909
+ when :revert
910
+ 'commit+'
930
911
  end
931
- format_desc(action, flag, 'opts*', before: before, after: case flag
932
- when :add
933
- 'pathspec*,pattern*'
934
- when :clean, :rm, :status
935
- 'pathspec*'
936
- end)
912
+ after = case flag
913
+ when :add
914
+ 'pathspec*,pattern*'
915
+ when :grep
916
+ 'tree*,pathspec*'
917
+ when :clean, :rm, :status
918
+ 'pathspec*'
919
+ end
920
+ format_desc(action, flag, 'opts*', before: before, after: after)
937
921
  task flag do |_, args|
938
922
  __send__(flag == :status ? :status : :git, flag, args.to_a)
939
923
  end
@@ -984,8 +968,12 @@ module Squared
984
968
  printsucc
985
969
  end
986
970
  op = OptionPartition.new(opts, OPT_GIT[:pull], cmd, project: self, no: OPT_GIT[:no][:pull])
987
- opts -= op.extras
988
- reg = matchmap op
971
+ reg = if op.empty?
972
+ []
973
+ else
974
+ opts = op.uniq(opts)
975
+ matchmap op
976
+ end
989
977
  session_done op.target
990
978
  heads = []
991
979
  cur = nil
@@ -994,10 +982,10 @@ module Squared
994
982
  cur ||= line.delete_prefix!('* ')
995
983
  heads << line if matchany?(line, reg)
996
984
  end
997
- raise_error('head not found', hint: 'for-each-ref') unless cur
985
+ raise_error 'head not found', hint: 'for-each-ref' unless cur
998
986
  opts << 'ff-only' if opts.empty? && !option('ff-only', equals: '0')
999
- (heads.dup << cur).each_with_index do |branch, index|
1000
- next unless (index < heads.size && cur != branch) || index == heads.size
987
+ (heads.dup << cur).each_with_index do |branch, i|
988
+ next unless (i < heads.size && cur != branch) || i == heads.size
1001
989
 
1002
990
  git_spawn 'switch --quiet', force && '--force', shell_quote(branch)
1003
991
  pull(nil, opts, sync: false, hint: branch) if heads.include?(branch)
@@ -1011,8 +999,8 @@ module Squared
1011
999
  no: OPT_GIT[:no][:pull] + OPT_GIT[:no][:fetch][:pull], remote: remote, flag: flag, from: :pull)
1012
1000
  source(sync: sync, sub: if stdout?
1013
1001
  [
1014
- { pat: /^(.+)(\|\s+\d+\s+)([^-]*)(-+)(.*)$/, styles: color(:red), index: 4 },
1015
- { pat: /^(.+)(\|\s+\d+\s+)(\++)(.*)$/, styles: color(:green), index: 3 }
1002
+ opt_style(color(:red), /^(.+)(\|\s+\d+\s+)([^-]*)(-+)(.*)$/, 4),
1003
+ opt_style(color(:green), /^(.+)(\|\s+\d+\s+)(\++)(.*)$/, 3)
1016
1004
  ]
1017
1005
  end, hint: hint, **threadargs)
1018
1006
  end
@@ -1028,7 +1016,7 @@ module Squared
1028
1016
 
1029
1017
  op = OptionPartition.new(opts, OPT_GIT[:rebase], cmd, project: self, no: OPT_GIT[:no][:rebase])
1030
1018
  op << upstream
1031
- append_head op.shift
1019
+ append_head op.shift&.delete_prefix(':')
1032
1020
  op.clear(pass: false)
1033
1021
  when :onto
1034
1022
  return unless upstream
@@ -1040,13 +1028,13 @@ module Squared
1040
1028
  else
1041
1029
  unless gitpath('REBASE_HEAD').exist?
1042
1030
  puts log_message(Logger::INFO, name, 'no rebase in progress', hint: command) if stdout?
1043
- return
1031
+ exit 1
1044
1032
  end
1045
1033
  return unless VAL_GIT[:rebase][:send].include?(command)
1046
1034
 
1047
1035
  cmd << "--#{command}"
1048
1036
  end
1049
- source(sync: sync)
1037
+ source
1050
1038
  end
1051
1039
 
1052
1040
  def autostash(*, sync: invoked_sync?('autostash'), **)
@@ -1090,22 +1078,33 @@ module Squared
1090
1078
  append_hash opts
1091
1079
  cmd << '--quiet' unless verbose
1092
1080
  append_value(data[0], path, delim: true)
1093
- source(sync: sync, banner: sync && !quiet?, multiple: !sync || quiet?)
1081
+ source(banner: sync && !quiet?, multiple: !sync || quiet?)
1094
1082
  end
1095
1083
 
1096
1084
  def stash(flag = nil, opts = [], sync: invoked_sync?('stash', flag))
1097
1085
  if flag
1098
- if flag == :all
1086
+ case flag
1087
+ when :all
1099
1088
  opts << 'include-untracked'
1100
1089
  flag = :push
1090
+ when :staged
1091
+ opts << 'staged'
1092
+ flag = :push
1093
+ when :worktree
1094
+ opts << 'keep-index'
1095
+ flag = :push
1096
+ end
1097
+ unless (file = gitpath('logs/refs/stash')).exist? || flag == :push
1098
+ puts log_message(Logger::INFO, name, 'no stashes were found', hint: flag) if stdout?
1099
+ exit 1
1101
1100
  end
1102
1101
  cmd, opts = git_session('stash', flag, opts: opts)
1103
1102
  list = OPT_GIT[:stash][:common] + OPT_GIT[:stash].fetch(flag, [])
1104
1103
  if flag == :list
1105
- list += collect_hash OPT_GIT[:log]
1104
+ list.concat(collect_hash(OPT_GIT[:log]))
1106
1105
  no = collect_hash OPT_GIT[:no][:log]
1107
1106
  end
1108
- op = OptionPartition.new(opts, list, cmd, project: self, no: no, first: flag == :push ? matchpathspec : nil)
1107
+ op = OptionPartition.new(opts, list, cmd, project: self, no: no, first: (matchpathspec if flag == :push))
1109
1108
  case flag
1110
1109
  when :push
1111
1110
  op.append?('message', readline('Enter message', force: true), force: true) if op.remove(':')
@@ -1116,30 +1115,32 @@ module Squared
1116
1115
  if op.empty?
1117
1116
  values = [['Branch name', true]]
1118
1117
  else
1119
- op << op.shift
1118
+ op.add_first(prefix: ':')
1120
1119
  end
1121
1120
  end
1122
1121
  out = choice_index('Choose a stash', git_spawn('stash list', stdout: false),
1123
- values: values, column: /^[^@]+@\{(\d+)\}/)
1122
+ values: values, column: /^[^@]+@\{(\d+)}/, force: true)
1124
1123
  if values
1125
1124
  op.merge(out.reverse)
1126
1125
  else
1127
1126
  op << out
1128
1127
  end
1129
1128
  elsif !op.empty?
1130
- op << op.shift
1129
+ op.add_first(prefix: ':')
1131
1130
  elsif flag == :branch
1132
- raise_error 'no branch name'
1131
+ raise_error ArgumentError, 'no branch name'
1133
1132
  end
1134
1133
  op.clear
1135
1134
  when :clear
1136
- source(stdout: true) if confirm("Remove #{sub_style('all', styles: theme[:active])} stash entries?", 'N')
1135
+ n = sub_style(file.read.lines.size, styles: theme[:inline])
1136
+ s = sub_style(name, styles: theme[:active])
1137
+ source(stdout: true) if confirm("Remove #{n} stash entries from #{s}?", 'N')
1137
1138
  return
1138
1139
  when :list
1139
1140
  op.clear
1140
1141
  out, banner, from = source(io: true)
1141
1142
  print_item banner
1142
- list_result(write_lines(out), 'objects', banner: banner, from: from)
1143
+ list_result(write_lines(out), 'objects', from: from)
1143
1144
  return
1144
1145
  end
1145
1146
  else
@@ -1177,18 +1178,18 @@ module Squared
1177
1178
  g = color(:green)
1178
1179
  sub = if session_arg?('short')
1179
1180
  [
1180
- { pat: /^(.)([A-Z?!])(.+)$/, styles: r, index: 2 },
1181
- { pat: /^([A-Z?!])(.+)$/, styles: g },
1182
- { pat: /^(\?\?)(.+)$/, styles: r },
1183
- { pat: /^(## )((?~\.{3}))(\.{3})(.+)$/, styles: [nil, g, nil, r], index: -1 }
1181
+ opt_style(r, /^(.)([A-Z?!])(.+)$/, 2),
1182
+ opt_style(g, /^([A-Z?!])(.+)$/),
1183
+ opt_style(r, /^(\?\?)(.+)$/),
1184
+ opt_style([nil, g, nil, r], /^(## )((?~\.{3}))(\.{3})(.+)$/, -1)
1184
1185
  ]
1185
1186
  else
1186
- [{ pat: /^(\t+)([a-z]+: +.+)$/, styles: r, index: 2 }]
1187
+ opt_style(r, /^(\t+)([a-z]+: +.+)$/, 2)
1187
1188
  end
1188
1189
  end
1189
1190
  out, banner, from = source(io: true)
1190
1191
  ret = write_lines(out, banner: banner, sub: sub)
1191
- list_result(ret, 'files', action: 'modified', banner: banner, from: from)
1192
+ list_result(ret, 'files', from: from, action: 'modified')
1192
1193
  end
1193
1194
 
1194
1195
  def revbuild(flag = nil, opts = [], sync: nil, **kwargs)
@@ -1217,26 +1218,24 @@ module Squared
1217
1218
  op.clear(append: true)
1218
1219
  args = op.to_a
1219
1220
  else
1220
- args = []
1221
- option('untracked-files', prefix: 'git') { |val| args << basic_option('untracked-files', val) }
1222
- option('ignore-submodules', prefix: 'git') { |val| args << basic_option('ignore-submodules', val) }
1223
- option('ignored', prefix: 'git') { |val| args << basic_option('ignored', val) }
1221
+ args = [
1222
+ option('untracked-files', prefix: 'git') { |val| basic_option('untracked-files', val) },
1223
+ option('ignore-submodules', prefix: 'git') { |val| basic_option('ignore-submodules', val) },
1224
+ option('ignored', prefix: 'git') { |val| basic_option('ignored', val) }
1225
+ ].compact
1224
1226
  end
1225
1227
  if (cur = workspace.rev_entry(name)) && cur['revision'] == sha && !env('REVBUILD_FORCE')
1226
1228
  files = status_digest(*args, **kwargs)
1227
1229
  if cur['files'].size == files.size && cur['files'].find { |key, val| files[key] != val }.nil?
1230
+ workspace.rev_timeutc(name, 'build') unless (since = workspace.rev_timesince(name, 'build'))
1228
1231
  if stdout?
1229
- if (since = workspace.rev_timesince(name, 'build'))
1230
- puts log_message(Logger::INFO, name, 'no changes', subject: 'revbuild', hint: "#{since} ago")
1231
- else
1232
- workspace.rev_timeutc(name, 'build')
1233
- end
1232
+ puts log_message(Logger::INFO, name, 'no changes', subject: 'revbuild', hint: since && "#{since} ago")
1234
1233
  end
1235
1234
  return
1236
1235
  end
1237
1236
  end
1238
1237
  start = time_epoch
1239
- build(*@output, sync: sync, from: :'git:revbuild')
1238
+ build(@output, sync: sync, from: :'git:revbuild')
1240
1239
  rescue StandardError => e
1241
1240
  print_error(e, pass: true)
1242
1241
  else
@@ -1249,9 +1248,8 @@ module Squared
1249
1248
  cmd, opts = git_session('reset', opts: opts)
1250
1249
  case flag
1251
1250
  when :commit, :index
1252
- op = OptionPartition.new(opts, OPT_GIT[:reset] + VAL_GIT[:reset], cmd,
1253
- project: self, no: OPT_GIT[:no][:reset],
1254
- first: flag == :index ? matchpathspec : nil)
1251
+ op = OptionPartition.new(opts, OPT_GIT[:reset] + VAL_GIT[:reset] + OPT_GIT[:log][:diff_context], cmd,
1252
+ project: self, no: OPT_GIT[:no][:reset], first: (matchpathspec if flag == :index))
1255
1253
  if flag == :commit
1256
1254
  op.append(commit)
1257
1255
  .clear(pass: false)
@@ -1282,7 +1280,7 @@ module Squared
1282
1280
 
1283
1281
  def checkout(flag, opts = [], branch: nil, origin: nil, create: nil, commit: nil, detach: nil, merge: false)
1284
1282
  cmd, opts = git_session('checkout', opts: opts)
1285
- append_option 'force', 'f', 'merge'
1283
+ append_option 'f', 'force', 'merge'
1286
1284
  case flag
1287
1285
  when :branch
1288
1286
  cmd << '--detach' if detach == 'd' || option('detach')
@@ -1295,13 +1293,13 @@ module Squared
1295
1293
  cmd << '-m' if merge
1296
1294
  cmd << '--detach' << commit
1297
1295
  else
1298
- op = OptionPartition.new(opts, OPT_GIT[:checkout], cmd, project: self, no: OPT_GIT[:no][:checkout],
1299
- first: flag == :path ? matchpathspec : nil)
1296
+ list = OPT_GIT[:checkout] + OPT_GIT[:log][:diff_context]
1297
+ op = OptionPartition.new(opts, list, cmd, project: self, no: OPT_GIT[:no][:checkout],
1298
+ first: (matchpathspec if flag == :path))
1300
1299
  if flag == :path
1301
1300
  append_head
1302
1301
  append_pathspec(op.extras, pass: false)
1303
- print_success if success?(source)
1304
- return
1302
+ return success?(source)
1305
1303
  end
1306
1304
  op.append(commit)
1307
1305
  .clear(pass: false)
@@ -1318,7 +1316,7 @@ module Squared
1318
1316
  elsif !session_arg?('s', 'sign', 'u', 'local-user')
1319
1317
  cmd << '--annotate'
1320
1318
  end
1321
- cmd << '--force' if option('force', 'f')
1319
+ cmd << '--force' if option('f', 'force')
1322
1320
  if !commit && message && (sha = commithash(message))
1323
1321
  commit = sha
1324
1322
  message = nil
@@ -1334,12 +1332,15 @@ module Squared
1334
1332
  out, banner, from = source(io: true)
1335
1333
  print_item banner
1336
1334
  ret = write_lines(out, grep: op.extras)
1337
- list_result(ret, 'tags', grep: op.extras, banner: banner, from: from)
1335
+ list_result(ret, 'tags', from: from, grep: op.extras)
1338
1336
  return
1339
1337
  end
1340
- remote ||= option 'remote'
1341
- source
1342
- git_spawn('push', flag == :delete ? '-d' : nil, remote, *refs.map { |val| shell_quote(val) }) if remote
1338
+ remote ||= option('remote')
1339
+ source.tap do |ret|
1340
+ next unless ret && remote
1341
+
1342
+ git_spawn('push', ('-d' if flag == :delete), remote, *refs.map! { |val| shell_quote(val) })
1343
+ end
1343
1344
  end
1344
1345
 
1345
1346
  def log!(flag, opts = [], range: [], index: [])
@@ -1357,13 +1358,14 @@ module Squared
1357
1358
  append_pathspec op.extras
1358
1359
  source(exception: false)
1359
1360
  end
1360
- alias log_ log!
1361
1361
 
1362
1362
  def diff(flag, opts = [], refs: [], branch: nil, range: [], index: [])
1363
1363
  cmd, opts = git_session('diff', opts: opts)
1364
- op = OptionPartition.new(opts, collect_hash(OPT_GIT[:diff]) + OPT_GIT[:log][:diff], cmd,
1364
+ op = OptionPartition.new(opts,
1365
+ collect_hash(OPT_GIT[:diff]) + OPT_GIT[:log][:diff] + OPT_GIT[:log][:diff_context],
1366
+ cmd,
1365
1367
  project: self, no: OPT_GIT[:no][:log][:diff],
1366
- first: flag == :files ? nil : matchpathspec)
1368
+ first: (matchpathspec unless flag == :files))
1367
1369
  case flag
1368
1370
  when :files, :view, :between, :contain
1369
1371
  op.delete('--cached')
@@ -1371,7 +1373,16 @@ module Squared
1371
1373
  append_nocolor
1372
1374
  if flag == :files
1373
1375
  op << '--no-index'
1376
+ patch = refs.pop
1374
1377
  append_pathspec(refs, parent: true)
1378
+ if patch
1379
+ patch = basepath patch
1380
+ exit 1 if patch.exist? && !confirm_basic('Overwrite?', patch)
1381
+ op << '>' << shell_quote(patch)
1382
+ source(banner: false)
1383
+ puts patch.read if patch.exist? && (stdin? || verbose?)
1384
+ return
1385
+ end
1375
1386
  else
1376
1387
  op << '--merge-base' if option('merge-base')
1377
1388
  case flag
@@ -1384,7 +1395,7 @@ module Squared
1384
1395
  op.add_quote(branch) if branch
1385
1396
  if !index.empty?
1386
1397
  if op.arg?('cached')
1387
- raise_error("one commit only: #{index.join(', ')}", hint: 'cached') if index.size > 1
1398
+ raise_error "single commit: #{index.join(', ')}", hint: 'cached' unless index.size == 1
1388
1399
  op << index.first
1389
1400
  else
1390
1401
  op.merge(index)
@@ -1400,7 +1411,7 @@ module Squared
1400
1411
 
1401
1412
  def commit(flag, opts = [], refs: [], ref: nil, squash: nil, pick: nil, message: nil, pass: false)
1402
1413
  fixup = flag == :fixup
1403
- amend = !fixup && flag.to_s.start_with?('amend')
1414
+ amend = flag.match?(/^amend/) && !fixup
1404
1415
  unless flag == :add || pick == 'reword'
1405
1416
  pathspec = if flag == :all || ((fixup || amend) && refs.size == 1 && refs.first == '*')
1406
1417
  '--all'
@@ -1411,9 +1422,9 @@ module Squared
1411
1422
  end
1412
1423
  end
1413
1424
  if fixup
1414
- source git_session('commit', basic_option('fixup', "#{pick ? "#{pick}:" : ''}#{ref}"), pathspec)
1415
- source git_output('rebase --autosquash', squash) if squash.is_a?(String)
1416
- return
1425
+ ret = source(git_session('commit', basic_option('fixup', pick ? "#{pick}:#{ref}" : ref), pathspec))
1426
+ source git_output('rebase --autosquash', squash) if ret && squash.is_a?(String)
1427
+ return ret
1417
1428
  end
1418
1429
  message ||= messageopt
1419
1430
  if !message && !amend
@@ -1425,7 +1436,8 @@ module Squared
1425
1436
  origin = nil
1426
1437
  upstream = nil
1427
1438
  cmd, opts = git_session('add', opts: opts)
1428
- op = OptionPartition.new(opts, OPT_GIT[:add], cmd, project: self, first: matchpathspec)
1439
+ op = OptionPartition.new(opts, OPT_GIT[:add] + OPT_GIT[:log][:diff_context], cmd,
1440
+ project: self, no: OPT_GIT[:no][:add], first: matchpathspec)
1429
1441
  op << '--verbose' if verbose
1430
1442
  format = '%(if)%(HEAD)%(then)%(refname:short)...%(upstream:short)...%(upstream:track)%(end)'
1431
1443
  git_spawn 'fetch --no-tags --quiet'
@@ -1434,10 +1446,9 @@ module Squared
1434
1446
 
1435
1447
  branch, origin, hint = line.split('...')
1436
1448
  if hint && !hint.match?(/^\[(\D+0,\D+0)\]$/)
1437
- raise_error('work tree is not usable', hint: hint[1..-2])
1438
- elsif !origin || origin.empty?
1449
+ raise_error 'work tree is not usable', hint: hint[1..-2]
1450
+ elsif (!origin || origin.empty?) && !dryrun?
1439
1451
  return nil if pass
1440
- break if dryrun?
1441
1452
 
1442
1453
  unless (origin = option('upstream', prefix: 'git', ignore: false))
1443
1454
  if (origin = choice_refs('Choose an upstream', 'remotes', attempts: 1, force: false))
@@ -1446,6 +1457,7 @@ module Squared
1446
1457
  end
1447
1458
  origin = readline('Enter an upstream', force: true)
1448
1459
  end
1460
+ raise_error ArgumentError, 'missing remote name', hint: origin unless origin.include?('/')
1449
1461
  upstream = true
1450
1462
  end
1451
1463
  break
@@ -1456,14 +1468,13 @@ module Squared
1456
1468
  append_pathspec op.extras
1457
1469
  end
1458
1470
  co = git_session('commit', options: false)
1459
- pu = git_output 'push'
1460
- co << '--amend' if amend
1461
- pu << '--set-upstream' if upstream
1471
+ pu = git_output 'push', upstream && '--set-upstream'
1462
1472
  if dryrun?
1463
- op.adjoin('--dry-run')
1473
+ op.delete('--dry-run')
1464
1474
  co << '--dry-run'
1465
1475
  pu << '--dry-run'
1466
1476
  end
1477
+ co << '--amend' if amend
1467
1478
  if message
1468
1479
  append_message message
1469
1480
  elsif flag == :'amend-orig' || option('edit', equals: '0')
@@ -1471,6 +1482,7 @@ module Squared
1471
1482
  end
1472
1483
  pu << '--force-with-lease' if amend
1473
1484
  pu.merge(repotrack(origin, branch))
1485
+ puts if pass
1474
1486
  adding = git_spawn 'diff --name-only --no-color'
1475
1487
  source op
1476
1488
  cached = git_spawn 'diff --cached --name-only --no-color'
@@ -1480,10 +1492,13 @@ module Squared
1480
1492
  end
1481
1493
  source co
1482
1494
  source pu
1483
- elsif banner?
1484
- puts 'Nothing to commit'
1485
- elsif stdout?
1486
- puts log_message(Logger::INFO, name, 'nothing to commit', hint: flag)
1495
+ else
1496
+ if banner?
1497
+ puts 'Nothing to commit'
1498
+ elsif stdout?
1499
+ puts log_message(Logger::INFO, name, 'nothing to commit', hint: flag)
1500
+ end
1501
+ exit 1
1487
1502
  end
1488
1503
  end
1489
1504
 
@@ -1493,26 +1508,25 @@ module Squared
1493
1508
  case flag
1494
1509
  when :commit, :'no-commit'
1495
1510
  op = OptionPartition.new(opts, OPT_GIT[:merge], cmd, project: self, no: OPT_GIT[:no][:merge])
1496
- op << "--#{flag}"
1497
- op.delim
1511
+ op << "--#{flag}" << '--'
1498
1512
  if branch
1499
1513
  op << branch
1500
1514
  op.clear(pass: false)
1501
1515
  else
1502
- raise_error 'no branch/commit' if op.empty?
1516
+ raise_error ArgumentError, 'no branch/commit' if op.empty?
1503
1517
  append_commit(*op.extras)
1504
1518
  end
1505
1519
  else
1506
1520
  unless gitpath('MERGE_HEAD').exist?
1507
1521
  puts log_message(Logger::INFO, name, 'no merge in progress', hint: command) if stdout?
1508
- return
1522
+ exit 1
1509
1523
  end
1510
1524
  return unless VAL_GIT[:merge][:send].include?(command)
1511
1525
 
1512
1526
  cmd << "--#{command}"
1513
1527
  display = command == 'abort'
1514
1528
  end
1515
- print_success if success?(source, display)
1529
+ success?(source, display)
1516
1530
  end
1517
1531
 
1518
1532
  def branch(flag = nil, opts = [], refs: [], ref: nil, target: nil, remote: nil)
@@ -1530,11 +1544,11 @@ module Squared
1530
1544
  '--track'
1531
1545
  end
1532
1546
  end
1533
- cmd << '--force' if option('force', 'f')
1547
+ cmd << '--force' if option('f', 'force')
1534
1548
  cmd << shell_quote(target)
1535
1549
  cmd << shell_quote(ref) if ref
1536
1550
  when :track
1537
- raise_error('invalid upstream', hint: ref) unless ref.include?('/')
1551
+ raise_error 'invalid upstream', hint: ref unless ref.include?('/')
1538
1552
  if ref.delete_prefix!('^')
1539
1553
  cmd << '--unset-upstream' << shell_quote(ref)
1540
1554
  remote = false
@@ -1545,7 +1559,7 @@ module Squared
1545
1559
  end
1546
1560
  when :delete
1547
1561
  remote&.each do |val|
1548
- source git_output('push --delete', *val.split('/', 2).map { |s| shell_quote(s) })
1562
+ source git_output('push --delete', *val.split('/', 2).map! { |s| shell_quote(s) })
1549
1563
  end
1550
1564
  force, list = refs.partition { |val| val.start_with?(/[~^]/) }
1551
1565
  force.each do |val|
@@ -1559,13 +1573,13 @@ module Squared
1559
1573
  remote = nil
1560
1574
  when :move, :copy
1561
1575
  s = +"-#{flag.to_s[0]}"
1562
- s.upcase! if option('force', 'f')
1576
+ s.upcase! if option('f', 'force')
1563
1577
  cmd << s
1564
1578
  refs.compact.each { |val| cmd << shell_quote(val) }
1565
1579
  stdout = true
1566
1580
  when :current
1567
1581
  cmd << '--show-current'
1568
- source(banner: verbosetype > 1, stdout: true)
1582
+ source(banner: verbose?, stdout: true)
1569
1583
  return
1570
1584
  when :list
1571
1585
  op = OptionPartition.new(opts, OPT_GIT[:branch], cmd << '--list',
@@ -1574,10 +1588,10 @@ module Squared
1574
1588
  out, banner, from = source(io: true)
1575
1589
  print_item banner
1576
1590
  ret = write_lines(out, sub: [
1577
- { pat: /^(\*\s+)(\S+)(.*)$/, styles: color(:green), index: 2 },
1578
- { pat: %r{^(\s*)(remotes/\S+)(.*)$}, styles: color(:red), index: 2 }
1591
+ opt_style(color(:green), /^(\*\s+)(\S+)(.*)$/, 2),
1592
+ opt_style(color(:red), %r{^(\s*)(remotes/\S+)(.*)$}, 2)
1579
1593
  ])
1580
- list_result(ret, 'branches', banner: banner, from: from)
1594
+ list_result(ret, 'branches', from: from)
1581
1595
  return
1582
1596
  else
1583
1597
  if (head = git_spawn('rev-parse --abbrev-ref HEAD').chomp).empty?
@@ -1600,7 +1614,7 @@ module Squared
1600
1614
  r3 = write.call($4, $5)
1601
1615
  end
1602
1616
  r1 = nil if r1 == "origin/#{data[0]}"
1603
- [" Branch: #{a + (r1 ? " (#{r1})" : '')}", r2, r3, " Commit: #{b}", "Message: #{r[2]}"]
1617
+ [" Branch: #{a.subhint(r1)}", r2, r3, " Commit: #{b}", "Message: #{r[2]}"]
1604
1618
  .compact
1605
1619
  .join("\n")
1606
1620
  end
@@ -1609,21 +1623,17 @@ module Squared
1609
1623
  print_error(name, 'no ref found', subject: 'branch', hint: 'head', pass: true) if ret == 0
1610
1624
  return
1611
1625
  end
1612
- return unless success?(source(stdout: stdout))
1626
+ return unless success?(source(stdout: stdout), !ref && flag == :create) && !ref && remote && target
1613
1627
 
1614
- if !ref
1615
- print_success if flag == :create
1616
- elsif remote && target
1617
- source git_output('push -u', shell_quote(ref.split('/', 2).first), shell_quote(target))
1618
- end
1628
+ source git_output('push -u', shell_quote(ref.split('/', 2).first), shell_quote(target))
1619
1629
  end
1620
1630
 
1621
1631
  def switch(flag, opts = [], branch: nil, commit: nil, track: nil)
1622
1632
  cmd, opts = git_session('switch', opts: opts)
1623
- cmd << '--force' if option('force', 'f')
1633
+ cmd << '--force' if option('f', 'force')
1624
1634
  if flag == :branch
1625
- op = OptionPartition.new(opts, OPT_GIT[:switch], cmd, project: self, no: OPT_GIT[:no][:switch])
1626
- op.add_quote(branch)
1635
+ OptionPartition.new(opts, OPT_GIT[:switch], cmd, project: self, no: OPT_GIT[:no][:switch])
1636
+ .add_quote(branch)
1627
1637
  else
1628
1638
  case flag
1629
1639
  when :create
@@ -1653,16 +1663,18 @@ module Squared
1653
1663
  op.add_quote(branch, '--', path, url)
1654
1664
  else
1655
1665
  op.adjoin(flag)
1656
- op << '--recursive' if option('recursive', 'r')
1666
+ op << '--recursive' if option('r', 'recursive')
1657
1667
  op.splice(path: true)
1658
1668
  end
1659
- print_success if success?(source, flag == :branch)
1669
+ source.tap { |ret| success?(ret, flag == :branch) }
1660
1670
  end
1661
1671
 
1662
1672
  def restore(flag, opts = [], commit: nil, files: nil)
1663
1673
  cmd, opts = git_session('restore', shell_option(flag, commit, escape: false, force: false), opts: opts)
1664
- op = OptionPartition.new(opts, OPT_GIT[:restore], cmd, project: self, no: OPT_GIT[:no][:restore],
1665
- first: matchpathspec)
1674
+ op = OptionPartition.new(opts,
1675
+ OPT_GIT[:restore] + OPT_GIT[:log][:diff_context],
1676
+ cmd,
1677
+ project: self, no: OPT_GIT[:no][:restore], first: matchpathspec)
1666
1678
  append_pathspec(op.extras + (files || []), pass: false)
1667
1679
  source(sync: false, stderr: true)
1668
1680
  end
@@ -1688,9 +1700,9 @@ module Squared
1688
1700
  else
1689
1701
  opts << format if format
1690
1702
  end
1691
- op = OptionPartition.new(opts, OPT_GIT[:show] + OPT_GIT[:diff][:show] + OPT_GIT[:log][:diff], cmd,
1692
- project: self,
1693
- no: OPT_GIT[:no][:show] + collect_hash(OPT_GIT[:no][:log], pass: [:base]))
1703
+ list = OPT_GIT[:show] + OPT_GIT[:diff][:show] + OPT_GIT[:log][:diff] + OPT_GIT[:log][:diff_context]
1704
+ op = OptionPartition.new(opts, list, cmd, project: self, pass: [:base],
1705
+ no: OPT_GIT[:no][:show] + collect_hash(OPT_GIT[:no][:log]))
1694
1706
  op.append(delim: true)
1695
1707
  source(exception: false, banner: flag != :oneline)
1696
1708
  end
@@ -1699,7 +1711,7 @@ module Squared
1699
1711
  cmd, opts = git_session('rev-parse', opts: opts)
1700
1712
  case flag
1701
1713
  when :commit
1702
- cmd << (size.to_i.zero? ? '--verify' : basic_option('short', [size.to_i, 5].max))
1714
+ cmd << ((n = size.to_i) > 0 ? basic_option('short', [n, 5].max) : '--verify')
1703
1715
  append_commit(ref, head: true)
1704
1716
  when :branch
1705
1717
  cmd << '--abbrev-ref'
@@ -1709,85 +1721,100 @@ module Squared
1709
1721
  cmd << '--sq-quote'
1710
1722
  args = true
1711
1723
  end
1712
- op = OptionPartition.new(opts, OPT_GIT[:rev_parse], cmd, project: self, no: OPT_GIT[:no][:rev_parse],
1713
- args: args)
1714
- op.append(escape: args)
1724
+ OptionPartition.new(opts, OPT_GIT[:rev_parse], cmd, project: self, no: OPT_GIT[:no][:rev_parse], args: args)
1725
+ .append(escape: args)
1715
1726
  end
1716
- source(banner: verbosetype > 1)
1727
+ source(banner: verbose?)
1717
1728
  end
1718
1729
 
1719
1730
  def ls_remote(flag, opts = [], remote: nil)
1720
- cmd, opts = git_session('ls-remote', '--refs', opts: opts)
1731
+ cmd, opts = git_session('ls-remote --refs', opts: opts)
1721
1732
  cmd << "--#{flag}" unless flag == :remote
1722
1733
  op = OptionPartition.new(opts, OPT_GIT[:ls_remote], cmd, project: self)
1723
1734
  op.add_quote(remote) if remote
1724
1735
  out, banner, from = source(io: true)
1725
1736
  print_item banner
1726
1737
  ret = write_lines(out, grep: op.extras, prefix: "refs/#{flag}/")
1727
- list_result(ret, flag.to_s, grep: op.extras, banner: banner, from: from)
1738
+ list_result(ret, flag.to_s, from: from, grep: op.extras)
1728
1739
  end
1729
1740
 
1730
1741
  def ls_files(flag, opts = [])
1731
- cmd, opts = git_session('ls-files', "--#{flag}", opts: opts)
1742
+ cmd, opts = git_session("ls-files --#{flag}", opts: opts)
1732
1743
  op = OptionPartition.new(opts, OPT_GIT[:ls_files], cmd, project: self)
1733
1744
  op.splice(path: true, pattern: true)
1734
1745
  out, banner, from = source(io: true)
1735
1746
  print_item banner
1736
1747
  ret = write_lines(out, grep: op.extras)
1737
- list_result(ret, 'files', grep: op.extras, banner: banner, from: from)
1748
+ list_result(ret, 'files', from: from, grep: op.extras)
1738
1749
  end
1739
1750
 
1740
1751
  def git(flag, opts = [])
1741
1752
  cmd, opts = git_session(flag, opts: opts)
1742
- op = OptionPartition.new(opts, OPT_GIT[:git].fetch(flag, []) + OPT_GIT.fetch(flag, []), cmd,
1743
- project: self, no: OPT_GIT[:no][flag], first: case flag
1744
- when :blame, :revert
1745
- nil
1746
- else matchpathspec
1747
- end)
1753
+ list = OPT_GIT[:git].fetch(flag, []) + OPT_GIT.fetch(flag, [])
1754
+ list.concat(OPT_GIT[:log][:diff_context]) if flag == :add
1755
+ op = OptionPartition.new(opts, list, cmd, project: self, no: OPT_GIT[:no][flag], single: /\A\d+\z/,
1756
+ first: case flag
1757
+ when :blame, :revert then nil
1758
+ else matchpathspec
1759
+ end)
1748
1760
  case flag
1749
1761
  when :blame
1750
- raise_error 'no file found' unless (n = op.index { |s| basepath(s).file? })
1751
- op.delim << shell_quote(basepath(op.delete_at(n)))
1752
- op.clear
1762
+ raise_error Errno::ENOENT, 'no file target' unless (n = op.index { |s| basepath(s).file? })
1763
+ op.append(basepath(op.remove_at(n)), delim: true)
1764
+ .clear
1753
1765
  when :revert
1754
1766
  if VAL_GIT[:rebase][:send].any? { |val| op.arg?(val) }
1755
1767
  op.clear
1756
1768
  elsif op.empty?
1757
- raise_error 'no commit given'
1769
+ raise_error 'no commit target'
1758
1770
  else
1759
1771
  append_commit(*op.extras)
1760
1772
  end
1761
1773
  when :add
1762
1774
  if flag == :add && !op.arg?('pathspec-from-file')
1763
- grep, list = op.partition { |val| OptionPartition.pattern?(val) }
1764
- unless grep.empty? && !list.empty?
1775
+ grep, pathspec = op.partition { |val| OptionPartition.pattern?(val) }
1776
+ unless grep.empty? && !pathspec.empty?
1765
1777
  grep.map! { |val| Regexp.new(val[1..-2]) }
1766
- files = []
1767
- status_data.each do |a, b|
1768
- next if b.strip.empty? || (!grep.empty? && grep.none? { |pat| pat.match?(a) })
1778
+ files = [].tap do |out|
1779
+ status_data.each do |a, b|
1780
+ next if b.strip.empty? || (!grep.empty? && grep.none? { |pat| pat.match?(a) })
1769
1781
 
1770
- files << "#{sub_style(b, styles: color(:red))} #{a}"
1782
+ out << "#{sub_style(b, styles: color(:red))} #{a}"
1783
+ end
1771
1784
  end
1772
1785
  unless files.empty?
1773
- files = choice_index('Select files', files, multiple: true, trim: /^\S+\s/,
1774
- accept: [['Add?', false, true]])
1786
+ files = choice_index('Select files', files, multiple: true, force: true, trim: /^\S+\s/,
1787
+ accept: [accept_y('Add?')])
1775
1788
  end
1776
- op.swap(list + files)
1789
+ op.swap(pathspec + files)
1777
1790
  end
1778
1791
  end
1779
1792
  return source(git_session('status -s'), banner: false) unless append_pathspec(op.extras)
1780
-
1781
- print_success if success?(source, flag == :add && !op.arg?('verbose'))
1782
- return
1793
+ return success?(source) if flag == :add && !op.arg?('verbose')
1783
1794
  when :mv
1784
1795
  refs = projectmap op.extras
1785
1796
  raise_error 'no source/destination' unless refs.size > 1
1786
1797
  op.merge(refs)
1787
1798
  when :rm, :clean
1788
1799
  append_pathspec(op.extras, expect: flag == :rm)
1800
+ when :grep
1801
+ op.each do |val|
1802
+ if op.include?('--')
1803
+ op.add_path(val)
1804
+ elsif op.exist?(val, glob: true)
1805
+ op << '--'
1806
+ op.add_path(val)
1807
+ else
1808
+ op.add_quote(val)
1809
+ end
1810
+ end
1811
+ end
1812
+ case flag
1813
+ when :revert, :mv, :rm
1814
+ source(sync: false, stderr: true)
1815
+ else
1816
+ source
1789
1817
  end
1790
- source(sync: false, stderr: true)
1791
1818
  end
1792
1819
 
1793
1820
  def clone?
@@ -1806,10 +1833,6 @@ module Squared
1806
1833
 
1807
1834
  def source(cmd = @session, exception: true, io: false, sync: true, stdout: false, stderr: false, banner: true,
1808
1835
  multiple: false, hint: nil, from: nil, send: :system, **kwargs)
1809
- unless cmd
1810
- print_error('no git session started', subject: project, hint: from, pass: true)
1811
- return
1812
- end
1813
1836
  cmd = cmd.target if cmd.is_a?(OptionPartition)
1814
1837
  if io && banner == false
1815
1838
  from = nil
@@ -1819,20 +1842,15 @@ module Squared
1819
1842
  banner = nil unless banner? && !multiple
1820
1843
  args = true
1821
1844
  end
1822
- if cmd.respond_to?(:done)
1823
- if from.nil? && (from = cmd.drop(1).find { |val| val.match?(/\A[a-z]{1,2}[a-z-]*\z/) })
1824
- from = :"git:#{from}"
1825
- end
1826
- banner &&= cmd.temp { |val| val.start_with?(/--(?:work-tree|git-dir)/) }
1845
+ if from.nil? && (from = cmd.drop(1).find { |val| val.match?(/\A[a-z]{1,2}[a-z-]*\z/) })
1846
+ from = :"git:#{from}"
1827
1847
  end
1848
+ banner &&= cmd.temp { |val| val.start_with?(/--(?:work-tree|git-dir)/) } if cmd.respond_to?(:temp)
1828
1849
  from = nil if from == false
1829
1850
  end
1830
1851
  cmd = session_done cmd
1831
1852
  log&.info cmd
1832
- banner = if banner
1833
- banner = (banner.is_a?(String) ? banner : cmd).gsub(File.join(path, ''), '')
1834
- format_banner(hint ? "#{banner} (#{hint})" : banner)
1835
- end
1853
+ banner = format_banner(banner.is_a?(String) ? banner : cmd, hint: hint, strip: true) if banner
1836
1854
  on :first, from
1837
1855
  begin
1838
1856
  if io
@@ -1841,11 +1859,13 @@ module Squared
1841
1859
  return args ? [IO.popen(cmd), banner || '', from] : IO.popen(cmd)
1842
1860
  elsif stdin? ? sync : stdout
1843
1861
  print_item banner unless multiple
1844
- ret = `#{cmd}`.chomp
1845
- if !ret.empty?
1862
+ ret = `#{cmd}`
1863
+ raise(ret.chomp.empty? ? $?.to_s : ret) unless $?.success?
1864
+
1865
+ if ret.empty?
1866
+ success?(nil, !banner.nil?)
1867
+ else
1846
1868
  puts ret
1847
- elsif success?(!banner.nil?)
1848
- print_success
1849
1869
  end
1850
1870
  elsif !kwargs[:sub] && (sync || (!exception && !stderr))
1851
1871
  print_item banner unless multiple
@@ -1857,7 +1877,7 @@ module Squared
1857
1877
  n = write_lines(out, banner: banner, pass: true, **kwargs)
1858
1878
  if n == 0
1859
1879
  n = write_lines(err, banner: banner)
1860
- print_success if success?(n == 0 && !banner.nil?)
1880
+ success?(nil, n == 0 && !banner.nil?)
1861
1881
  else
1862
1882
  write_lines(err, loglevel: Logger::DEBUG)
1863
1883
  end
@@ -1876,17 +1896,14 @@ module Squared
1876
1896
  end
1877
1897
 
1878
1898
  def write_lines(data, grep: [], prefix: nil, sub: nil, banner: nil, loglevel: nil, pass: false, first: false)
1879
- grep = grep.empty? ? nil : matchmap(grep, prefix)
1880
- sub = nil if stdin?
1899
+ grep = (matchmap(grep, prefix) unless grep.empty?)
1900
+ sub = (as_a sub unless stdin?)
1881
1901
  ret = 0
1882
1902
  out = []
1883
1903
  data.each do |line|
1884
1904
  next if grep&.none? { |pat| pat.match?(line) }
1905
+ next if block_given? && !(line = yield line)
1885
1906
 
1886
- if block_given?
1887
- line = yield line
1888
- next unless line
1889
- end
1890
1907
  if loglevel
1891
1908
  log&.add loglevel, line
1892
1909
  else
@@ -1900,24 +1917,18 @@ module Squared
1900
1917
  ret += 1
1901
1918
  break if first
1902
1919
  end
1903
- if banner && (ret > 0 || (!pass && !first))
1904
- if banner.empty?
1905
- print_item(*out)
1906
- else
1907
- print_item(banner, *out)
1908
- end
1909
- end
1920
+ print_item banner, out if banner && (ret > 0 || (!pass && !first))
1910
1921
  ret
1911
1922
  end
1912
1923
 
1913
- def list_result(size, type, action: 'found', grep: [], banner: nil, from: nil)
1924
+ def list_result(size, type, grep: [], action: 'found', from: nil)
1914
1925
  if size == 0
1915
1926
  puts empty_status("No #{type} were #{action}", 'grep', grep.join(', '))
1916
- elsif stdout? && (banner.nil? || (banner.is_a?(String) && !banner.empty?))
1927
+ elsif stdout?
1917
1928
  styles = theme.fetch(:banner, []).reject { |s| s.to_s.end_with?('!') }
1918
1929
  styles << :bold if styles.size <= 1
1919
1930
  puts print_footer("#{size} #{size == 1 ? type.sub(/(?:(?<!l)e)?s\z/, '') : type}",
1920
- sub: [{ pat: /^(\d+)(.+)$/, styles: styles }])
1931
+ sub: opt_style(styles, /^(\d+)(.+)$/))
1921
1932
  end
1922
1933
  on :last, from
1923
1934
  end
@@ -1933,7 +1944,7 @@ module Squared
1933
1944
  end
1934
1945
  args = []
1935
1946
  args << quote_option('sort', sort) if sort
1936
- args << basic_option('count', env('GIT_COUNT', ARG[:CHOICE])) if count
1947
+ args << shell_option('count', env('GIT_COUNT', ARG[:CHOICE])) if count
1937
1948
  choice_index(msg, foreachref(type, *args, format: format), trim: trim, **kwargs)
1938
1949
  end
1939
1950
 
@@ -1966,17 +1977,17 @@ module Squared
1966
1977
  end
1967
1978
 
1968
1979
  def status_data(*args)
1969
- ret = []
1970
- git_spawn('status -z -uall', *args).split("\x0").each do |line|
1971
- next unless line =~ /^(.)(.) (.+)$/
1980
+ [].tap do |ret|
1981
+ git_spawn('status -z -uall', *args).split("\x0").each do |line|
1982
+ next unless line =~ /^(.)(.) (.+)$/
1972
1983
 
1973
- ret << [$3, $2, $1]
1984
+ ret << [$3, $2, $1]
1985
+ end
1974
1986
  end
1975
- ret
1976
1987
  end
1977
1988
 
1978
1989
  def append_pull(opts, list, target: @session, flag: nil, no: nil, remote: nil, from: nil)
1979
- target << '--force' if option('force', 'f', target: target)
1990
+ target << '--force' if option('f', 'force', target: target)
1980
1991
  append_submodules(target: target, from: from)
1981
1992
  return if !remote && opts.empty?
1982
1993
 
@@ -1988,6 +1999,7 @@ module Squared
1988
1999
  when 'rebase'
1989
2000
  op << basic_option($1, $2) if VAL_GIT[:rebase][:value].include?($2)
1990
2001
  when 'shallow-since'
2002
+ require 'date'
1991
2003
  op.append?($1) { Date.parse($2).strftime('%F %T') }
1992
2004
  when 'recurse-submodules'
1993
2005
  op.append?($1, $2, type: :basic)
@@ -2002,7 +2014,7 @@ module Squared
2002
2014
  end
2003
2015
  op << '--verbose' if (flag || from == :fetch) && stdout? && !op.arg?('quiet')
2004
2016
  if remote
2005
- op.append(remote)
2017
+ op.append(remote, delim: true)
2006
2018
  if (val = option('refspec', target: target, strict: true))
2007
2019
  op.append(*split_escape(val))
2008
2020
  else
@@ -2047,9 +2059,7 @@ module Squared
2047
2059
 
2048
2060
  def append_message(val = nil, target: @session)
2049
2061
  val = messageopt if val.to_s.empty?
2050
- return unless val
2051
-
2052
- target << quote_option('message', val)
2062
+ target << quote_option('message', val) if val
2053
2063
  end
2054
2064
 
2055
2065
  def append_head(val = nil, target: @session)
@@ -2067,9 +2077,7 @@ module Squared
2067
2077
  when '1', 'true'
2068
2078
  target << '--recurse-submodules'
2069
2079
  else
2070
- projectmap(split_escape(val)).each do |path|
2071
- target << basic_option('recurse-submodules', path)
2072
- end
2080
+ projectmap(split_escape(val)).each { |path| target << basic_option('recurse-submodules', path) }
2073
2081
  end
2074
2082
  else
2075
2083
  target << case val
@@ -2116,28 +2124,24 @@ module Squared
2116
2124
  stdout: stdout, banner: banner, **kwargs)
2117
2125
  end
2118
2126
 
2119
- def dryrun?(*, target: @session, **)
2120
- return false unless target
2121
-
2122
- target.include?('--dry-run') || !option('dry-run', target: target).nil?
2127
+ def dryrun?(*, target: @session, prefix: target&.first)
2128
+ Array(target).include?('--dry-run') || !option('dry-run', target: target, prefix: prefix).nil?
2123
2129
  end
2124
2130
 
2125
2131
  def quiet?(*, target: @session, **)
2126
2132
  return false unless target
2127
2133
 
2128
- target.include?('--quiet') || (target.include?('-q') && stripext(target.first) == 'git')
2134
+ target.include?('--quiet') || (target.include?('-q') && target.first.stripext == 'git')
2129
2135
  end
2130
2136
 
2131
2137
  def gitpath(*args)
2132
- path.join('.git', *args)
2138
+ basepath('.git', *args)
2133
2139
  end
2134
2140
 
2135
2141
  def repotrack(origin, branch, quote: true)
2136
- unless origin && branch && (i = origin.index('/'))
2137
- raise_error(ArgumentError, "missing #{origin ? 'branch' : 'remote'} name", hint: origin)
2138
- end
2139
- branch = "#{branch}:#{origin[(i + 1)..-1]}" unless origin.end_with?("/#{branch}")
2140
- [origin[0..(i - 1)], branch].tap { |ret| ret.map! { |val| shell_quote(val) } if quote }
2142
+ i = origin.index('/')
2143
+ branch = "#{branch}:#{origin[i.succ..-1]}" unless origin.end_with?("/#{branch}")
2144
+ [origin[0..i.pred], branch].tap { |ret| ret.map! { |val| shell_quote(val) } if quote }
2141
2145
  end
2142
2146
 
2143
2147
  def commithash(val)