grit 2.3.0 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +35 -0
- data/grit.gemspec +4 -3
- data/lib/grit.rb +17 -13
- data/lib/grit/actor.rb +20 -4
- data/lib/grit/blame.rb +12 -7
- data/lib/grit/commit.rb +39 -1
- data/lib/grit/git-ruby/internal/pack.rb +3 -3
- data/lib/grit/git-ruby/repository.rb +29 -21
- data/lib/grit/git.rb +181 -58
- data/lib/grit/index.rb +44 -11
- data/lib/grit/jruby.rb +45 -0
- data/lib/grit/lazy.rb +5 -3
- data/lib/grit/process.rb +294 -0
- data/lib/grit/ref.rb +2 -0
- data/lib/grit/repo.rb +45 -8
- data/lib/grit/tag.rb +2 -0
- data/lib/grit/tree.rb +2 -0
- metadata +6 -5
- data/lib/grit/open3_detach.rb +0 -46
data/History.txt
CHANGED
@@ -1,3 +1,38 @@
|
|
1
|
+
== 2.4.0 / 2011-01-06
|
2
|
+
* Major Enhancements
|
3
|
+
* Add support for parsing git notes.
|
4
|
+
* Add `git cat-file --batch` support with Grit::Repo#batch.
|
5
|
+
* Grit::Process is a custom written external command invocation heavily
|
6
|
+
optimized for running git commands quickly and efficiently.
|
7
|
+
* Grit::Git#native takes an :input option for piping data into git
|
8
|
+
commands
|
9
|
+
* Grit::Git#native takes an :env option for setting the git child
|
10
|
+
process's
|
11
|
+
environment without futsing with the parent's environment.
|
12
|
+
* Grit::Git#native takes an :chdir option for setting the current working
|
13
|
+
directory (PWD) of the git child process.
|
14
|
+
* Grit::Git#native takes an :raise => true option that causes an exception
|
15
|
+
to be raised when the git child process exits non-zero.
|
16
|
+
* Minor Enhancements
|
17
|
+
* Grit::Index#commit supports custom committer/author names and dates.
|
18
|
+
* Performance enhancements with internal command output buffering.
|
19
|
+
* Reduce fork/execs needed to execute a smoke command from between 3-4
|
20
|
+
to 1.
|
21
|
+
* Git child processes are now properly parented under the grit Ruby
|
22
|
+
process instead of being dropped under init.
|
23
|
+
* Bug Fixes
|
24
|
+
* Zero-Padding issue in Grit::Index was fixed.
|
25
|
+
* Fix issue where Repo#diff skips the first diff (#42)
|
26
|
+
* Fix Repo.init_bare for repo names not ending in .git (#40)
|
27
|
+
* Fix a variety of process hangs when git stderr output or data written
|
28
|
+
to stdin exceeded PIPE_BUF bytes.
|
29
|
+
|
30
|
+
== 2.3.2 / 2011-01-06
|
31
|
+
* Erroneously released. SemVer violation and misc release screwups.
|
32
|
+
|
33
|
+
== 2.3.1
|
34
|
+
* Skipped for unknown reasons.
|
35
|
+
|
1
36
|
== 2.3.0 / 2010-09-29
|
2
37
|
* Minor Enhancements
|
3
38
|
* Add Grit::Repo.init.
|
data/grit.gemspec
CHANGED
@@ -4,8 +4,8 @@ Gem::Specification.new do |s|
|
|
4
4
|
s.rubygems_version = '1.3.5'
|
5
5
|
|
6
6
|
s.name = 'grit'
|
7
|
-
s.version = '2.
|
8
|
-
s.date = '
|
7
|
+
s.version = '2.4.0'
|
8
|
+
s.date = '2011-01-06'
|
9
9
|
s.rubyforge_project = 'grit'
|
10
10
|
|
11
11
|
s.summary = "Ruby Git bindings."
|
@@ -59,9 +59,10 @@ Gem::Specification.new do |s|
|
|
59
59
|
lib/grit/git-ruby/repository.rb
|
60
60
|
lib/grit/git.rb
|
61
61
|
lib/grit/index.rb
|
62
|
+
lib/grit/jruby.rb
|
62
63
|
lib/grit/lazy.rb
|
63
64
|
lib/grit/merge.rb
|
64
|
-
lib/grit/
|
65
|
+
lib/grit/process.rb
|
65
66
|
lib/grit/ref.rb
|
66
67
|
lib/grit/repo.rb
|
67
68
|
lib/grit/ruby1.9.rb
|
data/lib/grit.rb
CHANGED
@@ -9,27 +9,26 @@ require 'timeout'
|
|
9
9
|
require 'logger'
|
10
10
|
require 'digest/sha1'
|
11
11
|
|
12
|
-
if defined? RUBY_ENGINE && RUBY_ENGINE == 'jruby'
|
13
|
-
require 'open3'
|
14
|
-
elsif RUBY_PLATFORM.downcase =~ /mswin(?!ce)|mingw|bccwin/
|
15
|
-
require 'win32/open3'
|
16
|
-
else
|
17
|
-
require 'grit/open3_detach'
|
18
|
-
end
|
19
|
-
|
20
12
|
# third party
|
21
|
-
|
13
|
+
|
22
14
|
begin
|
23
|
-
gem "mime-types", ">=0"
|
24
15
|
require 'mime/types'
|
25
|
-
|
26
|
-
|
16
|
+
require 'rubygems'
|
17
|
+
rescue LoadError
|
18
|
+
require 'rubygems'
|
19
|
+
begin
|
20
|
+
gem "mime-types", ">=0"
|
21
|
+
require 'mime/types'
|
22
|
+
rescue Gem::LoadError => e
|
23
|
+
puts "WARNING: Gem LoadError: #{e.message}"
|
24
|
+
end
|
27
25
|
end
|
28
26
|
|
29
27
|
# ruby 1.9 compatibility
|
30
28
|
require 'grit/ruby1.9'
|
31
29
|
|
32
30
|
# internal requires
|
31
|
+
require 'grit/process'
|
33
32
|
require 'grit/lazy'
|
34
33
|
require 'grit/errors'
|
35
34
|
require 'grit/git-ruby'
|
@@ -50,8 +49,13 @@ require 'grit/submodule'
|
|
50
49
|
require 'grit/blame'
|
51
50
|
require 'grit/merge'
|
52
51
|
|
52
|
+
# platform specific requires
|
53
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/
|
54
|
+
require 'grit/jruby'
|
55
|
+
end
|
56
|
+
|
53
57
|
module Grit
|
54
|
-
VERSION = '2.
|
58
|
+
VERSION = '2.4.0'
|
55
59
|
|
56
60
|
class << self
|
57
61
|
# Set +debug+ to true to log all git calls and responses
|
data/lib/grit/actor.rb
CHANGED
@@ -11,12 +11,10 @@ module Grit
|
|
11
11
|
alias_method :to_s, :name
|
12
12
|
|
13
13
|
# Create an Actor from a string.
|
14
|
-
# +str+ is the string, which is expected to be in regular git format
|
15
14
|
#
|
16
|
-
#
|
17
|
-
# John Doe <jdoe@example.com>
|
15
|
+
# str - The String in this format: 'John Doe <jdoe@example.com>'
|
18
16
|
#
|
19
|
-
# Returns Actor
|
17
|
+
# Returns Git::Actor.
|
20
18
|
def self.from_string(str)
|
21
19
|
case str
|
22
20
|
when /<.+>/
|
@@ -27,6 +25,24 @@ module Grit
|
|
27
25
|
end
|
28
26
|
end
|
29
27
|
|
28
|
+
# Outputs an actor string for Git commits.
|
29
|
+
#
|
30
|
+
# actor = Actor.new('bob', 'bob@email.com')
|
31
|
+
# actor.output(time) # => "bob <bob@email.com> UNIX_TIME +0700"
|
32
|
+
#
|
33
|
+
# time - The Time the commit was authored or committed.
|
34
|
+
#
|
35
|
+
# Returns a String.
|
36
|
+
def output(time)
|
37
|
+
out = @name.to_s.dup
|
38
|
+
if @email
|
39
|
+
out << " <#{@email}>"
|
40
|
+
end
|
41
|
+
hours = (time.utc_offset.to_f / 3600).to_i # 60 * 60, seconds to hours
|
42
|
+
rem = time.utc_offset.abs % 3600
|
43
|
+
out << " #{time.to_i} #{hours >= 0 ? :+ : :-}#{hours.abs.to_s.rjust(2, '0')}#{rem.to_s.rjust(2, '0')}"
|
44
|
+
end
|
45
|
+
|
30
46
|
# Pretty object inspection
|
31
47
|
def inspect
|
32
48
|
%Q{#<Grit::Actor "#{@name} <#{@email}>">}
|
data/lib/grit/blame.rb
CHANGED
@@ -26,16 +26,21 @@ module Grit
|
|
26
26
|
if line[0, 1] == "\t"
|
27
27
|
lines << line[1, line.size]
|
28
28
|
elsif m = /^(\w{40}) (\d+) (\d+)/.match(line)
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
info[m[3].to_i] = [commits[m[1]], m[2].to_i]
|
29
|
+
commit_id, old_lineno, lineno = m[1], m[2].to_i, m[3].to_i
|
30
|
+
commits[commit_id] = nil if !commits.key?(commit_id)
|
31
|
+
info[lineno] = [commit_id, old_lineno]
|
33
32
|
end
|
34
33
|
end
|
35
34
|
|
35
|
+
# load all commits in single call
|
36
|
+
@repo.batch(*commits.keys).each do |commit|
|
37
|
+
commits[commit.id] = commit
|
38
|
+
end
|
39
|
+
|
36
40
|
# get it together
|
37
|
-
info.sort.each do |lineno,
|
38
|
-
|
41
|
+
info.sort.each do |lineno, (commit_id, old_lineno)|
|
42
|
+
commit = commits[commit_id]
|
43
|
+
final << BlameLine.new(lineno, old_lineno, commit, lines[lineno - 1])
|
39
44
|
end
|
40
45
|
|
41
46
|
@lines = final
|
@@ -58,4 +63,4 @@ module Grit
|
|
58
63
|
|
59
64
|
end # Blame
|
60
65
|
|
61
|
-
end # Grit
|
66
|
+
end # Grit
|
data/lib/grit/commit.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
module Grit
|
2
2
|
|
3
3
|
class Commit
|
4
|
+
extend Lazy
|
5
|
+
|
4
6
|
attr_reader :id
|
5
7
|
attr_reader :repo
|
6
8
|
lazy_reader :parents
|
@@ -13,6 +15,31 @@ module Grit
|
|
13
15
|
lazy_reader :short_message
|
14
16
|
lazy_reader :author_string
|
15
17
|
|
18
|
+
# Parses output from the `git-cat-file --batch'.
|
19
|
+
#
|
20
|
+
# repo - Grit::Repo instance.
|
21
|
+
# sha - String SHA of the Commit.
|
22
|
+
# size - Fixnum size of the object.
|
23
|
+
# object - Parsed String output from `git cat-file --batch`.
|
24
|
+
#
|
25
|
+
# Returns an Array of Grit::Commit objects.
|
26
|
+
def self.parse_batch(repo, sha, size, object)
|
27
|
+
info, message = object.split("\n\n", 2)
|
28
|
+
|
29
|
+
lines = info.split("\n")
|
30
|
+
tree = lines.shift.split(' ', 2).last
|
31
|
+
parents = []
|
32
|
+
parents << lines.shift[7..-1] while lines.first[0, 6] == 'parent'
|
33
|
+
author, authored_date = Grit::Commit.actor(lines.shift)
|
34
|
+
committer, committed_date = Grit::Commit.actor(lines.shift)
|
35
|
+
|
36
|
+
Grit::Commit.new(
|
37
|
+
repo, sha, parents, tree,
|
38
|
+
author, authored_date,
|
39
|
+
committer, committed_date,
|
40
|
+
message.to_s.split("\n"))
|
41
|
+
end
|
42
|
+
|
16
43
|
# Instantiate a new Commit
|
17
44
|
# +id+ is the id of the commit
|
18
45
|
# +parents+ is an array of commit ids (will be converted into Commit instances)
|
@@ -167,7 +194,7 @@ module Grit
|
|
167
194
|
|
168
195
|
def show
|
169
196
|
if parents.size > 1
|
170
|
-
diff = @repo.git.native(
|
197
|
+
diff = @repo.git.native(:diff, {:full_index => true}, "#{parents[0].id}...#{parents[1].id}")
|
171
198
|
else
|
172
199
|
diff = @repo.git.show({:full_index => true, :pretty => 'raw'}, @id)
|
173
200
|
end
|
@@ -214,6 +241,17 @@ module Grit
|
|
214
241
|
@repo.git.format_patch({'1' => true, :stdout => true}, to_s)
|
215
242
|
end
|
216
243
|
|
244
|
+
def notes
|
245
|
+
ret = {}
|
246
|
+
notes = Note.find_all(@repo)
|
247
|
+
notes.each do |note|
|
248
|
+
if n = note.commit.tree/(self.id)
|
249
|
+
ret[note.name] = n.data
|
250
|
+
end
|
251
|
+
end
|
252
|
+
ret
|
253
|
+
end
|
254
|
+
|
217
255
|
# Pretty object inspection
|
218
256
|
def inspect
|
219
257
|
%Q{#<Grit::Commit "#{@id}">}
|
@@ -317,7 +317,7 @@ module Grit
|
|
317
317
|
if indata.size == 0
|
318
318
|
raise PackFormatError, 'error reading pack data'
|
319
319
|
end
|
320
|
-
outdata
|
320
|
+
outdata << zstr.inflate(indata)
|
321
321
|
end
|
322
322
|
if outdata.size > destsize
|
323
323
|
raise PackFormatError, 'error reading pack data'
|
@@ -351,9 +351,9 @@ module Grit
|
|
351
351
|
cp_size |= delta.getord(pos += 1) << 16 if c & 0x40 != 0
|
352
352
|
cp_size = 0x10000 if cp_size == 0
|
353
353
|
pos += 1
|
354
|
-
dest
|
354
|
+
dest << base[cp_off,cp_size]
|
355
355
|
elsif c != 0
|
356
|
-
dest
|
356
|
+
dest << delta[pos,c]
|
357
357
|
pos += c
|
358
358
|
else
|
359
359
|
raise PackFormatError, 'invalid delta data'
|
@@ -701,21 +701,35 @@ module Grit
|
|
701
701
|
@loose
|
702
702
|
end
|
703
703
|
|
704
|
-
def
|
705
|
-
# load alternate loose, too
|
704
|
+
def each_alternate_path(path)
|
706
705
|
alt = File.join(path, 'info/alternates')
|
707
|
-
if File.exists?(alt)
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
706
|
+
return if !File.exists?(alt)
|
707
|
+
|
708
|
+
File.readlines(alt).each do |line|
|
709
|
+
path = line.chomp
|
710
|
+
if path[0, 2] == '..'
|
711
|
+
yield File.expand_path(File.join(@git_dir, 'objects', path))
|
712
|
+
|
713
|
+
# XXX this is here for backward compatibility with grit < 2.3.0
|
714
|
+
# relative alternate objects paths are expanded relative to the
|
715
|
+
# objects directory, not the git repository directory.
|
716
|
+
yield File.expand_path(File.join(@git_dir, path))
|
717
|
+
else
|
718
|
+
yield path
|
715
719
|
end
|
716
720
|
end
|
717
721
|
end
|
718
722
|
|
723
|
+
def load_alternate_loose(path)
|
724
|
+
# load alternate loose, too
|
725
|
+
each_alternate_path path do |path|
|
726
|
+
next if @loaded.include?(path)
|
727
|
+
next if !File.exist?(path)
|
728
|
+
load_loose(path)
|
729
|
+
load_alternate_loose(path)
|
730
|
+
end
|
731
|
+
end
|
732
|
+
|
719
733
|
def load_loose(path)
|
720
734
|
@loaded << path
|
721
735
|
return if !File.exists?(path)
|
@@ -732,17 +746,11 @@ module Grit
|
|
732
746
|
end
|
733
747
|
|
734
748
|
def load_alternate_packs(path)
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
end
|
741
|
-
full_pack = File.join(line.chomp, 'pack')
|
742
|
-
next if @loaded_packs.include?(full_pack)
|
743
|
-
load_packs(full_pack)
|
744
|
-
load_alternate_packs(File.join(line.chomp))
|
745
|
-
end
|
749
|
+
each_alternate_path path do |path|
|
750
|
+
full_pack = File.join(path, 'pack')
|
751
|
+
next if @loaded_packs.include?(full_pack)
|
752
|
+
load_packs(full_pack)
|
753
|
+
load_alternate_packs(path)
|
746
754
|
end
|
747
755
|
end
|
748
756
|
|
data/lib/grit/git.rb
CHANGED
@@ -3,7 +3,8 @@ module Grit
|
|
3
3
|
|
4
4
|
class Git
|
5
5
|
class GitTimeout < RuntimeError
|
6
|
-
|
6
|
+
attr_accessor :command
|
7
|
+
attr_accessor :bytes_read
|
7
8
|
|
8
9
|
def initialize(command = nil, bytes_read = nil)
|
9
10
|
@command = command
|
@@ -11,6 +12,25 @@ module Grit
|
|
11
12
|
end
|
12
13
|
end
|
13
14
|
|
15
|
+
# Raised when a native git command exits with non-zero.
|
16
|
+
class CommandFailed < StandardError
|
17
|
+
# The full git command that failed as a String.
|
18
|
+
attr_reader :command
|
19
|
+
|
20
|
+
# The integer exit status.
|
21
|
+
attr_reader :exitstatus
|
22
|
+
|
23
|
+
# Everything output on the command's stderr as a String.
|
24
|
+
attr_reader :err
|
25
|
+
|
26
|
+
def initialize(command, exitstatus, err='')
|
27
|
+
@command = command
|
28
|
+
@exitstatus = exitstatus
|
29
|
+
@err = err
|
30
|
+
super "Command exited with #{exitstatus}: #{command}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
14
34
|
undef_method :clone
|
15
35
|
|
16
36
|
include GitRuby
|
@@ -34,14 +54,16 @@ module Grit
|
|
34
54
|
end
|
35
55
|
|
36
56
|
class << self
|
37
|
-
attr_accessor :
|
57
|
+
attr_accessor :git_timeout, :git_max_size
|
58
|
+
def git_binary
|
59
|
+
@git_binary ||=
|
60
|
+
ENV['PATH'].split(':').
|
61
|
+
map { |p| File.join(p, 'git') }.
|
62
|
+
find { |p| File.exist?(p) }
|
63
|
+
end
|
64
|
+
attr_writer :git_binary
|
38
65
|
end
|
39
66
|
|
40
|
-
if RUBY_PLATFORM.downcase =~ /mswin(?!ce)|mingw|bccwin/
|
41
|
-
self.git_binary = "git" # using search path
|
42
|
-
else
|
43
|
-
self.git_binary = "/usr/bin/env git"
|
44
|
-
end
|
45
67
|
self.git_timeout = 10
|
46
68
|
self.git_max_size = 5242880 # 5.megabytes
|
47
69
|
|
@@ -206,35 +228,157 @@ module Grit
|
|
206
228
|
# RAW CALLS WITH ENV SETTINGS END
|
207
229
|
|
208
230
|
|
209
|
-
|
210
|
-
#
|
211
|
-
# the
|
212
|
-
#
|
213
|
-
#
|
214
|
-
#
|
231
|
+
# Execute a git command, bypassing any library implementation.
|
232
|
+
#
|
233
|
+
# cmd - The name of the git command as a Symbol. Underscores are
|
234
|
+
# converted to dashes as in :rev_parse => 'rev-parse'.
|
235
|
+
# options - Command line option arguments passed to the git command.
|
236
|
+
# Single char keys are converted to short options (:a => -a).
|
237
|
+
# Multi-char keys are converted to long options (:arg => '--arg').
|
238
|
+
# Underscores in keys are converted to dashes. These special options
|
239
|
+
# are used to control command execution and are not passed in command
|
240
|
+
# invocation:
|
241
|
+
# :timeout - Maximum amount of time the command can run for before
|
242
|
+
# being aborted. When true, use Grit::Git.git_timeout; when numeric,
|
243
|
+
# use that number of seconds; when false or 0, disable timeout.
|
244
|
+
# :base - Set false to avoid passing the --git-dir argument when
|
245
|
+
# invoking the git command.
|
246
|
+
# :env - Hash of environment variable key/values that are set on the
|
247
|
+
# child process.
|
248
|
+
# :raise - When set true, commands that exit with a non-zero status
|
249
|
+
# raise a CommandFailed exception. This option is available only on
|
250
|
+
# platforms that support fork(2).
|
251
|
+
# args - Non-option arguments passed on the command line.
|
252
|
+
#
|
253
|
+
# Optionally yields to the block an IO object attached to the child
|
254
|
+
# process's STDIN.
|
215
255
|
#
|
216
256
|
# Examples
|
257
|
+
# git.native(:rev_list, {:max_count => 10, :header => true}, "master")
|
258
|
+
#
|
259
|
+
# Returns a String with all output written to the child process's stdout.
|
260
|
+
# Raises Grit::Git::GitTimeout when the timeout is exceeded or when more
|
261
|
+
# than Grit::Git.git_max_size bytes are output.
|
262
|
+
# Raises Grit::Git::CommandFailed when the :raise option is set true and the
|
263
|
+
# git command exits with a non-zero exit status. The CommandFailed's #command,
|
264
|
+
# #exitstatus, and #err attributes can be used to retrieve additional
|
265
|
+
# detail about the error.
|
266
|
+
def native(cmd, options = {}, *args, &block)
|
267
|
+
args = args.first if args.size == 1 && args[0].is_a?(Array)
|
268
|
+
args.map! { |a| a.to_s.strip }
|
269
|
+
args.reject! { |a| a.empty? }
|
270
|
+
|
271
|
+
# special option arguments
|
272
|
+
env = options.delete(:env) || {}
|
273
|
+
raise_errors = options.delete(:raise)
|
274
|
+
|
275
|
+
# fall back to using a shell when the last argument looks like it wants to
|
276
|
+
# start a pipeline for compatibility with previous versions of grit.
|
277
|
+
return run(prefix, cmd, '', options, args) if args[-1].to_s[0] == ?|
|
278
|
+
|
279
|
+
# more options
|
280
|
+
input = options.delete(:input)
|
281
|
+
timeout = options.delete(:timeout); timeout = true if timeout.nil?
|
282
|
+
base = options.delete(:base); base = true if base.nil?
|
283
|
+
chdir = options.delete(:chdir)
|
284
|
+
|
285
|
+
# build up the git process argv
|
286
|
+
argv = []
|
287
|
+
argv << Git.git_binary
|
288
|
+
argv << "--git-dir=#{git_dir}" if base
|
289
|
+
argv << cmd.to_s.tr('_', '-')
|
290
|
+
argv.concat(options_to_argv(options))
|
291
|
+
argv.concat(args)
|
292
|
+
|
293
|
+
# run it and deal with fallout
|
294
|
+
Grit.log(argv.join(' ')) if Grit.debug
|
295
|
+
|
296
|
+
process =
|
297
|
+
Grit::Process.new(argv, env,
|
298
|
+
:input => input,
|
299
|
+
:chdir => chdir,
|
300
|
+
:timeout => (Grit::Git.git_timeout if timeout == true),
|
301
|
+
:max => (Grit::Git.git_max_size if timeout == true)
|
302
|
+
)
|
303
|
+
status = process.status
|
304
|
+
Grit.log(process.out) if Grit.debug
|
305
|
+
Grit.log(process.err) if Grit.debug
|
306
|
+
if raise_errors && !status.success?
|
307
|
+
raise CommandFailed.new(argv.join(' '), status.exitstatus, process.err)
|
308
|
+
else
|
309
|
+
process.out
|
310
|
+
end
|
311
|
+
rescue Grit::Process::TimeoutExceeded, Grit::Process::MaximumOutputExceeded
|
312
|
+
raise GitTimeout, argv.join(' ')
|
313
|
+
end
|
314
|
+
|
315
|
+
# Methods not defined by a library implementation execute the git command
|
316
|
+
# using #native, passing the method name as the git command name.
|
317
|
+
#
|
318
|
+
# Examples:
|
217
319
|
# git.rev_list({:max_count => 10, :header => true}, "master")
|
320
|
+
def method_missing(cmd, options={}, *args, &block)
|
321
|
+
native(cmd, options, *args, &block)
|
322
|
+
end
|
323
|
+
|
324
|
+
# Transform a ruby-style options hash to command-line arguments sutiable for
|
325
|
+
# use with Kernel::exec. No shell escaping is performed.
|
218
326
|
#
|
219
|
-
# Returns String
|
220
|
-
def
|
221
|
-
|
327
|
+
# Returns an Array of String option arguments.
|
328
|
+
def options_to_argv(options)
|
329
|
+
argv = []
|
330
|
+
options.each do |key, val|
|
331
|
+
if key.to_s.size == 1
|
332
|
+
if val == true
|
333
|
+
argv << "-#{key}"
|
334
|
+
elsif val == false
|
335
|
+
# ignore
|
336
|
+
else
|
337
|
+
argv << "-#{key}"
|
338
|
+
argv << val.to_s
|
339
|
+
end
|
340
|
+
else
|
341
|
+
if val == true
|
342
|
+
argv << "--#{key.to_s.tr('_', '-')}"
|
343
|
+
elsif val == false
|
344
|
+
# ignore
|
345
|
+
else
|
346
|
+
argv << "--#{key.to_s.tr('_', '-')}=#{val}"
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
argv
|
222
351
|
end
|
223
352
|
|
224
|
-
#
|
353
|
+
# Simple wrapper around Timeout::timeout.
|
354
|
+
#
|
355
|
+
# seconds - Float number of seconds before a Timeout::Error is raised. When
|
356
|
+
# true, the Grit::Git.git_timeout value is used. When the timeout is less
|
357
|
+
# than or equal to 0, no timeout is established.
|
225
358
|
#
|
226
|
-
#
|
227
|
-
def
|
228
|
-
|
359
|
+
# Raises Timeout::Error when the timeout has elapsed.
|
360
|
+
def timeout_after(seconds)
|
361
|
+
seconds = self.class.git_timeout if seconds == true
|
362
|
+
if seconds && seconds > 0
|
363
|
+
Timeout.timeout(seconds) { yield }
|
364
|
+
else
|
365
|
+
yield
|
366
|
+
end
|
229
367
|
end
|
230
368
|
|
231
|
-
|
369
|
+
# DEPRECATED OPEN3-BASED COMMAND EXECUTION
|
370
|
+
|
371
|
+
def run(prefix, cmd, postfix, options, args, &block)
|
232
372
|
timeout = options.delete(:timeout) rescue nil
|
233
373
|
timeout = true if timeout.nil?
|
234
374
|
|
235
375
|
base = options.delete(:base) rescue nil
|
236
376
|
base = true if base.nil?
|
237
377
|
|
378
|
+
if input = options.delete(:input)
|
379
|
+
block = lambda { |stdin| stdin.write(input) }
|
380
|
+
end
|
381
|
+
|
238
382
|
opt_args = transform_options(options)
|
239
383
|
|
240
384
|
if RUBY_PLATFORM.downcase =~ /mswin(?!ce)|mingw|bccwin/
|
@@ -246,50 +390,29 @@ module Grit
|
|
246
390
|
gitdir = base ? "--git-dir='#{self.git_dir}'" : ""
|
247
391
|
call = "#{prefix}#{Git.git_binary} #{gitdir} #{cmd.to_s.gsub(/_/, '-')} #{(opt_args + ext_args).join(' ')}#{e(postfix)}"
|
248
392
|
end
|
393
|
+
|
249
394
|
Grit.log(call) if Grit.debug
|
250
|
-
response, err = timeout ? sh(call) : wild_sh(call)
|
395
|
+
response, err = timeout ? sh(call, &block) : wild_sh(call, &block)
|
251
396
|
Grit.log(response) if Grit.debug
|
252
397
|
Grit.log(err) if Grit.debug
|
253
398
|
response
|
254
399
|
end
|
255
400
|
|
256
|
-
def sh(command)
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
end
|
268
|
-
end
|
269
|
-
|
270
|
-
while tmp = stderr.read(1024)
|
271
|
-
err += tmp
|
272
|
-
end
|
273
|
-
end
|
274
|
-
[ret, err]
|
275
|
-
rescue Timeout::Error, Grit::Git::GitTimeout
|
276
|
-
bytes = @bytes_read
|
277
|
-
@bytes_read = 0
|
278
|
-
raise GitTimeout.new(command, bytes)
|
279
|
-
end
|
280
|
-
|
281
|
-
def wild_sh(command)
|
282
|
-
ret, err = '', ''
|
283
|
-
Open3.popen3(command) do |_, stdout, stderr|
|
284
|
-
while tmp = stdout.read(1024)
|
285
|
-
ret += tmp
|
286
|
-
end
|
401
|
+
def sh(command, &block)
|
402
|
+
process =
|
403
|
+
Grit::Process.new(
|
404
|
+
command, {},
|
405
|
+
:timeout => Git.git_timeout,
|
406
|
+
:max => Git.git_max_size
|
407
|
+
)
|
408
|
+
[process.out, process.err]
|
409
|
+
rescue Grit::Process::TimeoutExceeded, Grit::Process::MaximumOutputExceeded
|
410
|
+
raise GitTimeout, command
|
411
|
+
end
|
287
412
|
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
end
|
292
|
-
[ret, err]
|
413
|
+
def wild_sh(command, &block)
|
414
|
+
process = Grit::Process.new(command)
|
415
|
+
[process.out, process.err]
|
293
416
|
end
|
294
417
|
|
295
418
|
# Transform Ruby style options into git command line options
|
data/lib/grit/index.rb
CHANGED
@@ -63,7 +63,27 @@ module Grit
|
|
63
63
|
self.current_tree = self.repo.tree(tree)
|
64
64
|
end
|
65
65
|
|
66
|
-
# Public: Commit the contents of the index
|
66
|
+
# Public: Commit the contents of the index. This method supports two
|
67
|
+
# formats for arguments:
|
68
|
+
#
|
69
|
+
# message - The String commit message.
|
70
|
+
# options - An optional Hash of index options.
|
71
|
+
# :parents - Array of String commit SHA1s or Grit::Commit
|
72
|
+
# objects to attach this commit to to form a
|
73
|
+
# new head (default: nil).
|
74
|
+
# :actor - The Grit::Actor details of the user making
|
75
|
+
# the commit (default: nil).
|
76
|
+
# :last_tree - The String SHA1 of a tree to compare with
|
77
|
+
# in order to avoid making empty commits
|
78
|
+
# (default: nil).
|
79
|
+
# :head - The String branch name to write this head to
|
80
|
+
# (default: "master").
|
81
|
+
# :committed_date - The Time that the commit was made.
|
82
|
+
# (Default: Time.now)
|
83
|
+
# :authored_date - The Time that the commit was authored.
|
84
|
+
# (Default: committed_date)
|
85
|
+
#
|
86
|
+
# The legacy argument style looks like:
|
67
87
|
#
|
68
88
|
# message - The String commit message.
|
69
89
|
# parents - Array of String commit SHA1s or Grit::Commit objects to
|
@@ -77,6 +97,20 @@ module Grit
|
|
77
97
|
#
|
78
98
|
# Returns a String of the SHA1 of the new commit.
|
79
99
|
def commit(message, parents = nil, actor = nil, last_tree = nil, head = 'master')
|
100
|
+
if parents.is_a?(Hash)
|
101
|
+
actor = parents[:actor]
|
102
|
+
committer = parents[:committer]
|
103
|
+
author = parents[:author]
|
104
|
+
last_tree = parents[:last_tree]
|
105
|
+
head = parents[:head]
|
106
|
+
committed_date = parents[:committed_date]
|
107
|
+
authored_date = parents[:authored_date]
|
108
|
+
parents = parents[:parents]
|
109
|
+
end
|
110
|
+
|
111
|
+
committer ||= actor
|
112
|
+
author ||= committer
|
113
|
+
|
80
114
|
tree_sha1 = write_tree(self.tree, self.current_tree)
|
81
115
|
|
82
116
|
# don't write identical commits
|
@@ -88,18 +122,16 @@ module Grit
|
|
88
122
|
contents << ['parent', p].join(' ')
|
89
123
|
end if parents
|
90
124
|
|
91
|
-
|
92
|
-
name = actor.name
|
93
|
-
email = actor.email
|
94
|
-
else
|
125
|
+
committer ||= begin
|
95
126
|
config = Config.new(self.repo)
|
96
|
-
name
|
97
|
-
email = config['user.email']
|
127
|
+
Actor.new(config['user.name'], config['user.email'])
|
98
128
|
end
|
129
|
+
author ||= committer
|
130
|
+
committed_date ||= Time.now
|
131
|
+
authored_date ||= committed_date
|
99
132
|
|
100
|
-
|
101
|
-
contents << ['
|
102
|
-
contents << ['committer', author_string].join(' ')
|
133
|
+
contents << ['author', author.output(authored_date)].join(' ')
|
134
|
+
contents << ['committer', committer.output(committed_date)].join(' ')
|
103
135
|
contents << ''
|
104
136
|
contents << message
|
105
137
|
|
@@ -125,7 +157,8 @@ module Grit
|
|
125
157
|
sha = [obj.id].pack("H*")
|
126
158
|
k = obj.name
|
127
159
|
k += '/' if (obj.class == Grit::Tree)
|
128
|
-
|
160
|
+
tmode = obj.mode.to_i.to_s ## remove zero-padding
|
161
|
+
tree_contents[k] = "%s %s\0%s" % [tmode, obj.name, sha]
|
129
162
|
end if now_tree
|
130
163
|
|
131
164
|
# overwrite with new tree contents
|
data/lib/grit/jruby.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'grit/process'
|
2
|
+
|
3
|
+
module Grit
|
4
|
+
# Override the Grit::Process class's popen4 and waitpid methods to work around
|
5
|
+
# various quirks in JRuby.
|
6
|
+
class Process
|
7
|
+
# Use JRuby's built in IO.popen4 but emulate the special spawn env
|
8
|
+
# and options arguments as best we can.
|
9
|
+
def popen4(*argv)
|
10
|
+
env = (argv.shift if argv[0].is_a?(Hash)) || {}
|
11
|
+
opt = (argv.pop if argv[-1].is_a?(Hash)) || {}
|
12
|
+
|
13
|
+
# emulate :chdir option
|
14
|
+
if opt[:chdir]
|
15
|
+
previous_dir = Dir.pwd
|
16
|
+
Dir.chdir(opt[:chdir])
|
17
|
+
else
|
18
|
+
previous_dir = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
# emulate :env option
|
22
|
+
if env.size > 0
|
23
|
+
previous_env = ENV
|
24
|
+
ENV.merge!(env)
|
25
|
+
else
|
26
|
+
previous_env = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
pid, stdin, stdout, stderr = IO.popen4(*argv)
|
30
|
+
ensure
|
31
|
+
ENV.replace(previous_env) if previous_env
|
32
|
+
Dir.chdir(previous_dir) if previous_dir
|
33
|
+
end
|
34
|
+
|
35
|
+
# JRuby always raises ECHILD on pids returned from its IO.popen4 method
|
36
|
+
# for some reason. Return a fake Process::Status object.
|
37
|
+
FakeStatus = Struct.new(:pid, :exitstatus, :success?, :fake?)
|
38
|
+
def waitpid(pid)
|
39
|
+
::Process::waitpid(pid)
|
40
|
+
$?
|
41
|
+
rescue Errno::ECHILD
|
42
|
+
FakeStatus.new(pid, 0, true, true)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/grit/lazy.rb
CHANGED
@@ -16,6 +16,10 @@
|
|
16
16
|
# => 2
|
17
17
|
#
|
18
18
|
module Lazy
|
19
|
+
def self.extended(klass)
|
20
|
+
klass.send(:attr_writer, :lazy_source)
|
21
|
+
end
|
22
|
+
|
19
23
|
def lazy_reader(*args)
|
20
24
|
args.each do |arg|
|
21
25
|
ivar = "@#{arg}"
|
@@ -28,6 +32,4 @@ module Lazy
|
|
28
32
|
end
|
29
33
|
end
|
30
34
|
end
|
31
|
-
end
|
32
|
-
|
33
|
-
Object.extend Lazy unless Object.ancestors.include? Lazy
|
35
|
+
end
|
data/lib/grit/process.rb
ADDED
@@ -0,0 +1,294 @@
|
|
1
|
+
module Grit
|
2
|
+
# Grit::Process includes logic for executing child processes and
|
3
|
+
# reading/writing from their standard input, output, and error streams.
|
4
|
+
#
|
5
|
+
# Create an run a process to completion:
|
6
|
+
#
|
7
|
+
# >> process = Grit::Process.new(['git', '--help'])
|
8
|
+
#
|
9
|
+
# Retrieve stdout or stderr output:
|
10
|
+
#
|
11
|
+
# >> process.out
|
12
|
+
# => "usage: git [--version] [--exec-path[=GIT_EXEC_PATH]]\n ..."
|
13
|
+
# >> process.err
|
14
|
+
# => ""
|
15
|
+
#
|
16
|
+
# Check process exit status information:
|
17
|
+
#
|
18
|
+
# >> process.status
|
19
|
+
# => #<Process::Status: pid=80718,exited(0)>
|
20
|
+
#
|
21
|
+
# Grit::Process is designed to take all input in a single string and
|
22
|
+
# provides all output as single strings. It is therefore not well suited
|
23
|
+
# to streaming large quantities of data in and out of commands.
|
24
|
+
#
|
25
|
+
# Q: Why not use popen3 or hand-roll fork/exec code?
|
26
|
+
#
|
27
|
+
# - It's more efficient than popen3 and provides meaningful process
|
28
|
+
# hierarchies because it performs a single fork/exec. (popen3 double forks
|
29
|
+
# to avoid needing to collect the exit status and also calls
|
30
|
+
# Process::detach which creates a Ruby Thread!!!!).
|
31
|
+
#
|
32
|
+
# - It's more portable than hand rolled pipe, fork, exec code because
|
33
|
+
# fork(2) and exec(2) aren't available on all platforms. In those cases,
|
34
|
+
# Grit::Process falls back to using whatever janky substitutes the platform
|
35
|
+
# provides.
|
36
|
+
#
|
37
|
+
# - It handles all max pipe buffer hang cases, which is non trivial to
|
38
|
+
# implement correctly and must be accounted for with either popen3 or
|
39
|
+
# hand rolled fork/exec code.
|
40
|
+
class Process
|
41
|
+
# Create and execute a new process.
|
42
|
+
#
|
43
|
+
# argv - Array of [command, arg1, ...] strings to use as the new
|
44
|
+
# process's argv. When argv is a String, the shell is used
|
45
|
+
# to interpret the command.
|
46
|
+
# env - The new process's environment variables. This is merged with
|
47
|
+
# the current environment as if by ENV.merge(env).
|
48
|
+
# options - Additional options:
|
49
|
+
# :input => str to write str to the process's stdin.
|
50
|
+
# :timeout => int number of seconds before we given up.
|
51
|
+
# :max => total number of output bytes
|
52
|
+
# A subset of Process:spawn options are also supported on all
|
53
|
+
# platforms:
|
54
|
+
# :chdir => str to start the process in different working dir.
|
55
|
+
#
|
56
|
+
# Returns a new Process instance that has already executed to completion.
|
57
|
+
# The out, err, and status attributes are immediately available.
|
58
|
+
def initialize(argv, env={}, options={})
|
59
|
+
@argv = argv
|
60
|
+
@env = env
|
61
|
+
|
62
|
+
@options = options.dup
|
63
|
+
@input = @options.delete(:input)
|
64
|
+
@timeout = @options.delete(:timeout)
|
65
|
+
@max = @options.delete(:max)
|
66
|
+
@options.delete(:chdir) if @options[:chdir].nil?
|
67
|
+
|
68
|
+
exec!
|
69
|
+
end
|
70
|
+
|
71
|
+
# All data written to the child process's stdout stream as a String.
|
72
|
+
attr_reader :out
|
73
|
+
|
74
|
+
# All data written to the child process's stderr stream as a String.
|
75
|
+
attr_reader :err
|
76
|
+
|
77
|
+
# A Process::Status object with information on how the child exited.
|
78
|
+
attr_reader :status
|
79
|
+
|
80
|
+
# Total command execution time (wall-clock time)
|
81
|
+
attr_reader :runtime
|
82
|
+
|
83
|
+
# Determine if the process did exit with a zero exit status.
|
84
|
+
def success?
|
85
|
+
@status && @status.success?
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
# Execute command, write input, and read output. This is called
|
90
|
+
# immediately when a new instance of this object is initialized.
|
91
|
+
def exec!
|
92
|
+
# when argv is a string, use /bin/sh to interpret command
|
93
|
+
argv = @argv
|
94
|
+
argv = ['/bin/sh', '-c', argv.to_str] if argv.respond_to?(:to_str)
|
95
|
+
|
96
|
+
# spawn the process and hook up the pipes
|
97
|
+
pid, stdin, stdout, stderr = popen4(@env, *(argv + [@options]))
|
98
|
+
|
99
|
+
# async read from all streams into buffers
|
100
|
+
@out, @err = read_and_write(@input, stdin, stdout, stderr, @timeout, @max)
|
101
|
+
|
102
|
+
# grab exit status
|
103
|
+
@status = waitpid(pid)
|
104
|
+
rescue Object => boom
|
105
|
+
[stdin, stdout, stderr].each { |fd| fd.close rescue nil }
|
106
|
+
if @status.nil?
|
107
|
+
::Process.kill('TERM', pid) rescue nil
|
108
|
+
@status = waitpid(pid) rescue nil
|
109
|
+
end
|
110
|
+
raise
|
111
|
+
end
|
112
|
+
|
113
|
+
# Exception raised when the total number of bytes output on the command's
|
114
|
+
# stderr and stdout streams exceeds the maximum output size (:max option).
|
115
|
+
class MaximumOutputExceeded < StandardError
|
116
|
+
end
|
117
|
+
|
118
|
+
# Exception raised when timeout is exceeded.
|
119
|
+
class TimeoutExceeded < StandardError
|
120
|
+
end
|
121
|
+
|
122
|
+
# Maximum buffer size for reading
|
123
|
+
BUFSIZE = (32 * 1024)
|
124
|
+
|
125
|
+
# Start a select loop writing any input on the child's stdin and reading
|
126
|
+
# any output from the child's stdout or stderr.
|
127
|
+
#
|
128
|
+
# input - String input to write on stdin. May be nil.
|
129
|
+
# stdin - The write side IO object for the child's stdin stream.
|
130
|
+
# stdout - The read side IO object for the child's stdout stream.
|
131
|
+
# stderr - The read side IO object for the child's stderr stream.
|
132
|
+
# timeout - An optional Numeric specifying the total number of seconds
|
133
|
+
# the read/write operations should occur for.
|
134
|
+
#
|
135
|
+
# Returns an [out, err] tuple where both elements are strings with all
|
136
|
+
# data written to the stdout and stderr streams, respectively.
|
137
|
+
# Raises TimeoutExceeded when all data has not been read / written within
|
138
|
+
# the duration specified in the timeout argument.
|
139
|
+
# Raises MaximumOutputExceeded when the total number of bytes output
|
140
|
+
# exceeds the amount specified by the max argument.
|
141
|
+
def read_and_write(input, stdin, stdout, stderr, timeout=nil, max=nil)
|
142
|
+
input ||= ''
|
143
|
+
max = nil if max && max <= 0
|
144
|
+
out, err = '', ''
|
145
|
+
offset = 0
|
146
|
+
|
147
|
+
timeout = nil if timeout && timeout <= 0.0
|
148
|
+
@runtime = 0.0
|
149
|
+
start = Time.now
|
150
|
+
|
151
|
+
writers = [stdin]
|
152
|
+
readers = [stdout, stderr]
|
153
|
+
t = timeout
|
154
|
+
while readers.any? || writers.any?
|
155
|
+
ready = IO.select(readers, writers, readers + writers, t)
|
156
|
+
raise TimeoutExceeded if ready.nil?
|
157
|
+
|
158
|
+
# write to stdin stream
|
159
|
+
ready[1].each do |fd|
|
160
|
+
begin
|
161
|
+
boom = nil
|
162
|
+
size = fd.write_nonblock(input)
|
163
|
+
input = input[size, input.size]
|
164
|
+
rescue Errno::EPIPE => boom
|
165
|
+
rescue Errno::EAGAIN, Errno::EINTR
|
166
|
+
end
|
167
|
+
if boom || input.size == 0
|
168
|
+
stdin.close
|
169
|
+
writers.delete(stdin)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# read from stdout and stderr streams
|
174
|
+
ready[0].each do |fd|
|
175
|
+
buf = (fd == stdout) ? out : err
|
176
|
+
begin
|
177
|
+
buf << fd.readpartial(BUFSIZE)
|
178
|
+
rescue Errno::EAGAIN, Errno::EINTR
|
179
|
+
rescue EOFError
|
180
|
+
readers.delete(fd)
|
181
|
+
fd.close
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# keep tabs on the total amount of time we've spent here
|
186
|
+
@runtime = Time.now - start
|
187
|
+
if timeout
|
188
|
+
t = timeout - @runtime
|
189
|
+
raise TimeoutExceeded if t < 0.0
|
190
|
+
end
|
191
|
+
|
192
|
+
# maybe we've hit our max output
|
193
|
+
if max && ready[0].any? && (out.size + err.size) > max
|
194
|
+
raise MaximumOutputExceeded
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
[out, err]
|
199
|
+
end
|
200
|
+
|
201
|
+
# Spawn a child process, perform IO redirection and environment prep, and
|
202
|
+
# return the running process's pid.
|
203
|
+
#
|
204
|
+
# This method implements a limited subset of Ruby 1.9's Process::spawn.
|
205
|
+
# The idea is that we can just use that when available, since most platforms
|
206
|
+
# will eventually build in special (and hopefully good) support for it.
|
207
|
+
#
|
208
|
+
# env - Hash of { name => val } environment variables set in the child
|
209
|
+
# process.
|
210
|
+
# argv - New process's argv as an Array. When this value is a string,
|
211
|
+
# the command may be run through the system /bin/sh or
|
212
|
+
# options - Supports a subset of Process::spawn options, including:
|
213
|
+
# :chdir => str to change the directory to str in the child
|
214
|
+
# FD => :close to close a file descriptor in the child
|
215
|
+
# :in => FD to redirect child's stdin to FD
|
216
|
+
# :out => FD to redirect child's stdout to FD
|
217
|
+
# :err => FD to redirect child's stderr to FD
|
218
|
+
#
|
219
|
+
# Returns the pid of the new process as an integer. The process exit status
|
220
|
+
# must be obtained using Process::waitpid.
|
221
|
+
def spawn(env, *argv)
|
222
|
+
options = (argv.pop if argv[-1].kind_of?(Hash)) || {}
|
223
|
+
fork do
|
224
|
+
# { fd => :close } in options means close that fd
|
225
|
+
options.each { |k,v| k.close if v == :close && !k.closed? }
|
226
|
+
|
227
|
+
# reopen stdin, stdout, and stderr on provided fds
|
228
|
+
STDIN.reopen(options[:in])
|
229
|
+
STDOUT.reopen(options[:out])
|
230
|
+
STDERR.reopen(options[:err])
|
231
|
+
|
232
|
+
# setup child environment
|
233
|
+
env.each { |k, v| ENV[k] = v }
|
234
|
+
|
235
|
+
# { :chdir => '/' } in options means change into that dir
|
236
|
+
::Dir.chdir(options[:chdir]) if options[:chdir]
|
237
|
+
|
238
|
+
# do the deed
|
239
|
+
::Kernel::exec(*argv)
|
240
|
+
exit! 1
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# Start a process with spawn options and return
|
245
|
+
# popen4([env], command, arg1, arg2, [opt])
|
246
|
+
#
|
247
|
+
# env - The child process's environment as a Hash.
|
248
|
+
# command - The command and zero or more arguments.
|
249
|
+
# options - An options hash.
|
250
|
+
#
|
251
|
+
# See Ruby 1.9 IO.popen and Process::spawn docs for more info:
|
252
|
+
# http://www.ruby-doc.org/core-1.9/classes/IO.html#M001640
|
253
|
+
#
|
254
|
+
# Returns a [pid, stdin, stderr, stdout] tuple where pid is the child
|
255
|
+
# process's pid, stdin is a writeable IO object, and stdout + stderr are
|
256
|
+
# readable IO objects.
|
257
|
+
def popen4(*argv)
|
258
|
+
# create some pipes (see pipe(2) manual -- the ruby docs suck)
|
259
|
+
ird, iwr = IO.pipe
|
260
|
+
ord, owr = IO.pipe
|
261
|
+
erd, ewr = IO.pipe
|
262
|
+
|
263
|
+
# spawn the child process with either end of pipes hooked together
|
264
|
+
opts =
|
265
|
+
((argv.pop if argv[-1].is_a?(Hash)) || {}).merge(
|
266
|
+
# redirect fds # close other sides
|
267
|
+
:in => ird, iwr => :close,
|
268
|
+
:out => owr, ord => :close,
|
269
|
+
:err => ewr, erd => :close
|
270
|
+
)
|
271
|
+
pid = spawn(*(argv + [opts]))
|
272
|
+
|
273
|
+
[pid, iwr, ord, erd]
|
274
|
+
ensure
|
275
|
+
# we're in the parent, close child-side fds
|
276
|
+
[ird, owr, ewr].each { |fd| fd.close }
|
277
|
+
end
|
278
|
+
|
279
|
+
# Wait for the child process to exit
|
280
|
+
#
|
281
|
+
# Returns the Process::Status object obtained by reaping the process.
|
282
|
+
def waitpid(pid)
|
283
|
+
::Process::waitpid(pid)
|
284
|
+
$?
|
285
|
+
end
|
286
|
+
|
287
|
+
# Use native Process::spawn implementation on Ruby 1.9.
|
288
|
+
if ::Process.respond_to?(:spawn)
|
289
|
+
def spawn(*argv)
|
290
|
+
::Process.spawn(*argv)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
data/lib/grit/ref.rb
CHANGED
data/lib/grit/repo.rb
CHANGED
@@ -2,6 +2,9 @@ module Grit
|
|
2
2
|
|
3
3
|
class Repo
|
4
4
|
DAEMON_EXPORT_FILE = 'git-daemon-export-ok'
|
5
|
+
BATCH_PARSERS = {
|
6
|
+
'commit' => ::Grit::Commit
|
7
|
+
}
|
5
8
|
|
6
9
|
# Public: The String path of the Git repo.
|
7
10
|
attr_accessor :path
|
@@ -55,7 +58,7 @@ module Grit
|
|
55
58
|
|
56
59
|
# Public: Initialize a git repository (create it on the filesystem). By
|
57
60
|
# default, the newly created repository will contain a working directory.
|
58
|
-
# If you would like to create a bare repo, use
|
61
|
+
# If you would like to create a bare repo, use Grit::Repo.init_bare.
|
59
62
|
#
|
60
63
|
# path - The String full path to the repo. Traditionally ends with
|
61
64
|
# "/<name>.git".
|
@@ -96,6 +99,7 @@ module Grit
|
|
96
99
|
git = Git.new(path)
|
97
100
|
git.fs_mkdir('..')
|
98
101
|
git.init(git_options)
|
102
|
+
repo_options = {:is_bare => true}.merge(repo_options)
|
99
103
|
self.new(path, repo_options)
|
100
104
|
end
|
101
105
|
|
@@ -155,6 +159,40 @@ module Grit
|
|
155
159
|
Repo.new(self.path)
|
156
160
|
end
|
157
161
|
|
162
|
+
# Public: Return the full Git objects from the given SHAs. Only Commit
|
163
|
+
# objects are parsed for now.
|
164
|
+
#
|
165
|
+
# *shas - Array of String SHAs.
|
166
|
+
#
|
167
|
+
# Returns an Array of Grit objects (Grit::Commit).
|
168
|
+
def batch(*shas)
|
169
|
+
shas.flatten!
|
170
|
+
text = git.native(:cat_file, {:batch => true, :input => (shas * "\n")})
|
171
|
+
parse_batch(text)
|
172
|
+
end
|
173
|
+
|
174
|
+
# Parses `git cat-file --batch` output, returning an array of Grit objects.
|
175
|
+
#
|
176
|
+
# text - Raw String output.
|
177
|
+
#
|
178
|
+
# Returns an Array of Grit objects (Grit::Commit).
|
179
|
+
def parse_batch(text)
|
180
|
+
io = StringIO.new(text)
|
181
|
+
objects = []
|
182
|
+
while line = io.gets
|
183
|
+
sha, type, size = line.split(" ", 3)
|
184
|
+
parser = BATCH_PARSERS[type]
|
185
|
+
if type == 'missing' || !parser
|
186
|
+
objects << nil
|
187
|
+
next
|
188
|
+
end
|
189
|
+
|
190
|
+
object = io.read(size.to_i + 1)
|
191
|
+
objects << parser.parse_batch(self, sha, size, object)
|
192
|
+
end
|
193
|
+
objects
|
194
|
+
end
|
195
|
+
|
158
196
|
# The project's description. Taken verbatim from GIT_REPO/description
|
159
197
|
#
|
160
198
|
# Returns String
|
@@ -415,7 +453,8 @@ module Grit
|
|
415
453
|
else
|
416
454
|
# NO PARENTS:
|
417
455
|
cmd = "-r -t #{commit_sha}"
|
418
|
-
revs = self.git.
|
456
|
+
revs = self.git.native(:ls_tree, {:timeout => false, :r => true, :t => true}, commit_sha).
|
457
|
+
split("\n").map{ |a| a.split("\t").first.split(' ')[2] }
|
419
458
|
end
|
420
459
|
revs << self.commit(commit_sha).tree.id
|
421
460
|
Grit.no_quote = false
|
@@ -461,7 +500,7 @@ module Grit
|
|
461
500
|
diff = self.git.native('diff', {}, a, b, '--', *paths)
|
462
501
|
|
463
502
|
if diff =~ /diff --git a/
|
464
|
-
diff = diff.sub(
|
503
|
+
diff = diff.sub(/.*?(diff --git a)/m, '\1')
|
465
504
|
else
|
466
505
|
diff = ''
|
467
506
|
end
|
@@ -558,11 +597,9 @@ module Grit
|
|
558
597
|
# Returns Array[String] (pathnames of alternates)
|
559
598
|
def alternates
|
560
599
|
alternates_path = "objects/info/alternates"
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
[]
|
565
|
-
end
|
600
|
+
self.git.fs_read(alternates_path).strip.split("\n")
|
601
|
+
rescue Errno::ENOENT
|
602
|
+
[]
|
566
603
|
end
|
567
604
|
|
568
605
|
# Sets the alternates
|
data/lib/grit/tag.rb
CHANGED
data/lib/grit/tree.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 31
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 2
|
8
|
-
-
|
8
|
+
- 4
|
9
9
|
- 0
|
10
|
-
version: 2.
|
10
|
+
version: 2.4.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Tom Preston-Werner
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date:
|
19
|
+
date: 2011-01-06 00:00:00 -08:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
@@ -105,9 +105,10 @@ files:
|
|
105
105
|
- lib/grit/git-ruby/repository.rb
|
106
106
|
- lib/grit/git.rb
|
107
107
|
- lib/grit/index.rb
|
108
|
+
- lib/grit/jruby.rb
|
108
109
|
- lib/grit/lazy.rb
|
109
110
|
- lib/grit/merge.rb
|
110
|
-
- lib/grit/
|
111
|
+
- lib/grit/process.rb
|
111
112
|
- lib/grit/ref.rb
|
112
113
|
- lib/grit/repo.rb
|
113
114
|
- lib/grit/ruby1.9.rb
|
data/lib/grit/open3_detach.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
module Open3
|
2
|
-
extend self
|
3
|
-
|
4
|
-
def popen3(*cmd)
|
5
|
-
pw = IO::pipe # pipe[0] for read, pipe[1] for write
|
6
|
-
pr = IO::pipe
|
7
|
-
pe = IO::pipe
|
8
|
-
|
9
|
-
pid = fork{
|
10
|
-
# child
|
11
|
-
fork{
|
12
|
-
# grandchild
|
13
|
-
pw[1].close
|
14
|
-
STDIN.reopen(pw[0])
|
15
|
-
pw[0].close
|
16
|
-
|
17
|
-
pr[0].close
|
18
|
-
STDOUT.reopen(pr[1])
|
19
|
-
pr[1].close
|
20
|
-
|
21
|
-
pe[0].close
|
22
|
-
STDERR.reopen(pe[1])
|
23
|
-
pe[1].close
|
24
|
-
|
25
|
-
exec(*cmd)
|
26
|
-
}
|
27
|
-
exit!(0)
|
28
|
-
}
|
29
|
-
|
30
|
-
pw[0].close
|
31
|
-
pr[1].close
|
32
|
-
pe[1].close
|
33
|
-
Process.waitpid(pid)
|
34
|
-
pi = [pw[1], pr[0], pe[0]]
|
35
|
-
pw[1].sync = true
|
36
|
-
if defined? yield
|
37
|
-
begin
|
38
|
-
return yield(*pi)
|
39
|
-
ensure
|
40
|
-
Process.detach(pid) if pid
|
41
|
-
pi.each { |p| p.close unless p.closed? }
|
42
|
-
end
|
43
|
-
end
|
44
|
-
pi
|
45
|
-
end
|
46
|
-
end
|