Class: Grit::Git

Inherits:
Object
  • Object
show all
Includes:
GitRuby
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

#blame_tree, #cat_file, #cat_ref, #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.


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

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, &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")

320
321
322
# File 'lib/grit/git.rb', line 320

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

Class Attribute Details

.git_binaryObject


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

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


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

def git_max_size
  @git_max_size
end

.git_timeoutObject

Returns the value of attribute git_timeout


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

def git_timeout
  @git_timeout
end

Instance Attribute Details

#bytes_readObject

Returns the value of attribute bytes_read


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

def bytes_read
  @bytes_read
end

#git_dirObject

Returns the value of attribute git_dir


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

def git_dir
  @git_dir
end

#work_treeObject

Returns the value of attribute work_tree


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

def work_tree
  @work_tree
end

Class Method Details

.with_timeout(timeout = 10.seconds) ⇒ Object


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

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


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

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


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

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


168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/grit/git.rb', line 168

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


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

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

#exist?Boolean

Returns:

  • (Boolean)

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

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


148
149
150
# File 'lib/grit/git.rb', line 148

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


123
124
125
# File 'lib/grit/git.rb', line 123

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)

94
95
96
# File 'lib/grit/git.rb', line 94

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


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

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


132
133
134
# File 'lib/grit/git.rb', line 132

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


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

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


111
112
113
114
115
116
117
# File 'lib/grit/git.rb', line 111

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


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

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


152
153
154
155
156
157
158
159
160
# File 'lib/grit/git.rb', line 152

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, &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).

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

266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/grit/git.rb', line 266

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

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

  # fall back to using a shell when the last argument looks like it wants to
  # start a pipeline for compatibility with previous versions of grit.
  return run(prefix, cmd, '', options, args) if args[-1].to_s[0] == ?|

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

  # build up the git process argv
  argv = []
  argv << Git.git_binary
  argv << "--git-dir=#{git_dir}" if base
  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 =
    Grit::Process.new(argv, env,
      :input   => input,
      :chdir   => chdir,
      :timeout => (Grit::Git.git_timeout if timeout == true),
      :max     => (Grit::Git.git_max_size if timeout == true)
    )
  status = process.status
  Grit.log(process.out) if Grit.debug
  Grit.log(process.err) if Grit.debug
  if raise_errors && !status.success?
    raise CommandFailed.new(argv.join(' '), status.exitstatus, process.err)
  else
    process.out
  end
rescue Grit::Process::TimeoutExceeded, Grit::Process::MaximumOutputExceeded
  raise GitTimeout, argv.join(' ')
end

#object_exists?(object_id) ⇒ Boolean

Returns:

  • (Boolean)

46
47
48
# File 'lib/grit/git.rb', line 46

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.


328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
# File 'lib/grit/git.rb', line 328

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


42
43
44
# File 'lib/grit/git.rb', line 42

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

#raw_git(command, index) ⇒ Object


221
222
223
224
225
226
227
# File 'lib/grit/git.rb', line 221

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


209
210
211
212
213
214
215
216
217
218
219
# File 'lib/grit/git.rb', line 209

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, &block) ⇒ Object

DEPRECATED OPEN3-BASED COMMAND EXECUTION


371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
# File 'lib/grit/git.rb', line 371

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

  base     = options.delete(:base) rescue nil
  base     = true if base.nil?

  if input = options.delete(:input)
    block = lambda { |stdin| stdin.write(input) }
  end

  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 == '|' || Grit.no_quote) ? a : "\"#{e(a)}\"" }
    gitdir = base ? "--git-dir=\"#{self.git_dir}\"" : ""
    call = "#{prefix}#{Git.git_binary} #{gitdir} #{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 == '|' || Grit.no_quote) ? a : "'#{e(a)}'" }
    gitdir = base ? "--git-dir='#{self.git_dir}'" : ""
    call = "#{prefix}#{Git.git_binary} #{gitdir} #{cmd.to_s.gsub(/_/, '-')} #{(opt_args + ext_args).join(' ')}#{e(postfix)}"
  end

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

#select_existing_objects(object_ids) ⇒ Object


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

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

#sh(command, &block) ⇒ Object


401
402
403
404
405
406
407
408
409
410
411
# File 'lib/grit/git.rb', line 401

def sh(command, &block)
  process =
    Grit::Process.new(
      command, {},
      :timeout => Git.git_timeout,
      :max     => Git.git_max_size
    )
  [process.out, process.err]
rescue Grit::Process::TimeoutExceeded, Grit::Process::MaximumOutputExceeded
  raise GitTimeout, command
end

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


85
86
87
# File 'lib/grit/git.rb', line 85

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.


360
361
362
363
364
365
366
367
# File 'lib/grit/git.rb', line 360

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

423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
# File 'lib/grit/git.rb', line 423

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, &block) ⇒ Object


413
414
415
416
# File 'lib/grit/git.rb', line 413

def wild_sh(command, &block)
  process = Grit::Process.new(command)
  [process.out, process.err]
end