grit 2.0.0 → 2.1.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.

Files changed (55) hide show
  1. data/History.txt +21 -0
  2. data/PURE_TODO +35 -0
  3. data/Rakefile +149 -0
  4. data/{test/bench/benchmarks.rb → benchmarks.rb} +6 -3
  5. data/benchmarks.txt +21 -0
  6. data/examples/ex_index.rb +7 -0
  7. data/grit.gemspec +76 -0
  8. data/lib/grit.rb +7 -5
  9. data/lib/grit/commit.rb +26 -12
  10. data/lib/grit/diff.rb +22 -13
  11. data/lib/grit/errors.rb +4 -1
  12. data/lib/grit/git-ruby.rb +12 -7
  13. data/lib/grit/git-ruby/git_object.rb +2 -2
  14. data/lib/grit/git-ruby/internal/loose.rb +1 -1
  15. data/lib/grit/git-ruby/internal/pack.rb +2 -0
  16. data/lib/grit/git-ruby/repository.rb +33 -6
  17. data/lib/grit/git.rb +9 -4
  18. data/lib/grit/index.rb +1 -1
  19. data/lib/{open3_detach.rb → grit/open3_detach.rb} +0 -0
  20. data/lib/grit/ref.rb +3 -1
  21. data/lib/grit/repo.rb +81 -1
  22. data/lib/grit/tag.rb +57 -4
  23. data/lib/grit/tree.rb +1 -1
  24. metadata +30 -46
  25. data/VERSION.yml +0 -4
  26. data/test/helper.rb +0 -18
  27. data/test/profile.rb +0 -21
  28. data/test/suite.rb +0 -6
  29. data/test/test_actor.rb +0 -35
  30. data/test/test_blame.rb +0 -32
  31. data/test/test_blame_tree.rb +0 -33
  32. data/test/test_blob.rb +0 -83
  33. data/test/test_commit.rb +0 -207
  34. data/test/test_commit_stats.rb +0 -33
  35. data/test/test_commit_write.rb +0 -20
  36. data/test/test_config.rb +0 -58
  37. data/test/test_diff.rb +0 -18
  38. data/test/test_file_index.rb +0 -56
  39. data/test/test_git.rb +0 -105
  40. data/test/test_grit.rb +0 -32
  41. data/test/test_head.rb +0 -47
  42. data/test/test_index_status.rb +0 -40
  43. data/test/test_merge.rb +0 -17
  44. data/test/test_raw.rb +0 -16
  45. data/test/test_real.rb +0 -19
  46. data/test/test_reality.rb +0 -17
  47. data/test/test_remote.rb +0 -14
  48. data/test/test_repo.rb +0 -349
  49. data/test/test_rubygit.rb +0 -192
  50. data/test/test_rubygit_alt.rb +0 -40
  51. data/test/test_rubygit_index.rb +0 -76
  52. data/test/test_rubygit_iv2.rb +0 -28
  53. data/test/test_submodule.rb +0 -69
  54. data/test/test_tag.rb +0 -67
  55. data/test/test_tree.rb +0 -101
@@ -4,20 +4,23 @@ module Grit
4
4
  attr_reader :a_path, :b_path
5
5
  attr_reader :a_blob, :b_blob
6
6
  attr_reader :a_mode, :b_mode
7
- attr_reader :new_file, :deleted_file
8
- attr_reader :diff
7
+ attr_reader :new_file, :deleted_file, :renamed_file
8
+ attr_reader :similarity_index
9
+ attr_accessor :diff
9
10
 
10
- def initialize(repo, a_path, b_path, a_blob, b_blob, a_mode, b_mode, new_file, deleted_file, diff)
11
- @repo = repo
11
+ def initialize(repo, a_path, b_path, a_blob, b_blob, a_mode, b_mode, new_file, deleted_file, diff, renamed_file = false, similarity_index = 0)
12
+ @repo = repo
12
13
  @a_path = a_path
13
14
  @b_path = b_path
14
15
  @a_blob = a_blob =~ /^0{40}$/ ? nil : Blob.create(repo, :id => a_blob)
15
16
  @b_blob = b_blob =~ /^0{40}$/ ? nil : Blob.create(repo, :id => b_blob)
16
17
  @a_mode = a_mode
17
18
  @b_mode = b_mode
18
- @new_file = new_file || @a_blob.nil?
19
- @deleted_file = deleted_file || @b_blob.nil?
20
- @diff = diff
19
+ @new_file = new_file || @a_blob.nil?
20
+ @deleted_file = deleted_file || @b_blob.nil?
21
+ @renamed_file = renamed_file
22
+ @similarity_index = similarity_index.to_i
23
+ @diff = diff
21
24
  end
22
25
 
23
26
  def self.list_from_string(repo, text)
@@ -38,17 +41,23 @@ module Grit
38
41
  next
39
42
  end
40
43
 
41
- new_file = false
44
+ sim_index = 0
45
+ new_file = false
42
46
  deleted_file = false
47
+ renamed_file = false
43
48
 
44
49
  if lines.first =~ /^new file/
45
50
  m, b_mode = lines.shift.match(/^new file mode (.+)$/)
46
- a_mode = nil
47
- new_file = true
51
+ a_mode = nil
52
+ new_file = true
48
53
  elsif lines.first =~ /^deleted file/
49
- m, a_mode = lines.shift.match(/^deleted file mode (.+)$/)
50
- b_mode = nil
54
+ m, a_mode = lines.shift.match(/^deleted file mode (.+)$/)
55
+ b_mode = nil
51
56
  deleted_file = true
57
+ elsif lines.first =~ /^similarity index (\d+)\%/
58
+ sim_index = $1.to_i
59
+ renamed_file = true
60
+ 2.times { lines.shift } # shift away the 2 `rename from/to ...` lines
52
61
  end
53
62
 
54
63
  m, a_blob, b_blob, b_mode = *lines.shift.match(%r{^index ([0-9A-Fa-f]+)\.\.([0-9A-Fa-f]+) ?(.+)?$})
@@ -60,7 +69,7 @@ module Grit
60
69
  end
61
70
  diff = diff_lines.join("\n")
62
71
 
63
- diffs << Diff.new(repo, a_path, b_path, a_blob, b_blob, a_mode, b_mode, new_file, deleted_file, diff)
72
+ diffs << Diff.new(repo, a_path, b_path, a_blob, b_blob, a_mode, b_mode, new_file, deleted_file, diff, renamed_file, sim_index)
64
73
  end
65
74
 
66
75
  diffs
@@ -4,4 +4,7 @@ module Grit
4
4
 
5
5
  class NoSuchPathError < StandardError
6
6
  end
7
- end
7
+
8
+ class InvalidObjectType < StandardError
9
+ end
10
+ end
@@ -18,28 +18,33 @@ module Grit
18
18
  end
19
19
  end
20
20
 
21
- def cat_file(options, ref)
21
+ def cat_file(options, sha)
22
22
  if options[:t]
23
- file_type(ref)
23
+ file_type(sha)
24
24
  elsif options[:s]
25
- file_size(ref)
25
+ file_size(sha)
26
26
  elsif options[:p]
27
- try_run { ruby_git.cat_file(ref) }
27
+ try_run { ruby_git.cat_file(sha) }
28
28
  end
29
29
  rescue Grit::GitRuby::Repository::NoSuchShaFound
30
30
  ''
31
31
  end
32
32
 
33
+ def cat_ref(options, ref)
34
+ sha = rev_parse({}, ref)
35
+ cat_file(options, sha)
36
+ end
37
+
33
38
  # lib/grit/tree.rb:16: output = repo.git.ls_tree({}, treeish, *paths)
34
39
  def ls_tree(options, treeish, *paths)
35
40
  sha = rev_parse({}, treeish)
36
- ruby_git.ls_tree(sha, paths.flatten)
41
+ ruby_git.ls_tree(sha, paths.flatten, options.delete(:r))
37
42
  rescue Grit::GitRuby::Repository::NoSuchShaFound
38
43
  ''
39
44
  end
40
45
 
41
46
  # git diff --full-index 'ec037431382e83c3e95d4f2b3d145afbac8ea55d' 'f1ec1aea10986159456846b8a05615b87828d6c6'
42
- def diff(options, sha1, sha2)
47
+ def diff(options, sha1, sha2 = nil)
43
48
  try_run { ruby_git.diff(sha1, sha2, options) }
44
49
  end
45
50
 
@@ -100,7 +105,7 @@ module Grit
100
105
  ## !! more - partials and such !!
101
106
 
102
107
  # revert to calling git - grr
103
- return method_missing('rev-parse', {}, string).chomp
108
+ return method_missing('rev-parse', options, string).chomp
104
109
  end
105
110
 
106
111
  def refs(options, prefix)
@@ -337,8 +337,8 @@ module Grit
337
337
  end
338
338
 
339
339
  def raw_content
340
- "object %s\ntype %s\ntag %s\ntagger %s\n\n" % \
341
- [@object, @type, @tag, @tagger] + @message
340
+ ("object %s\ntype %s\ntag %s\ntagger %s\n\n" % \
341
+ [@object, @type, @tag, @tagger]) + @message.to_s
342
342
  end
343
343
 
344
344
  def type
@@ -29,7 +29,7 @@ module Grit
29
29
  begin
30
30
  return nil unless sha1[0...2] && sha1[2..39]
31
31
  path = @directory + '/' + sha1[0...2] + '/' + sha1[2..39]
32
- get_raw_object(File.open(path, 'rb').read)
32
+ get_raw_object(File.read(path))
33
33
  rescue Errno::ENOENT
34
34
  nil
35
35
  end
@@ -69,12 +69,14 @@ module Grit
69
69
  idx = FileWindow.new(idxfile, @version)
70
70
  yield idx
71
71
  idx.unmap
72
+ ensure
72
73
  idxfile.close
73
74
  end
74
75
 
75
76
  def with_packfile
76
77
  packfile = File.open(@name, 'rb')
77
78
  yield packfile
79
+ ensure
78
80
  packfile.close
79
81
  end
80
82
 
@@ -169,7 +169,8 @@ module Grit
169
169
  # returns the raw (cat-file) output for a tree
170
170
  # if given a commit sha, it will print the tree of that commit
171
171
  # if given a path limiter array, it will limit the output to those
172
- def ls_tree(sha, paths = [])
172
+ # if asked for recrusive trees, will traverse trees
173
+ def ls_tree(sha, paths = [], recursive = false)
173
174
  if paths.size > 0
174
175
  # pathing
175
176
  part = []
@@ -178,20 +179,44 @@ module Grit
178
179
  end
179
180
  return part.join("\n")
180
181
  else
181
- get_raw_tree(sha)
182
+ get_raw_tree(sha, recursive)
182
183
  end
183
184
  end
184
185
 
185
- def get_raw_tree(sha)
186
+ def get_raw_tree(sha, recursive = false)
186
187
  o = get_raw_object_by_sha1(sha)
187
188
  if o.type == :commit
188
- cat_file(get_object_by_sha1(sha).tree)
189
+ tree = get_object_by_sha1(sha).tree
189
190
  elsif o.type == :tag
190
191
  commit_sha = get_object_by_sha1(sha).object
191
- cat_file(get_object_by_sha1(commit_sha).tree)
192
+ tree = get_object_by_sha1(commit_sha).tree
192
193
  elsif o.type == :tree
193
- cat_file(sha)
194
+ tree = sha
195
+ else
196
+ return nil
194
197
  end
198
+
199
+ recursive ? get_raw_trees(tree) : cat_file(tree)
200
+ end
201
+
202
+ # Grabs tree contents recursively,
203
+ # e.g. `git ls-tree -r sha`
204
+ def get_raw_trees(sha, path = '')
205
+ out = ''
206
+ cat_file(sha).split("\n").each do |line|
207
+ mode, type, sha, name = line.split(/\s/)
208
+
209
+ if type == 'tree'
210
+ full_name = path.empty? ? name : "#{path}/#{name}"
211
+ out << get_raw_trees(sha, full_name)
212
+ elsif path.empty?
213
+ out << line + "\n"
214
+ else
215
+ out << line.gsub(name, "#{path}/#{name}") + "\n"
216
+ end
217
+ end
218
+
219
+ out
195
220
  end
196
221
 
197
222
  # return array of tree entries
@@ -219,6 +244,7 @@ module Grit
219
244
  tree
220
245
  end
221
246
  else
247
+ raise NoSuchPath if tree.nil?
222
248
  next_path = paths.shift
223
249
  dir_name = tree.split("\n").select { |p| p.split("\t")[1] == next_path }.first
224
250
  raise NoSuchPath if !dir_name
@@ -231,6 +257,7 @@ module Grit
231
257
  end
232
258
  end
233
259
  else
260
+ raise NoSuchPath if tree.nil?
234
261
  tree = tree.split("\n")
235
262
  tree = tree.select { |p| p.split("\t")[1] == path }
236
263
  if append
@@ -27,6 +27,12 @@ module Grit
27
27
  ruby_git.object_exists?(object_id)
28
28
  end
29
29
 
30
+ def select_existing_objects(object_ids)
31
+ object_ids.select do |object_id|
32
+ object_exists?(object_id)
33
+ end
34
+ end
35
+
30
36
  class << self
31
37
  attr_accessor :git_binary, :git_timeout, :git_max_size
32
38
  end
@@ -72,7 +78,7 @@ module Grit
72
78
  #
73
79
  # Returns the String contents of the file
74
80
  def fs_read(file)
75
- File.open(File.join(self.git_dir, file)).read
81
+ File.read(File.join(self.git_dir, file))
76
82
  end
77
83
 
78
84
  # Write a normal file to the filesystem.
@@ -229,13 +235,12 @@ module Grit
229
235
  opt_args = transform_options(options)
230
236
 
231
237
  if RUBY_PLATFORM.downcase =~ /mswin(?!ce)|mingw|bccwin/
232
- ext_args = args.reject { |a| a.empty? }.map { |a| (a == '--' || a[0].chr == '|') ? a : "\"#{e(a)}\"" }
238
+ ext_args = args.reject { |a| a.empty? }.map { |a| (a == '--' || a[0].chr == '|' || Grit.no_quote) ? a : "\"#{e(a)}\"" }
233
239
  call = "#{prefix}#{Git.git_binary} --git-dir=\"#{self.git_dir}\" #{cmd.to_s.gsub(/_/, '-')} #{(opt_args + ext_args).join(' ')}#{e(postfix)}"
234
240
  else
235
- ext_args = args.reject { |a| a.empty? }.map { |a| (a == '--' || a[0].chr == '|') ? a : "'#{e(a)}'" }
241
+ ext_args = args.reject { |a| a.empty? }.map { |a| (a == '--' || a[0].chr == '|' || Grit.no_quote) ? a : "'#{e(a)}'" }
236
242
  call = "#{prefix}#{Git.git_binary} --git-dir='#{self.git_dir}' #{cmd.to_s.gsub(/_/, '-')} #{(opt_args + ext_args).join(' ')}#{e(postfix)}"
237
243
  end
238
-
239
244
  Grit.log(call) if Grit.debug
240
245
  response, err = timeout ? sh(call) : wild_sh(call)
241
246
  Grit.log(response) if Grit.debug
@@ -102,7 +102,7 @@ module Grit
102
102
  ctree = now_tree/k if now_tree
103
103
  sha = write_tree(v, ctree)
104
104
  sha = [sha].pack("H*")
105
- str = "%s %s\0%s" % ['040000', k, sha]
105
+ str = "%s %s\0%s" % ['40000', k, sha]
106
106
  tree_contents[k + '/'] = str
107
107
  end
108
108
  end
@@ -63,7 +63,9 @@ module Grit
63
63
  def self.current(repo, options = {})
64
64
  head = repo.git.fs_read('HEAD').chomp
65
65
  if /ref: refs\/heads\/(.*)/.match(head)
66
- self.new($1, repo.git.rev_parse(options, 'HEAD'))
66
+ id = repo.git.rev_parse(options, 'HEAD')
67
+ commit = Commit.create(repo, :id => id)
68
+ self.new($1, commit)
67
69
  end
68
70
  end
69
71
 
@@ -132,6 +132,30 @@ module Grit
132
132
  Tag.find_all(self)
133
133
  end
134
134
 
135
+ # Finds the most recent annotated tag name that is reachable from a commit.
136
+ #
137
+ # @repo.recent_tag_name('master')
138
+ # # => "v1.0-0-abcdef"
139
+ #
140
+ # committish - optional commit SHA, branch, or tag name.
141
+ # options - optional hash of options to pass to git.
142
+ # Default: {:always => true}
143
+ # :tags => true # use lightweight tags too.
144
+ # :abbrev => Integer # number of hex digits to form the unique
145
+ # name. Defaults to 7.
146
+ # :long => true # always output tag + commit sha
147
+ # # see `git describe` docs for more options.
148
+ #
149
+ # Returns the String tag name, or just the commit if no tag is
150
+ # found. If there have been updates since the tag was made, a
151
+ # suffix is added with the number of commits since the tag, and
152
+ # the abbreviated object name of the most recent commit.
153
+ # Returns nil if the committish value is not found.
154
+ def recent_tag_name(committish = nil, options = {})
155
+ value = git.describe({:always => true}.update(options), committish.to_s).to_s.strip
156
+ value.size.zero? ? nil : value
157
+ end
158
+
135
159
  # An array of Remote objects representing the remote branches in
136
160
  # this repo
137
161
  #
@@ -251,6 +275,46 @@ module Grit
251
275
  end
252
276
  end
253
277
 
278
+ def objects(refs)
279
+ Grit.no_quote = true
280
+ obj = self.git.rev_list({:objects => true, :timeout => false}, refs).split("\n").map { |a| a[0, 40] }
281
+ Grit.no_quote = false
282
+ obj
283
+ end
284
+
285
+ def commit_objects(refs)
286
+ Grit.no_quote = true
287
+ obj = self.git.rev_list({:timeout => false}, refs).split("\n").map { |a| a[0, 40] }
288
+ Grit.no_quote = false
289
+ obj
290
+ end
291
+
292
+ def objects_between(ref1, ref2 = nil)
293
+ if ref2
294
+ refs = "#{ref2}..#{ref1}"
295
+ else
296
+ refs = ref1
297
+ end
298
+ self.objects(refs)
299
+ end
300
+
301
+ def diff_objects(commit_sha, parents = true)
302
+ revs = []
303
+ Grit.no_quote = true
304
+ if parents
305
+ # PARENTS:
306
+ cmd = "-r -t -m #{commit_sha}"
307
+ revs = self.git.diff_tree({:timeout => false}, cmd).strip.split("\n").map{ |a| r = a.split(' '); r[3] if r[1] != '160000' }
308
+ else
309
+ # NO PARENTS:
310
+ cmd = "-r -t #{commit_sha}"
311
+ revs = self.git.method_missing('ls-tree', {:timeout => false}, "-r -t #{commit_sha}").split("\n").map{ |a| a.split("\t").first.split(' ')[2] }
312
+ end
313
+ revs << self.commit(commit_sha).tree.id
314
+ Grit.no_quote = false
315
+ return revs.uniq.compact
316
+ end
317
+
254
318
  # The Tree object for the given treeish reference
255
319
  # +treeish+ is the reference (default 'master')
256
320
  # +paths+ is an optional Array of directory paths to restrict the tree (deafult [])
@@ -287,7 +351,14 @@ module Grit
287
351
  # +b+ is the other commit
288
352
  # +paths+ is an optional list of file paths on which to restrict the diff
289
353
  def diff(a, b, *paths)
290
- self.git.diff({}, a, b, '--', *paths)
354
+ diff = self.git.native('diff', {}, a, b, '--', *paths)
355
+
356
+ if diff =~ /diff --git a/
357
+ diff = diff.sub(/.+?(diff --git a)/m, '\1')
358
+ else
359
+ diff = ''
360
+ end
361
+ Diff.list_from_string(self, diff)
291
362
  end
292
363
 
293
364
  # The commit diff for the given commit
@@ -314,6 +385,15 @@ module Grit
314
385
  self.new(path, repo_options)
315
386
  end
316
387
 
388
+ def self.init_bare_or_open(path, git_options = {}, repo_options = {})
389
+ git = Git.new(path)
390
+ if !git.exist?
391
+ git.fs_mkdir(path)
392
+ git.init(git_options)
393
+ end
394
+ self.new(path, repo_options)
395
+ end
396
+
317
397
  # Fork a bare git repository from this repo
318
398
  # +path+ is the full path of the new repo (traditionally ends with /<name>.git)
319
399
  # +options+ is any additional options to the git clone command (:bare and :shared are true by default)
@@ -1,15 +1,68 @@
1
1
  module Grit
2
2
 
3
3
  class Tag < Ref
4
+ lazy_reader :message
5
+ lazy_reader :tagger
6
+ lazy_reader :tag_date
7
+
4
8
  def self.find_all(repo, options = {})
5
9
  refs = repo.git.refs(options, prefix)
6
10
  refs.split("\n").map do |ref|
7
11
  name, id = *ref.split(' ')
8
- cid = repo.git.commit_from_sha(id)
9
- raise "Unknown object type." if cid == ''
10
- commit = Commit.create(repo, :id => cid)
11
- self.new(name, commit)
12
+ sha = repo.git.commit_from_sha(id)
13
+ raise "Unknown object type." if sha == ''
14
+ commit = Commit.create(repo, :id => sha)
15
+ new(name, commit)
16
+ end
17
+ end
18
+
19
+ # Parses the results from `cat-file -p`
20
+ #
21
+ # data - String tag object data. Example:
22
+ # object 7bcc0ee821cdd133d8a53e8e7173a334fef448aa
23
+ # type commit
24
+ # tag v0.7.0
25
+ # tagger USER <EMAIL> DATE
26
+ #
27
+ # v0.7.0
28
+ #
29
+ # Returns parsed Hash. Example:
30
+ # {:message => "...", :tagger => "bob", :tag_date => ...}
31
+ def self.parse_tag_data(data)
32
+ return unless data =~ /^object/
33
+ parsed = {}
34
+ lines = data.split("\n")
35
+ lines.shift # type commit
36
+ lines.shift # tag name
37
+ lines.shift
38
+ author_line = lines.shift
39
+ parsed[:tagger], parsed[:tag_date] = Commit.actor(author_line)
40
+ if !parsed[:tagger] || !parsed[:tagger].name
41
+ parsed[:tag_date] ||= Time.utc(1970)
42
+ parsed[:tagger] = Actor.from_string(author_line.sub(/^tagger /, ''))
43
+ end
44
+ lines.shift # blank line
45
+ parsed[:message] = []
46
+ while lines.first && lines.first !~ /-----BEGIN PGP SIGNATURE-----/
47
+ parsed[:message] << lines.shift
48
+ end
49
+ parsed[:message] = parsed[:message] * "\n"
50
+ parsed
51
+ end
52
+
53
+ def lazy_source
54
+ data = commit.repo.git.cat_ref({:p => true}, name)
55
+ @message = commit.short_message
56
+ @tagger = commit.author
57
+ @tag_date = commit.authored_date
58
+ return self if data.empty?
59
+
60
+ if parsed = self.class.parse_tag_data(data)
61
+ @message = parsed[:message]
62
+ @tagger = parsed[:tagger]
63
+ @tag_date = parsed[:tag_date]
12
64
  end
65
+ self
13
66
  end
14
67
  end
15
68