grit 1.1.1 → 2.0.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 (66) hide show
  1. data/History.txt +9 -0
  2. data/README.md +37 -11
  3. data/VERSION.yml +3 -3
  4. data/examples/ex_add_commit.rb +13 -0
  5. data/examples/ex_index.rb +14 -0
  6. data/lib/grit.rb +10 -3
  7. data/lib/grit/actor.rb +5 -5
  8. data/lib/grit/blob.rb +12 -12
  9. data/lib/grit/commit.rb +3 -3
  10. data/lib/grit/commit_stats.rb +26 -26
  11. data/lib/grit/config.rb +9 -9
  12. data/lib/grit/diff.rb +16 -16
  13. data/lib/grit/errors.rb +1 -1
  14. data/lib/grit/git-ruby.rb +108 -27
  15. data/lib/grit/git-ruby/commit_db.rb +11 -11
  16. data/lib/grit/git-ruby/file_index.rb +28 -28
  17. data/lib/grit/git-ruby/git_object.rb +14 -14
  18. data/lib/grit/git-ruby/internal/file_window.rb +4 -4
  19. data/lib/grit/git-ruby/internal/loose.rb +10 -10
  20. data/lib/grit/git-ruby/internal/pack.rb +29 -29
  21. data/lib/grit/git-ruby/internal/raw_object.rb +4 -4
  22. data/lib/grit/git-ruby/object.rb +9 -9
  23. data/lib/grit/git-ruby/repository.rb +111 -107
  24. data/lib/grit/git.rb +191 -14
  25. data/lib/grit/index.rb +21 -21
  26. data/lib/grit/lazy.rb +1 -1
  27. data/lib/grit/merge.rb +9 -9
  28. data/lib/grit/ref.rb +6 -31
  29. data/lib/grit/repo.rb +110 -65
  30. data/lib/grit/ruby1.9.rb +1 -1
  31. data/lib/grit/status.rb +24 -24
  32. data/lib/grit/submodule.rb +15 -15
  33. data/lib/grit/tag.rb +7 -57
  34. data/lib/grit/tree.rb +12 -12
  35. data/test/bench/benchmarks.rb +126 -0
  36. data/test/helper.rb +18 -0
  37. data/test/profile.rb +21 -0
  38. data/test/suite.rb +6 -0
  39. data/test/test_actor.rb +35 -0
  40. data/test/test_blame.rb +32 -0
  41. data/test/test_blame_tree.rb +33 -0
  42. data/test/test_blob.rb +83 -0
  43. data/test/test_commit.rb +207 -0
  44. data/test/test_commit_stats.rb +33 -0
  45. data/test/test_commit_write.rb +20 -0
  46. data/test/test_config.rb +58 -0
  47. data/test/test_diff.rb +18 -0
  48. data/test/test_file_index.rb +56 -0
  49. data/test/test_git.rb +105 -0
  50. data/test/test_grit.rb +32 -0
  51. data/test/test_head.rb +47 -0
  52. data/test/test_index_status.rb +40 -0
  53. data/test/test_merge.rb +17 -0
  54. data/test/test_raw.rb +16 -0
  55. data/test/test_real.rb +19 -0
  56. data/test/test_reality.rb +17 -0
  57. data/test/test_remote.rb +14 -0
  58. data/test/test_repo.rb +349 -0
  59. data/test/test_rubygit.rb +192 -0
  60. data/test/test_rubygit_alt.rb +40 -0
  61. data/test/test_rubygit_index.rb +76 -0
  62. data/test/test_rubygit_iv2.rb +28 -0
  63. data/test/test_submodule.rb +69 -0
  64. data/test/test_tag.rb +67 -0
  65. data/test/test_tree.rb +101 -0
  66. metadata +43 -13
@@ -1,5 +1,6 @@
1
+ require 'tempfile'
1
2
  module Grit
2
-
3
+
3
4
  class Git
4
5
  class GitTimeout < RuntimeError
5
6
  attr_reader :command, :bytes_read
@@ -11,36 +12,195 @@ module Grit
11
12
  end
12
13
 
13
14
  undef_method :clone
14
-
15
+
15
16
  include GitRuby
16
-
17
+
18
+ def exist?
19
+ File.exist?(self.git_dir)
20
+ end
21
+
22
+ def put_raw_object(content, type)
23
+ ruby_git.put_raw_object(content, type)
24
+ end
25
+
26
+ def object_exists?(object_id)
27
+ ruby_git.object_exists?(object_id)
28
+ end
29
+
17
30
  class << self
18
31
  attr_accessor :git_binary, :git_timeout, :git_max_size
19
32
  end
20
-
21
- self.git_binary = "/usr/bin/env git"
33
+
34
+ if RUBY_PLATFORM.downcase =~ /mswin(?!ce)|mingw|bccwin/
35
+ self.git_binary = "git" # using search path
36
+ else
37
+ self.git_binary = "/usr/bin/env git"
38
+ end
22
39
  self.git_timeout = 10
23
40
  self.git_max_size = 5242880 # 5.megabytes
24
-
41
+
25
42
  def self.with_timeout(timeout = 10.seconds)
26
43
  old_timeout = Grit::Git.git_timeout
27
44
  Grit::Git.git_timeout = timeout
28
45
  yield
29
46
  Grit::Git.git_timeout = old_timeout
30
47
  end
31
-
32
- attr_accessor :git_dir, :bytes_read
33
-
48
+
49
+ attr_accessor :git_dir, :bytes_read, :work_tree
50
+
34
51
  def initialize(git_dir)
35
52
  self.git_dir = git_dir
53
+ self.work_tree = git_dir.gsub(/\/\.git$/,'')
36
54
  self.bytes_read = 0
37
55
  end
38
-
56
+
39
57
  def shell_escape(str)
40
58
  str.to_s.gsub("'", "\\\\'").gsub(";", '\\;')
41
59
  end
42
60
  alias_method :e, :shell_escape
43
-
61
+
62
+ # Check if a normal file exists on the filesystem
63
+ # +file+ is the relative path from the Git dir
64
+ #
65
+ # Returns Boolean
66
+ def fs_exist?(file)
67
+ File.exist?(File.join(self.git_dir, file))
68
+ end
69
+
70
+ # Read a normal file from the filesystem.
71
+ # +file+ is the relative path from the Git dir
72
+ #
73
+ # Returns the String contents of the file
74
+ def fs_read(file)
75
+ File.open(File.join(self.git_dir, file)).read
76
+ end
77
+
78
+ # Write a normal file to the filesystem.
79
+ # +file+ is the relative path from the Git dir
80
+ # +contents+ is the String content to be written
81
+ #
82
+ # Returns nothing
83
+ def fs_write(file, contents)
84
+ path = File.join(self.git_dir, file)
85
+ FileUtils.mkdir_p(File.dirname(path))
86
+ File.open(path, 'w') do |f|
87
+ f.write(contents)
88
+ end
89
+ end
90
+
91
+ # Delete a normal file from the filesystem
92
+ # +file+ is the relative path from the Git dir
93
+ #
94
+ # Returns nothing
95
+ def fs_delete(file)
96
+ FileUtils.rm_rf(File.join(self.git_dir, file))
97
+ end
98
+
99
+ # Move a normal file
100
+ # +from+ is the relative path to the current file
101
+ # +to+ is the relative path to the destination file
102
+ #
103
+ # Returns nothing
104
+ def fs_move(from, to)
105
+ FileUtils.mv(File.join(self.git_dir, from), File.join(self.git_dir, to))
106
+ end
107
+
108
+ # Make a directory
109
+ # +dir+ is the relative path to the directory to create
110
+ #
111
+ # Returns nothing
112
+ def fs_mkdir(dir)
113
+ FileUtils.mkdir_p(File.join(self.git_dir, dir))
114
+ end
115
+
116
+ # Chmod the the file or dir and everything beneath
117
+ # +file+ is the relative path from the Git dir
118
+ #
119
+ # Returns nothing
120
+ def fs_chmod(mode, file = '/')
121
+ FileUtils.chmod_R(mode, File.join(self.git_dir, file))
122
+ end
123
+
124
+ def list_remotes
125
+ remotes = []
126
+ Dir.chdir(File.join(self.git_dir, 'refs/remotes')) do
127
+ remotes = Dir.glob('*')
128
+ end
129
+ remotes
130
+ rescue
131
+ []
132
+ end
133
+
134
+ def create_tempfile(seed, unlink = false)
135
+ path = Tempfile.new(seed).path
136
+ File.unlink(path) if unlink
137
+ return path
138
+ end
139
+
140
+ def commit_from_sha(id)
141
+ git_ruby_repo = GitRuby::Repository.new(self.git_dir)
142
+ object = git_ruby_repo.get_object_by_sha1(id)
143
+
144
+ if object.type == :commit
145
+ id
146
+ elsif object.type == :tag
147
+ object.object
148
+ else
149
+ ''
150
+ end
151
+ end
152
+
153
+ def check_applies(head_sha, applies_sha)
154
+ git_index = create_tempfile('index', true)
155
+ (o1, exit1) = raw_git("git read-tree #{head_sha} 2>/dev/null", git_index)
156
+ (o2, exit2) = raw_git("git diff #{applies_sha}^ #{applies_sha} | git apply --check --cached >/dev/null 2>/dev/null", git_index)
157
+ return (exit1 + exit2)
158
+ end
159
+
160
+ def get_patch(applies_sha)
161
+ git_index = create_tempfile('index', true)
162
+ (patch, exit2) = raw_git("git diff #{applies_sha}^ #{applies_sha}", git_index)
163
+ patch
164
+ end
165
+
166
+ def apply_patch(head_sha, patch)
167
+ git_index = create_tempfile('index', true)
168
+
169
+ git_patch = create_tempfile('patch')
170
+ File.open(git_patch, 'w+') { |f| f.print patch }
171
+
172
+ raw_git("git read-tree #{head_sha} 2>/dev/null", git_index)
173
+ (op, exit) = raw_git("git apply --cached < #{git_patch}", git_index)
174
+ if exit == 0
175
+ return raw_git("git write-tree", git_index).first.chomp
176
+ end
177
+ false
178
+ end
179
+
180
+ # RAW CALLS WITH ENV SETTINGS
181
+ def raw_git_call(command, index)
182
+ tmp = ENV['GIT_INDEX_FILE']
183
+ ENV['GIT_INDEX_FILE'] = index
184
+ out = `#{command}`
185
+ after = ENV['GIT_INDEX_FILE'] # someone fucking with us ??
186
+ ENV['GIT_INDEX_FILE'] = tmp
187
+ if after != index
188
+ raise 'environment was changed for the git call'
189
+ end
190
+ [out, $?.exitstatus]
191
+ end
192
+
193
+ def raw_git(command, index)
194
+ output = nil
195
+ Dir.chdir(self.git_dir) do
196
+ output = raw_git_call(command, index)
197
+ end
198
+ output
199
+ end
200
+ # RAW CALLS WITH ENV SETTINGS END
201
+
202
+
203
+
44
204
  # Run the given git command with the specified arguments and return
45
205
  # the result as a String
46
206
  # +cmd+ is the command
@@ -55,14 +215,27 @@ module Grit
55
215
  run('', cmd, '', options, args)
56
216
  end
57
217
 
218
+ # Bypass any pure Ruby implementations and go straight to the native Git command
219
+ #
220
+ # Returns String
221
+ def native(cmd, options = {}, *args)
222
+ method_missing(cmd, options, *args)
223
+ end
224
+
58
225
  def run(prefix, cmd, postfix, options, args)
59
226
  timeout = options.delete(:timeout) rescue nil
60
227
  timeout = true if timeout.nil?
61
228
 
62
229
  opt_args = transform_options(options)
63
- ext_args = args.reject { |a| a.empty? }.map { |a| (a == '--' || a[0].chr == '|') ? a : "'#{e(a)}'" }
64
230
 
65
- call = "#{prefix}#{Git.git_binary} --git-dir='#{self.git_dir}' #{cmd.to_s.gsub(/_/, '-')} #{(opt_args + ext_args).join(' ')}#{e(postfix)}"
231
+ 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)}\"" }
233
+ call = "#{prefix}#{Git.git_binary} --git-dir=\"#{self.git_dir}\" #{cmd.to_s.gsub(/_/, '-')} #{(opt_args + ext_args).join(' ')}#{e(postfix)}"
234
+ else
235
+ ext_args = args.reject { |a| a.empty? }.map { |a| (a == '--' || a[0].chr == '|') ? a : "'#{e(a)}'" }
236
+ call = "#{prefix}#{Git.git_binary} --git-dir='#{self.git_dir}' #{cmd.to_s.gsub(/_/, '-')} #{(opt_args + ext_args).join(' ')}#{e(postfix)}"
237
+ end
238
+
66
239
  Grit.log(call) if Grit.debug
67
240
  response, err = timeout ? sh(call) : wild_sh(call)
68
241
  Grit.log(response) if Grit.debug
@@ -120,6 +293,8 @@ module Grit
120
293
  if opt.to_s.size == 1
121
294
  if options[opt] == true
122
295
  args << "-#{opt}"
296
+ elsif options[opt] == false
297
+ # ignore
123
298
  else
124
299
  val = options.delete(opt)
125
300
  args << "-#{opt.to_s} '#{e(val)}'"
@@ -127,6 +302,8 @@ module Grit
127
302
  else
128
303
  if options[opt] == true
129
304
  args << "--#{opt.to_s.gsub(/_/, '-')}"
305
+ elsif options[opt] == false
306
+ # ignore
130
307
  else
131
308
  val = options.delete(opt)
132
309
  args << "--#{opt.to_s.gsub(/_/, '-')}='#{e(val)}'"
@@ -136,5 +313,5 @@ module Grit
136
313
  args
137
314
  end
138
315
  end # Git
139
-
316
+
140
317
  end # Grit
@@ -1,14 +1,14 @@
1
1
  module Grit
2
-
2
+
3
3
  class Index
4
4
  attr_accessor :repo, :tree, :current_tree
5
-
5
+
6
6
  def initialize(repo)
7
7
  self.repo = repo
8
8
  self.tree = {}
9
9
  self.current_tree = nil
10
10
  end
11
-
11
+
12
12
  # Add a file to the index
13
13
  # +path+ is the path (including filename)
14
14
  # +data+ is the binary contents of the file
@@ -17,18 +17,18 @@ module Grit
17
17
  def add(file_path, data)
18
18
  path = file_path.split('/')
19
19
  filename = path.pop
20
-
20
+
21
21
  current = self.tree
22
-
22
+
23
23
  path.each do |dir|
24
24
  current[dir] ||= {}
25
25
  node = current[dir]
26
26
  current = node
27
27
  end
28
-
28
+
29
29
  current[filename] = data
30
30
  end
31
-
31
+
32
32
  # Sets the current tree
33
33
  # +tree+ the branch/tag/sha... to use - a string
34
34
  #
@@ -36,7 +36,7 @@ module Grit
36
36
  def read_tree(tree)
37
37
  self.current_tree = self.repo.tree(tree)
38
38
  end
39
-
39
+
40
40
  # Commit the contents of the index
41
41
  # +message+ is the commit message [nil]
42
42
  # +parents+ is one or more commits to attach this commit to to form a new head [nil]
@@ -48,11 +48,11 @@ module Grit
48
48
  def commit(message, parents = nil, actor = nil, last_tree = nil, head = 'master')
49
49
  tree_sha1 = write_tree(self.tree, self.current_tree)
50
50
  return false if tree_sha1 == last_tree # don't write identical commits
51
-
51
+
52
52
  contents = []
53
53
  contents << ['tree', tree_sha1].join(' ')
54
54
  parents.each do |p|
55
- contents << ['parent', p].join(' ') if p
55
+ contents << ['parent', p].join(' ') if p
56
56
  end if parents
57
57
 
58
58
  if actor
@@ -63,25 +63,25 @@ module Grit
63
63
  name = config['user.name']
64
64
  email = config['user.email']
65
65
  end
66
-
66
+
67
67
  author_string = "#{name} <#{email}> #{Time.now.to_i} -0700" # !! TODO : gotta fix this
68
68
  contents << ['author', author_string].join(' ')
69
69
  contents << ['committer', author_string].join(' ')
70
70
  contents << ''
71
71
  contents << message
72
-
73
- commit_sha1 = self.repo.git.ruby_git.put_raw_object(contents.join("\n"), 'commit')
74
-
72
+
73
+ commit_sha1 = self.repo.git.put_raw_object(contents.join("\n"), 'commit')
74
+
75
75
  self.repo.update_ref(head, commit_sha1)
76
76
  end
77
-
77
+
78
78
  # Recursively write a tree to the index
79
79
  # +tree+ is the tree
80
80
  #
81
81
  # Returns the SHA1 String of the tree
82
82
  def write_tree(tree, now_tree = nil)
83
83
  tree_contents = {}
84
-
84
+
85
85
  # fill in original tree
86
86
  now_tree.contents.each do |obj|
87
87
  sha = [obj.id].pack("H*")
@@ -89,7 +89,7 @@ module Grit
89
89
  k += '/' if (obj.class == Grit::Tree)
90
90
  tree_contents[k] = "%s %s\0%s" % [obj.mode.to_s, obj.name, sha]
91
91
  end if now_tree
92
-
92
+
93
93
  # overwrite with new tree contents
94
94
  tree.each do |k, v|
95
95
  case v
@@ -107,16 +107,16 @@ module Grit
107
107
  end
108
108
  end
109
109
  tr = tree_contents.sort.map { |k, v| v }.join('')
110
- self.repo.git.ruby_git.put_raw_object(tr, 'tree')
110
+ self.repo.git.put_raw_object(tr, 'tree')
111
111
  end
112
-
112
+
113
113
  # Write the blob to the index
114
114
  # +data+ is the data to write
115
115
  #
116
116
  # Returns the SHA1 String of the blob
117
117
  def write_blob(data)
118
- self.repo.git.ruby_git.put_raw_object(data, 'blob')
118
+ self.repo.git.put_raw_object(data, 'blob')
119
119
  end
120
120
  end # Index
121
-
121
+
122
122
  end # Grit
@@ -1,6 +1,6 @@
1
1
  ##
2
2
  # Allows attributes to be declared as lazy, meaning that they won't be
3
- # computed until they are asked for.
3
+ # computed until they are asked for.
4
4
  #
5
5
  # Works by delegating each lazy_reader to a cached lazy_source method.
6
6
  #
@@ -1,20 +1,20 @@
1
1
  module Grit
2
-
2
+
3
3
  class Merge
4
-
4
+
5
5
  STATUS_BOTH = 'both'
6
6
  STATUS_OURS = 'ours'
7
7
  STATUS_THEIRS = 'theirs'
8
-
8
+
9
9
  attr_reader :conflicts, :text, :sections
10
-
10
+
11
11
  def initialize(str)
12
12
  status = STATUS_BOTH
13
-
13
+
14
14
  section = 1
15
15
  @conflicts = 0
16
16
  @text = {}
17
-
17
+
18
18
  lines = str.split("\n")
19
19
  lines.each do |line|
20
20
  if /^<<<<<<< (.*?)/.match(line)
@@ -24,7 +24,7 @@ module Grit
24
24
  elsif line == '======='
25
25
  status = STATUS_THEIRS
26
26
  elsif /^>>>>>>> (.*?)/.match(line)
27
- status = STATUS_BOTH
27
+ status = STATUS_BOTH
28
28
  section += 1
29
29
  else
30
30
  @text[section] ||= {}
@@ -35,11 +35,11 @@ module Grit
35
35
  @text = @text.values
36
36
  @sections = @text.size
37
37
  end
38
-
38
+
39
39
  # Pretty object inspection
40
40
  def inspect
41
41
  %Q{#<Grit::Merge}
42
42
  end
43
43
  end # Merge
44
-
44
+
45
45
  end # Grit
@@ -10,37 +10,12 @@ module Grit
10
10
  #
11
11
  # Returns Grit::Ref[] (baked)
12
12
  def find_all(repo, options = {})
13
- refs = []
14
- already = {}
15
- Dir.chdir(repo.path) do
16
- files = Dir.glob(prefix + '/**/*')
17
- files.each do |ref|
18
- next if !File.file?(ref)
19
- id = File.read(ref).chomp
20
- name = ref.sub("#{prefix}/", '')
21
- commit = Commit.create(repo, :id => id)
22
- if !already[name]
23
- refs << self.new(name, commit)
24
- already[name] = true
25
- end
26
- end
27
-
28
- if File.file?('packed-refs')
29
- File.readlines('packed-refs').each do |line|
30
- if m = /^(\w{40}) (.*?)$/.match(line)
31
- next if !Regexp.new('^' + prefix).match(m[2])
32
- name = m[2].sub("#{prefix}/", '')
33
- commit = Commit.create(repo, :id => m[1])
34
- if !already[name]
35
- refs << self.new(name, commit)
36
- already[name] = true
37
- end
38
- end
39
- end
40
- end
13
+ refs = repo.git.refs(options, prefix)
14
+ refs.split("\n").map do |ref|
15
+ name, id = *ref.split(' ')
16
+ commit = Commit.create(repo, :id => id)
17
+ self.new(name, commit)
41
18
  end
42
-
43
- refs
44
19
  end
45
20
 
46
21
  protected
@@ -86,7 +61,7 @@ module Grit
86
61
  #
87
62
  # Returns Grit::Head (baked)
88
63
  def self.current(repo, options = {})
89
- head = File.open(File.join(repo.path, 'HEAD')).read.chomp
64
+ head = repo.git.fs_read('HEAD').chomp
90
65
  if /ref: refs\/heads\/(.*)/.match(head)
91
66
  self.new($1, repo.git.rev_parse(options, 'HEAD'))
92
67
  end