Class: Grit::Git

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

Defined Under Namespace

Classes: GitTimeout

Class Attribute Summary collapse

Instance Attribute Summary collapse

Attributes included from GitRuby

#git_file_index, #ruby_git_repo

Class Method Summary collapse

Instance Method Summary collapse

Methods included from GitRuby

#blame_tree, #cat_file, #diff, #file_index, #file_size, #file_type, #init, #ls_tree, read_bytes_until, #refs, #rev_list, #rev_parse, #ruby_git, #tags

Constructor Details

#initialize(git_dir) ⇒ Git

Returns a new instance of Git.



57
58
59
60
61
# File 'lib/grit/lib/grit/git.rb', line 57

def initialize(git_dir)
  self.git_dir    = git_dir
  self.work_tree  = git_dir.gsub(/\/\.git$/,'')
  self.bytes_read = 0
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(cmd, options = {}, *args) ⇒ Object

Run the given git command with the specified arguments and return the result as a String

+cmd+ is the command
+options+ is a hash of Ruby style options
+args+ is the list of arguments (to be joined by spaces)

Examples

git.rev_list({:max_count => 10, :header => true}, "master")

Returns String



220
221
222
# File 'lib/grit/lib/grit/git.rb', line 220

def method_missing(cmd, options = {}, *args)
  run('', cmd, '', options, args)
end

Class Attribute Details

.git_binaryObject

Returns the value of attribute git_binary.



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

def git_binary
  @git_binary
end

.git_max_sizeObject

Returns the value of attribute git_max_size.



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

def git_max_size
  @git_max_size
end

.git_timeoutObject

Returns the value of attribute git_timeout.



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

def git_timeout
  @git_timeout
end

Instance Attribute Details

#bytes_readObject

Returns the value of attribute bytes_read.



55
56
57
# File 'lib/grit/lib/grit/git.rb', line 55

def bytes_read
  @bytes_read
end

#git_dirObject

Returns the value of attribute git_dir.



55
56
57
# File 'lib/grit/lib/grit/git.rb', line 55

def git_dir
  @git_dir
end

#work_treeObject

Returns the value of attribute work_tree.



55
56
57
# File 'lib/grit/lib/grit/git.rb', line 55

def work_tree
  @work_tree
end

Class Method Details

.with_timeout(timeout = 10.seconds) ⇒ Object



48
49
50
51
52
53
# File 'lib/grit/lib/grit/git.rb', line 48

def self.with_timeout(timeout = 10.seconds)
  old_timeout = Grit::Git.git_timeout
  Grit::Git.git_timeout = timeout
  yield
  Grit::Git.git_timeout = old_timeout
end

Instance Method Details

#apply_patch(head_sha, patch) ⇒ Object



172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/grit/lib/grit/git.rb', line 172

def apply_patch(head_sha, patch)
  git_index = create_tempfile('index', true)

  git_patch = create_tempfile('patch')
  File.open(git_patch, 'w+') { |f| f.print patch }

  raw_git("git read-tree #{head_sha} 2>/dev/null", git_index)
  (op, exit) = raw_git("git apply --cached < #{git_patch}", git_index)
  if exit == 0
    return raw_git("git write-tree", git_index).first.chomp
  end
  false
end

#check_applies(head_sha, applies_sha) ⇒ Object



159
160
161
162
163
164
# File 'lib/grit/lib/grit/git.rb', line 159

def check_applies(head_sha, applies_sha)
  git_index = create_tempfile('index', true)
  (o1, exit1) = raw_git("git read-tree #{head_sha} 2>/dev/null", git_index)
  (o2, exit2) = raw_git("git diff #{applies_sha}^ #{applies_sha} | git apply --check --cached >/dev/null 2>/dev/null", git_index)
  return (exit1 + exit2)
end

#commit_from_sha(id) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/grit/lib/grit/git.rb', line 146

def commit_from_sha(id)
  git_ruby_repo = GitRuby::Repository.new(self.git_dir)
  object = git_ruby_repo.get_object_by_sha1(id)

  if object.type == :commit
    id
  elsif object.type == :tag
    object.object
  else
    ''
  end
end

#create_tempfile(seed, unlink = false) ⇒ Object



140
141
142
143
144
# File 'lib/grit/lib/grit/git.rb', line 140

def create_tempfile(seed, unlink = false)
  path = Tempfile.new(seed).path
  File.unlink(path) if unlink
  return path
end

#exist?Boolean

Returns:

  • (Boolean)


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

def exist?
  File.exist?(self.git_dir)
end

#fs_chmod(mode, file = '/') ⇒ Object

Chmod the the file or dir and everything beneath

+file+ is the relative path from the Git dir

Returns nothing



126
127
128
# File 'lib/grit/lib/grit/git.rb', line 126

def fs_chmod(mode, file = '/')
  FileUtils.chmod_R(mode, File.join(self.git_dir, file))
end

#fs_delete(file) ⇒ Object

Delete a normal file from the filesystem

+file+ is the relative path from the Git dir

Returns nothing



101
102
103
# File 'lib/grit/lib/grit/git.rb', line 101

def fs_delete(file)
  FileUtils.rm_rf(File.join(self.git_dir, file))
end

#fs_exist?(file) ⇒ Boolean

Check if a normal file exists on the filesystem

+file+ is the relative path from the Git dir

Returns Boolean

Returns:

  • (Boolean)


72
73
74
# File 'lib/grit/lib/grit/git.rb', line 72

def fs_exist?(file)
  File.exist?(File.join(self.git_dir, file))
end

#fs_mkdir(dir) ⇒ Object

Make a directory

+dir+ is the relative path to the directory to create

Returns nothing



118
119
120
# File 'lib/grit/lib/grit/git.rb', line 118

def fs_mkdir(dir)
  FileUtils.mkdir_p(File.join(self.git_dir, dir))
end

#fs_move(from, to) ⇒ Object

Move a normal file

+from+ is the relative path to the current file
+to+ is the relative path to the destination file

Returns nothing



110
111
112
# File 'lib/grit/lib/grit/git.rb', line 110

def fs_move(from, to)
  FileUtils.mv(File.join(self.git_dir, from), File.join(self.git_dir, to))
end

#fs_read(file) ⇒ Object

Read a normal file from the filesystem.

+file+ is the relative path from the Git dir

Returns the String contents of the file



80
81
82
# File 'lib/grit/lib/grit/git.rb', line 80

def fs_read(file)
  File.read(File.join(self.git_dir, file))
end

#fs_write(file, contents) ⇒ Object

Write a normal file to the filesystem.

+file+ is the relative path from the Git dir
+contents+ is the String content to be written

Returns nothing



89
90
91
92
93
94
95
# File 'lib/grit/lib/grit/git.rb', line 89

def fs_write(file, contents)
  path = File.join(self.git_dir, file)
  FileUtils.mkdir_p(File.dirname(path))
  File.open(path, 'w') do |f|
    f.write(contents)
  end
end

#get_patch(applies_sha) ⇒ Object



166
167
168
169
170
# File 'lib/grit/lib/grit/git.rb', line 166

def get_patch(applies_sha)
  git_index = create_tempfile('index', true)
  (patch, exit2) = raw_git("git diff #{applies_sha}^ #{applies_sha}", git_index)
  patch
end

#list_remotesObject



130
131
132
133
134
135
136
137
138
# File 'lib/grit/lib/grit/git.rb', line 130

def list_remotes
  remotes = []
  Dir.chdir(File.join(self.git_dir, 'refs/remotes')) do
    remotes = Dir.glob('*')
  end
  remotes
rescue
  []
end

#native(cmd, options = {}, *args) ⇒ Object

Bypass any pure Ruby implementations and go straight to the native Git command

Returns String



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

def native(cmd, options = {}, *args)
  method_missing(cmd, options, *args)
end

#object_exists?(object_id) ⇒ Boolean

Returns:

  • (Boolean)


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

def object_exists?(object_id)
  ruby_git.object_exists?(object_id)
end

#put_raw_object(content, type) ⇒ Object



22
23
24
# File 'lib/grit/lib/grit/git.rb', line 22

def put_raw_object(content, type)
  ruby_git.put_raw_object(content, type)
end

#raw_git(command, index) ⇒ Object



199
200
201
202
203
204
205
# File 'lib/grit/lib/grit/git.rb', line 199

def raw_git(command, index)
  output = nil
  Dir.chdir(self.git_dir) do
    output = raw_git_call(command, index)
  end
  output
end

#raw_git_call(command, index) ⇒ Object

RAW CALLS WITH ENV SETTINGS



187
188
189
190
191
192
193
194
195
196
197
# File 'lib/grit/lib/grit/git.rb', line 187

def raw_git_call(command, index)
  tmp = ENV['GIT_INDEX_FILE']
  ENV['GIT_INDEX_FILE'] = index
  out = `#{command}`
  after = ENV['GIT_INDEX_FILE'] # someone fucking with us ??
  ENV['GIT_INDEX_FILE'] = tmp
  if after != index
    raise 'environment was changed for the git call'
  end
  [out, $?.exitstatus]
end

#run(prefix, cmd, postfix, options, args) ⇒ Object



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

def run(prefix, cmd, postfix, options, args)
  timeout  = options.delete(:timeout) rescue nil
  timeout  = true if timeout.nil?

  opt_args = transform_options(options)

  if RUBY_PLATFORM.downcase =~ /mswin(?!ce)|mingw|bccwin/
    ext_args = args.reject { |a| a.empty? }.map { |a| (a == '--' || a[0].chr == '|') ? a : "\"#{e(a)}\"" }
    call = "#{prefix}#{Git.git_binary} --git-dir=\"#{self.git_dir}\" #{cmd.to_s.gsub(/_/, '-')} #{(opt_args + ext_args).join(' ')}#{e(postfix)}"
  else
    ext_args = args.reject { |a| a.empty? }.map { |a| (a == '--' || a[0].chr == '|') ? a : "'#{e(a)}'" }
    call = "#{prefix}#{Git.git_binary} --git-dir='#{self.git_dir}' #{cmd.to_s.gsub(/_/, '-')} #{(opt_args + ext_args).join(' ')}#{e(postfix)}"
  end

  Grit.log(call) if Grit.debug
  response, err = timeout ? sh(call) : wild_sh(call)
  Grit.log(response) if Grit.debug
  Grit.log(err) if Grit.debug
  response
end

#select_existing_objects(object_ids) ⇒ Object



30
31
32
33
34
# File 'lib/grit/lib/grit/git.rb', line 30

def select_existing_objects(object_ids)
  object_ids.select do |object_id|
    object_exists?(object_id)
  end
end

#sh(command) ⇒ Object



252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/grit/lib/grit/git.rb', line 252

def sh(command)
  ret, err = '', ''
  Open3.popen3(command) do |_, stdout, stderr|
    Timeout.timeout(self.class.git_timeout) do
      while tmp = stdout.read(1024)
        ret += tmp
        if (@bytes_read += tmp.size) > self.class.git_max_size
          bytes = @bytes_read
          @bytes_read = 0
          raise GitTimeout.new(command, bytes)
        end
      end
    end

    while tmp = stderr.read(1024)
      err += tmp
    end
  end
  [ret, err]
rescue Timeout::Error, Grit::Git::GitTimeout
  bytes = @bytes_read
  @bytes_read = 0
  raise GitTimeout.new(command, bytes)
end

#shell_escape(str) ⇒ Object Also known as: e



63
64
65
# File 'lib/grit/lib/grit/git.rb', line 63

def shell_escape(str)
  str.to_s.gsub("'", "\\\\'").gsub(";", '\\;')
end

#transform_options(options) ⇒ Object

Transform Ruby style options into git command line options

+options+ is a hash of Ruby style options

Returns String[]

e.g. ["--max-count=10", "--header"]


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
# File 'lib/grit/lib/grit/git.rb', line 296

def transform_options(options)
  args = []
  options.keys.each do |opt|
    if opt.to_s.size == 1
      if options[opt] == true
        args << "-#{opt}"
      elsif options[opt] == false
        # ignore
      else
        val = options.delete(opt)
        args << "-#{opt.to_s} '#{e(val)}'"
      end
    else
      if options[opt] == true
        args << "--#{opt.to_s.gsub(/_/, '-')}"
      elsif options[opt] == false
        # ignore
      else
        val = options.delete(opt)
        args << "--#{opt.to_s.gsub(/_/, '-')}='#{e(val)}'"
      end
    end
  end
  args
end

#wild_sh(command) ⇒ Object



277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/grit/lib/grit/git.rb', line 277

def wild_sh(command)
  ret, err = '', ''
  Open3.popen3(command) do |_, stdout, stderr|
    while tmp = stdout.read(1024)
      ret += tmp
    end

    while tmp = stderr.read(1024)
      err += tmp
    end
  end
  [ret, err]
end