Class: Git::Lib

Inherits:
Object
  • Object
show all
Defined in:
lib/git/lib.rb

Constant Summary collapse

CAT_FILE_HEADER_LINE =
/\A(?<key>\w+) (?<value>.*)\z/
BRANCH_LINE_REGEXP =
/
  ^
    # Prefix indicates if this branch is checked out. The prefix is one of:
    (?:
      (?<current>\*[[:blank:]]) |  # Current branch (checked out in the current worktree)
      (?<worktree>\+[[:blank:]]) | # Branch checked out in a different worktree
      [[:blank:]]{2}               # Branch not checked out
    )

    # The branch's full refname
    (?:
      (?<not_a_branch>\(not[[:blank:]]a[[:blank:]]branch\)) |
      (?:\(HEAD[[:blank:]]detached[[:blank:]]at[[:blank:]](?<detached_ref>[^\)]+)\)) |
      (?<refname>[^[[:blank:]]]+)
    )

    # Optional symref
    # If this ref is a symbolic reference, this is the ref referenced
    (?:
      [[:blank:]]->[[:blank:]](?<symref>.*)
    )?
  $
/x
@@semaphore =
Mutex.new

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(base = nil, logger = nil) ⇒ Lib

Create a new Git::Lib object

Parameters:

  • base (Git::Base, Hash) (defaults to: nil)

    An object that passes in values for @git_work_dir, @git_dir, and @git_index_file

  • logger (Logger) (defaults to: nil)

Options Hash (base):

  • :working_directory (Pathname)
  • :repository (Pathname)
  • :index (Pathname)


48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/git/lib.rb', line 48

def initialize(base = nil, logger = nil)
  @git_dir = nil
  @git_index_file = nil
  @git_work_dir = nil
  @path = nil
  @logger = logger || Logger.new(nil)

  if base.is_a?(Git::Base)
    @git_dir = base.repo.path
    @git_index_file = base.index.path if base.index
    @git_work_dir = base.dir.path if base.dir
  elsif base.is_a?(Hash)
    @git_dir = base[:repository]
    @git_index_file = base[:index]
    @git_work_dir = base[:working_directory]
  end
end

Instance Attribute Details

#git_dirPathname (readonly)

The path to the Git repository directory. The default is "#{git_work_dir}/.git".

Returns:

  • (Pathname)

    the Git repository directory.

See Also:

  • repository](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefrepositoryarepository)


26
27
28
# File 'lib/git/lib.rb', line 26

def git_dir
  @git_dir
end

#git_index_filePathname (readonly)

The Git index file used to stage changes (using git add) before they are committed.

Returns:

  • (Pathname)

    the Git index file

See Also:

  • index file](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefindexaindex)


35
36
37
# File 'lib/git/lib.rb', line 35

def git_index_file
  @git_index_file
end

#git_work_dirPathname (readonly)

The path to the Git working copy. The default is '"./.git"'.

Returns:

  • (Pathname)

    the path to the Git working copy.

See Also:

  • working tree](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefworkingtreeaworkingtree)


17
18
19
# File 'lib/git/lib.rb', line 17

def git_work_dir
  @git_work_dir
end

Class Method Details

.warn_if_old_command(lib)



1116
1117
1118
1119
1120
1121
1122
1123
# File 'lib/git/lib.rb', line 1116

def self.warn_if_old_command(lib)
  return true if @version_checked
  @version_checked = true
  unless lib.meets_required_version?
    $stderr.puts "[WARNING] The git gem requires git #{lib.required_command_version.join('.')} or later, but only found #{lib.current_command_version.join('.')}. You should probably upgrade."
  end
  true
end

Instance Method Details

#add(paths = '.', options = {})

updates the repository index using the working directory content

lib.add('path/to/file') lib.add(['path/to/file1','path/to/file2']) lib.add(:all => true)

options: :all => true :force => true

Parameters:

  • paths (String, Array) (defaults to: '.')

    files paths to be added to the repository

  • options (Hash) (defaults to: {})


663
664
665
666
667
668
669
670
671
672
673
674
675
676
# File 'lib/git/lib.rb', line 663

def add(paths='.',options={})
  arr_opts = []

  arr_opts << '--all' if options[:all]
  arr_opts << '--force' if options[:force]

  arr_opts << '--'

  arr_opts << paths

  arr_opts.flatten!

  command('add', *arr_opts)
end

#apply(patch_file)



758
759
760
761
762
# File 'lib/git/lib.rb', line 758

def apply(patch_file)
  arr_opts = []
  arr_opts << '--' << patch_file if patch_file
  command('apply', *arr_opts)
end

#apply_mail(patch_file)



764
765
766
767
768
# File 'lib/git/lib.rb', line 764

def apply_mail(patch_file)
  arr_opts = []
  arr_opts << '--' << patch_file if patch_file
  command('am', *arr_opts)
end

#archive(sha, file = nil, opts = {})

creates an archive file

options :format (zip, tar) :prefix :remote :path



1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
# File 'lib/git/lib.rb', line 1068

def archive(sha, file = nil, opts = {})
  opts[:format] ||= 'zip'

  if opts[:format] == 'tgz'
    opts[:format] = 'tar'
    opts[:add_gzip] = true
  end

  if !file
    tempfile = Tempfile.new('archive')
    file = tempfile.path
    # delete it now, before we write to it, so that Ruby doesn't delete it
    # when it finalizes the Tempfile.
    tempfile.close!
  end

  arr_opts = []
  arr_opts << "--format=#{opts[:format]}" if opts[:format]
  arr_opts << "--prefix=#{opts[:prefix]}" if opts[:prefix]
  arr_opts << "--remote=#{opts[:remote]}" if opts[:remote]
  arr_opts << sha
  arr_opts << '--' << opts[:path] if opts[:path]
  command('archive', *arr_opts, redirect: " > #{escape file}")
  if opts[:add_gzip]
    file_content = File.read(file)
    Zlib::GzipWriter.open(file) do |gz|
      gz.write(file_content)
    end
  end
  return file
end

#branch_contains(commit, branch_name = "")



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

def branch_contains(commit, branch_name="")
  command("branch",  branch_name, "--contains", commit)
end

#branch_current



446
447
448
# File 'lib/git/lib.rb', line 446

def branch_current
  branches_all.select { |b| b[1] }.first[0] rescue nil
end

#branch_delete(branch)



809
810
811
# File 'lib/git/lib.rb', line 809

def branch_delete(branch)
  command('branch', '-D', branch)
end

#branch_new(branch)



805
806
807
# File 'lib/git/lib.rb', line 805

def branch_new(branch)
  command('branch', branch)
end

#branches_all



392
393
394
395
396
397
398
399
400
401
402
403
404
# File 'lib/git/lib.rb', line 392

def branches_all
  command_lines('branch', '-a').map do |line|
    match_data = line.match(BRANCH_LINE_REGEXP)
    raise GitExecuteError, 'Unexpected branch line format' unless match_data
    next nil if match_data[:not_a_branch] || match_data[:detached_ref]
    [
      match_data[:refname],
      !match_data[:current].nil?,
      !match_data[:worktree].nil?,
      match_data[:symref]
    ]
  end.compact
end

#change_head_branch(branch_name)



364
365
366
# File 'lib/git/lib.rb', line 364

def change_head_branch(branch_name)
  command('symbolic-ref', 'HEAD', "refs/heads/#{branch_name}")
end

#checkout(branch = nil, opts = {})

Runs checkout command to checkout or create branch

accepts options: :new_branch :force :start_point

Parameters:

  • branch (String) (defaults to: nil)
  • opts (Hash) (defaults to: {})


822
823
824
825
826
827
828
829
830
831
832
833
834
835
# File 'lib/git/lib.rb', line 822

def checkout(branch = nil, opts = {})
  if branch.is_a?(Hash) && opts == {}
    opts = branch
    branch = nil
  end

  arr_opts = []
  arr_opts << '-b' if opts[:new_branch] || opts[:b]
  arr_opts << '--force' if opts[:force] || opts[:f]
  arr_opts << branch if branch
  arr_opts << opts[:start_point] if opts[:start_point] && arr_opts.include?('-b')

  command('checkout', *arr_opts)
end

#checkout_file(version, file)



837
838
839
840
841
842
# File 'lib/git/lib.rb', line 837

def checkout_file(version, file)
  arr_opts = []
  arr_opts << version
  arr_opts << file
  command('checkout', *arr_opts)
end

#checkout_index(opts = {})



1051
1052
1053
1054
1055
1056
1057
1058
1059
# File 'lib/git/lib.rb', line 1051

def checkout_index(opts = {})
  arr_opts = []
  arr_opts << "--prefix=#{opts[:prefix]}" if opts[:prefix]
  arr_opts << "--force" if opts[:force]
  arr_opts << "--all" if opts[:all]
  arr_opts << '--' << opts[:path_limiter] if opts[:path_limiter].is_a? String

  command('checkout-index', *arr_opts)
end

#clean(opts = {})



737
738
739
740
741
742
743
744
745
# File 'lib/git/lib.rb', line 737

def clean(opts = {})
  arr_opts = []
  arr_opts << '--force' if opts[:force]
  arr_opts << '-ff' if opts[:ff]
  arr_opts << '-d' if opts[:d]
  arr_opts << '-x' if opts[:x]

  command('clean', *arr_opts)
end

#clone(repository_url, directory, opts = {}) ⇒ Hash

tries to clone the given repo

accepts options: :bare:: no working directory :branch:: name of branch to track (rather than 'master') :depth:: the number of commits back to pull :origin:: name of remote (same as remote) :path:: directory where the repo will be cloned :remote:: name of remote (rather than 'origin') :recursive:: after the clone is created, initialize all submodules within, using their default settings.

TODO - make this work with SSH password or auth_key

Returns:

  • (Hash)

    the options to pass to Base.new



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/git/lib.rb', line 96

def clone(repository_url, directory, opts = {})
  @path = opts[:path] || '.'
  clone_dir = opts[:path] ? File.join(@path, directory) : directory

  arr_opts = []
  arr_opts << '--bare' if opts[:bare]
  arr_opts << '--branch' << opts[:branch] if opts[:branch]
  arr_opts << '--depth' << opts[:depth].to_i if opts[:depth] && opts[:depth].to_i > 0
  arr_opts << '--config' << opts[:config] if opts[:config]
  arr_opts << '--origin' << opts[:remote] || opts[:origin] if opts[:remote] || opts[:origin]
  arr_opts << '--recursive' if opts[:recursive]
  arr_opts << '--mirror' if opts[:mirror]

  arr_opts << '--'

  arr_opts << repository_url
  arr_opts << clone_dir

  command('clone', *arr_opts)

  return_base_opts_from_clone(clone_dir, opts)
end

#commit(message, opts = {})

Takes the commit message with the options and executes the commit command

accepts options: :amend :all :allow_empty :author :date :no_verify :allow_empty_message :gpg_sign (accepts true or a gpg key ID as a String) :no_gpg_sign (conflicts with :gpg_sign)

Parameters:

  • message (String)

    the commit message to be used

  • opts (Hash) (defaults to: {})

    the commit options to be used



703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
# File 'lib/git/lib.rb', line 703

def commit(message, opts = {})
  arr_opts = []
  arr_opts << "--message=#{message}" if message
  arr_opts << '--amend' << '--no-edit' if opts[:amend]
  arr_opts << '--all' if opts[:add_all] || opts[:all]
  arr_opts << '--allow-empty' if opts[:allow_empty]
  arr_opts << "--author=#{opts[:author]}" if opts[:author]
  arr_opts << "--date=#{opts[:date]}" if opts[:date].is_a? String
  arr_opts << '--no-verify' if opts[:no_verify]
  arr_opts << '--allow-empty-message' if opts[:allow_empty_message]

  if opts[:gpg_sign] && opts[:no_gpg_sign]
    raise ArgumentError, 'cannot specify :gpg_sign and :no_gpg_sign'
  elsif opts[:gpg_sign]
    arr_opts <<
      if opts[:gpg_sign] == true
        '--gpg-sign'
      else
        "--gpg-sign=#{opts[:gpg_sign]}"
      end
  elsif opts[:no_gpg_sign]
    arr_opts << '--no-gpg-sign'
  end

  command('commit', *arr_opts)
end

#commit_data(sha)

returns useful array of raw commit object data



237
238
239
240
241
# File 'lib/git/lib.rb', line 237

def commit_data(sha)
  sha = sha.to_s
  cdata = command_lines('cat-file', 'commit', sha)
  process_commit_data(cdata, sha)
end

#commit_tree(tree, opts = {})



1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
# File 'lib/git/lib.rb', line 1034

def commit_tree(tree, opts = {})
  opts[:message] ||= "commit tree #{tree}"
  t = Tempfile.new('commit-message')
  t.write(opts[:message])
  t.close

  arr_opts = []
  arr_opts << tree
  arr_opts << '-p' << opts[:parent] if opts[:parent]
  arr_opts += [opts[:parents]].map { |p| ['-p', p] }.flatten if opts[:parents]
  command('commit-tree', *arr_opts, redirect: "< #{escape t.path}")
end

#config_get(name)



579
580
581
582
583
584
585
586
587
588
589
# File 'lib/git/lib.rb', line 579

def config_get(name)
  do_get = Proc.new do |path|
    command('config', '--get', name)
  end

  if @git_dir
    Dir.chdir(@git_dir, &do_get)
  else
    do_get.call
  end
end

#config_list



595
596
597
598
599
600
601
602
603
604
605
# File 'lib/git/lib.rb', line 595

def config_list
  build_list = Proc.new do |path|
    parse_config_list command_lines('config', '--list')
  end

  if @git_dir
    Dir.chdir(@git_dir, &build_list)
  else
    build_list.call
  end
end

#config_remote(name)



569
570
571
572
573
574
575
576
577
# File 'lib/git/lib.rb', line 569

def config_remote(name)
  hsh = {}
  config_list.each do |key, value|
    if /remote.#{name}/.match(key)
      hsh[key.gsub("remote.#{name}.", '')] = value
    end
  end
  hsh
end

#config_set(name, value, options = {})

WRITE COMMANDS ##



639
640
641
642
643
644
645
# File 'lib/git/lib.rb', line 639

def config_set(name, value, options = {})
  if options[:file].to_s.empty?
    command('config', name, value)
  else
    command('config', '--file', options[:file], name, value)
  end
end

#conflicts

:yields: file, your, their



876
877
878
879
880
881
882
883
884
885
886
887
888
889
# File 'lib/git/lib.rb', line 876

def conflicts # :yields: file, your, their
  self.unmerged.each do |f|
    your_tempfile = Tempfile.new("YOUR-#{File.basename(f)}")
    your = your_tempfile.path
    your_tempfile.close # free up file for git command process
    command('show', ":2:#{f}", redirect: "> #{escape your}")

    their_tempfile = Tempfile.new("THEIR-#{File.basename(f)}")
    their = their_tempfile.path
    their_tempfile.close # free up file for git command process
    command('show', ":3:#{f}", redirect: "> #{escape their}")
    yield(f, your, their)
  end
end

#current_command_version

returns the current version of git, as an Array of Fixnums.



1101
1102
1103
1104
1105
1106
# File 'lib/git/lib.rb', line 1101

def current_command_version
  output = command('version')
  version = output[/\d+(\.\d+)+/]
  version_parts = version.split('.').collect { |i| i.to_i }
  version_parts.fill(0, version_parts.length...3)
end

#describe(committish = nil, opts = {})

Returns most recent tag that is reachable from a commit

accepts options: :all :tags :contains :debug :exact_match :dirty :abbrev :candidates :long :always :math

@param [String|NilClass] committish target commit sha or object name @param [Symbol=>Object] opts the given options @return [String] the tag name



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/git/lib.rb', line 167

def describe(committish=nil, opts={})
  arr_opts = []

  arr_opts << '--all' if opts[:all]
  arr_opts << '--tags' if opts[:tags]
  arr_opts << '--contains' if opts[:contains]
  arr_opts << '--debug' if opts[:debug]
  arr_opts << '--long' if opts[:long]
  arr_opts << '--always' if opts[:always]
  arr_opts << '--exact-match' if opts[:exact_match] || opts[:"exact-match"]

  arr_opts << '--dirty' if opts[:dirty] == true
  arr_opts << "--dirty=#{opts[:dirty]}" if opts[:dirty].is_a?(String)

  arr_opts << "--abbrev=#{opts[:abbrev]}" if opts[:abbrev]
  arr_opts << "--candidates=#{opts[:candidates]}" if opts[:candidates]
  arr_opts << "--match=#{opts[:match]}" if opts[:match]

  arr_opts << committish if committish

  return command('describe', *arr_opts)
end

#diff_files

compares the index and the working directory



524
525
526
# File 'lib/git/lib.rb', line 524

def diff_files
  diff_as_hash('diff-files')
end

#diff_full(obj1 = 'HEAD', obj2 = nil, opts = {})



480
481
482
483
484
485
486
487
# File 'lib/git/lib.rb', line 480

def diff_full(obj1 = 'HEAD', obj2 = nil, opts = {})
  diff_opts = ['-p']
  diff_opts << obj1
  diff_opts << obj2 if obj2.is_a?(String)
  diff_opts << '--' << opts[:path_limiter] if opts[:path_limiter].is_a? String

  command('diff', *diff_opts)
end

#diff_index(treeish)

compares the index and the repository



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

def diff_index(treeish)
  diff_as_hash('diff-index', treeish)
end

#diff_name_status(reference1 = nil, reference2 = nil, opts = {})



509
510
511
512
513
514
515
516
517
518
519
520
521
# File 'lib/git/lib.rb', line 509

def diff_name_status(reference1 = nil, reference2 = nil, opts = {})
  opts_arr = ['--name-status']
  opts_arr << reference1 if reference1
  opts_arr << reference2 if reference2

  opts_arr << '--' << opts[:path] if opts[:path]

  command_lines('diff', *opts_arr).inject({}) do |memo, line|
    status, path = line.split("\t")
    memo[path] = status
    memo
  end
end

#diff_stats(obj1 = 'HEAD', obj2 = nil, opts = {})



489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
# File 'lib/git/lib.rb', line 489

def diff_stats(obj1 = 'HEAD', obj2 = nil, opts = {})
  diff_opts = ['--numstat']
  diff_opts << obj1
  diff_opts << obj2 if obj2.is_a?(String)
  diff_opts << '--' << opts[:path_limiter] if opts[:path_limiter].is_a? String

  hsh = {:total => {:insertions => 0, :deletions => 0, :lines => 0, :files => 0}, :files => {}}

  command_lines('diff', *diff_opts).each do |file|
    (insertions, deletions, filename) = file.split("\t")
    hsh[:total][:insertions] += insertions.to_i
    hsh[:total][:deletions] += deletions.to_i
    hsh[:total][:lines] = (hsh[:total][:deletions] + hsh[:total][:insertions])
    hsh[:total][:files] += 1
    hsh[:files][filename] = {:insertions => insertions.to_i, :deletions => deletions.to_i}
  end

  hsh
end

#each_cat_file_header(data)



264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/git/lib.rb', line 264

def each_cat_file_header(data)
  while (match = CAT_FILE_HEADER_LINE.match(data.shift))
    key = match[:key]
    value_lines = [match[:value]]

    while data.first.start_with?(' ')
      value_lines << data.shift.lstrip
    end

    yield key, value_lines.join("\n")
  end
end

#fetch(remote, opts)



947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
# File 'lib/git/lib.rb', line 947

def fetch(remote, opts)
  arr_opts = []
  arr_opts << '--all' if opts[:all]
  arr_opts << '--tags' if opts[:t] || opts[:tags]
  arr_opts << '--prune' if opts[:p] || opts[:prune]
  arr_opts << '--prune-tags' if opts[:P] || opts[:'prune-tags']
  arr_opts << '--force' if opts[:f] || opts[:force]
  arr_opts << '--unshallow' if opts[:unshallow]
  arr_opts << '--depth' << opts[:depth] if opts[:depth]
  arr_opts << '--' if remote || opts[:ref]
  arr_opts << remote if remote
  arr_opts << opts[:ref] if opts[:ref]

  command('fetch', *arr_opts)
end

#full_log_commits(opts = {})



200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/git/lib.rb', line 200

def full_log_commits(opts={})
  arr_opts = log_common_options(opts)

  arr_opts << '--pretty=raw'
  arr_opts << "--skip=#{opts[:skip]}" if opts[:skip]

  arr_opts += log_path_options(opts)

  full_log = command_lines('log', *arr_opts)

  process_commit_log_data(full_log)
end

#full_tree(sha)



356
357
358
# File 'lib/git/lib.rb', line 356

def full_tree(sha)
  command_lines('ls-tree', '-r', sha)
end

#gc



1018
1019
1020
# File 'lib/git/lib.rb', line 1018

def gc
  command('gc', '--prune', '--aggressive', '--auto')
end

#global_config_get(name)



591
592
593
# File 'lib/git/lib.rb', line 591

def global_config_get(name)
  command('config', '--global', '--get', name)
end

#global_config_list



607
608
609
# File 'lib/git/lib.rb', line 607

def global_config_list
  parse_config_list command_lines('config', '--global', '--list')
end

#global_config_set(name, value)



647
648
649
# File 'lib/git/lib.rb', line 647

def global_config_set(name, value)
  command('config', '--global', name, value)
end

#grep(string, opts = {})

returns hash [tree-ish] = [[line_no, match], [line_no, match2]] [tree-ish] = [[line_no, match], [line_no, match2]]



457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
# File 'lib/git/lib.rb', line 457

def grep(string, opts = {})
  opts[:object] ||= 'HEAD'

  grep_opts = ['-n']
  grep_opts << '-i' if opts[:ignore_case]
  grep_opts << '-v' if opts[:invert_match]
  grep_opts << '-E' if opts[:extended_regexp]
  grep_opts << '-e'
  grep_opts << string
  grep_opts << opts[:object] if opts[:object].is_a?(String)
  grep_opts.push('--', opts[:path_limiter]) if opts[:path_limiter].is_a?(String)
  grep_opts.push('--', *opts[:path_limiter]) if opts[:path_limiter].is_a?(Array)

  hsh = {}
  command_lines('grep', *grep_opts).each do |line|
    if m = /(.*?)\:(\d+)\:(.*)/.match(line)
      hsh[m[1]] ||= []
      hsh[m[1]] << [m[2].to_i, m[3]]
    end
  end
  hsh
end

#ignored_files



564
565
566
# File 'lib/git/lib.rb', line 564

def ignored_files
  command_lines('ls-files', '--others', '-i', '--exclude-standard')
end

#init(opts = {})

creates or reinitializes the repository

options: :bare :working_directory :initial_branch



73
74
75
76
77
78
79
# File 'lib/git/lib.rb', line 73

def init(opts={})
  arr_opts = []
  arr_opts << '--bare' if opts[:bare]
  arr_opts << "--initial-branch=#{opts[:initial_branch]}" if opts[:initial_branch]

  command('init', *arr_opts)
end

#list_files(ref_dir)



439
440
441
442
443
444
# File 'lib/git/lib.rb', line 439

def list_files(ref_dir)
  dir = File.join(@git_dir, 'refs', ref_dir)
  files = []
  Dir.chdir(dir) { files = Dir.glob('**/*').select { |f| File.file?(f) } } rescue nil
  files
end

#log_commits(opts = {})



190
191
192
193
194
195
196
197
198
# File 'lib/git/lib.rb', line 190

def log_commits(opts={})
  arr_opts = log_common_options(opts)

  arr_opts << '--pretty=oneline'

  arr_opts += log_path_options(opts)

  command_lines('log', *arr_opts).map { |l| l.split.first }
end

#ls_files(location = nil)



533
534
535
536
537
538
539
540
541
542
543
544
545
# File 'lib/git/lib.rb', line 533

def ls_files(location=nil)
  location ||= '.'
  hsh = {}
  command_lines('ls-files', '--stage', location).each do |line|
    (info, file) = line.split("\t")
    (mode, sha, stage) = info.split
    if file.start_with?('"') && file.end_with?('"')
      file = Git::EscapedPath.new(file[1..-2]).unescape
    end
    hsh[file] = {:path => file, :mode_index => mode, :sha_index => sha, :stage => stage}
  end
  hsh
end

#ls_remote(location = nil, opts = {})



547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
# File 'lib/git/lib.rb', line 547

def ls_remote(location=nil, opts={})
  arr_opts = []
  arr_opts << '--refs' if opts[:refs]
  arr_opts << (location || '.')

  Hash.new{ |h,k| h[k] = {} }.tap do |hsh|
    command_lines('ls-remote', *arr_opts).each do |line|
      (sha, info) = line.split("\t")
      (ref, type, name) = info.split('/', 3)
      type ||= 'head'
      type = 'branches' if type == 'heads'
      value = {:ref => ref, :sha => sha}
      hsh[type].update( name.nil? ? value : { name => value })
    end
  end
end

#ls_tree(sha)



340
341
342
343
344
345
346
347
348
349
350
# File 'lib/git/lib.rb', line 340

def ls_tree(sha)
  data = { 'blob' => {}, 'tree' => {}, 'commit' => {} }

  command_lines('ls-tree', sha).each do |line|
    (info, filenm) = line.split("\t")
    (mode, type, sha) = info.split
    data[type][filenm] = {:mode => mode, :sha => sha}
  end

  data
end

#meets_required_version?Boolean

Returns:

  • (Boolean)


1112
1113
1114
# File 'lib/git/lib.rb', line 1112

def meets_required_version?
  (self.current_command_version <=>  self.required_command_version) >= 0
end

#merge(branch, message = nil, opts = {})



844
845
846
847
848
849
850
851
# File 'lib/git/lib.rb', line 844

def merge(branch, message = nil, opts = {})
  arr_opts = []
  arr_opts << '--no-commit' if opts[:no_commit]
  arr_opts << '--no-ff' if opts[:no_ff]
  arr_opts << '-m' << message if message
  arr_opts += Array(branch)
  command('merge', *arr_opts)
end

#merge_base(*args)



853
854
855
856
857
858
859
860
861
862
863
864
865
866
# File 'lib/git/lib.rb', line 853

def merge_base(*args)
  opts = args.last.is_a?(Hash) ? args.pop : {}

  arg_opts = []

  arg_opts << '--octopus' if opts[:octopus]
  arg_opts << '--independent' if opts[:independent]
  arg_opts << '--fork-point' if opts[:fork_point]
  arg_opts << '--all' if opts[:all]

  arg_opts += args

  command('merge-base', *arg_opts).lines.map(&:strip)
end

#mv(file1, file2)



352
353
354
# File 'lib/git/lib.rb', line 352

def mv(file1, file2)
  command_lines('mv', '--', file1, file2)
end

#namerev(string)



224
225
226
# File 'lib/git/lib.rb', line 224

def namerev(string)
  command('name-rev', string).split[1]
end

#object_contents(sha, &block)



336
337
338
# File 'lib/git/lib.rb', line 336

def object_contents(sha, &block)
  command('cat-file', '-p', sha, &block)
end

#object_size(sha)



232
233
234
# File 'lib/git/lib.rb', line 232

def object_size(sha)
  command('cat-file', '-s', sha).to_i
end

#object_type(sha)



228
229
230
# File 'lib/git/lib.rb', line 228

def object_type(sha)
  command('cat-file', '-t', sha)
end

#parse_config(file)



620
621
622
# File 'lib/git/lib.rb', line 620

def parse_config(file)
  parse_config_list command_lines('config', '--list', '--file', file)
end

#parse_config_list(lines)



611
612
613
614
615
616
617
618
# File 'lib/git/lib.rb', line 611

def parse_config_list(lines)
  hsh = {}
  lines.each do |line|
    (key, *values) = line.split('=')
    hsh[key] = values.join('=')
  end
  hsh
end

#process_commit_data(data, sha)



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/git/lib.rb', line 243

def process_commit_data(data, sha)
  hsh = {
    'sha'    => sha,
    'parent' => []
  }

  each_cat_file_header(data) do |key, value|
    if key == 'parent'
      hsh['parent'] << value
    else
      hsh[key] = value
    end
  end

  hsh['message'] = data.join("\n") + "\n"

  return hsh
end

#process_commit_log_data(data)



295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
# File 'lib/git/lib.rb', line 295

def process_commit_log_data(data)
  in_message = false

  hsh_array = []

  hsh = nil

  data.each do |line|
    line = line.chomp

    if line[0].nil?
      in_message = !in_message
      next
    end

    in_message = false if in_message && line[0..3] != "    "

    if in_message
      hsh['message'] << "#{line[4..-1]}\n"
      next
    end

    key, *value = line.split
    value = value.join(' ')

    case key
      when 'commit'
        hsh_array << hsh if hsh
        hsh = {'sha' => value, 'message' => '', 'parent' => []}
      when 'parent'
        hsh['parent'] << value
      else
        hsh[key] = value
    end
  end

  hsh_array << hsh if hsh

  return hsh_array
end

#process_tag_data(data, name)



283
284
285
286
287
288
289
290
291
292
293
# File 'lib/git/lib.rb', line 283

def process_tag_data(data, name)
  hsh = { 'name' => name }

  each_cat_file_header(data) do |key, value|
    hsh[key] = value
  end

  hsh['message'] = data.join("\n") + "\n"

  return hsh
end

#pull(remote = nil, branch = nil)

Raises:

  • (ArgumentError)


998
999
1000
1001
1002
1003
1004
1005
# File 'lib/git/lib.rb', line 998

def pull(remote = nil, branch = nil)
  raise ArgumentError, "You must specify a remote if a branch is specified" if remote.nil? && !branch.nil?

  arr_opts = []
  arr_opts << remote if remote
  arr_opts << branch if branch
  command('pull', *arr_opts)
end

#push(remote = nil, branch = nil, opts = nil)

Raises:

  • (ArgumentError)


963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
# File 'lib/git/lib.rb', line 963

def push(remote = nil, branch = nil, opts = nil)
  if opts.nil? && branch.instance_of?(Hash)
    opts = branch
    branch = nil
  end

  if opts.nil? && remote.instance_of?(Hash)
    opts = remote
    remote = nil
  end

  opts ||= {}

  # Small hack to keep backwards compatibility with the 'push(remote, branch, tags)' method signature.
  opts = {:tags => opts} if [true, false].include?(opts)

  raise ArgumentError, "You must specify a remote if a branch is specified" if remote.nil? && !branch.nil?

  arr_opts = []
  arr_opts << '--mirror'  if opts[:mirror]
  arr_opts << '--delete'  if opts[:delete]
  arr_opts << '--force'  if opts[:force] || opts[:f]
  Array(opts[:push_option]).each { |o| arr_opts << '--push-option' << o } if opts[:push_option]
  arr_opts << remote if remote
  arr_opts_with_branch = arr_opts.dup
  arr_opts_with_branch << branch if branch

  if opts[:mirror]
      command('push', *arr_opts_with_branch)
  else
      command('push', *arr_opts_with_branch)
      command('push', '--tags', *arr_opts) if opts[:tags]
  end
end

#read_tree(treeish, opts = {})

reads a tree into the current index file



1023
1024
1025
1026
1027
1028
# File 'lib/git/lib.rb', line 1023

def read_tree(treeish, opts = {})
  arr_opts = []
  arr_opts << "--prefix=#{opts[:prefix]}" if opts[:prefix]
  arr_opts += [treeish]
  command('read-tree', *arr_opts)
end

#remote_add(name, url, opts = {})



891
892
893
894
895
896
897
898
899
900
# File 'lib/git/lib.rb', line 891

def remote_add(name, url, opts = {})
  arr_opts = ['add']
  arr_opts << '-f' if opts[:with_fetch] || opts[:fetch]
  arr_opts << '-t' << opts[:track] if opts[:track]
  arr_opts << '--'
  arr_opts << name
  arr_opts << url

  command('remote', *arr_opts)
end

#remote_remove(name)



910
911
912
# File 'lib/git/lib.rb', line 910

def remote_remove(name)
  command('remote', 'rm', name)
end

#remote_set_url(name, url)



902
903
904
905
906
907
908
# File 'lib/git/lib.rb', line 902

def remote_set_url(name, url)
  arr_opts = ['set-url']
  arr_opts << name
  arr_opts << url

  command('remote', *arr_opts)
end

#remotes



914
915
916
# File 'lib/git/lib.rb', line 914

def remotes
  command_lines('remote')
end

#repack



1014
1015
1016
# File 'lib/git/lib.rb', line 1014

def repack
  command('repack', '-a', '-d')
end

#repository_default_branch(repository) ⇒ String

Returns the name of the default branch of the given repository

Parameters:

  • repository (URI, Pathname, String)

    The (possibly remote) repository to clone from

Returns:

  • (String)

    the name of the default branch



133
134
135
136
137
138
139
140
141
142
143
# File 'lib/git/lib.rb', line 133

def repository_default_branch(repository)
  output = command('ls-remote', '--symref', '--', repository, 'HEAD')

  match_data = output.match(%r{^ref: refs/remotes/origin/(?<default_branch>[^\t]+)\trefs/remotes/origin/HEAD$})
  return match_data[:default_branch] if match_data

  match_data = output.match(%r{^ref: refs/heads/(?<default_branch>[^\t]+)\tHEAD$})
  return match_data[:default_branch] if match_data

  raise 'Unable to determine the default branch'
end

#required_command_version



1108
1109
1110
# File 'lib/git/lib.rb', line 1108

def required_command_version
  [1, 6]
end

#reset(commit, opts = {})



730
731
732
733
734
735
# File 'lib/git/lib.rb', line 730

def reset(commit, opts = {})
  arr_opts = []
  arr_opts << '--hard' if opts[:hard]
  arr_opts << commit if commit
  command('reset', *arr_opts)
end

#return_base_opts_from_clone(clone_dir, opts)



119
120
121
122
123
124
125
# File 'lib/git/lib.rb', line 119

def return_base_opts_from_clone(clone_dir, opts)
  base_opts = {}
  base_opts[:repository] = clone_dir if (opts[:bare] || opts[:mirror])
  base_opts[:working_directory] = clone_dir unless (opts[:bare] || opts[:mirror])
  base_opts[:log] = opts[:log] if opts[:log]
  base_opts
end

#revert(commitish, opts = {})



747
748
749
750
751
752
753
754
755
756
# File 'lib/git/lib.rb', line 747

def revert(commitish, opts = {})
  # Forcing --no-edit as default since it's not an interactive session.
  opts = {:no_edit => true}.merge(opts)

  arr_opts = []
  arr_opts << '--no-edit' if opts[:no_edit]
  arr_opts << commitish

  command('revert', *arr_opts)
end

#revparse(string)



213
214
215
216
217
218
219
220
221
222
# File 'lib/git/lib.rb', line 213

def revparse(string)
  return string if string =~ /^[A-Fa-f0-9]{40}$/  # passing in a sha - just no-op it
  rev = ['head', 'remotes', 'tags'].map do |d|
    File.join(@git_dir, 'refs', d, string)
  end.find do |path|
    File.file?(path)
  end
  return File.read(rev).chomp if rev
  command('rev-parse', string)
end

#rm(path = '.', opts = {})



678
679
680
681
682
683
684
685
686
# File 'lib/git/lib.rb', line 678

def rm(path = '.', opts = {})
  arr_opts = ['-f']  # overrides the up-to-date check by default
  arr_opts << '-r' if opts[:recursive]
  arr_opts << '--cached' if opts[:cached]
  arr_opts << '--'
  arr_opts += Array(path)

  command('rm', *arr_opts)
end

#show(objectish = nil, path = nil) ⇒ String

Shows objects

Parameters:

  • objectish (String|NilClass) (defaults to: nil)

    the target object reference (nil == HEAD)

  • path (String|NilClass) (defaults to: nil)

    the path of the file to be shown

Returns:

  • (String)

    the object information



629
630
631
632
633
634
635
# File 'lib/git/lib.rb', line 629

def show(objectish=nil, path=nil)
  arr_opts = []

  arr_opts << (path ? "#{objectish}:#{path}" : objectish)

  command('show', *arr_opts.compact, chomp: false)
end

#stash_apply(id = nil)



789
790
791
792
793
794
795
# File 'lib/git/lib.rb', line 789

def stash_apply(id = nil)
  if id
    command('stash', 'apply', id)
  else
    command('stash', 'apply')
  end
end

#stash_clear



797
798
799
# File 'lib/git/lib.rb', line 797

def stash_clear
  command('stash', 'clear')
end

#stash_list



801
802
803
# File 'lib/git/lib.rb', line 801

def stash_list
  command('stash', 'list')
end

#stash_save(message)



784
785
786
787
# File 'lib/git/lib.rb', line 784

def stash_save(message)
  output = command('stash', 'save', message)
  output =~ /HEAD is now at/
end

#stashes_all



770
771
772
773
774
775
776
777
778
779
780
781
782
# File 'lib/git/lib.rb', line 770

def stashes_all
  arr = []
  filename = File.join(@git_dir, 'logs/refs/stash')
  if File.exist?(filename)
    File.open(filename) do |f|
      f.each_with_index do |line, i|
        m = line.match(/:(.*)$/)
        arr << [i, m[1].strip]
      end
    end
  end
  arr
end

#tag(name, *opts)



922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
# File 'lib/git/lib.rb', line 922

def tag(name, *opts)
  target = opts[0].instance_of?(String) ? opts[0] : nil

  opts = opts.last.instance_of?(Hash) ? opts.last : {}

  if (opts[:a] || opts[:annotate]) && !(opts[:m] || opts[:message])
    raise  "Can not create an [:a|:annotate] tag without the precense of [:m|:message]."
  end

  arr_opts = []

  arr_opts << '-f' if opts[:force] || opts[:f]
  arr_opts << '-a' if opts[:a] || opts[:annotate]
  arr_opts << '-s' if opts[:s] || opts[:sign]
  arr_opts << '-d' if opts[:d] || opts[:delete]
  arr_opts << name
  arr_opts << target if target

  if opts[:m] || opts[:message]
    arr_opts << '-m' << (opts[:m] || opts[:message])
  end

  command('tag', *arr_opts)
end

#tag_data(name)



277
278
279
280
281
# File 'lib/git/lib.rb', line 277

def tag_data(name)
  sha = sha.to_s
  tdata = command_lines('cat-file', 'tag', name)
  process_tag_data(tdata, name)
end

#tag_sha(tag_name)



1007
1008
1009
1010
1011
1012
# File 'lib/git/lib.rb', line 1007

def tag_sha(tag_name)
  head = File.join(@git_dir, 'refs', 'tags', tag_name)
  return File.read(head).chomp if File.exist?(head)

  command('show-ref',  '--tags', '-s', tag_name)
end

#tags



918
919
920
# File 'lib/git/lib.rb', line 918

def tags
  command_lines('tag')
end

#tree_depth(sha)



360
361
362
# File 'lib/git/lib.rb', line 360

def tree_depth(sha)
  full_tree(sha).size
end

#unmerged



868
869
870
871
872
873
874
# File 'lib/git/lib.rb', line 868

def unmerged
  unmerged = []
  command_lines('diff', "--cached").each do |line|
    unmerged << $1 if line =~ /^\* Unmerged path (.*)/
  end
  unmerged
end

#update_ref(ref, commit)



1047
1048
1049
# File 'lib/git/lib.rb', line 1047

def update_ref(ref, commit)
  command('update-ref', ref, commit)
end

#worktree_add(dir, commitish = nil)



426
427
428
429
# File 'lib/git/lib.rb', line 426

def worktree_add(dir, commitish = nil)
  return command('worktree', 'add', dir, commitish) if !commitish.nil?
  command('worktree', 'add', dir)
end

#worktree_prune



435
436
437
# File 'lib/git/lib.rb', line 435

def worktree_prune
  command('worktree', 'prune')
end

#worktree_remove(dir)



431
432
433
# File 'lib/git/lib.rb', line 431

def worktree_remove(dir)
  command('worktree', 'remove', dir)
end

#worktrees_all



406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
# File 'lib/git/lib.rb', line 406

def worktrees_all
  arr = []
  directory = ''
  # Output example for `worktree list --porcelain`:
  # worktree /code/public/ruby-git
  # HEAD 4bef5abbba073c77b4d0ccc1ffcd0ed7d48be5d4
  # branch refs/heads/master
  #
  # worktree /tmp/worktree-1
  # HEAD b8c63206f8d10f57892060375a86ae911fad356e
  # detached
  #
  command_lines('worktree', 'list', '--porcelain').each do |w|
    s = w.split("\s")
    directory = s[1] if s[0] == 'worktree'
    arr << [directory, s[1]] if s[0] == 'HEAD'
  end
  arr
end

#write_tree



1030
1031
1032
# File 'lib/git/lib.rb', line 1030

def write_tree
  command('write-tree')
end