Class: Braid::Operations::Git

Inherits:
Proxy
  • Object
show all
Defined in:
lib/braid/operations.rb

Defined Under Namespace

Classes: BlobWithMode

Constant Summary collapse

ObjectID =

A string representing a Git object ID (i.e., hash). This type alias is used as documentation and is not enforced, so there’s a risk that we mistakenly mark something as an ObjectID when it can actually be a String that is not an ObjectID.

T.type_alias { String }
ObjectExpr =

A string containing an expression that can be evaluated to an object ID by ‘git rev-parse`. This type is enforced even less than `ObjectID` (in some cases, we apply it directly to user input without validation), but it still serves to document our intent.

T.type_alias { String }
TreeItem =

An ObjectID used as a TreeItem represents a tree.

T.type_alias { T.any(ObjectID, BlobWithMode) }

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Proxy

#require_version, #require_version!, #version

Methods included from T::Sig

#sig

Class Method Details

.commandObject



185
186
187
# File 'lib/braid/operations.rb', line 185

def self.command
  'git'
end

Instance Method Details

#add(path) ⇒ Object



450
451
452
# File 'lib/braid/operations.rb', line 450

def add(path)
  invoke('add', [path])
end

#add_item_to_index(item, path, update_worktree) ⇒ Object



381
382
383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/braid/operations.rb', line 381

def add_item_to_index(item, path, update_worktree)
  if item.is_a?(BlobWithMode)
    invoke('update-index', ['--add', '--cacheinfo', "#{item.mode},#{item.hash},#{path}"])
    if update_worktree
      # XXX If this fails, we've already updated the index.
      invoke('checkout-index', [path])
    end
  else
    # According to
    # https://lore.kernel.org/git/e48a281a4d3db0a04c0609fcb8658e4fcc797210.1646166271.git.gitgitgadget@gmail.com/,
    # `--prefix=` is valid if the path is empty.
    invoke('read-tree', ["--prefix=#{path}", update_worktree ? '-u' : '-i', item])
  end
end

#BlobWithModeObject



341
342
343
# File 'lib/braid/operations.rb', line 341

def BlobWithMode
  Git::BlobWithMode
end

#clone(args) ⇒ Object



512
513
514
# File 'lib/braid/operations.rb', line 512

def clone(args)
  invoke('clone', args)
end

#commit(message, args = []) ⇒ Object



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/braid/operations.rb', line 230

def commit(message, args = [])
  cmd = ['git', 'commit', '--no-verify']
  message_file = nil
  if message # allow nil
    message_file = Tempfile.new('braid_commit')
    message_file.print("Braid: #{message}")
    message_file.flush
    message_file.close
    cmd += ['-F', T.must(message_file.path)]
  end
  cmd += args
  status, out, err = exec(cmd)
  message_file.unlink if message_file

  if status == 0
    true
  elsif out.match(/nothing.* to commit/)
    false
  else
    raise ShellExecutionError.new(err, out)
  end
end

#config(args) ⇒ Object



445
446
447
# File 'lib/braid/operations.rb', line 445

def config(args)
  invoke('config', args) rescue nil
end

#diff(args) ⇒ Object



479
480
481
# File 'lib/braid/operations.rb', line 479

def diff(args)
  invoke('diff', args)
end

#diff_to_stdout(args) ⇒ Object



484
485
486
487
488
# File 'lib/braid/operations.rb', line 484

def diff_to_stdout(args)
  # For now, ignore the exit code.  It can be 141 (SIGPIPE) if the user
  # quits the pager before reading all the output.
  system(['git', 'diff'] + args)
end

#ensure_clean!Object



497
498
499
# File 'lib/braid/operations.rb', line 497

def ensure_clean!
  status_clean? || raise(LocalChangesPresent)
end

#fetch(remote = nil, args = []) ⇒ Object



254
255
256
257
# File 'lib/braid/operations.rb', line 254

def fetch(remote = nil, args = [])
  args = ['-n', remote] + args if remote
  exec!(['git', 'fetch'] + args)
end

#get_tree_item(tree, path) ⇒ Object



352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
# File 'lib/braid/operations.rb', line 352

def get_tree_item(tree, path)
  if path.nil? || path == ''
    tree
  else
    m = /^([^ ]*) ([^ ]*) ([^\t]*)\t.*$/.match(invoke('ls-tree', [tree, path]))
    if m.nil?
      # This can happen if the user runs `braid add` with a `--path` that
      # doesn't exist.  TODO: Make the error message more user-friendly in
      # that case.
      raise BraidError, 'No tree item exists at the given path'
    end
    mode = T.must(m[1])
    type = T.must(m[2])
    hash = T.must(m[3])
    if type == 'tree'
      hash
    elsif type == 'blob'
      return BlobWithMode.new(hash, mode)
    else
      raise BraidError, 'Tree item is not a tree or a blob'
    end
  end
end

#headObject



502
503
504
# File 'lib/braid/operations.rb', line 502

def head
  rev_parse('HEAD')
end

#initObject



507
508
509
# File 'lib/braid/operations.rb', line 507

def init
  invoke('init', [])
end

#is_inside_worktreeObject



215
216
217
# File 'lib/braid/operations.rb', line 215

def is_inside_worktree
  invoke('rev-parse', ['--is-inside-work-tree']) == 'true'
end

#ls_remote(args) ⇒ Object



538
539
540
# File 'lib/braid/operations.rb', line 538

def ls_remote(args)
  invoke('ls-remote', args)
end

#make_tree_with_item(main_content, item_path, item) ⇒ Object



430
431
432
433
434
435
436
437
438
439
440
441
442
# File 'lib/braid/operations.rb', line 430

def make_tree_with_item(main_content, item_path, item)
  with_temporary_index do
    # If item_path is '', then rm_r_cached will fail.  But in that case,
    # we can skip loading the main content because it would be deleted
    # anyway.
    if main_content && item_path != ''
      read_tree_im(main_content)
      rm_r_cached(item_path)
    end
    add_item_to_index(item, item_path, false)
    write_tree
  end
end

#merge_base(target, source) ⇒ Object



261
262
263
264
265
# File 'lib/braid/operations.rb', line 261

def merge_base(target, source)
  invoke('merge-base', [target, source])
rescue ShellExecutionError
  nil
end

#merge_trees(base_treeish, local_treeish, remote_treeish) ⇒ Object



314
315
316
317
318
319
320
# File 'lib/braid/operations.rb', line 314

def merge_trees(base_treeish, local_treeish, remote_treeish)
  invoke('merge-recursive', [base_treeish, '--', local_treeish, remote_treeish])
  true
rescue ShellExecutionError => error
  # 'CONFLICT' messages go to stdout.
  raise MergeError, error.out
end

#push(args) ⇒ Object



533
534
535
# File 'lib/braid/operations.rb', line 533

def push(args)
  invoke('push', args)
end

#read_ls_files(prefix) ⇒ Object



323
324
325
# File 'lib/braid/operations.rb', line 323

def read_ls_files(prefix)
  invoke('ls-files', [prefix])
end

#read_tree_im(treeish) ⇒ Object



399
400
401
# File 'lib/braid/operations.rb', line 399

def read_tree_im(treeish)
  invoke('read-tree', ['-im', treeish])
end

#read_tree_um(treeish) ⇒ Object



404
405
406
# File 'lib/braid/operations.rb', line 404

def read_tree_um(treeish)
  invoke('read-tree', ['-um', treeish])
end

#relative_working_dirObject



225
226
227
# File 'lib/braid/operations.rb', line 225

def relative_working_dir
  invoke('rev-parse', ['--show-prefix'])
end

#remote_add(remote, path) ⇒ Object



279
280
281
282
# File 'lib/braid/operations.rb', line 279

def remote_add(remote, path)
  invoke('remote', ['add', remote, path])
  true
end

#remote_rm(remote) ⇒ Object



285
286
287
288
# File 'lib/braid/operations.rb', line 285

def remote_rm(remote)
  invoke('remote', ['rm', remote])
  true
end

#remote_url(remote) ⇒ Object



292
293
294
295
296
297
# File 'lib/braid/operations.rb', line 292

def remote_url(remote)
  key = "remote.#{remote}.url"
  invoke('config', [key])
rescue ShellExecutionError
  nil
end

#repo_file_path(path) ⇒ Object



205
206
207
# File 'lib/braid/operations.rb', line 205

def repo_file_path(path)
  invoke('rev-parse', ['--git-path', path])
end

#reset_hard(target) ⇒ Object



300
301
302
303
# File 'lib/braid/operations.rb', line 300

def reset_hard(target)
  invoke('reset', ['--hard', target])
  true
end

#rev_list(args) ⇒ Object



523
524
525
# File 'lib/braid/operations.rb', line 523

def rev_list(args)
  invoke('rev-list', args)
end

#rev_parse(expr) ⇒ Object



268
269
270
271
272
# File 'lib/braid/operations.rb', line 268

def rev_parse(expr)
  invoke('rev-parse', [expr])
rescue ShellExecutionError
  raise UnknownRevision, expr
end

#rm(path) ⇒ Object



455
456
457
# File 'lib/braid/operations.rb', line 455

def rm(path)
  invoke('rm', [path])
end

#rm_r(path) ⇒ Object



460
461
462
463
# File 'lib/braid/operations.rb', line 460

def rm_r(path)
  invoke('rm', ['-r', path])
  true
end

#rm_r_cached(path) ⇒ Object



467
468
469
470
# File 'lib/braid/operations.rb', line 467

def rm_r_cached(path)
  invoke('rm', ['-r', '--cached', path])
  true
end

#status_clean?Boolean

Returns:

  • (Boolean)


491
492
493
494
# File 'lib/braid/operations.rb', line 491

def status_clean?
  _, out, _ = exec(['git', 'status'])
  !out.split("\n").grep(/nothing to commit/).empty?
end

#tree_hash(path, treeish = 'HEAD') ⇒ Object



473
474
475
476
# File 'lib/braid/operations.rb', line 473

def tree_hash(path, treeish = 'HEAD')
  out = invoke('ls-tree', [treeish, '-d', path])
  T.must(out.split[2])
end

#update_ref(args) ⇒ Object



528
529
530
# File 'lib/braid/operations.rb', line 528

def update_ref(args)
  invoke('update-ref', args)
end

#with_temporary_index(&blk) ⇒ Object



420
421
422
423
424
425
426
427
# File 'lib/braid/operations.rb', line 420

def with_temporary_index(&blk)
  Dir.mktmpdir('braid_index') do |dir|
    Operations::with_modified_environment(
      {'GIT_INDEX_FILE' => File.join(dir, 'index')}) do
      yield
    end
  end
end

#write_treeObject



410
411
412
# File 'lib/braid/operations.rb', line 410

def write_tree
  invoke('write-tree', [])
end