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,3 +1,19 @@
1
+ == 2.5.0 / 2012-04-22
2
+ * Minor Enhancements
3
+ * 100% Git-compliant actor creation.
4
+ * Handle newlines in author/committer.
5
+ * Grit::Git check_applies/patch related methods take command hash.
6
+ * Tags api now resty.
7
+ * Remove all the grit jruby hacks in favor of updated posix-spawn.
8
+ * Add Grit::Commit#patch_id.
9
+ * Support large packfiles with index v2.
10
+ * Bug Fixes
11
+ * Fix Loose Objects with non-ASCII content in Ruby 1.9
12
+ * Fix bugs in Grit::Repo #objects, #commit_objects, and #diff_objects
13
+ due to passing multiple arguments in a single argv element.
14
+ * ruby rev_list passes --verify to native rev_parse in fallback.
15
+ * Git ls-tree raises on non-zero exit.
16
+
1
17
  == 2.4.1 / 2011-01-13
2
18
  * Minor Enhancements
3
19
  * Grit::Process is used to implement Grit::Git#check_applies,
@@ -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.4.1'
8
- s.date = '2011-01-13'
7
+ s.version = '2.5.0'
8
+ s.date = '2012-04-22'
9
9
  s.rubyforge_project = 'grit'
10
10
 
11
11
  s.summary = "Ruby Git bindings."
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
20
20
  s.rdoc_options = ["--charset=UTF-8"]
21
21
  s.extra_rdoc_files = %w[README.md LICENSE]
22
22
 
23
+ s.add_dependency('posix-spawn', "~> 0.3.6")
23
24
  s.add_dependency('mime-types', "~> 1.15")
24
25
  s.add_dependency('diff-lcs', "~> 1.1")
25
26
 
@@ -49,20 +50,16 @@ Gem::Specification.new do |s|
49
50
  lib/grit/errors.rb
50
51
  lib/grit/git-ruby.rb
51
52
  lib/grit/git-ruby/commit_db.rb
52
- lib/grit/git-ruby/file_index.rb
53
53
  lib/grit/git-ruby/git_object.rb
54
54
  lib/grit/git-ruby/internal/file_window.rb
55
55
  lib/grit/git-ruby/internal/loose.rb
56
56
  lib/grit/git-ruby/internal/pack.rb
57
57
  lib/grit/git-ruby/internal/raw_object.rb
58
- lib/grit/git-ruby/object.rb
59
58
  lib/grit/git-ruby/repository.rb
60
59
  lib/grit/git.rb
61
60
  lib/grit/index.rb
62
- lib/grit/jruby.rb
63
61
  lib/grit/lazy.rb
64
62
  lib/grit/merge.rb
65
- lib/grit/process.rb
66
63
  lib/grit/ref.rb
67
64
  lib/grit/repo.rb
68
65
  lib/grit/ruby1.9.rb
@@ -28,7 +28,6 @@ end
28
28
  require 'grit/ruby1.9'
29
29
 
30
30
  # internal requires
31
- require 'grit/process'
32
31
  require 'grit/lazy'
33
32
  require 'grit/errors'
34
33
  require 'grit/git-ruby'
@@ -49,13 +48,8 @@ require 'grit/submodule'
49
48
  require 'grit/blame'
50
49
  require 'grit/merge'
51
50
 
52
- # platform specific requires
53
- if defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/
54
- require 'grit/jruby'
55
- end
56
-
57
51
  module Grit
58
- VERSION = '2.4.1'
52
+ VERSION = '2.5.0'
59
53
 
60
54
  class << self
61
55
  # Set +debug+ to true to log all git calls and responses
@@ -34,13 +34,13 @@ module Grit
34
34
  #
35
35
  # Returns a String.
36
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')}"
37
+ offset = time.utc_offset / 60
38
+ "%s <%s> %d %+.2d%.2d" % [
39
+ @name,
40
+ @email || "null",
41
+ time.to_i,
42
+ offset / 60,
43
+ offset.abs % 60]
44
44
  end
45
45
 
46
46
  # Pretty object inspection
@@ -4,12 +4,16 @@ module Grit
4
4
 
5
5
  attr_reader :lines
6
6
 
7
- def initialize(repo, file, commit)
7
+ def initialize(repo, file, commit, lines=nil)
8
8
  @repo = repo
9
9
  @file = file
10
10
  @commit = commit
11
- @lines = []
12
- load_blame
11
+ if lines.nil?
12
+ @lines = []
13
+ load_blame
14
+ else
15
+ @lines = lines
16
+ end
13
17
  end
14
18
 
15
19
  def load_blame
@@ -13,7 +13,6 @@ module Grit
13
13
  lazy_reader :committed_date
14
14
  lazy_reader :message
15
15
  lazy_reader :short_message
16
- lazy_reader :author_string
17
16
 
18
17
  # Parses output from the `git-cat-file --batch'.
19
18
  #
@@ -61,7 +60,7 @@ module Grit
61
60
  @committer = committer
62
61
  @committed_date = committed_date
63
62
  @message = message.join("\n")
64
- @short_message = message.select { |x| !x.strip.empty? }[0] || ''
63
+ @short_message = message.find { |x| !x.strip.empty? } || ''
65
64
  end
66
65
 
67
66
  def id_abbrev
@@ -149,8 +148,13 @@ module Grit
149
148
  parents = []
150
149
  parents << lines.shift.split.last while lines.first =~ /^parent/
151
150
 
152
- author, authored_date = self.actor(lines.shift)
153
- committer, committed_date = self.actor(lines.shift)
151
+ author_line = lines.shift
152
+ author_line << lines.shift if lines[0] !~ /^committer /
153
+ author, authored_date = self.actor(author_line)
154
+
155
+ committer_line = lines.shift
156
+ committer_line << lines.shift if lines[0] && lines[0] != '' && lines[0] !~ /^encoding/
157
+ committer, committed_date = self.actor(committer_line)
154
158
 
155
159
  # not doing anything with this yet, but it's sometimes there
156
160
  encoding = lines.shift.split.last if lines.first =~ /^encoding/
@@ -252,6 +256,21 @@ module Grit
252
256
  ret
253
257
  end
254
258
 
259
+ # Calculates the commit's Patch ID. The Patch ID is essentially the SHA1
260
+ # of the diff that the commit is introducing.
261
+ #
262
+ # Returns the 40 character hex String if a patch-id could be calculated
263
+ # or nil otherwise.
264
+ def patch_id
265
+ show = @repo.git.show({}, @id)
266
+ patch_line = @repo.git.native(:patch_id, :input => show)
267
+ if patch_line =~ /^([0-9a-f]{40}) [0-9a-f]{40}\n$/
268
+ $1
269
+ else
270
+ nil
271
+ end
272
+ end
273
+
255
274
  # Pretty object inspection
256
275
  def inspect
257
276
  %Q{#<Grit::Commit "#{@id}">}
@@ -1,5 +1,4 @@
1
1
  require 'grit/git-ruby/repository'
2
- require 'grit/git-ruby/file_index'
3
2
 
4
3
  module Grit
5
4
 
@@ -48,30 +47,33 @@ module Grit
48
47
  try_run { ruby_git.diff(sha1, sha2, options) }
49
48
  end
50
49
 
51
- def rev_list(options, ref = 'master')
50
+ def rev_list(options, *refs)
51
+ refs = ['master'] if refs.empty?
52
52
  options.delete(:skip) if options[:skip].to_i == 0
53
53
  allowed_options = [:max_count, :since, :until, :pretty] # this is all I can do right now
54
- if ((options.keys - allowed_options).size > 0)
55
- return method_missing('rev-list', options, ref)
54
+ if ((options.keys - allowed_options).size > 0) || refs.size > 1
55
+ method_missing('rev-list', options, *refs)
56
56
  elsif (options.size == 0)
57
57
  # pure rev-list
58
+ ref = refs.first
58
59
  begin
59
- return file_index.commits_from(rev_parse({}, ref)).join("\n") + "\n"
60
+ file_index.commits_from(rev_parse({}, ref)).join("\n") + "\n"
60
61
  rescue
61
- return method_missing('rev-list', options, ref)
62
+ method_missing('rev-list', options, *refs)
62
63
  end
63
64
  else
64
- aref = rev_parse({}, ref)
65
+ ref = refs.first
66
+ aref = rev_parse({:verify => true}, ref)
65
67
  if aref.is_a? Array
66
- return method_missing('rev-list', options, ref)
68
+ method_missing('rev-list', options, *refs)
67
69
  else
68
- return try_run { ruby_git.rev_list(aref, options) }
70
+ try_run { ruby_git.rev_list(aref, options) }
69
71
  end
70
72
  end
71
73
  end
72
74
 
73
75
  def rev_parse(options, string)
74
- raise RuntimeError, "invalid string: #{string}" unless string.is_a?(String)
76
+ raise RuntimeError, "invalid string: #{string.inspect}" unless string.is_a?(String)
75
77
 
76
78
  if string =~ /\.\./
77
79
  (sha1, sha2) = string.split('..')
@@ -197,21 +199,6 @@ module Grit
197
199
  try_run { ruby_git.cat_file_type(ref).to_s }
198
200
  end
199
201
 
200
- def blame_tree(commit, path = nil)
201
- begin
202
- path = [path].join('/').to_s + '/' if (path && path != '')
203
- path = '' if !path.is_a? String
204
- commits = file_index.last_commits(rev_parse({}, commit), looking_for(commit, path))
205
- clean_paths(commits)
206
- rescue FileIndex::IndexFileNotFound
207
- {}
208
- end
209
- end
210
-
211
- def file_index
212
- @git_file_index ||= FileIndex.new(@git_dir)
213
- end
214
-
215
202
  def ruby_git
216
203
  @ruby_git_repo ||= Repository.new(@git_dir)
217
204
  end
@@ -49,6 +49,7 @@ module Grit
49
49
  # base class for all git objects (blob, tree, commit, tag)
50
50
  class GitObject
51
51
  attr_accessor :repository
52
+ attr_accessor :sha
52
53
 
53
54
  def GitObject.from_raw(rawobject, repository = nil)
54
55
  case rawobject.type
@@ -290,7 +291,8 @@ module Grit
290
291
  end
291
292
 
292
293
  class Tag < GitObject
293
- attr_accessor :object, :type, :tag, :tagger, :message
294
+ attr_accessor :object, :tag, :tagger, :message, :object_type
295
+ attr_writer :type
294
296
 
295
297
  def self.from_raw(rawobject, repository=nil)
296
298
 
@@ -330,6 +332,7 @@ module Grit
330
332
  def initialize(object, type, tag, tagger, message, repository=nil)
331
333
  @object = object
332
334
  @type = type
335
+ @object_type = type
333
336
  @tag = tag
334
337
  @tagger = tagger
335
338
  @repository = repository
@@ -36,7 +36,7 @@ module Grit
36
36
  end
37
37
 
38
38
  def get_raw_object(buf)
39
- if buf.length < 2
39
+ if buf.bytesize < 2
40
40
  raise LooseObjectError, "object file too small"
41
41
  end
42
42
 
@@ -56,14 +56,14 @@ module Grit
56
56
  type, size, used = unpack_object_header_gently(buf)
57
57
  content = Zlib::Inflate.inflate(buf[used..-1])
58
58
  end
59
- raise LooseObjectError, "size mismatch" if content.length != size
59
+ raise LooseObjectError, "size mismatch" if content.bytesize != size
60
60
  return RawObject.new(type, content)
61
61
  end
62
62
 
63
63
  # currently, I'm using the legacy format because it's easier to do
64
64
  # this function takes content and a type and writes out the loose object and returns a sha
65
65
  def put_raw_object(content, type)
66
- size = content.length.to_s
66
+ size = content.bytesize.to_s
67
67
  LooseStorage.verify_header(type, size)
68
68
 
69
69
  header = "#{type} #{size}\0"
@@ -85,7 +85,7 @@ module Grit
85
85
 
86
86
  # simply figure out the sha
87
87
  def self.calculate_sha(content, type)
88
- size = content.length.to_s
88
+ size = content.bytesize.to_s
89
89
  verify_header(type, size)
90
90
  header = "#{type} #{size}\0"
91
91
  store = header + content
@@ -109,7 +109,7 @@ module Grit
109
109
  size = c & 15;
110
110
  shift = 4;
111
111
  while c & 0x80 != 0
112
- if buf.length <= used
112
+ if buf.bytesize <= used
113
113
  raise LooseObjectError, "object file too short"
114
114
  end
115
115
  c = buf.getord(used)
@@ -30,6 +30,7 @@ module Grit
30
30
  SHA1Size = 20
31
31
  IdxOffsetSize = 4
32
32
  OffsetSize = 4
33
+ ExtendedOffsetSize = 8
33
34
  CrcSize = 4
34
35
  OffsetStart = FanOutCount * IdxOffsetSize
35
36
  SHA1Start = OffsetStart + OffsetSize
@@ -46,11 +47,13 @@ module Grit
46
47
  end
47
48
 
48
49
  def with_idx(index_file = nil)
49
- if !index_file
50
- index_file = @name
51
- idxfile = File.open(@name[0...-4]+'idx', 'rb')
52
- else
50
+ index_file ||= @name[0...-4] + 'idx'
51
+
52
+ begin
53
53
  idxfile = File.open(index_file, 'rb')
54
+ rescue Errno::ENOENT => boom
55
+ # file went away. bail out without yielding.
56
+ return
54
57
  end
55
58
 
56
59
  # read header
@@ -70,14 +73,19 @@ module Grit
70
73
  yield idx
71
74
  idx.unmap
72
75
  ensure
73
- idxfile.close
76
+ idxfile.close if idxfile
74
77
  end
75
78
 
76
79
  def with_packfile
77
- packfile = File.open(@name, 'rb')
80
+ begin
81
+ packfile = File.open(@name, 'rb')
82
+ rescue Errno::ENOENT
83
+ # file went away. bail out without yielding.
84
+ return
85
+ end
78
86
  yield packfile
79
87
  ensure
80
- packfile.close
88
+ packfile.close if packfile
81
89
  end
82
90
 
83
91
  def cache_objects
@@ -207,6 +215,12 @@ module Grit
207
215
  else
208
216
  pos = OffsetStart + (@size * (SHA1Size + CrcSize)) + (mid * OffsetSize)
209
217
  offset = idx[pos, OffsetSize].unpack('N')[0]
218
+ if offset & 0x80000000 > 0
219
+ offset &= 0x7fffffff
220
+ pos = OffsetStart + (@size * (SHA1Size + CrcSize + OffsetSize)) + (offset * ExtendedOffsetSize)
221
+ words = idx[pos, ExtendedOffsetSize].unpack('NN')
222
+ offset = (words[0] << 32) | words[1]
223
+ end
210
224
  return offset
211
225
  end
212
226
  else
@@ -31,6 +31,13 @@ module Grit
31
31
  def sha1
32
32
  Digest::SHA1.digest("%s %d\0" % [@type, @content.length] + @content)
33
33
  end
34
+
35
+ def to_hash
36
+ {
37
+ :type => @type,
38
+ :content => @content
39
+ }
40
+ end
34
41
  end
35
42
  end
36
43
  end
@@ -475,60 +475,60 @@ module Grit
475
475
  # [full_path, 'modified', tree1_hash, tree2_hash]
476
476
  # ]
477
477
  def quick_diff(tree1, tree2, path = '.', recurse = true)
478
- # handle empty trees
479
- changed = []
480
- return changed if tree1 == tree2
481
-
482
- t1 = list_tree(tree1) if tree1
483
- t2 = list_tree(tree2) if tree2
484
-
485
- # finding files that are different
486
- t1['blob'].each do |file, hsh|
487
- t2_file = t2['blob'][file] rescue nil
488
- full = File.join(path, file)
489
- if !t2_file
490
- changed << [full, 'added', hsh[:sha], nil] # not in parent
491
- elsif (hsh[:sha] != t2_file[:sha])
492
- changed << [full, 'modified', hsh[:sha], t2_file[:sha]] # file changed
493
- end
494
- end if t1
495
- t2['blob'].each do |file, hsh|
496
- if !t1 || !t1['blob'][file]
497
- changed << [File.join(path, file), 'removed', nil, hsh[:sha]]
498
- end
499
- end if t2
500
-
501
- t1['tree'].each do |dir, hsh|
502
- t2_tree = t2['tree'][dir] rescue nil
503
- full = File.join(path, dir)
504
- if !t2_tree
505
- if recurse
506
- changed += quick_diff(hsh[:sha], nil, full, true)
507
- else
508
- changed << [full, 'added', hsh[:sha], nil] # not in parent
509
- end
510
- elsif (hsh[:sha] != t2_tree[:sha])
511
- if recurse
512
- changed += quick_diff(hsh[:sha], t2_tree[:sha], full, true)
513
- else
514
- changed << [full, 'modified', hsh[:sha], t2_tree[:sha]] # file changed
515
- end
516
- end
517
- end if t1
518
- t2['tree'].each do |dir, hsh|
519
- t1_tree = t1['tree'][dir] rescue nil
520
- full = File.join(path, dir)
521
- if !t1_tree
522
- if recurse
523
- changed += quick_diff(nil, hsh[:sha], full, true)
524
- else
525
- changed << [full, 'removed', nil, hsh[:sha]]
526
- end
527
- end
528
- end if t2
529
-
530
- changed
531
- end
478
+ # handle empty trees
479
+ changed = []
480
+ return changed if tree1 == tree2
481
+
482
+ t1 = list_tree(tree1) if tree1
483
+ t2 = list_tree(tree2) if tree2
484
+
485
+ # finding files that are different
486
+ t1['blob'].each do |file, hsh|
487
+ t2_file = t2['blob'][file] rescue nil
488
+ full = File.join(path, file)
489
+ if !t2_file
490
+ changed << [full, 'added', hsh[:sha], nil] # not in parent
491
+ elsif (hsh[:sha] != t2_file[:sha])
492
+ changed << [full, 'modified', hsh[:sha], t2_file[:sha]] # file changed
493
+ end
494
+ end if t1
495
+ t2['blob'].each do |file, hsh|
496
+ if !t1 || !t1['blob'][file]
497
+ changed << [File.join(path, file), 'removed', nil, hsh[:sha]]
498
+ end
499
+ end if t2
500
+
501
+ t1['tree'].each do |dir, hsh|
502
+ t2_tree = t2['tree'][dir] rescue nil
503
+ full = File.join(path, dir)
504
+ if !t2_tree
505
+ if recurse
506
+ changed += quick_diff(hsh[:sha], nil, full, true)
507
+ else
508
+ changed << [full, 'added', hsh[:sha], nil] # not in parent
509
+ end
510
+ elsif (hsh[:sha] != t2_tree[:sha])
511
+ if recurse
512
+ changed += quick_diff(hsh[:sha], t2_tree[:sha], full, true)
513
+ else
514
+ changed << [full, 'modified', hsh[:sha], t2_tree[:sha]] # file changed
515
+ end
516
+ end
517
+ end if t1
518
+ t2['tree'].each do |dir, hsh|
519
+ t1_tree = t1['tree'][dir] rescue nil
520
+ full = File.join(path, dir)
521
+ if !t1_tree
522
+ if recurse
523
+ changed += quick_diff(nil, hsh[:sha], full, true)
524
+ else
525
+ changed << [full, 'removed', nil, hsh[:sha]]
526
+ end
527
+ end
528
+ end if t2
529
+
530
+ changed
531
+ end
532
532
 
533
533
  # returns true if the files in path_limiter were changed, or no path limiter
534
534
  # used by the log() function when passed with a path_limiter
@@ -551,9 +551,9 @@ module Grit
551
551
 
552
552
  if path && !(path == '' || path == '.' || path == './')
553
553
  paths = path.split('/')
554
- paths.each do |path|
554
+ paths.each do |pathname|
555
555
  tree = get_object_by_sha1(tree_sha)
556
- if entry = tree.entry.select { |e| e.name == path }.first
556
+ if entry = tree.entry.select { |e| e.name == pathname }.first
557
557
  tree_sha = entry.sha1 rescue nil
558
558
  else
559
559
  return false
@@ -720,9 +720,9 @@ module Grit
720
720
  end
721
721
  end
722
722
 
723
- def load_alternate_loose(path)
723
+ def load_alternate_loose(pathname)
724
724
  # load alternate loose, too
725
- each_alternate_path path do |path|
725
+ each_alternate_path pathname do |path|
726
726
  next if @loaded.include?(path)
727
727
  next if !File.exist?(path)
728
728
  load_loose(path)
@@ -745,8 +745,8 @@ module Grit
745
745
  @packs
746
746
  end
747
747
 
748
- def load_alternate_packs(path)
749
- each_alternate_path path do |path|
748
+ def load_alternate_packs(pathname)
749
+ each_alternate_path pathname do |path|
750
750
  full_pack = File.join(path, 'pack')
751
751
  next if @loaded_packs.include?(full_pack)
752
752
  load_packs(full_pack)