Class: Grit::Git

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

Defined Under Namespace

Classes: CommandFailed, 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

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

Constructor Details

#initialize(git_dir, options = {}) ⇒ Git

Returns a new instance of Git.



104
105
106
107
108
# File 'lib/grit/git.rb', line 104

def initialize(git_dir, options={})
  self.git_dir    = git_dir
  self.work_tree  = options[:work_tree]
  self.bytes_read = 0
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

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

Methods not defined by a library implementation execute the git command using #native, passing the method name as the git command name.

Examples:

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


375
376
377
# File 'lib/grit/git.rb', line 375

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

Class Attribute Details

.git_binaryObject



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

def git_binary
  @git_binary ||=
    ENV['PATH'].split(':').
      map  { |p| File.join(p, 'git') }.
      find { |p| File.exist?(p) }
end

.git_max_sizeObject

Returns the value of attribute git_max_size.



78
79
80
# File 'lib/grit/git.rb', line 78

def git_max_size
  @git_max_size
end

.git_timeoutObject

Returns the value of attribute git_timeout.



78
79
80
# File 'lib/grit/git.rb', line 78

def git_timeout
  @git_timeout
end

Instance Attribute Details

#bytes_readObject

Returns the value of attribute bytes_read.



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

def bytes_read
  @bytes_read
end

#git_dirObject

Returns the value of attribute git_dir.



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

def git_dir
  @git_dir
end

#work_treeObject

Returns the value of attribute work_tree.



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

def work_tree
  @work_tree
end

Class Method Details

.with_timeout(timeout = 10) ⇒ Object



91
92
93
94
95
96
97
98
99
100
# File 'lib/grit/git.rb', line 91

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

  begin
    Grit::Git.git_timeout = timeout
    yield
  ensure
    Grit::Git.git_timeout = old_timeout
  end
end

Instance Method Details

#apply_patch(options = {}, head_sha = nil, patch = nil) ⇒ Object

Applies the given patch against the given SHA of the current repo.

options - grit command hash head_sha - String SHA or ref to apply the patch to. patch - The String patch to apply. Get this from #get_patch.

Returns the String Tree SHA on a successful patch application, or false.



261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/grit/git.rb', line 261

def apply_patch(options={}, head_sha=nil, patch=nil)
  options, head_sha, patch = {}, options, head_sha if !options.is_a?(Hash)
  options = options.dup
  options[:env] &&= options[:env].dup
  options[:raise] = true

  git_index = create_tempfile('index', true)
  (options[:env] ||= {}).merge!('GIT_INDEX_FILE' => git_index)

  begin
    native(:read_tree, options.dup, head_sha)
    native(:apply, options.merge(:cached => true, :input => patch))
  rescue CommandFailed
    return false
  end
  native(:write_tree, :env => options[:env]).to_s.chomp!
end

#check_applies(options = {}, head_sha = nil, applies_sha = nil) ⇒ Object

Checks if the patch of a commit can be applied to the given head.

options - grit command options hash head_sha - String SHA or ref to check the patch against. applies_sha - String SHA of the patch. The patch itself is retrieved

with #get_patch.

Returns 0 if the patch applies cleanly (according to ‘git apply`), or an Integer that is the sum of the failed exit statuses.



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/grit/git.rb', line 216

def check_applies(options={}, head_sha=nil, applies_sha=nil)
  options, head_sha, applies_sha = {}, options, head_sha if !options.is_a?(Hash)
  options = options.dup
  options[:env] &&= options[:env].dup

  git_index = create_tempfile('index', true)
  (options[:env] ||= {}).merge!('GIT_INDEX_FILE' => git_index)
  options[:raise] = true

  status = 0
  begin
    native(:read_tree, options.dup, head_sha)
    stdin = native(:diff, options.dup, "#{applies_sha}^", applies_sha)
    native(:apply, options.merge(:check => true, :cached => true, :input => stdin))
  rescue CommandFailed => fail
    status += fail.exitstatus
  end
  status
end

#commit_from_sha(id) ⇒ Object



194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/grit/git.rb', line 194

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



188
189
190
191
192
# File 'lib/grit/git.rb', line 188

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

#exist?Boolean

Returns:

  • (Boolean)


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

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



178
179
180
# File 'lib/grit/git.rb', line 178

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



153
154
155
# File 'lib/grit/git.rb', line 153

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)


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

def fs_exist?(file)
  path = File.join(self.git_dir, file)
  raise "Invalid path: #{path}" unless File.absolute_path(path) == path
  File.exist?(path)
end

#fs_mkdir(dir) ⇒ Object

Make a directory

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

Returns nothing



170
171
172
# File 'lib/grit/git.rb', line 170

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



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

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



129
130
131
132
133
# File 'lib/grit/git.rb', line 129

def fs_read(file)
  path = File.join(self.git_dir, file)
  raise "Invalid path: #{path}" unless File.absolute_path(path) == path
  File.read(path)
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



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

def fs_write(file, contents)
  path = File.join(self.git_dir, file)
  raise "Invalid path: #{path}" unless File.absolute_path(path) == path
  FileUtils.mkdir_p(File.dirname(path))
  File.open(path, 'w') do |f|
    f.write(contents)
  end
end

#get_git_object(object_id) ⇒ Object



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

def get_git_object(object_id)
  ruby_git.get_raw_object_by_sha1(object_id).to_hash
end

#get_patch(options = {}, applies_sha = nil) ⇒ Object

Gets a patch for a given SHA using ‘git diff`.

options - grit command options hash applies_sha - String SHA to get the patch from, using this command:

`git diff #{applies_sha}^ #{applies_sha}`

Returns the String patch from ‘git diff`.



243
244
245
246
247
248
249
250
251
252
# File 'lib/grit/git.rb', line 243

def get_patch(options={}, applies_sha=nil)
  options, applies_sha = {}, options if !options.is_a?(Hash)
  options = options.dup
  options[:env] &&= options[:env].dup

  git_index = create_tempfile('index', true)
  (options[:env] ||= {}).merge!('GIT_INDEX_FILE' => git_index)

  native(:diff, options, "#{applies_sha}^", applies_sha)
end

#get_raw_object(object_id) ⇒ Object



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

def get_raw_object(object_id)
  ruby_git.get_raw_object_by_sha1(object_id).content
end

#list_remotesObject



182
183
184
185
186
# File 'lib/grit/git.rb', line 182

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

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

Execute a git command, bypassing any library implementation.

cmd - The name of the git command as a Symbol. Underscores are

converted to dashes as in :rev_parse => 'rev-parse'.

options - Command line option arguments passed to the git command.

Single char keys are converted to short options (:a => -a).
Multi-char keys are converted to long options (:arg => '--arg').
Underscores in keys are converted to dashes. These special options
are used to control command execution and are not passed in command
invocation:
  :timeout - Maximum amount of time the command can run for before
    being aborted. When true, use Grit::Git.git_timeout; when numeric,
    use that number of seconds; when false or 0, disable timeout.
  :base - Set false to avoid passing the --git-dir argument when
    invoking the git command.
  :env - Hash of environment variable key/values that are set on the
    child process.
  :raise - When set true, commands that exit with a non-zero status
    raise a CommandFailed exception. This option is available only on
    platforms that support fork(2).
  :process_info - By default, a single string with output written to
    the process's stdout is returned. Setting this option to true
    results in a [exitstatus, out, err] tuple being returned instead.

args - Non-option arguments passed on the command line.

Optionally yields to the block an IO object attached to the child process’s STDIN.

Examples

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

Returns a String with all output written to the child process’s stdout

when the :process_info option is not set.

Returns a [exitstatus, out, err] tuple when the :process_info option is

set. The exitstatus is an small integer that was the process's exit
status. The out and err elements are the data written to stdout and
stderr as Strings.

Raises Grit::Git::GitTimeout when the timeout is exceeded or when more

than Grit::Git.git_max_size bytes are output.

Raises Grit::Git::CommandFailed when the :raise option is set true and the

git command exits with a non-zero exit status. The CommandFailed's #command,
#exitstatus, and #err attributes can be used to retrieve additional
detail about the error.


322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
# File 'lib/grit/git.rb', line 322

def native(cmd, options = {}, *args, &block)
  args     = args.first if args.size == 1 && args[0].is_a?(Array)
  args.map!    { |a| a.to_s }
  args.reject! { |a| a.empty? }

  # special option arguments
  env = options.delete(:env) || {}
  raise_errors = options.delete(:raise)
  process_info = options.delete(:process_info)

  # more options
  input    = options.delete(:input)
  timeout  = options.delete(:timeout); timeout = true if timeout.nil?
  base     = options.delete(:base);    base    = true if base.nil?

  # build up the git process argv
  argv = []
  argv << Git.git_binary
  argv << "--git-dir=#{git_dir}" if base
  argv << "--work-tree=#{work_tree}" if work_tree
  argv << cmd.to_s.tr('_', '-')
  argv.concat(options_to_argv(options))
  argv.concat(args)

  # run it and deal with fallout
  Grit.log(argv.join(' ')) if Grit.debug

  process =
    Child.new(env, *(argv + [{
      :input   => input,
      :timeout => (Grit::Git.git_timeout if timeout == true),
      :max     => (Grit::Git.git_max_size if timeout == true)
    }]))
  Grit.log(process.out) if Grit.debug
  Grit.log(process.err) if Grit.debug

  status = process.status
  if raise_errors && !status.success?
    raise CommandFailed.new(argv.join(' '), status.exitstatus, process.err, process.out)
  elsif process_info
    [status.exitstatus, process.out, process.err]
  else
    process.out
  end
rescue TimeoutExceeded, MaximumOutputExceeded
  raise GitTimeout, argv.join(' ')
end

#object_exists?(object_id) ⇒ Boolean

Returns:

  • (Boolean)


67
68
69
# File 'lib/grit/git.rb', line 67

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

#options_to_argv(options) ⇒ Object

Transform a ruby-style options hash to command-line arguments sutiable for use with Kernel::exec. No shell escaping is performed.

Returns an Array of String option arguments.



383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
# File 'lib/grit/git.rb', line 383

def options_to_argv(options)
  argv = []
  options.each do |key, val|
    if key.to_s.size == 1
      if val == true
        argv << "-#{key}"
      elsif val == false
        # ignore
      else
        argv << "-#{key}"
        argv << val.to_s
      end
    else
      if val == true
        argv << "--#{key.to_s.tr('_', '-')}"
      elsif val == false
        # ignore
      else
        argv << "--#{key.to_s.tr('_', '-')}=#{val}"
      end
    end
  end
  argv
end

#put_raw_object(content, type) ⇒ Object



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

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

#select_existing_objects(object_ids) ⇒ Object



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

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

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



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

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

#timeout_after(seconds) ⇒ Object

Simple wrapper around Timeout::timeout.

seconds - Float number of seconds before a Timeout::Error is raised. When

true, the Grit::Git.git_timeout value is used. When the timeout is less
than or equal to 0, no timeout is established.

Raises Timeout::Error when the timeout has elapsed.



415
416
417
418
419
420
421
422
# File 'lib/grit/git.rb', line 415

def timeout_after(seconds)
  seconds = self.class.git_timeout if seconds == true
  if seconds && seconds > 0
    Timeout.timeout(seconds) { yield }
  else
    yield
  end
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"]


429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
# File 'lib/grit/git.rb', line 429

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