squared 0.1.4 → 0.2.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.
@@ -2,11 +2,93 @@
2
2
 
3
3
  module Squared
4
4
  module Workspace
5
+ module Git
6
+ GIT_REPO = {}
7
+ GIT_PROTO = %r{^(?:https?|ssh|git|file)://}i.freeze
8
+ private_constant :GIT_REPO, :GIT_PROTO
9
+
10
+ def git(name, uri = nil, base: nil, repo: [], options: {})
11
+ data = {}
12
+ check = ->(proj) { proj.is_a?(Project::Git) && git_clone?(proj.path) }
13
+ if uri.is_a?(Array)
14
+ base = name
15
+ uri.each { |val| repo << proj if (proj = @project[val.to_s]) && check.(proj) }
16
+ elsif uri
17
+ data[name.to_s] = uri
18
+ elsif name.is_a?(Enumerable)
19
+ data = name.to_h
20
+ elsif name.is_a?(String) && name =~ GIT_PROTO
21
+ base = name
22
+ @project.each_value { |proj| repo << proj if !proj.parent && check.(proj) }
23
+ else
24
+ warn log_message(Logger::WARN, name, subject: 'git', hint: 'invalid') if warning
25
+ return self
26
+ end
27
+ if base
28
+ base = base =~ GIT_PROTO ? "#{base.chomp('/')}/" : @root.join(base)
29
+ repo.each do |target|
30
+ if target.is_a?(Project::Git)
31
+ data[target.localname] = target.project
32
+ else
33
+ data[target.to_s] = nil
34
+ end
35
+ end
36
+ end
37
+ data.each do |key, val|
38
+ if val.is_a?(Hash)
39
+ uri = val.fetch(:uri, '')
40
+ opts = val.fetch(:options, {})
41
+ else
42
+ uri = val.is_a?(String) ? val : key.to_s
43
+ opts = options
44
+ end
45
+ unless uri.match?(GIT_PROTO) || Pathname.new(uri).absolute?
46
+ if uri.start_with?('.')
47
+ uri = @root.join(uri)
48
+ elsif base
49
+ uri = base.is_a?(Pathname) ? base.join(uri) : base + uri
50
+ else
51
+ next
52
+ end
53
+ end
54
+ key = task_name(key)
55
+ (GIT_REPO[main] ||= {})[key] = [uri.to_s, opts]
56
+ (@kind[key] ||= []) << Project::Git
57
+ end
58
+ self
59
+ end
60
+
61
+ def git_repo(name)
62
+ (ret = GIT_REPO[main]) && ret[name]
63
+ end
64
+
65
+ def git_clone?(path, name = nil)
66
+ return false if name && !git_repo(name)
67
+
68
+ !path.exist? || path.empty?
69
+ end
70
+ end
71
+ Application.include Git
72
+
5
73
  module Project
6
74
  class Git < Base
7
- OPT_FETCH = %w[all tags prune append atomic unshallow dry-run depth=i deepen=i since=d jobs=i].freeze
8
- OPT_PULL = %w[ff ff-only edit autostash squash verify <fetch].freeze
9
- private_constant :OPT_PULL, :OPT_FETCH
75
+ FOR_FETCH = %w[atomic multiple negotiate-only prefetch progress prune-tags refetch recurse-submodules=s].freeze
76
+ NO_FETCH = %w[all ipv4 ipv6 recurse-submodules show-forced-updates tags].freeze
77
+ FNO_FETCH = %w[auto-gc auto-maintenance write-commit-graph write-fetch-head].freeze
78
+ OPT_FETCH = (FOR_FETCH + NO_FETCH + FNO_FETCH +
79
+ %w[append dry-run force keep prune quiet set-upstream unshallow update-shallow verbose
80
+ deepen=i depth=i jobs=i negotiation-tip=s refmap=s server-option=s shallow-exclude=s
81
+ shallow-since=d upload-pack=s]).freeze
82
+ NO_PULL = %w[allow-unrelated-histories autostash commit edit ff signoff squash stat verify verify-signatures
83
+ gpg-sign=? log=? rebase=?].freeze
84
+ OPT_PULL = (NO_PULL + %w[ff-only signoff=s strategy=s strategy-option=s]).freeze
85
+ OPT_REBASE = %w[continue skip abort quit].freeze
86
+ VAL_REBASE = %w[true false merges interactive].freeze
87
+ NO_BRANCH = %w[abbrev=i color=? column=? track=?].freeze
88
+ OPT_BRANCH = (NO_BRANCH + %w[all create-reflog ignore-case quiet remotes verbose
89
+ contains=s format=s merged=s no-contains=s no-merged=s points-at=s sort=s]).freeze
90
+ private_constant :FOR_FETCH, :NO_FETCH, :FNO_FETCH, :OPT_FETCH, :NO_PULL, :OPT_PULL, :OPT_REBASE, :VAL_REBASE,
91
+ :NO_BRANCH, :OPT_BRANCH
10
92
 
11
93
  class << self
12
94
  include Rake::DSL
@@ -17,17 +99,17 @@ module Squared
17
99
  namespace(name = ws.task_name('git')) do
18
100
  all = ws.task_join(name, 'all')
19
101
 
20
- desc Common::Format.message(*"#{all}[git?=rebase|stash,depend?]".split(':'))
21
- task 'all', [:git, :depend] do |_, args|
22
- cmd = case args.git
23
- when 'rebase'
24
- [ws.task_sync('rebase')]
25
- when 'stash'
102
+ ws.format_desc(all, %w[stash|rebase depend])
103
+ task 'all' do |_, args|
104
+ opts = args.to_a
105
+ cmd = if opts.include?('stash')
26
106
  [ws.task_sync('stash'), ws.task_sync('pull')]
107
+ elsif opts.include?('rebase')
108
+ [ws.task_sync('rebase')]
27
109
  else
28
110
  [ws.task_sync('pull')]
29
111
  end
30
- cmd << ws.task_sync('depend') if args.depend && !ws.series[:depend].empty?
112
+ cmd << ws.task_sync('depend') if opts.include?('depend') && !ws.series[:depend].empty?
31
113
  cmd << ws.task_sync('build')
32
114
  Common::Utils.task_invoke(*cmd, **ws.invokeargs)
33
115
  end
@@ -37,11 +119,11 @@ module Squared
37
119
  end
38
120
 
39
121
  def tasks
40
- %i[pull rebase fetch stash status].freeze
122
+ %i[pull rebase fetch clone stash status].freeze
41
123
  end
42
124
 
43
125
  def batchargs
44
- [ref, { 'pull-s': %i[stash pull], 'rebase-s': %i[stash rebase] }]
126
+ [ref, { 'pull+s': %i[stash pull], 'rebase+s': %i[stash rebase] }]
45
127
  end
46
128
 
47
129
  def config?(val)
@@ -52,19 +134,21 @@ module Squared
52
134
  end
53
135
 
54
136
  @@tasks[ref] = {
55
- checkout: %i[branch detach force merge].freeze,
56
- commit: %i[add amend amend-orig all no-all].freeze,
57
- diff: %i[head cached branch files].freeze,
58
- fetch: %i[all submodules unshallow].freeze,
59
- files: %i[cached modified deleted others].freeze,
60
- pull: %i[head rebase no-rebase commit no-commit submodules].freeze,
61
- stash: %i[push pop apply list clear].freeze,
62
- refs: %i[heads tags].freeze,
63
- reset: %i[head soft mixed hard merge keep submodules].freeze,
64
- restore: %i[worktree staged overlay].freeze,
65
- rev: %i[commit branch].freeze,
66
- show: %i[oneline pretty format].freeze,
67
- tag: %i[add delete list merged].freeze
137
+ 'checkout' => %i[branch track detach force merge].freeze,
138
+ 'commit' => %i[add amend amend-orig all no-all].freeze,
139
+ 'diff' => %i[head cached branch files between contain].freeze,
140
+ 'pull' => %i[origin remote].freeze,
141
+ 'fetch' => %i[origin remote].freeze,
142
+ 'stash' => %i[push pop apply list clear].freeze,
143
+ 'rebase' => %i[onto root send].freeze,
144
+ 'branch' => %i[create set delete move copy list edit current].freeze,
145
+ 'refs' => %i[heads tags].freeze,
146
+ 'files' => %i[cached modified deleted others].freeze,
147
+ 'reset' => %i[head patch mode].freeze,
148
+ 'restore' => %i[source worktree staged overlay].freeze,
149
+ 'rev' => %i[commit branch].freeze,
150
+ 'show' => %i[format oneline].freeze,
151
+ 'tag' => %i[add delete list merged].freeze
68
152
  }.freeze
69
153
 
70
154
  def initialize(*, **)
@@ -76,114 +160,131 @@ module Squared
76
160
  Git.ref
77
161
  end
78
162
 
79
- def populate(*)
163
+ def populate(*, **)
80
164
  super
81
165
  return unless ref?(Git.ref)
82
166
 
83
167
  namespace name do
84
168
  @@tasks[Git.ref].each do |action, flags|
169
+ next if @pass.include?(action)
170
+
85
171
  namespace action do
86
172
  flags.each do |flag|
87
173
  case action
88
- when :pull, :fetch
89
- desc format_desc(action, flag, action == :pull ? OPT_PULL : OPT_FETCH)
90
- task flag, [:opts] do |_, args|
91
- __send__(action, flag, opts: args.to_a)
174
+ when 'pull', 'fetch'
175
+ if flag == :remote
176
+ if @@task_desc
177
+ format_desc(action, flag, ['refspec=s'] + (action == 'pull' ? OPT_PULL : OPT_FETCH),
178
+ before: 'remote')
179
+ end
180
+ task flag, [:remote] do |_, args|
181
+ remote = param_guard(action, flag, args: args, key: :remote)
182
+ __send__(action, flag, remote: remote, opts: args.extras)
183
+ end
184
+ else
185
+ format_desc(action, flag, action == 'pull' ? OPT_PULL : OPT_FETCH)
186
+ task flag do |_, args|
187
+ __send__(action, flag, opts: args.to_a)
188
+ end
92
189
  end
93
- when :commit, :restore
94
- if flag == :all
95
- desc format_desc(action, flag, 'message?')
190
+ when 'commit', 'restore'
191
+ case flag
192
+ when :all
193
+ format_desc action, flag, 'message?'
96
194
  task flag, [:message] do |_, args|
97
195
  commit(flag, message: args.fetch(:message, nil))
98
196
  end
197
+ when :source
198
+ format_desc action, flag, 'tree,pathspec*'
199
+ task flag, [:tree] do |_, args|
200
+ tree = param_guard(action, flag, args: args, key: :tree)
201
+ restore(flag, args.extras, tree: tree)
202
+ end
99
203
  else
100
- desc format_desc(action, flag, 'pathspec+')
101
- task flag, [:pathspec] do |_, args|
102
- files = guard_params(action, flag, args: args.to_a)
103
- __send__(action, flag, files)
204
+ format_desc action, flag, 'pathspec+'
205
+ task flag do |_, args|
206
+ files = param_guard(action, flag, args: args.to_a)
207
+ __send__ action, flag, files
104
208
  end
105
209
  end
106
- when :tag
210
+ when 'tag'
107
211
  case flag
108
212
  when :list
109
- desc format_desc(action, flag, 'pattern*')
110
- task flag, [:pattern] do |_, args|
111
- tag(flag, args.to_a)
213
+ format_desc action, flag, 'pattern*'
214
+ task flag do |_, args|
215
+ tag flag, args.to_a
112
216
  end
113
217
  when :merged
114
- desc format_desc(action, flag, 'commit,pattern*')
115
- task flag, [:commit, :pattern] do |_, args|
116
- commit = guard_params(action, flag, args: args, key: :commit)
117
- tag(flag, args.to_a.drop(1), commit: commit)
218
+ format_desc action, flag, 'commit,pattern*'
219
+ task flag, [:commit] do |_, args|
220
+ commit = param_guard(action, flag, args: args, key: :commit)
221
+ tag(flag, args.extras, commit: commit)
118
222
  end
119
223
  when :delete
120
- desc format_desc(action, flag, 'name+')
121
- task flag, [:name] do |_, args|
122
- name = guard_params(action, flag, args: args.to_a)
123
- tag(flag, name)
224
+ format_desc action, flag, 'name+'
225
+ task flag do |_, args|
226
+ name = param_guard(action, flag, args: args.to_a)
227
+ tag flag, name
124
228
  end
125
- else
126
- desc format_desc(action, flag, 'name,message?,commit?')
229
+ when :add
230
+ format_desc action, flag, 'name,message?,commit?'
127
231
  task flag, [:name, :message, :commit] do |_, args|
128
- name = guard_params(action, flag, args: args, key: :name)
232
+ name = param_guard(action, flag, args: args, key: :name)
129
233
  tag(flag, [name], message: args.message, commit: args.commit)
130
234
  end
131
235
  end
132
- when :stash
236
+ when 'stash'
133
237
  if flag == :push
134
- desc format_desc(action, flag, 'pathspec*')
135
- task flag, [:pathspec] do |_, args|
136
- stash(flag, args.to_a)
238
+ format_desc action, flag, 'pathspec*'
239
+ task flag do |_, args|
240
+ stash flag, args.to_a
137
241
  end
138
242
  else
139
- desc format_desc(action, flag, 'commit?')
243
+ format_desc action, flag, 'commit?'
140
244
  task flag, [:commit] do |_, args|
141
245
  stash(flag, commit: args.commit)
142
246
  end
143
247
  end
144
- when :rev
145
- desc format_desc(action, flag, "ref?=HEAD#{flag == :commit ? ',size?' : ''}")
146
- task flag, [:ref, :size] do |_, args|
147
- rev(flag, ref: args.ref, size: args.size)
148
- end
149
- when :refs, :files
150
- desc format_desc(action, flag, 'grep?=pattern')
151
- task flag, [:grep] do |_, args|
152
- __send__(action, flag, grep: args.fetch(:grep, nil))
153
- end
154
- when :diff
248
+ when 'diff'
155
249
  case flag
156
250
  when :head
157
- desc format_desc(action, flag, 'index?=0,pathspec*')
158
- task flag, [:pathspec] do |_, args|
251
+ format_desc action, flag, 'index?=0,pathspec*'
252
+ task flag do |_, args|
159
253
  files = args.to_a
160
254
  diff(flag, files, index: /\A\d+\z/.match?(files[0]) && !option('index') ? files.shift.to_i : 0)
161
255
  end
162
256
  when :cached
163
- desc format_desc(action, flag, 'pathspec*')
164
- task flag, [:pathspec] do |_, args|
165
- diff(flag, args.to_a)
257
+ format_desc action, flag, 'pathspec*'
258
+ task flag do |_, args|
259
+ diff flag, args.to_a
166
260
  end
167
261
  when :branch
168
- desc format_desc(action, flag, 'name,pathspec*')
262
+ format_desc action, flag, 'name,pathspec*'
169
263
  task flag, [:name] do |_, args|
170
- branch = guard_params(action, flag, args: args, key: :name)
171
- diff(flag, args.to_a.drop(1), branch: branch)
264
+ branch = param_guard(action, flag, args: args, key: :name)
265
+ diff(flag, args.extras, branch: branch)
172
266
  end
173
267
  when :files
174
- desc format_desc(action, flag, 'path1,path2')
268
+ format_desc action, flag, 'path1,path2'
175
269
  task flag, [:path1, :path2] do |_, args|
176
- path1 = guard_params(action, flag, args: args, key: :path1)
177
- path2 = guard_params(action, flag, args: args, key: :path2)
178
- diff(flag, [path1, path2])
270
+ path1 = param_guard(action, flag, args: args, key: :path1)
271
+ path2 = param_guard(action, flag, args: args, key: :path2)
272
+ diff flag, [path1, path2]
273
+ end
274
+ else
275
+ format_desc action, flag, 'commit1,commit2,pathspec*'
276
+ task flag, [:commit1, :commit2] do |_, args|
277
+ commit1 = param_guard(action, flag, args: args, key: :commit1)
278
+ commit2 = param_guard(action, flag, args: args, key: :commit2)
279
+ diff(flag, args.extras, range: [commit1, commit2])
179
280
  end
180
281
  end
181
- when :checkout
282
+ when 'checkout'
182
283
  case flag
183
284
  when :branch
184
- desc format_desc(action, flag, 'name,create?=b|B,commit?,detach?=d')
285
+ format_desc action, flag, 'name,create?=[bB],commit?,detach?=d'
185
286
  task flag, [:name, :create, :commit, :detach] do |_, args|
186
- branch = guard_params(action, flag, args: args, key: :name)
287
+ branch = param_guard(action, flag, args: args, key: :name)
187
288
  create = args.create
188
289
  if args.commit == 'd'
189
290
  detach = 'd'
@@ -200,45 +301,131 @@ module Squared
200
301
  detach = args.detach
201
302
  commit = args.commit
202
303
  end
203
- guard_params(action, flag, args: { create: create }, key: :create, pat: /\Ab\z/i) if create
304
+ param_guard(action, flag, args: { create: create }, key: :create, pat: /\Ab\z/i) if create
204
305
  checkout(flag, branch: branch, create: create, commit: commit, detach: detach)
205
306
  end
307
+ when :track
308
+ format_desc action, flag, 'origin,(^)name?'
309
+ task flag, [:origin, :name] do |_, args|
310
+ origin = param_guard(action, flag, args: args, key: :origin)
311
+ checkout(flag, branch: args.name, origin: origin)
312
+ end
206
313
  when :detach
207
- desc format_desc(action, flag, 'branch/commit?')
314
+ format_desc action, flag, 'branch/commit?'
208
315
  task flag, [:commit] do |_, args|
209
316
  checkout(flag, commit: args.commit)
210
317
  end
211
318
  else
212
- desc format_desc(action, flag, 'pathspec*')
213
- task flag, [:pathspec] do |_, args|
214
- checkout(flag, args.to_a)
319
+ format_desc action, flag, 'pathspec*'
320
+ task flag do |_, args|
321
+ checkout flag, args.to_a
215
322
  end
216
323
  end
217
- when :reset
218
- if flag == :head
219
- desc format_desc(action, flag, 'ref,pathspec+')
220
- task flag, [:ref, :pathspec] do |_, args|
221
- files = guard_params(action, flag, args: args.to_a.drop(1))
222
- reset(flag, files, ref: args.ref)
324
+ when 'branch'
325
+ case flag
326
+ when :create
327
+ format_desc action, flag, 'name,ref?=HEAD'
328
+ task flag, [:name, :ref] do |_, args|
329
+ name = param_guard(action, flag, args: args, key: :name)
330
+ branch(flag, target: name, ref: args.ref)
331
+ end
332
+ when :set
333
+ format_desc(action, flag, '(!)upstream,name?')
334
+ task flag, [:upstream, :name] do |_, args|
335
+ upstream = param_guard(action, flag, args: args, key: :upstream)
336
+ branch(flag, target: args.name, ref: upstream)
337
+ end
338
+ when :delete
339
+ format_desc action, flag, '(^~)name+'
340
+ task flag, [:name] do |_, args|
341
+ name = param_guard(action, flag, args: args.to_a)
342
+ branch flag, name
343
+ end
344
+ when :edit
345
+ format_desc action, flag, 'name?'
346
+ task flag, [:name] do |_, args|
347
+ branch(flag, target: args.name)
348
+ end
349
+ when :list
350
+ format_desc(action, flag, OPT_BRANCH, before: 'pattern*')
351
+ task flag do |_, args|
352
+ branch(flag, opts: args.to_a)
353
+ end
354
+ when :current
355
+ format_desc action, flag
356
+ task flag do
357
+ branch flag
223
358
  end
224
359
  else
225
- desc format_desc(action, flag, 'ref?=HEAD')
226
- task flag, [:ref] do |_, args|
227
- reset(flag, ref: args.ref)
360
+ format_desc action, flag, 'newbranch,oldbranch?'
361
+ task flag, [:newbranch, :oldbranch] do |_, args|
362
+ newbranch = param_guard(action, flag, args: args, key: :newbranch)
363
+ branch flag, [args.oldbranch, newbranch]
228
364
  end
229
365
  end
230
- when :show
231
- if flag == :oneline
232
- desc format_desc(action, flag, 'object*')
233
- task flag, [:object] do |_, args|
234
- show(args.to_a, pretty: 'oneline', abbrev: true)
366
+ when 'reset'
367
+ case flag
368
+ when :mode
369
+ if @@task_desc
370
+ format_desc(action, flag, %w[soft mixed hard merge keep {no-}submodules], after: 'ref?=HEAD',
371
+ arg: false)
372
+ end
373
+ task flag, [:mode, :ref] do |_, args|
374
+ mode = param_guard(action, flag, args: args, key: :mode)
375
+ reset(flag, mode: mode, ref: args.ref)
235
376
  end
236
377
  else
237
- desc format_desc(action, flag, 'format,object*')
238
- task flag, [:format, :object] do |_, args|
239
- show(args.to_a.drop(1), "#{flag}": args.format)
378
+ format_desc action, flag, 'ref,pathspec+'
379
+ task flag, [:ref] do |_, args|
380
+ files = param_guard(action, flag, args: args.extras)
381
+ reset(flag, files, ref: args.ref)
382
+ end
383
+ end
384
+ when 'show'
385
+ case flag
386
+ when :oneline
387
+ format_desc action, flag, 'object*'
388
+ task flag do |_, args|
389
+ show(args.to_a, format: 'oneline', opts: { 'abbrev-commit': true })
390
+ end
391
+ when :format
392
+ format_desc action, flag, 'format?,object*'
393
+ task flag, [:format] do |_, args|
394
+ show(args.extras, format: args.format)
395
+ end
396
+ end
397
+ when 'rebase'
398
+ case flag
399
+ when :onto
400
+ format_desc action, flag, 'commit,upstream,branch?=HEAD'
401
+ task flag, [:commit, :upstream, :branch] do |_, args|
402
+ commit = param_guard(action, flag, args: args, key: :commit)
403
+ upstream = param_guard(action, flag, args: args, key: :upstream)
404
+ rebase(flag, commit: commit, upstream: upstream, branch: args.branch)
405
+ end
406
+ when :root
407
+ format_desc action, flag, 'branch,onto?'
408
+ task flag, [:branch, :onto] do |_, args|
409
+ branch = param_guard(action, flag, args: args, key: :branch)
410
+ rebase(flag, commit: args.onto, branch: branch)
411
+ end
412
+ when :send
413
+ format_desc(action, flag, OPT_REBASE, arg: nil)
414
+ task flag, [:command] do |_, args|
415
+ command = param_guard(action, flag, args: args, key: :command)
416
+ rebase(flag, command: command)
240
417
  end
241
418
  end
419
+ when 'rev'
420
+ format_desc(action, flag, "ref?=HEAD#{flag == :commit ? ',size?' : ''}")
421
+ task flag, [:ref, :size] do |_, args|
422
+ rev_parse(flag, ref: args.ref, size: args.size)
423
+ end
424
+ when 'refs', 'files'
425
+ format_desc action, flag, 'grep?=pattern'
426
+ task flag, [:grep] do |_, args|
427
+ __send__(action == 'refs' ? :ls_remote : :ls_files, flag, grep: args.fetch(:grep, nil))
428
+ end
242
429
  end
243
430
  end
244
431
  end
@@ -246,40 +433,97 @@ module Squared
246
433
  end
247
434
  end
248
435
 
249
- def pull(flag = nil, sync: invoked_sync?('pull', flag), opts: [])
436
+ def generate(keys = [], **)
437
+ keys << :clone if clone?
438
+ super
439
+ end
440
+
441
+ def pull(flag = nil, sync: invoked_sync?('pull', flag), remote: nil, opts: [])
250
442
  cmd = git_session 'pull'
251
- if flag == :'no-rebase' || option('rebase', equals: '0')
252
- cmd << '--no-rebase'
253
- elsif flag == :rebase || option('rebase')
254
- cmd << '--rebase'
255
- end
256
- if flag == :'no-commit' || option('commit', equals: '0')
257
- cmd << '--no-commit'
258
- elsif flag == :commit || option('commit')
259
- cmd << '--commit'
260
- end
261
- append_pull opts, OPT_PULL, flag
262
- sub = if verbose
263
- [
264
- { pat: /^(.+)(\|\s+\d+\s+)([^-]*)(-+)(.*)$/, styles: :red, index: 4 },
265
- { pat: /^(.+)(\|\s+\d+\s+)(\++)(-*)(.*)$/, styles: :green, index: 3 }
266
- ]
267
- end
268
- source(sync: sync, sub: sub, **threadargs)
443
+ if (val = option('rebase', ignore: false))
444
+ cmd << case val
445
+ when '0'
446
+ '--no-rebase'
447
+ else
448
+ VAL_REBASE.include?(val) ? basic_option('rebase', val) : '--rebase'
449
+ end
450
+ end
451
+ append_pull(opts, OPT_PULL + OPT_FETCH - (FOR_FETCH + FNO_FETCH), NO_PULL + NO_FETCH, remote: remote,
452
+ flag: flag)
453
+ source(sync: sync, sub: if verbose
454
+ [
455
+ { pat: /^(.+)(\|\s+\d+\s+)([^-]*)(-+)(.*)$/, styles: :red, index: 4 },
456
+ { pat: /^(.+)(\|\s+\d+\s+)(\++)(-*)(.*)$/, styles: :green, index: 3 }
457
+ ]
458
+ end, **threadargs)
269
459
  end
270
460
 
271
- def rebase
272
- pull(:rebase, sync: invoked_sync?('rebase'))
461
+ def rebase(flag = nil, sync: invoked_sync?('rebase', flag), command: nil, commit: nil, upstream: nil,
462
+ branch: nil)
463
+ return pull(:rebase, sync: sync) unless flag
464
+
465
+ cmd = git_session 'rebase'
466
+ cmd << '--interactive' unless flag == :send || !option('interactive', 'i')
467
+ case flag
468
+ when :onto
469
+ return unless upstream
470
+
471
+ cmd << shell_option('onto', commit)
472
+ cmd << shell_escape(upstream)
473
+ append_head branch
474
+ when :root
475
+ return unless branch
476
+
477
+ cmd << shell_option('onto', commit) if commit
478
+ cmd << '--root' << shell_escape(branch)
479
+ when :send
480
+ return unless OPT_REBASE.include?(command)
481
+
482
+ cmd << "--#{command}"
483
+ else
484
+ return
485
+ end
486
+ source
273
487
  end
274
488
 
275
- def fetch(flag = nil, opts: [])
489
+ def fetch(flag = nil, sync: invoked_sync?('fetch', flag), remote: nil, opts: [])
276
490
  cmd = git_session 'fetch'
277
- cmd << '--all' if flag == :all || option('all')
278
- append_pull opts, OPT_FETCH, flag
279
- source(sync: invoked_sync?('fetch', flag), **threadargs)
491
+ cmd << '--all' if !remote && !opts.include?('multiple') && option('all')
492
+ cmd << '--verbose' if verbose && !opts.include?('quiet')
493
+ append_pull(opts, OPT_FETCH, NO_FETCH + FNO_FETCH, remote: remote, flag: flag)
494
+ source(sync: sync, **threadargs)
495
+ end
496
+
497
+ def clone(*, sync: invoked_sync?('clone'), **)
498
+ return unless clone? && (data = workspace.git_repo(name))
499
+
500
+ cmd = git_session('clone', worktree: false)
501
+ opts = data[1].dup
502
+ if (val = option('depth', ignore: false))
503
+ if (n = val.to_i) > 0
504
+ opts[:depth] = n
505
+ else
506
+ opts.delete(:depth)
507
+ end
508
+ end
509
+ opts[:origin] = val if (val = option('origin', ignore: false))
510
+ opts[:branch] = val if (val = option('branch', strict: true))
511
+ opts[:local] = val != '0' if (val = option('local', strict: true))
512
+ opts.delete(:'recurse-submodules') || opts.delete(:'no-recurse-submodules') if append_submodules(:clone)
513
+ append_hash opts
514
+ if cmd.include?('--quiet') || cmd.include?('-q')
515
+ quiet = true
516
+ elsif verbose
517
+ quiet = false
518
+ else
519
+ cmd << '--quiet'
520
+ quiet = true
521
+ end
522
+ append_value([data[0], path], delim: true, escape: false)
523
+ source(banner: sync && !quiet, multiple: !sync || quiet)
280
524
  end
281
525
 
282
- def stash(flag = nil, files = [], commit: nil)
526
+ def stash(flag = nil, files = [], sync: invoked_sync?('stash', flag), commit: nil)
283
527
  cmd = git_session 'stash', flag || 'push'
284
528
  case flag
285
529
  when :apply, :pop
@@ -290,13 +534,13 @@ module Squared
290
534
  append_message option('message', 'm', ignore: false)
291
535
  append_pathspec files
292
536
  end
293
- source(sync: invoked_sync?('stash', flag), **threadargs)
537
+ source(sync: sync, **threadargs)
294
538
  end
295
539
 
296
- def status
540
+ def status(*, sync: invoked_sync?('status'), **)
297
541
  cmd = git_session 'status', option('long') ? '--long' : '--short'
298
542
  if (val = option('ignore-submodules', ignore: false))
299
- cmd << shell_option('ignore-submodules', case val
543
+ cmd << basic_option('ignore-submodules', case val
300
544
  when '0', 'none'
301
545
  'none'
302
546
  when '1', 'untracked'
@@ -305,11 +549,11 @@ module Squared
305
549
  'dirty'
306
550
  else
307
551
  'all'
308
- end, escape: false)
552
+ end)
309
553
  end
310
554
  append_pathspec
311
- out, banner = source(io: true)
312
- if invoked_sync?('status')
555
+ out, banner, from = source(io: true)
556
+ if sync
313
557
  print_item banner
314
558
  banner = nil
315
559
  end
@@ -322,43 +566,58 @@ module Squared
322
566
  styles: [nil, :green, nil, :red], index: -1 }
323
567
  ]
324
568
  end)
325
- list_result(ret, 'files', action: 'modified')
569
+ list_result(ret, 'files', from: from, action: 'modified')
326
570
  end
327
571
 
328
572
  def reset(flag, files = [], ref: nil)
329
573
  cmd = git_session 'reset'
330
- if flag == :head
331
- append_commit ref
332
- append_pathspec files
333
- else
334
- case flag
335
- when :hard, :soft, :merge, :keep
336
- cmd << "--#{flag}"
337
- when :submodules
338
- append_submodules flag
339
- else
574
+ case flag
575
+ when :mode
576
+ case mode
577
+ when 'mixed'
340
578
  cmd << '--mixed'
579
+ cmd << '-N' if option('n')
341
580
  cmd << '--no-refresh' if option('refresh', equals: '0')
581
+ when 'soft', 'hard', 'merge', 'keep'
582
+ cmd << "--#{mode}"
583
+ when 'submodules'
584
+ cmd << '--recurse-submodules'
585
+ when 'no-submodules'
586
+ cmd << '--no-recurse-submodules'
587
+ else
588
+ return
342
589
  end
343
- append_commit ref
590
+ when :patch
591
+ cmd << '--patch'
344
592
  end
593
+ append_commit ref
594
+ append_pathspec files unless files.empty?
345
595
  source
346
596
  end
347
597
 
348
- def checkout(flag, files = [], branch: nil, create: nil, commit: nil, detach: nil)
598
+ def checkout(flag, files = [], branch: nil, origin: nil, create: nil, commit: nil, detach: nil)
349
599
  cmd = git_session 'checkout'
350
600
  case flag
351
601
  when :branch
352
602
  cmd << '--detach' if detach == 'd' || option('detach')
353
- if create
354
- cmd << "-#{create}" << branch
355
- if (val = option('start-point'))
356
- cmd << val
357
- end
358
- else
359
- cmd << branch
603
+ if (val = option('track'))
604
+ cmd << shell_option('track', val)
360
605
  end
606
+ cmd << if create
607
+ shell_option(create, branch)
608
+ else
609
+ branch
610
+ end
361
611
  cmd << commit
612
+ when :track
613
+ if branch
614
+ if branch.start_with?('^')
615
+ opt = 'B'
616
+ branch = branch[1..-1]
617
+ end
618
+ cmd << shell_option(opt || 'b', branch)
619
+ end
620
+ cmd << '--track' << origin
362
621
  when :detach
363
622
  cmd << "--#{flag}" << commit
364
623
  else
@@ -380,7 +639,11 @@ module Squared
380
639
  out = cmd.to_s
381
640
  cmd << '--annotate' if %w[-s --sign -u --local-user].none? { |val| out.include?(" #{val}") }
382
641
  end
383
- append_message message
642
+ if !commit && message && (hash = commithash(message))
643
+ commit = hash
644
+ else
645
+ append_message message
646
+ end
384
647
  append_value(refs, escape: false)
385
648
  append_head commit
386
649
  when :delete, :list, :merged
@@ -391,44 +654,25 @@ module Squared
391
654
  source
392
655
  end
393
656
 
394
- def rev(flag, ref: nil, size: nil)
395
- git_session 'rev-parse', if flag == :branch
396
- '--abbrev-ref'
397
- else
398
- (n = size.to_i) > 0 ? shell_option('short', [n, 5].max, escape: false) : '--verify'
399
- end
400
- append_commit ref
401
- source
402
- end
403
-
404
- def refs(flag, grep: nil)
405
- git_session 'ls-remote', "--#{flag}", '--refs'
406
- out, banner = source(io: true)
407
- print_item banner
408
- ret = write_lines(out, grep: grep)
409
- list_result(ret, flag.to_s, grep: grep)
410
- end
411
-
412
- def files(flag, grep: nil)
413
- git_session 'ls-files', "--#{flag}"
414
- out, banner = source(io: true)
415
- print_item banner
416
- ret = write_lines(out, grep: grep)
417
- list_result(ret, 'files', grep: grep)
418
- end
419
-
420
- def diff(flag, files = [], branch: nil, index: 0)
657
+ def diff(flag, files = [], branch: nil, index: 0, range: [])
421
658
  cmd = git_session 'diff'
422
- unless flag == :files
423
- if /\A#\h{5,40}\#\z/.match?(sha = files.first)
424
- sha = sha[1..-2]
425
- files.shift
426
- else
427
- sha = nil
659
+ sha = []
660
+ case flag
661
+ when :files, :between, :contain
662
+ cmd.delete('--cached')
663
+ else
664
+ items = files.dup
665
+ files.clear
666
+ items.each do |val|
667
+ if (hash = commithash(val))
668
+ sha << hash
669
+ else
670
+ files << val
671
+ end
428
672
  end
429
673
  end
430
674
  if (val = option('unified')).to_i > 0
431
- cmd << shell_option('unified', val, escape: false)
675
+ cmd << basic_option('unified', val)
432
676
  end
433
677
  append_nocolor
434
678
  case flag
@@ -439,14 +683,23 @@ module Squared
439
683
  cmd << branch
440
684
  when :files
441
685
  cmd << '--no-index'
686
+ when :between, :contain
687
+ cmd << shell_quote(range.join(flag == :between ? '..' : '...'))
442
688
  else
443
689
  if (val = option('index')) || index > 0
444
690
  cmd << "HEAD~#{val || index}"
445
- elsif sha && option('merge-base')
691
+ elsif !sha.empty? && option('merge-base')
446
692
  cmd << '--merge-base'
447
693
  end
448
694
  end
449
- cmd << sha
695
+ unless sha.empty?
696
+ if cmd.include?('--cached')
697
+ raise_error('diff', sha.join(', '), hint: 'one commit') if sha.size > 1
698
+ cmd << sha.first
699
+ else
700
+ cmd.merge(sha)
701
+ end
702
+ end
450
703
  append_pathspec(files, parent: flag == :files)
451
704
  source(exception: cmd.include?('--exit-code'))
452
705
  end
@@ -467,17 +720,17 @@ module Squared
467
720
  end
468
721
  origin = nil
469
722
  branch = nil
470
- upstream = false
471
- source('git fetch --no-tags --quiet', io: true, banner: false)
472
- source('git branch -vv --list', io: true, banner: false).first.each do |val|
473
- next unless (data = /\A\*\s(\S+)\s+(\h+)(?:\s\[(.+?)(?=\]\s)\])?\s/.match(val))
723
+ upstream = nil
724
+ source(git_output('fetch', '--no-tags', '--quiet'), io: true, banner: false)
725
+ source(git_output('branch', '-vv', '--list'), io: true, banner: false).first.each do |val|
726
+ next unless (data = /^\*\s(\S+)\s+(\h+)(?:\s\[(.+?)(?=\]\s)\])?\s/.match(val))
474
727
 
475
728
  branch = data[1]
476
729
  sha = data[2]
477
730
  if !data[3]
478
731
  unless (origin = option('repository', prefix: 'git', ignore: false))
479
- out = source('git log -n1 --format=%h%d', io: true, stdout: true, banner: false).first
480
- if (data = /\A#{sha} \(HEAD -> #{Regexp.escape(branch)}, (.+?)\)\z/.match(out))
732
+ out = source(git_output('log', '-n1', '--format=%h%d'), io: true, stdout: true, banner: false).first
733
+ if (data = /^#{sha} \(HEAD -> #{Regexp.escape(branch)}, (.+?)\)$/.match(out))
481
734
  split_escape(data[1]).each do |val|
482
735
  next unless val.end_with?("/#{branch}")
483
736
 
@@ -486,15 +739,14 @@ module Squared
486
739
  end
487
740
  end
488
741
  end
489
- upstream = !origin.nil?
490
- elsif (data = Regexp.new("\\A(.+)/#{Regexp.escape(branch)}\\z").match(data[3]))
742
+ upstream = true if origin
743
+ elsif (data = Regexp.new("^(.+)/#{Regexp.escape(branch)}$").match(data[3]))
491
744
  origin = data[1]
492
745
  end
493
746
  break
494
747
  end
495
748
  raise_error('commit', 'work tree is not usable') unless origin && branch
496
- cmd = git_session 'commit'
497
- cmd << '--dry-run' if option('dry-run')
749
+ cmd = git_session 'commit', option('dry-run') && '--dry-run'
498
750
  if amend
499
751
  cmd << '--amend'
500
752
  else
@@ -505,10 +757,9 @@ module Squared
505
757
  elsif flag == :'amend-orig' || option('no-edit')
506
758
  cmd << '--no-edit'
507
759
  end
508
- a = ['git add --verbose']
509
- b = ['git push']
510
- b << '--set-upstream' if upstream
511
- if dry_run?
760
+ a = git_output 'add', '--verbose'
761
+ b = git_output 'push', upstream && '--set-upstream'
762
+ if dryrun?
512
763
  a << '--dry-run'
513
764
  b << '--dry-run'
514
765
  end
@@ -516,50 +767,178 @@ module Squared
516
767
  b << '--force' if amend
517
768
  b << origin << branch
518
769
  puts if pass
519
- source a.join(' ')
770
+ source a
520
771
  source cmd
521
- source b.join(' ')
772
+ source b
773
+ end
774
+
775
+ def branch(flag, names = [], target: nil, ref: nil, opts: [])
776
+ cmd = git_session 'branch'
777
+ stdout = false
778
+ case flag
779
+ when :create
780
+ if (arg = option('track', ignore: false))
781
+ cmd << case arg
782
+ when '0'
783
+ '--no-track'
784
+ when 'direct', 'inherit'
785
+ basic_option('track', arg)
786
+ else
787
+ '--track'
788
+ end
789
+ end
790
+ cmd << '--force' if option('force')
791
+ when :set
792
+ if ref.start_with?('!')
793
+ cmd << '--unset-upstream' << shell_escape(arg[1..-1])
794
+ target = nil
795
+ stdout = true
796
+ else
797
+ cmd << quote_option('set-upstream-to', ref)
798
+ end
799
+ ref = nil
800
+ when :delete
801
+ force, list = names.partition { |val| val =~ /^[\^~]/ }
802
+ force.each do |val|
803
+ dr = val[0..2]
804
+ d = dr.include?('^') ? '-D' : '-d'
805
+ r = dr.include?('~') ? '-r' : nil
806
+ source git_output('branch', d, r, shell_quote(val.sub(/^[\^~]+/, '')))
807
+ end
808
+ return if list.empty?
809
+
810
+ cmd << '-d'
811
+ list.each { |val| cmd << shell_quote(val) }
812
+ when :move, :copy
813
+ flag = "-#{flag.to_s[0]}"
814
+ cmd << (option('force') ? flag.upcase : flag)
815
+ names.compact.each { |val| cmd << shell_quote(val) }
816
+ stdout = true
817
+ when :edit
818
+ cmd << '--edit-description'
819
+ when :current
820
+ cmd << '--show-current'
821
+ else
822
+ opts, pat = option_partition(opts, OPT_BRANCH, no: NO_BRANCH)
823
+ grep = []
824
+ opts.each do |opt|
825
+ if opt =~ /^(v+)$/
826
+ cmd << "-#{$1}"
827
+ elsif opt =~ pat
828
+ case $1
829
+ when 'column', 'format'
830
+ cmd << quote_option($1, $2)
831
+ when 'abbrev'
832
+ cmd << basic_option($1, $2) if $2.to_i > 0
833
+ else
834
+ cmd << shell_option($1, $2)
835
+ end
836
+ else
837
+ grep << opt
838
+ end
839
+ end
840
+ cmd << '--list'
841
+ grep.each { |val| cmd << shell_quote(val) }
842
+ out, banner, from = source(io: true)
843
+ print_item banner
844
+ ret = write_lines(out, sub: [
845
+ { pat: /^(\*\s+)(\S+)(\s*)$/, styles: :green, index: 2 },
846
+ { pat: %r{^(\s*)(remotes/\S+)(.*)$}, styles: :red, index: 2 }
847
+ ])
848
+ list_result(ret, 'branches', from: from)
849
+ return
850
+ end
851
+ cmd << shell_escape(target) if target
852
+ cmd << shell_escape(ref) if ref
853
+ source(stdout: stdout)
522
854
  end
523
855
 
524
- def restore(flag, files)
525
- git_session 'restore', "--#{flag}"
856
+ def restore(flag, files, tree: nil)
857
+ source = flag == :source
858
+ cmd = git_session 'restore', source && files.empty? ? '--patch' : nil, shell_option(flag, tree)
859
+ append_option %w[staged worktree]
526
860
  append_first %w[ours theirs]
527
- append_pathspec(files, expect: true)
528
- source(stdout: true)
861
+ append_pathspec(files, expect: !cmd.include?('--patch'))
862
+ source(stdout: !source)
529
863
  end
530
864
 
531
- def show(objs, pretty: nil, format: nil, abbrev: nil)
865
+ def show(objs, format: nil, opts: nil)
532
866
  cmd = git_session 'show'
533
- if (flag = pretty || format)
534
- cmd << case (val = flag.downcase)
867
+ if format
868
+ cmd << case (val = format.downcase)
535
869
  when 'oneline', 'short', 'medium', 'full', 'fuller', 'reference', 'email', 'raw'
536
- shell_option('format', val, escape: false)
870
+ basic_option('format', val)
537
871
  else
538
- shell_option('pretty', flag, escape: false, quote: true)
872
+ quote_option('pretty', format)
539
873
  end
540
874
  end
541
- if (abbrev ||= option('abbrev')) == true
542
- cmd << '--abbrev-commit'
543
- elsif abbrev.to_i > 0
544
- cmd << shell_option('abbrev', abbrev, escape: false)
875
+ if opts
876
+ append_hash opts
877
+ banner = format != 'oneline'
878
+ else
879
+ cmd << basic_option('abbrev', val) if (val = option('abbrev')) && val.to_i > 0
880
+ banner = true
545
881
  end
546
882
  append_value objs
547
- source(exception: false)
883
+ source(exception: false, banner: banner)
884
+ end
885
+
886
+ def rev_parse(flag, ref: nil, size: nil)
887
+ git_session 'rev-parse', if flag == :branch
888
+ '--abbrev-ref'
889
+ else
890
+ (n = size.to_i) > 0 ? basic_option('short', [n, 5].max) : '--verify'
891
+ end
892
+ append_commit ref
893
+ source
894
+ end
895
+
896
+ def ls_remote(flag, grep: nil)
897
+ git_session 'ls-remote', " --#{flag}"
898
+ out, banner, from = source(io: true)
899
+ print_item banner
900
+ ret = write_lines(out, grep: grep)
901
+ list_result(ret, flag.to_s, from: from, grep: grep)
902
+ end
903
+
904
+ def ls_files(flag, grep: nil)
905
+ git_session 'ls-files', " --#{flag}"
906
+ out, banner, from = source(io: true)
907
+ print_item banner
908
+ ret = write_lines(out, grep: grep)
909
+ list_result(ret, 'files', from: from, grep: grep)
910
+ end
911
+
912
+ def clone?
913
+ ref?(workspace.baseref) && workspace.git_clone?(path, name) ? 1 : false
914
+ end
915
+
916
+ def enabled?(*, **kwargs)
917
+ super || (kwargs[:base] == false && !!clone?)
548
918
  end
549
919
 
550
920
  private
551
921
 
552
922
  def source(cmd = @session, exception: true, io: false, sync: true, stdout: false, stderr: false, banner: true,
553
- sub: nil)
923
+ multiple: false, sub: nil)
924
+ banner = nil if multiple && banner
925
+ if cmd.respond_to?(:done)
926
+ if !(io && banner == false) && (from = cmd.drop(1).find { |val| val =~ /^[a-z][a-z\-]{2,}$/ })
927
+ from = :"git:#{from}"
928
+ end
929
+ banner &&= cmd.temp { |val| val.start_with?('--work-tree') || val.start_with?('--git-dir') }
930
+ end
554
931
  cmd = session_done(cmd)
555
932
  log.info cmd
556
- banner = format_banner(cmd.gsub(File.join(path, ''), ''), banner: banner)
557
- cmd = cmd.sub(/\Agit\b/, "git --work-tree=#{shell_quote(path)} --git-dir=#{shell_quote(gitpath)}")
933
+ on :first, from if from
934
+ banner = if banner
935
+ format_banner((banner.is_a?(String) ? banner : cmd).gsub(File.join(path, ''), ''), banner: true)
936
+ end
558
937
  begin
559
- if io
560
- [stdout ? `#{cmd}` : IO.popen(cmd), banner]
561
- elsif stdin? ? sync : stdout
562
- print_item banner
938
+ return [stdout ? `#{cmd}` : IO.popen(cmd), banner, from] if io
939
+
940
+ if stdin? ? sync : stdout
941
+ print_item banner unless multiple
563
942
  ret = `#{cmd}`
564
943
  if !ret.empty?
565
944
  puts ret
@@ -567,7 +946,7 @@ module Squared
567
946
  puts 'Success'
568
947
  end
569
948
  elsif sync || (!exception && !stderr)
570
- print_item banner
949
+ print_item banner unless multiple
571
950
  shell(cmd, exception: exception)
572
951
  else
573
952
  require 'open3'
@@ -587,9 +966,12 @@ module Squared
587
966
  end
588
967
  rescue StandardError => e
589
968
  log.error e
590
- raise if exception
969
+ ret = on(:error, from, e) if from
970
+ raise if exception && ret != true
591
971
 
592
972
  warn log_message(Logger::WARN, e) if warning?
973
+ else
974
+ on :last, from if from
593
975
  end
594
976
  end
595
977
 
@@ -617,80 +999,133 @@ module Squared
617
999
  ret
618
1000
  end
619
1001
 
620
- def list_result(size, type, action: 'found', grep: nil)
621
- return unless verbose
622
-
623
- if size > 0
624
- styles = theme.fetch(:banner, []).reject { |s| s.to_s.end_with?('!') }
625
- styles << :bold if styles.size <= 1
626
- puts print_footer("#{size} #{size == 1 ? type.sub(/s\z/, '') : type}",
627
- sub: { pat: /\A(\d+)(.+)\z/, styles: styles })
628
- else
629
- puts empty_status("No #{type} were #{action}", 'grep', grep)
1002
+ def list_result(size, type, from: nil, action: 'found', grep: nil)
1003
+ if verbose
1004
+ if size > 0
1005
+ styles = theme.fetch(:banner, []).reject { |s| s.to_s.end_with?('!') }
1006
+ styles << :bold if styles.size <= 1
1007
+ puts print_footer("#{size} #{size == 1 ? type.sub(/s\z/, '') : type}",
1008
+ sub: { pat: /\A(\d+)(.+)\z/, styles: styles })
1009
+ else
1010
+ puts empty_status("No #{type} were #{action}", 'grep', grep)
1011
+ end
630
1012
  end
1013
+ on :last, from if from
631
1014
  end
632
1015
 
633
- def append_pull(opts, list, flag = nil)
634
- append_submodules flag
635
- list = list.grep_v(/[^a-z\-]/)
1016
+ def append_pull(opts, list, no = [], target: @session, flag: nil, remote: nil)
1017
+ cmd << '--force' if option('force')
1018
+ modules = append_submodules(target: target)
1019
+ out = []
1020
+ refspec = []
1021
+ opts, pat = option_partition(opts, remote ? list + ['refspec=s'] : list, target: target, no: no)
636
1022
  opts.each do |opt|
637
- if list.include?(opt) || opt.match(/^(?:de(?:pth|epen)|jobs)=\d+$/)
638
- @session << "--#{opt}"
639
- elsif opt.match(/^(?:shallow-)?since=(.+)$/) && (val = Date.parse($1))
640
- @session << shell_option('shallow-since', val.strftime('%F %T'), escape: false, quote: true)
641
- elsif opt.end_with?('!') && list.include?(opt = opt[0..-2])
642
- @session << "--no-#{opt}"
1023
+ if opt =~ pat
1024
+ case $1
1025
+ when 'rebase'
1026
+ target << basic_option($1, $2) if VAL_REBASE.include?($2)
1027
+ when 'shallow-since'
1028
+ next unless (val = Date.parse($2))
1029
+
1030
+ target << quote_option($1, val.strftime('%F %T'))
1031
+ when 'recurse-submodules'
1032
+ target << shell_option($1, $2, quote: false) unless modules
1033
+ when 'shallow-exclude', 'gpg-sign', 'signoff', 'strategy', 'strategy-option'
1034
+ target << shell_option($1, $2)
1035
+ when 'refmap', 'negotiation-tip'
1036
+ target << quote_option($1, $2)
1037
+ when 'depth', 'deepen', 'log', 'jobs'
1038
+ target << basic_option($1, $2) if $2.to_i > 0
1039
+ when 'refspec'
1040
+ refspec << shell_escape($2, quote: true)
1041
+ end
1042
+ else
1043
+ out << opt
643
1044
  end
644
1045
  end
1046
+ if remote
1047
+ target << shell_escape(remote, quote: true)
1048
+ if (val = option('refspec', strict: true))
1049
+ append_value(split_escape(val), escape: false)
1050
+ else
1051
+ target.merge(refspec)
1052
+ end
1053
+ target.delete('--all')
1054
+ elsif target.include?('--multiple')
1055
+ target.merge(out.map { |opt| shell_escape(opt, quote: true) })
1056
+ out.clear
1057
+ end
1058
+ option_clear(out, target: target, subject: flag.to_s) if flag
645
1059
  end
646
1060
 
647
- def append_commit(val)
1061
+ def append_commit(val, target: @session)
648
1062
  val = val.to_s.strip
649
- @session << (val.empty? ? 'HEAD' : val)
1063
+ target << (val.empty? ? 'HEAD' : val)
650
1064
  end
651
1065
 
652
- def append_pathspec(files = [], expect: false, parent: false)
1066
+ def append_pathspec(files = [], target: @session, expect: false, parent: false)
653
1067
  if files.empty? && (val = option('pathspec'))
654
1068
  files = split_escape(val)
655
1069
  end
656
1070
  files = projectmap(files, parent: parent)
657
1071
  if !files.empty?
658
- @session << "-- #{files.join(' ')}"
1072
+ target << "-- #{files.join(' ')}"
659
1073
  elsif expect
660
1074
  raise_error(parent ? 'pathspec not present' : 'pathspec not within worktree', hint: 'invalid')
661
1075
  end
662
1076
  end
663
1077
 
664
- def append_message(val)
665
- @session << "--message=\"#{double_quote(val)}\"" unless val.to_s.empty?
1078
+ def append_message(val, target: @session)
1079
+ target << "--message=\"#{double_quote(val)}\"" unless val.to_s.empty?
666
1080
  end
667
1081
 
668
- def append_head(val = nil)
669
- return @session << val if val
1082
+ def append_head(val = nil, target: @session)
1083
+ return target << val if val
670
1084
 
671
- append_first(%w[head tree-ish object], flag: false, ignore: false)
1085
+ append_first(%w[head tree-ish object], target: target, flag: false, ignore: false)
672
1086
  end
673
1087
 
674
- def append_submodules(flag = nil)
675
- if option('recurse-submodules', equals: '0')
676
- @session << '--no-recurse-submodules'
677
- elsif flag == :submodules || option('recurse-submodules')
678
- @session << '--recurse-submodules'
1088
+ def append_submodules(from = nil, target: @session)
1089
+ return unless (val = option('recurse-submodules', ignore: false))
1090
+
1091
+ if from == :clone
1092
+ projectmap(split_escape(val)).each do |path|
1093
+ target << basic_option('recurse-submodules', path)
1094
+ end
1095
+ target
1096
+ else
1097
+ target << case val
1098
+ when 'no', '0'
1099
+ '--no-recurse-submodules'
1100
+ when 'yes', 'on-demand'
1101
+ "--recurse-submodules#{from == :reset ? '' : "=#{val}"}"
1102
+ else
1103
+ '--recurse-submodules'
1104
+ end
679
1105
  end
680
1106
  end
681
1107
 
682
- def git_session(*cmd)
683
- session('git', *cmd)
1108
+ def git_session(*cmd, worktree: true, **kwargs)
1109
+ dir = worktree ? ["--work-tree=#{shell_quote(path)}", "--git-dir=#{shell_quote(gitpath)}"] : []
1110
+ session('git', *dir, *cmd, **kwargs)
1111
+ end
1112
+
1113
+ def git_output(*cmd, **kwargs)
1114
+ git_session(*cmd, main: false, options: false, **kwargs)
684
1115
  end
685
1116
 
686
- def dry_run?
687
- @session.include?('--dry-run')
1117
+ def dryrun?(*)
1118
+ !!@session&.include?('--dry-run')
688
1119
  end
689
1120
 
690
1121
  def gitpath
691
1122
  basepath('.git')
692
1123
  end
693
1124
 
1125
+ def commithash(val)
1126
+ (data = /\A#\{(\h{5,40})\}\z/.match(val)) ? data[1] : nil
1127
+ end
1128
+
694
1129
  def threadargs
695
1130
  { stderr: true, exception: exception || !workspace.series.multiple? }
696
1131
  end