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)


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

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)


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

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)


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

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)


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

def git_work_dir
  @git_work_dir
end

Class Method Details

.warn_if_old_command(lib)



1125
1126
1127
1128
1129
1130
1131
1132
# File 'lib/git/lib.rb', line 1125

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: {})


653
654
655
656
657
658
659
660
661
662
663
664
665
666
# File 'lib/git/lib.rb', line 653

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)



748
749
750
751
752
# File 'lib/git/lib.rb', line 748

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

#apply_mail(patch_file)



754
755
756
757
758
# File 'lib/git/lib.rb', line 754

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



1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
# File 'lib/git/lib.rb', line 1061

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 = "")



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

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

#branch_current



452
453
454
# File 'lib/git/lib.rb', line 452

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

#branch_delete(branch)



799
800
801
# File 'lib/git/lib.rb', line 799

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

#branch_new(branch)



795
796
797
# File 'lib/git/lib.rb', line 795

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

#branches_all



395
396
397
398
399
400
401
402
403
404
405
406
407
# File 'lib/git/lib.rb', line 395

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)



367
368
369
# File 'lib/git/lib.rb', line 367

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: {})


812
813
814
815
816
817
818
819
820
821
822
823
824
825
# File 'lib/git/lib.rb', line 812

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)



827
828
829
830
831
832
# File 'lib/git/lib.rb', line 827

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

#checkout_index(opts = {})



1044
1045
1046
1047
1048
1049
1050
1051
1052
# File 'lib/git/lib.rb', line 1044

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 = {})



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

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 :filter:: specify partial clone :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



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

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 << '--filter' << opts[:filter] if opts[:filter]
  Array(opts[:config]).each { |c| arr_opts << '--config' << c }
  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



693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
# File 'lib/git/lib.rb', line 693

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



240
241
242
243
244
# File 'lib/git/lib.rb', line 240

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 = {})



1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
# File 'lib/git/lib.rb', line 1027

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

#compare_version_to(*other_version) ⇒ Integer

Returns current_command_version <=> other_version

Examples:

lib.current_command_version #=> [2, 42, 0]

lib.compare_version_to(2, 41, 0) #=> 1
lib.compare_version_to(2, 42, 0) #=> 0
lib.compare_version_to(2, 43, 0) #=> -1

Parameters:

  • other_version (Array<Object>)

    the other version to compare to

Returns:

  • (Integer)

    -1 if this version is less than other_version, 0 if equal, or 1 if greater than



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

def compare_version_to(*other_version)
  current_command_version <=> other_version
end

#config_get(name)



585
586
587
# File 'lib/git/lib.rb', line 585

def config_get(name)
  command('config', '--get', name, chdir: @git_dir)
end

#config_list



593
594
595
# File 'lib/git/lib.rb', line 593

def config_list
  parse_config_list command_lines('config', '--list', chdir: @git_dir)
end

#config_remote(name)



575
576
577
578
579
580
581
582
583
# File 'lib/git/lib.rb', line 575

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 ##



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

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



866
867
868
869
870
871
872
873
874
875
876
877
878
879
# File 'lib/git/lib.rb', line 866

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.



1094
1095
1096
1097
1098
1099
# File 'lib/git/lib.rb', line 1094

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



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

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



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

def diff_files
  diff_as_hash('diff-files')
end

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



486
487
488
489
490
491
492
493
# File 'lib/git/lib.rb', line 486

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



535
536
537
# File 'lib/git/lib.rb', line 535

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

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



515
516
517
518
519
520
521
522
523
524
525
526
527
# File 'lib/git/lib.rb', line 515

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 = {})



495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
# File 'lib/git/lib.rb', line 495

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)



267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/git/lib.rb', line 267

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)



937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
# File 'lib/git/lib.rb', line 937

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 << '--update-head-ok' if opts[:u] || opts[:'update-head-ok']
  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 = {})



203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/git/lib.rb', line 203

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)



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

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

#gc



1011
1012
1013
# File 'lib/git/lib.rb', line 1011

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

#global_config_get(name)



589
590
591
# File 'lib/git/lib.rb', line 589

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

#global_config_list



597
598
599
# File 'lib/git/lib.rb', line 597

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

#global_config_set(name, value)



637
638
639
# File 'lib/git/lib.rb', line 637

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]]



463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
# File 'lib/git/lib.rb', line 463

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



570
571
572
# File 'lib/git/lib.rb', line 570

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



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

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)



442
443
444
445
446
447
448
449
450
# File 'lib/git/lib.rb', line 442

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

#log_commits(opts = {})



193
194
195
196
197
198
199
200
201
# File 'lib/git/lib.rb', line 193

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)



539
540
541
542
543
544
545
546
547
548
549
550
551
# File 'lib/git/lib.rb', line 539

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 = {})



553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
# File 'lib/git/lib.rb', line 553

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)



343
344
345
346
347
348
349
350
351
352
353
# File 'lib/git/lib.rb', line 343

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)


1121
1122
1123
# File 'lib/git/lib.rb', line 1121

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

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



834
835
836
837
838
839
840
841
# File 'lib/git/lib.rb', line 834

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)



843
844
845
846
847
848
849
850
851
852
853
854
855
856
# File 'lib/git/lib.rb', line 843

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)



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

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

#namerev(string)



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

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

#object_contents(sha, &block)



339
340
341
# File 'lib/git/lib.rb', line 339

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

#object_size(sha)



235
236
237
# File 'lib/git/lib.rb', line 235

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

#object_type(sha)



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

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

#parse_config(file)



610
611
612
# File 'lib/git/lib.rb', line 610

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

#parse_config_list(lines)



601
602
603
604
605
606
607
608
# File 'lib/git/lib.rb', line 601

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)



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

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)



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
335
336
337
# File 'lib/git/lib.rb', line 298

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)



286
287
288
289
290
291
292
293
294
295
296
# File 'lib/git/lib.rb', line 286

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)


991
992
993
994
995
996
997
998
# File 'lib/git/lib.rb', line 991

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)


954
955
956
957
958
959
960
961
962
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
# File 'lib/git/lib.rb', line 954

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]
  arr_opts << '--all'     if opts[:all] && remote

  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



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

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 = {})



881
882
883
884
885
886
887
888
889
890
# File 'lib/git/lib.rb', line 881

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)



900
901
902
# File 'lib/git/lib.rb', line 900

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

#remote_set_url(name, url)



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

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

  command('remote', *arr_opts)
end

#remotes



904
905
906
# File 'lib/git/lib.rb', line 904

def remotes
  command_lines('remote')
end

#repack



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

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



136
137
138
139
140
141
142
143
144
145
146
# File 'lib/git/lib.rb', line 136

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



1117
1118
1119
# File 'lib/git/lib.rb', line 1117

def required_command_version
  [1, 6]
end

#reset(commit, opts = {})



720
721
722
723
724
725
# File 'lib/git/lib.rb', line 720

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)



122
123
124
125
126
127
128
# File 'lib/git/lib.rb', line 122

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 = {})



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

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)



216
217
218
219
220
221
222
223
224
225
# File 'lib/git/lib.rb', line 216

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 = {})



668
669
670
671
672
673
674
675
676
# File 'lib/git/lib.rb', line 668

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



619
620
621
622
623
624
625
# File 'lib/git/lib.rb', line 619

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)



779
780
781
782
783
784
785
# File 'lib/git/lib.rb', line 779

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

#stash_clear



787
788
789
# File 'lib/git/lib.rb', line 787

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

#stash_list



791
792
793
# File 'lib/git/lib.rb', line 791

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

#stash_save(message)



774
775
776
777
# File 'lib/git/lib.rb', line 774

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

#stashes_all



760
761
762
763
764
765
766
767
768
769
770
771
772
# File 'lib/git/lib.rb', line 760

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)



912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
# File 'lib/git/lib.rb', line 912

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)



280
281
282
283
284
# File 'lib/git/lib.rb', line 280

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)



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

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



908
909
910
# File 'lib/git/lib.rb', line 908

def tags
  command_lines('tag')
end

#tree_depth(sha)



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

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

#unmerged



858
859
860
861
862
863
864
# File 'lib/git/lib.rb', line 858

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

#update_ref(ref, commit)



1040
1041
1042
# File 'lib/git/lib.rb', line 1040

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

#worktree_add(dir, commitish = nil)



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

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

#worktree_prune



438
439
440
# File 'lib/git/lib.rb', line 438

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

#worktree_remove(dir)



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

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

#worktrees_all



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

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



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

def write_tree
  command('write-tree')
end