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.
- data/History.txt +16 -0
- data/grit.gemspec +3 -6
- data/lib/grit.rb +1 -7
- data/lib/grit/actor.rb +7 -7
- data/lib/grit/blame.rb +7 -3
- data/lib/grit/commit.rb +23 -4
- data/lib/grit/git-ruby.rb +12 -25
- data/lib/grit/git-ruby/git_object.rb +4 -1
- data/lib/grit/git-ruby/internal/loose.rb +5 -5
- data/lib/grit/git-ruby/internal/pack.rb +21 -7
- data/lib/grit/git-ruby/internal/raw_object.rb +7 -0
- data/lib/grit/git-ruby/repository.rb +60 -60
- data/lib/grit/git.rb +72 -25
- data/lib/grit/index.rb +34 -9
- data/lib/grit/repo.rb +68 -14
- data/lib/grit/tag.rb +34 -3
- data/lib/grit/tree.rb +2 -2
- metadata +57 -79
- data/lib/grit/git-ruby/file_index.rb +0 -193
- data/lib/grit/git-ruby/object.rb +0 -325
- data/lib/grit/jruby.rb +0 -45
- data/lib/grit/process.rb +0 -294
data/lib/grit/git.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
require 'tempfile'
|
2
|
+
require 'posix-spawn'
|
2
3
|
module Grit
|
3
4
|
|
4
5
|
class Git
|
6
|
+
include POSIX::Spawn
|
7
|
+
|
5
8
|
class GitTimeout < RuntimeError
|
6
9
|
attr_accessor :command
|
7
10
|
attr_accessor :bytes_read
|
@@ -23,11 +26,17 @@ module Grit
|
|
23
26
|
# Everything output on the command's stderr as a String.
|
24
27
|
attr_reader :err
|
25
28
|
|
26
|
-
def initialize(command, exitstatus, err='')
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
def initialize(command, exitstatus=nil, err='')
|
30
|
+
if exitstatus
|
31
|
+
@command = command
|
32
|
+
@exitstatus = exitstatus
|
33
|
+
@err = err
|
34
|
+
message = "Command failed [#{exitstatus}]: #{command}"
|
35
|
+
message << "\n\n" << err unless err.nil? || err.empty?
|
36
|
+
super message
|
37
|
+
else
|
38
|
+
super command
|
39
|
+
end
|
31
40
|
end
|
32
41
|
end
|
33
42
|
|
@@ -43,6 +52,14 @@ module Grit
|
|
43
52
|
ruby_git.put_raw_object(content, type)
|
44
53
|
end
|
45
54
|
|
55
|
+
def get_raw_object(object_id)
|
56
|
+
ruby_git.get_raw_object_by_sha1(object_id).content
|
57
|
+
end
|
58
|
+
|
59
|
+
def get_git_object(object_id)
|
60
|
+
ruby_git.get_raw_object_by_sha1(object_id).to_hash
|
61
|
+
end
|
62
|
+
|
46
63
|
def object_exists?(object_id)
|
47
64
|
ruby_git.object_exists?(object_id)
|
48
65
|
end
|
@@ -67,7 +84,7 @@ module Grit
|
|
67
84
|
self.git_timeout = 10
|
68
85
|
self.git_max_size = 5242880 # 5.megabytes
|
69
86
|
|
70
|
-
def self.with_timeout(timeout = 10
|
87
|
+
def self.with_timeout(timeout = 10)
|
71
88
|
old_timeout = Grit::Git.git_timeout
|
72
89
|
Grit::Git.git_timeout = timeout
|
73
90
|
yield
|
@@ -180,16 +197,23 @@ module Grit
|
|
180
197
|
|
181
198
|
# Checks if the patch of a commit can be applied to the given head.
|
182
199
|
#
|
200
|
+
# options - grit command options hash
|
183
201
|
# head_sha - String SHA or ref to check the patch against.
|
184
202
|
# applies_sha - String SHA of the patch. The patch itself is retrieved
|
185
203
|
# with #get_patch.
|
186
204
|
#
|
187
205
|
# Returns 0 if the patch applies cleanly (according to `git apply`), or
|
188
206
|
# an Integer that is the sum of the failed exit statuses.
|
189
|
-
def check_applies(head_sha, applies_sha)
|
207
|
+
def check_applies(options={}, head_sha=nil, applies_sha=nil)
|
208
|
+
options, head_sha, applies_sha = {}, options, head_sha if !options.is_a?(Hash)
|
209
|
+
options = options.dup
|
210
|
+
options[:env] &&= options[:env].dup
|
211
|
+
|
190
212
|
git_index = create_tempfile('index', true)
|
191
|
-
options
|
192
|
-
|
213
|
+
(options[:env] ||= {}).merge!('GIT_INDEX_FILE' => git_index)
|
214
|
+
options[:raise] = true
|
215
|
+
|
216
|
+
status = 0
|
193
217
|
begin
|
194
218
|
native(:read_tree, options.dup, head_sha)
|
195
219
|
stdin = native(:diff, options.dup, "#{applies_sha}^", applies_sha)
|
@@ -202,27 +226,38 @@ module Grit
|
|
202
226
|
|
203
227
|
# Gets a patch for a given SHA using `git diff`.
|
204
228
|
#
|
229
|
+
# options - grit command options hash
|
205
230
|
# applies_sha - String SHA to get the patch from, using this command:
|
206
231
|
# `git diff #{applies_sha}^ #{applies_sha}`
|
207
232
|
#
|
208
233
|
# Returns the String patch from `git diff`.
|
209
|
-
def get_patch(applies_sha)
|
234
|
+
def get_patch(options={}, applies_sha=nil)
|
235
|
+
options, applies_sha = {}, options if !options.is_a?(Hash)
|
236
|
+
options = options.dup
|
237
|
+
options[:env] &&= options[:env].dup
|
238
|
+
|
210
239
|
git_index = create_tempfile('index', true)
|
211
|
-
|
212
|
-
|
213
|
-
|
240
|
+
(options[:env] ||= {}).merge!('GIT_INDEX_FILE' => git_index)
|
241
|
+
|
242
|
+
native(:diff, options, "#{applies_sha}^", applies_sha)
|
214
243
|
end
|
215
244
|
|
216
245
|
# Applies the given patch against the given SHA of the current repo.
|
217
246
|
#
|
247
|
+
# options - grit command hash
|
218
248
|
# head_sha - String SHA or ref to apply the patch to.
|
219
249
|
# patch - The String patch to apply. Get this from #get_patch.
|
220
250
|
#
|
221
251
|
# Returns the String Tree SHA on a successful patch application, or false.
|
222
|
-
def apply_patch(head_sha, patch)
|
252
|
+
def apply_patch(options={}, head_sha=nil, patch=nil)
|
253
|
+
options, head_sha, patch = {}, options, head_sha if !options.is_a?(Hash)
|
254
|
+
options = options.dup
|
255
|
+
options[:env] &&= options[:env].dup
|
256
|
+
options[:raise] = true
|
257
|
+
|
223
258
|
git_index = create_tempfile('index', true)
|
259
|
+
(options[:env] ||= {}).merge!('GIT_INDEX_FILE' => git_index)
|
224
260
|
|
225
|
-
options = {:env => {'GIT_INDEX_FILE' => git_index}, :raise => true}
|
226
261
|
begin
|
227
262
|
native(:read_tree, options.dup, head_sha)
|
228
263
|
native(:apply, options.merge(:cached => true, :input => patch))
|
@@ -252,6 +287,9 @@ module Grit
|
|
252
287
|
# :raise - When set true, commands that exit with a non-zero status
|
253
288
|
# raise a CommandFailed exception. This option is available only on
|
254
289
|
# platforms that support fork(2).
|
290
|
+
# :process_info - By default, a single string with output written to
|
291
|
+
# the process's stdout is returned. Setting this option to true
|
292
|
+
# results in a [exitstatus, out, err] tuple being returned instead.
|
255
293
|
# args - Non-option arguments passed on the command line.
|
256
294
|
#
|
257
295
|
# Optionally yields to the block an IO object attached to the child
|
@@ -260,7 +298,12 @@ module Grit
|
|
260
298
|
# Examples
|
261
299
|
# git.native(:rev_list, {:max_count => 10, :header => true}, "master")
|
262
300
|
#
|
263
|
-
# Returns a String with all output written to the child process's stdout
|
301
|
+
# Returns a String with all output written to the child process's stdout
|
302
|
+
# when the :process_info option is not set.
|
303
|
+
# Returns a [exitstatus, out, err] tuple when the :process_info option is
|
304
|
+
# set. The exitstatus is an small integer that was the process's exit
|
305
|
+
# status. The out and err elements are the data written to stdout and
|
306
|
+
# stderr as Strings.
|
264
307
|
# Raises Grit::Git::GitTimeout when the timeout is exceeded or when more
|
265
308
|
# than Grit::Git.git_max_size bytes are output.
|
266
309
|
# Raises Grit::Git::CommandFailed when the :raise option is set true and the
|
@@ -269,12 +312,13 @@ module Grit
|
|
269
312
|
# detail about the error.
|
270
313
|
def native(cmd, options = {}, *args, &block)
|
271
314
|
args = args.first if args.size == 1 && args[0].is_a?(Array)
|
272
|
-
args.map! { |a| a.to_s
|
315
|
+
args.map! { |a| a.to_s }
|
273
316
|
args.reject! { |a| a.empty? }
|
274
317
|
|
275
318
|
# special option arguments
|
276
319
|
env = options.delete(:env) || {}
|
277
320
|
raise_errors = options.delete(:raise)
|
321
|
+
process_info = options.delete(:process_info)
|
278
322
|
|
279
323
|
# fall back to using a shell when the last argument looks like it wants to
|
280
324
|
# start a pipeline for compatibility with previous versions of grit.
|
@@ -298,21 +342,24 @@ module Grit
|
|
298
342
|
Grit.log(argv.join(' ')) if Grit.debug
|
299
343
|
|
300
344
|
process =
|
301
|
-
|
345
|
+
Child.new(env, *(argv + [{
|
302
346
|
:input => input,
|
303
347
|
:chdir => chdir,
|
304
348
|
:timeout => (Grit::Git.git_timeout if timeout == true),
|
305
349
|
:max => (Grit::Git.git_max_size if timeout == true)
|
306
|
-
)
|
307
|
-
status = process.status
|
350
|
+
}]))
|
308
351
|
Grit.log(process.out) if Grit.debug
|
309
352
|
Grit.log(process.err) if Grit.debug
|
353
|
+
|
354
|
+
status = process.status
|
310
355
|
if raise_errors && !status.success?
|
311
356
|
raise CommandFailed.new(argv.join(' '), status.exitstatus, process.err)
|
357
|
+
elsif process_info
|
358
|
+
[status.exitstatus, process.out, process.err]
|
312
359
|
else
|
313
360
|
process.out
|
314
361
|
end
|
315
|
-
rescue
|
362
|
+
rescue TimeoutExceeded, MaximumOutputExceeded
|
316
363
|
raise GitTimeout, argv.join(' ')
|
317
364
|
end
|
318
365
|
|
@@ -404,18 +451,18 @@ module Grit
|
|
404
451
|
|
405
452
|
def sh(command, &block)
|
406
453
|
process =
|
407
|
-
|
408
|
-
command,
|
454
|
+
Child.new(
|
455
|
+
command,
|
409
456
|
:timeout => Git.git_timeout,
|
410
457
|
:max => Git.git_max_size
|
411
458
|
)
|
412
459
|
[process.out, process.err]
|
413
|
-
rescue
|
460
|
+
rescue TimeoutExceeded, MaximumOutputExceeded
|
414
461
|
raise GitTimeout, command
|
415
462
|
end
|
416
463
|
|
417
464
|
def wild_sh(command, &block)
|
418
|
-
process =
|
465
|
+
process = Child.new(command)
|
419
466
|
[process.out, process.err]
|
420
467
|
end
|
421
468
|
|
data/lib/grit/index.rb
CHANGED
@@ -12,6 +12,10 @@ module Grit
|
|
12
12
|
# which the next commit will be based.
|
13
13
|
attr_accessor :current_tree
|
14
14
|
|
15
|
+
# Public: if a tree or commit is written, this stores the size of that object
|
16
|
+
attr_reader :last_tree_size
|
17
|
+
attr_reader :last_commit_size
|
18
|
+
|
15
19
|
# Initialize a new Index object.
|
16
20
|
#
|
17
21
|
# repo - The Grit::Repo to which the index belongs.
|
@@ -71,16 +75,16 @@ module Grit
|
|
71
75
|
# :parents - Array of String commit SHA1s or Grit::Commit
|
72
76
|
# objects to attach this commit to to form a
|
73
77
|
# new head (default: nil).
|
74
|
-
# :actor - The Grit::Actor details of the user making
|
78
|
+
# :actor - The Grit::Actor details of the user making
|
75
79
|
# the commit (default: nil).
|
76
80
|
# :last_tree - The String SHA1 of a tree to compare with
|
77
|
-
# in order to avoid making empty commits
|
81
|
+
# in order to avoid making empty commits
|
78
82
|
# (default: nil).
|
79
83
|
# :head - The String branch name to write this head to
|
80
|
-
# (default:
|
81
|
-
# :committed_date - The Time that the commit was made.
|
84
|
+
# (default: nil).
|
85
|
+
# :committed_date - The Time that the commit was made.
|
82
86
|
# (Default: Time.now)
|
83
|
-
# :authored_date - The Time that the commit was authored.
|
87
|
+
# :authored_date - The Time that the commit was authored.
|
84
88
|
# (Default: committed_date)
|
85
89
|
#
|
86
90
|
# The legacy argument style looks like:
|
@@ -97,7 +101,9 @@ module Grit
|
|
97
101
|
#
|
98
102
|
# Returns a String of the SHA1 of the new commit.
|
99
103
|
def commit(message, parents = nil, actor = nil, last_tree = nil, head = 'master')
|
104
|
+
commit_tree_sha = nil
|
100
105
|
if parents.is_a?(Hash)
|
106
|
+
commit_tree_sha = parents[:commit_tree_sha]
|
101
107
|
actor = parents[:actor]
|
102
108
|
committer = parents[:committer]
|
103
109
|
author = parents[:author]
|
@@ -111,7 +117,11 @@ module Grit
|
|
111
117
|
committer ||= actor
|
112
118
|
author ||= committer
|
113
119
|
|
114
|
-
|
120
|
+
if commit_tree_sha
|
121
|
+
tree_sha1 = commit_tree_sha
|
122
|
+
else
|
123
|
+
tree_sha1 = write_tree(self.tree, self.current_tree)
|
124
|
+
end
|
115
125
|
|
116
126
|
# don't write identical commits
|
117
127
|
return false if tree_sha1 == last_tree
|
@@ -135,9 +145,12 @@ module Grit
|
|
135
145
|
contents << ''
|
136
146
|
contents << message
|
137
147
|
|
138
|
-
|
148
|
+
contents = contents.join("\n")
|
149
|
+
@last_commit_size = contents.size
|
150
|
+
commit_sha1 = self.repo.git.put_raw_object(contents, 'commit')
|
139
151
|
|
140
|
-
self.repo.update_ref(head, commit_sha1)
|
152
|
+
self.repo.update_ref(head, commit_sha1) if head
|
153
|
+
commit_sha1
|
141
154
|
end
|
142
155
|
|
143
156
|
# Recursively write a tree to the index.
|
@@ -149,10 +162,12 @@ module Grit
|
|
149
162
|
# this tree will be based (default: nil).
|
150
163
|
#
|
151
164
|
# Returns the String SHA1 String of the tree.
|
152
|
-
def write_tree(tree, now_tree = nil)
|
165
|
+
def write_tree(tree = nil, now_tree = nil)
|
166
|
+
tree = self.tree if !tree
|
153
167
|
tree_contents = {}
|
154
168
|
|
155
169
|
# fill in original tree
|
170
|
+
now_tree = read_tree(now_tree) if(now_tree && now_tree.is_a?(String))
|
156
171
|
now_tree.contents.each do |obj|
|
157
172
|
sha = [obj.id].pack("H*")
|
158
173
|
k = obj.name
|
@@ -164,6 +179,15 @@ module Grit
|
|
164
179
|
# overwrite with new tree contents
|
165
180
|
tree.each do |k, v|
|
166
181
|
case v
|
182
|
+
when Array
|
183
|
+
sha, mode = v
|
184
|
+
if sha.size == 40 # must be a sha
|
185
|
+
sha = [sha].pack("H*")
|
186
|
+
mode = mode.to_i.to_s # leading 0s not allowed
|
187
|
+
k = k.split('/').last # slashes not allowed
|
188
|
+
str = "%s %s\0%s" % [mode, k, sha]
|
189
|
+
tree_contents[k] = str
|
190
|
+
end
|
167
191
|
when String
|
168
192
|
sha = write_blob(v)
|
169
193
|
sha = [sha].pack("H*")
|
@@ -181,6 +205,7 @@ module Grit
|
|
181
205
|
end
|
182
206
|
|
183
207
|
tr = tree_contents.sort.map { |k, v| v }.join('')
|
208
|
+
@last_tree_size = tr.size
|
184
209
|
self.repo.git.put_raw_object(tr, 'tree')
|
185
210
|
end
|
186
211
|
|
data/lib/grit/repo.rb
CHANGED
@@ -183,6 +183,7 @@ module Grit
|
|
183
183
|
sha, type, size = line.split(" ", 3)
|
184
184
|
parser = BATCH_PARSERS[type]
|
185
185
|
if type == 'missing' || !parser
|
186
|
+
io.seek(size.to_i + 1, IO::SEEK_CUR)
|
186
187
|
objects << nil
|
187
188
|
next
|
188
189
|
end
|
@@ -345,6 +346,21 @@ module Grit
|
|
345
346
|
[ Head.find_all(self), Tag.find_all(self), Remote.find_all(self) ].flatten
|
346
347
|
end
|
347
348
|
|
349
|
+
# returns an array of hashes representing all references
|
350
|
+
def refs_list
|
351
|
+
refs = self.git.for_each_ref
|
352
|
+
refarr = refs.split("\n").map do |line|
|
353
|
+
shatype, ref = line.split("\t")
|
354
|
+
sha, type = shatype.split(' ')
|
355
|
+
[ref, sha, type]
|
356
|
+
end
|
357
|
+
refarr
|
358
|
+
end
|
359
|
+
|
360
|
+
def delete_ref(ref)
|
361
|
+
self.git.native(:update_ref, {:d => true}, ref)
|
362
|
+
end
|
363
|
+
|
348
364
|
def commit_stats(start = 'master', max_count = 10, skip = 0)
|
349
365
|
options = {:max_count => max_count,
|
350
366
|
:skip => skip}
|
@@ -375,6 +391,11 @@ module Grit
|
|
375
391
|
Commit.find_all(self, "#{from}..#{to}").reverse
|
376
392
|
end
|
377
393
|
|
394
|
+
def fast_forwardable?(to, from)
|
395
|
+
mb = self.git.native(:merge_base, {}, [to, from]).strip
|
396
|
+
mb == from
|
397
|
+
end
|
398
|
+
|
378
399
|
# The Commits objects that are newer than the specified date.
|
379
400
|
# Commits are returned in chronological order.
|
380
401
|
# +start+ is the branch/commit name (default 'master')
|
@@ -415,23 +436,20 @@ module Grit
|
|
415
436
|
repo_refs = self.git.rev_list({}, ref).strip.split("\n")
|
416
437
|
other_repo_refs = other_repo.git.rev_list({}, other_ref).strip.split("\n")
|
417
438
|
|
418
|
-
(other_repo_refs - repo_refs).map do |
|
419
|
-
Commit.find_all(other_repo,
|
439
|
+
(other_repo_refs - repo_refs).map do |refn|
|
440
|
+
Commit.find_all(other_repo, refn, {:max_count => 1}).first
|
420
441
|
end
|
421
442
|
end
|
422
443
|
|
423
444
|
def objects(refs)
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
obj
|
445
|
+
refs = refs.split(/\s+/) if refs.respond_to?(:to_str)
|
446
|
+
self.git.rev_list({:objects => true, :timeout => false}, *refs).
|
447
|
+
split("\n").map { |a| a[0, 40] }
|
428
448
|
end
|
429
449
|
|
430
450
|
def commit_objects(refs)
|
431
|
-
|
432
|
-
|
433
|
-
Grit.no_quote = false
|
434
|
-
obj
|
451
|
+
refs = refs.split(/\s+/) if refs.respond_to?(:to_str)
|
452
|
+
self.git.rev_list({:timeout => false}, *refs).split("\n").map { |a| a[0, 40] }
|
435
453
|
end
|
436
454
|
|
437
455
|
def objects_between(ref1, ref2 = nil)
|
@@ -448,11 +466,10 @@ module Grit
|
|
448
466
|
Grit.no_quote = true
|
449
467
|
if parents
|
450
468
|
# PARENTS:
|
451
|
-
|
452
|
-
|
469
|
+
revs = self.git.diff_tree({:timeout => false, :r => true, :t => true, :m => true}, commit_sha).
|
470
|
+
strip.split("\n").map{ |a| r = a.split(' '); r[3] if r[1] != '160000' }
|
453
471
|
else
|
454
472
|
# NO PARENTS:
|
455
|
-
cmd = "-r -t #{commit_sha}"
|
456
473
|
revs = self.git.native(:ls_tree, {:timeout => false, :r => true, :t => true}, commit_sha).
|
457
474
|
split("\n").map{ |a| a.split("\t").first.split(' ')[2] }
|
458
475
|
end
|
@@ -463,7 +480,7 @@ module Grit
|
|
463
480
|
|
464
481
|
# The Tree object for the given treeish reference
|
465
482
|
# +treeish+ is the reference (default 'master')
|
466
|
-
# +paths+ is an optional Array of directory paths to restrict the tree (
|
483
|
+
# +paths+ is an optional Array of directory paths to restrict the tree (default [])
|
467
484
|
#
|
468
485
|
# Examples
|
469
486
|
# repo.tree('master', ['lib/'])
|
@@ -473,6 +490,43 @@ module Grit
|
|
473
490
|
Tree.construct(self, treeish, paths)
|
474
491
|
end
|
475
492
|
|
493
|
+
# quick way to get a simple array of hashes of the entries
|
494
|
+
# of a single tree or recursive tree listing from a given
|
495
|
+
# sha or reference
|
496
|
+
# +treeish+ is the reference (default 'master')
|
497
|
+
# +options+ is a hash or options - currently only takes :recursive
|
498
|
+
#
|
499
|
+
# Examples
|
500
|
+
# repo.lstree('master', :recursive => true)
|
501
|
+
#
|
502
|
+
# Returns array of hashes - one per tree entry
|
503
|
+
def lstree(treeish = 'master', options = {})
|
504
|
+
# check recursive option
|
505
|
+
opts = {:timeout => false, :l => true, :t => true}
|
506
|
+
if options[:recursive]
|
507
|
+
opts[:r] = true
|
508
|
+
end
|
509
|
+
# mode, type, sha, size, path
|
510
|
+
revs = self.git.native(:ls_tree, opts, treeish)
|
511
|
+
lines = revs.split("\n")
|
512
|
+
revs = lines.map do |a|
|
513
|
+
stuff, path = a.split("\t")
|
514
|
+
mode, type, sha, size = stuff.split(" ")
|
515
|
+
entry = {:mode => mode, :type => type, :sha => sha, :path => path}
|
516
|
+
entry[:size] = size.strip.to_i if size.strip != '-'
|
517
|
+
entry
|
518
|
+
end
|
519
|
+
revs
|
520
|
+
end
|
521
|
+
|
522
|
+
def object(sha)
|
523
|
+
obj = git.get_git_object(sha)
|
524
|
+
raw = Grit::GitRuby::Internal::RawObject.new(obj[:type], obj[:content])
|
525
|
+
object = Grit::GitRuby::GitObject.from_raw(raw)
|
526
|
+
object.sha = sha
|
527
|
+
object
|
528
|
+
end
|
529
|
+
|
476
530
|
# The Blob object for the given id
|
477
531
|
# +id+ is the SHA1 id of the blob
|
478
532
|
#
|