grit 2.4.1 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of grit might be problematic. Click here for more details.

@@ -1,325 +0,0 @@
1
- #
2
- # converted from the gitrb project
3
- #
4
- # authors:
5
- # Matthias Lederhofer <matled@gmx.net>
6
- # Simon 'corecode' Schubert <corecode@fs.ei.tum.de>
7
- # Scott Chacon <schacon@gmail.com>
8
- #
9
- # provides native ruby access to git objects and pack files
10
- #
11
-
12
- # These classes translate the raw binary data kept in the sha encoded files
13
- # into parsed data that can then be used in another fashion
14
- require 'stringio'
15
-
16
- module Grit
17
- module GitRuby
18
-
19
- # class for author/committer/tagger lines
20
- class UserInfo
21
- attr_accessor :name, :email, :date, :offset
22
-
23
- def initialize(str)
24
- m = /^(.*?) <(.*)> (\d+) ([+-])0*(\d+?)$/.match(str)
25
- if !m
26
- raise RuntimeError, "invalid header '%s' in commit" % str
27
- end
28
- @name = m[1]
29
- @email = m[2]
30
- @date = Time.at(Integer(m[3]))
31
- @offset = (m[4] == "-" ? -1 : 1)*Integer(m[5])
32
- end
33
-
34
- def to_s
35
- "%s <%s> %s %+05d" % [@name, @email, @date.to_i, @offset]
36
- end
37
- end
38
-
39
- # base class for all git objects (blob, tree, commit, tag)
40
- class Object
41
- attr_accessor :repository
42
-
43
- def Object.from_raw(rawobject, repository = nil)
44
- case rawobject.type
45
- when :blob
46
- return Blob.from_raw(rawobject, repository)
47
- when :tree
48
- return Tree.from_raw(rawobject, repository)
49
- when :commit
50
- return Commit.from_raw(rawobject, repository)
51
- when :tag
52
- return Tag.from_raw(rawobject, repository)
53
- else
54
- raise RuntimeError, "got invalid object-type"
55
- end
56
- end
57
-
58
- def initialize
59
- raise NotImplemented, "abstract class"
60
- end
61
-
62
- def type
63
- raise NotImplemented, "abstract class"
64
- end
65
-
66
- def raw_content
67
- raise NotImplemented, "abstract class"
68
- end
69
-
70
- def sha1
71
- Digest::SHA1.hexdigest("%s %d\0" % \
72
- [self.type, self.raw_content.length] + \
73
- self.raw_content)
74
- end
75
- end
76
-
77
- class Blob < Object
78
- attr_accessor :content
79
-
80
- def self.from_raw(rawobject, repository)
81
- new(rawobject.content)
82
- end
83
-
84
- def initialize(content, repository=nil)
85
- @content = content
86
- @repository = repository
87
- end
88
-
89
- def type
90
- :blob
91
- end
92
-
93
- def raw_content
94
- @content
95
- end
96
- end
97
-
98
- class DirectoryEntry
99
- S_IFMT = 00170000
100
- S_IFLNK = 0120000
101
- S_IFREG = 0100000
102
- S_IFDIR = 0040000
103
-
104
- attr_accessor :mode, :name, :sha1
105
- def initialize(mode, filename, sha1o)
106
- @mode = 0
107
- mode.each_byte do |i|
108
- @mode = (@mode << 3) | (i-'0'[0])
109
- end
110
- @name = filename
111
- @sha1 = sha1o
112
- if ![S_IFLNK, S_IFDIR, S_IFREG].include?(@mode & S_IFMT)
113
- raise RuntimeError, "unknown type for directory entry"
114
- end
115
- end
116
-
117
- def type
118
- case @mode & S_IFMT
119
- when S_IFLNK
120
- @type = :link
121
- when S_IFDIR
122
- @type = :directory
123
- when S_IFREG
124
- @type = :file
125
- else
126
- raise RuntimeError, "unknown type for directory entry"
127
- end
128
- end
129
-
130
- def type=(type)
131
- case @type
132
- when :link
133
- @mode = (@mode & ~S_IFMT) | S_IFLNK
134
- when :directory
135
- @mode = (@mode & ~S_IFMT) | S_IFDIR
136
- when :file
137
- @mode = (@mode & ~S_IFMT) | S_IFREG
138
- else
139
- raise RuntimeError, "invalid type"
140
- end
141
- end
142
-
143
- def format_type
144
- case type
145
- when :link
146
- 'link'
147
- when :directory
148
- 'tree'
149
- when :file
150
- 'blob'
151
- end
152
- end
153
-
154
- def format_mode
155
- "%06o" % @mode
156
- end
157
-
158
- def raw
159
- "%o %s\0%s" % [@mode, @name, [@sha1].pack("H*")]
160
- end
161
- end
162
-
163
-
164
- def self.read_bytes_until(io, char)
165
- string = ''
166
- if RUBY_VERSION > '1.9'
167
- while ((next_char = io.getc) != char) && !io.eof
168
- string += next_char
169
- end
170
- else
171
- while ((next_char = io.getc.chr) != char) && !io.eof
172
- string += next_char
173
- end
174
- end
175
- string
176
- end
177
-
178
-
179
- class Tree < Object
180
- attr_accessor :entry
181
-
182
- def self.from_raw(rawobject, repository=nil)
183
- raw = StringIO.new(rawobject.content)
184
-
185
- entries = []
186
- while !raw.eof?
187
- mode = Grit::GitRuby.read_bytes_until(raw, ' ')
188
- file_name = Grit::GitRuby.read_bytes_until(raw, "\0")
189
- raw_sha = raw.read(20)
190
- sha = raw_sha.unpack("H*").first
191
-
192
- entries << DirectoryEntry.new(mode, file_name, sha)
193
- end
194
- new(entries, repository)
195
- end
196
-
197
- def initialize(entries=[], repository = nil)
198
- @entry = entries
199
- @repository = repository
200
- end
201
-
202
- def type
203
- :tree
204
- end
205
-
206
- def raw_content
207
- # TODO: sort correctly
208
- #@entry.sort { |a,b| a.name <=> b.name }.
209
- @entry.collect { |e| [[e.format_mode, e.format_type, e.sha1].join(' '), e.name].join("\t") }.join("\n")
210
- end
211
-
212
- def actual_raw
213
- #@entry.collect { |e| e.raw.join(' '), e.name].join("\t") }.join("\n")
214
- end
215
- end
216
-
217
- class Commit < Object
218
- attr_accessor :author, :committer, :tree, :parent, :message, :headers
219
-
220
- def self.from_raw(rawobject, repository=nil)
221
- parent = []
222
- tree = author = committer = nil
223
-
224
- headers, message = rawobject.content.split(/\n\n/, 2)
225
- all_headers = headers.split(/\n/).map { |header| header.split(/ /, 2) }
226
- all_headers.each do |key, value|
227
- case key
228
- when "tree"
229
- tree = value
230
- when "parent"
231
- parent.push(value)
232
- when "author"
233
- author = UserInfo.new(value)
234
- when "committer"
235
- committer = UserInfo.new(value)
236
- else
237
- warn "unknown header '%s' in commit %s" % \
238
- [key, rawobject.sha1.unpack("H*")[0]]
239
- end
240
- end
241
- if not tree && author && committer
242
- raise RuntimeError, "incomplete raw commit object"
243
- end
244
- new(tree, parent, author, committer, message, headers, repository)
245
- end
246
-
247
- def initialize(tree, parent, author, committer, message, headers, repository=nil)
248
- @tree = tree
249
- @author = author
250
- @parent = parent
251
- @committer = committer
252
- @message = message
253
- @headers = headers
254
- @repository = repository
255
- end
256
-
257
- def type
258
- :commit
259
- end
260
-
261
- def raw_content
262
- "tree %s\n%sauthor %s\ncommitter %s\n\n" % [
263
- @tree,
264
- @parent.collect { |i| "parent %s\n" % i }.join,
265
- @author, @committer] + @message
266
- end
267
-
268
- def raw_log(sha)
269
- output = "commit #{sha}\n"
270
- output += @headers + "\n\n"
271
- output += @message.split("\n").map { |l| ' ' + l }.join("\n") + "\n\n"
272
- end
273
-
274
- end
275
-
276
- class Tag < Object
277
- attr_accessor :object, :type, :tag, :tagger, :message
278
-
279
- def self.from_raw(rawobject, repository=nil)
280
- headers, message = rawobject.content.split(/\n\n/, 2)
281
- headers = headers.split(/\n/).map { |header| header.split(/ /, 2) }
282
- headers.each do |key, value|
283
- case key
284
- when "object"
285
- object = value
286
- when "type"
287
- if !["blob", "tree", "commit", "tag"].include?(value)
288
- raise RuntimeError, "invalid type in tag"
289
- end
290
- type = value.to_sym
291
- when "tag"
292
- tag = value
293
- when "tagger"
294
- tagger = UserInfo.new(value)
295
- else
296
- warn "unknown header '%s' in tag" % \
297
- [key, rawobject.sha1.unpack("H*")[0]]
298
- end
299
- if not object && type && tag && tagger
300
- raise RuntimeError, "incomplete raw tag object"
301
- end
302
- end
303
- new(object, type, tag, tagger, repository)
304
- end
305
-
306
- def initialize(object, type, tag, tagger, repository=nil)
307
- @object = object
308
- @type = type
309
- @tag = tag
310
- @tagger = tagger
311
- @repository = repository
312
- end
313
-
314
- def raw_content
315
- "object %s\ntype %s\ntag %s\ntagger %s\n\n" % \
316
- [@object, @type, @tag, @tagger] + @message
317
- end
318
-
319
- def type
320
- :tag
321
- end
322
- end
323
-
324
- end
325
- end
@@ -1,45 +0,0 @@
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
@@ -1,294 +0,0 @@
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