3 class GitExecuteError < StandardError
13 def initialize(base = nil)
14 if base.is_a?(Git::Base)
15 @git_dir = base.repo.path
16 @git_index_file = base.index.path
17 @git_work_dir = base.dir.path
18 elsif base.is_a?(Hash)
19 @git_dir = base[:repository]
20 @git_index_file = base[:index]
21 @git_work_dir = base[:working_directory]
29 # tries to clone the given repo
31 # returns {:repository} (if bare)
32 # {:working_directory} otherwise
35 # :remote - name of remote (rather than 'origin')
36 # :bare - no working directory
38 # TODO - make this work with SSH password or auth_key
40 def clone(repository, name, opts = {})
41 @path = opts[:path] || '.'
42 clone_dir = File.join(@path, name)
45 arr_opts << "--bare" if opts[:bare]
46 arr_opts << "-o #{opts[:remote]}" if opts[:remote]
47 arr_opts << repository
50 command('clone', arr_opts)
52 opts[:bare] ? {:repository => clone_dir} : {:working_directory => clone_dir}
55 def log_commits(opts = {})
56 arr_opts = ['--pretty=oneline']
57 arr_opts << "-#{opts[:count]}" if opts[:count]
58 arr_opts << "--since=\"#{opts[:since]}\"" if opts[:since].is_a? String
59 arr_opts << "#{opts[:between][0]}..#{opts[:between][1].to_s}" if (opts[:between] && opts[:between].size == 2)
60 arr_opts << opts[:object] if opts[:object].is_a? String
61 arr_opts << '-- ' + opts[:path_limiter] if opts[:path_limiter].is_a? String
63 command_lines('log', arr_opts).map { |l| l.split.first }
67 command('rev-parse', string)
71 command('cat-file', ['-t', sha])
75 command('cat-file', ['-s', sha]).to_i
78 def object_contents(sha)
79 command('cat-file', ['-p', sha])
84 command_lines('branch', '-a').each do |b|
86 current = true if b[0, 2] == '* '
87 arr << [b.gsub('* ', '').strip, current]
93 command('config', ['--get', name])
98 command_lines('config', ['--list']).each do |line|
99 (key, value) = line.split('=')
105 def config_remote(name)
107 command_lines('config', ['--get-regexp', "remote.#{name}"]).each do |line|
108 (key, value) = line.split
109 hsh[key.gsub("remote.#{name}.", '')] = value
115 # [tree-ish] = [[line_no, match], [line_no, match2]]
116 # [tree-ish] = [[line_no, match], [line_no, match2]]
117 def grep(string, opts = {})
118 opts[:object] = 'HEAD' if !opts[:object]
121 grep_opts << '-i' if opts[:ignore_case]
122 grep_opts << '-v' if opts[:invert_match]
123 grep_opts << "-e '#{string}'"
124 grep_opts << opts[:object] if opts[:object].is_a?(String)
125 grep_opts << ('-- ' + opts[:path_limiter]) if opts[:path_limiter].is_a? String
127 command_lines('grep', grep_opts).each do |line|
128 if m = /(.*)\:(\d+)\:(.*)/.match(line)
130 hsh[m[1]] << [m[2].to_i, m[3]]
136 def diff_full(obj1 = 'HEAD', obj2 = nil, opts = {})
139 diff_opts << obj2 if obj2.is_a?(String)
140 diff_opts << ('-- ' + opts[:path_limiter]) if opts[:path_limiter].is_a? String
142 command('diff', diff_opts)
145 def diff_stats(obj1 = 'HEAD', obj2 = nil, opts = {})
146 diff_opts = ['--numstat']
148 diff_opts << obj2 if obj2.is_a?(String)
149 diff_opts << ('-- ' + opts[:path_limiter]) if opts[:path_limiter].is_a? String
151 hsh = {:total => {:insertions => 0, :deletions => 0, :lines => 0, :files => 0}, :files => {}}
153 command_lines('diff', diff_opts).each do |file|
154 (insertions, deletions, filename) = file.split("\t")
155 hsh[:total][:insertions] += insertions.to_i
156 hsh[:total][:deletions] += deletions.to_i
157 hsh[:total][:lines] = (hsh[:total][:deletions] + hsh[:total][:insertions])
158 hsh[:total][:files] += 1
159 hsh[:files][filename] = {:insertions => insertions.to_i, :deletions => deletions.to_i}
167 def command_lines(cmd, opts)
168 command(cmd, opts).split("\n")
171 def command(cmd, opts = {})
172 ENV['GIT_DIR'] = @git_dir if @git_dir
173 ENV['GIT_INDEX_FILE'] = @git_index_file if @git_index_file
174 ENV['GIT_WORK_DIR'] = @git_work_dir if @git_work_dir
175 Dir.chdir(@git_work_dir || @git_dir || @path) do
176 opts = opts.to_a.join(' ')
177 #puts "git #{cmd} #{opts}"
178 out = `git #{cmd} #{opts} 2>&1`.chomp
181 raise Git::GitExecuteError.new(out)