grit 2.4.1 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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)