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`. Ditto the remark about lack of enforcement.

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



193
194
195
# File 'lib/braid/operations.rb', line 193

def self.command
  'git'
end

Instance Method Details

#add(path) ⇒ Object



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

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

#add_item_to_index(item, path, update_worktree) ⇒ Object



387
388
389
390
391
392
393
394
395
396
397
398
399
400
# File 'lib/braid/operations.rb', line 387

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



347
348
349
# File 'lib/braid/operations.rb', line 347

def BlobWithMode
  Git::BlobWithMode
end

#clone(args) ⇒ Object



518
519
520
# File 'lib/braid/operations.rb', line 518

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

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



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/braid/operations.rb', line 236

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, err
  end
end

#config(args) ⇒ Object



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

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

#diff(args) ⇒ Object



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

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

#diff_to_stdout(args) ⇒ Object



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

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



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

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

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



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

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

#get_tree_item(tree, path) ⇒ Object



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
# File 'lib/braid/operations.rb', line 358

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 ShellExecutionError, '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 ShellExecutionError, 'Tree item is not a tree or a blob'
    end
  end
end

#headObject



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

def head
  rev_parse('HEAD')
end

#initObject



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

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

#is_inside_worktreeObject



221
222
223
# File 'lib/braid/operations.rb', line 221

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

#ls_remote(args) ⇒ Object



544
545
546
# File 'lib/braid/operations.rb', line 544

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

#make_tree_with_item(main_content, item_path, item) ⇒ Object



436
437
438
439
440
441
442
443
444
445
446
447
448
# File 'lib/braid/operations.rb', line 436

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



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

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

#merge_trees(base_treeish, local_treeish, remote_treeish) ⇒ Object



320
321
322
323
324
325
326
# File 'lib/braid/operations.rb', line 320

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



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

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

#read_ls_files(prefix) ⇒ Object



329
330
331
# File 'lib/braid/operations.rb', line 329

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

#read_tree_im(treeish) ⇒ Object



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

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

#read_tree_um(treeish) ⇒ Object



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

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

#relative_working_dirObject



231
232
233
# File 'lib/braid/operations.rb', line 231

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

#remote_add(remote, path) ⇒ Object



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

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

#remote_rm(remote) ⇒ Object



291
292
293
294
# File 'lib/braid/operations.rb', line 291

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

#remote_url(remote) ⇒ Object



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

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

#repo_file_path(path) ⇒ Object



211
212
213
# File 'lib/braid/operations.rb', line 211

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

#reset_hard(target) ⇒ Object



306
307
308
309
# File 'lib/braid/operations.rb', line 306

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

#rev_list(args) ⇒ Object



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

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

#rev_parse(expr) ⇒ Object



274
275
276
277
278
# File 'lib/braid/operations.rb', line 274

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

#rm(path) ⇒ Object



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

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

#rm_r(path) ⇒ Object



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

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

#rm_r_cached(path) ⇒ Object



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

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

#status_clean?Boolean

Returns:

  • (Boolean)


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

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

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



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

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

#update_ref(args) ⇒ Object



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

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

#with_temporary_index(&blk) ⇒ Object



426
427
428
429
430
431
432
433
# File 'lib/braid/operations.rb', line 426

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



416
417
418
# File 'lib/braid/operations.rb', line 416

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