mojombo-grit 0.8.1 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/History.txt +7 -0
  2. data/Manifest.txt +18 -1
  3. data/grit.gemspec +56 -10
  4. data/lib/{mojombo-grit.rb → grit.rb} +20 -4
  5. data/lib/grit/commit.rb +32 -11
  6. data/lib/grit/commit_stats.rb +104 -0
  7. data/lib/grit/git-ruby.rb +182 -0
  8. data/lib/grit/git-ruby/commit_db.rb +52 -0
  9. data/lib/grit/git-ruby/file_index.rb +186 -0
  10. data/lib/grit/git-ruby/git_object.rb +344 -0
  11. data/lib/grit/git-ruby/internal/loose.rb +136 -0
  12. data/lib/grit/git-ruby/internal/mmap.rb +58 -0
  13. data/lib/grit/git-ruby/internal/pack.rb +382 -0
  14. data/lib/grit/git-ruby/internal/raw_object.rb +37 -0
  15. data/lib/grit/git-ruby/object.rb +319 -0
  16. data/lib/grit/git-ruby/repository.rb +729 -0
  17. data/lib/grit/git.rb +33 -15
  18. data/lib/grit/head.rb +6 -15
  19. data/lib/grit/index.rb +121 -0
  20. data/lib/grit/ref.rb +95 -0
  21. data/lib/grit/repo.rb +95 -6
  22. data/lib/grit/status.rb +151 -0
  23. data/lib/grit/tree.rb +3 -2
  24. data/test/test_blob.rb +5 -0
  25. data/test/test_commit.rb +7 -5
  26. data/test/test_diff.rb +1 -1
  27. data/test/test_git.rb +20 -2
  28. data/test/test_grit.rb +32 -0
  29. data/test/test_head.rb +30 -5
  30. data/test/test_real.rb +8 -6
  31. data/test/test_remote.rb +14 -0
  32. data/test/test_repo.rb +86 -79
  33. data/test/test_tag.rb +2 -6
  34. data/test/test_tree.rb +5 -0
  35. metadata +40 -40
  36. data/test/fixtures/blame +0 -131
  37. data/test/fixtures/cat_file_blob +0 -1
  38. data/test/fixtures/cat_file_blob_size +0 -1
  39. data/test/fixtures/diff_2 +0 -54
  40. data/test/fixtures/diff_2f +0 -19
  41. data/test/fixtures/diff_f +0 -15
  42. data/test/fixtures/diff_i +0 -201
  43. data/test/fixtures/diff_mode_only +0 -1152
  44. data/test/fixtures/diff_new_mode +0 -17
  45. data/test/fixtures/diff_p +0 -610
  46. data/test/fixtures/for_each_ref +0 -0
  47. data/test/fixtures/for_each_ref_tags +0 -0
  48. data/test/fixtures/ls_tree_a +0 -7
  49. data/test/fixtures/ls_tree_b +0 -2
  50. data/test/fixtures/ls_tree_commit +0 -3
  51. data/test/fixtures/rev_list +0 -26
  52. data/test/fixtures/rev_list_count +0 -655
  53. data/test/fixtures/rev_list_single +0 -7
  54. data/test/fixtures/rev_parse +0 -1
  55. data/test/fixtures/show_empty_commit +0 -6
  56. data/test/fixtures/simple_config +0 -2
  57. data/test/helper.rb +0 -17
  58. data/test/profile.rb +0 -21
  59. data/test/suite.rb +0 -6
data/lib/grit/git.rb CHANGED
@@ -19,6 +19,8 @@ module Grit
19
19
 
20
20
  undef_method :clone
21
21
 
22
+ include GitRuby
23
+
22
24
  class << self
23
25
  attr_accessor :git_binary, :git_timeout
24
26
  end
@@ -44,31 +46,41 @@ module Grit
44
46
  #
45
47
  # Returns String
46
48
  def method_missing(cmd, options = {}, *args)
49
+ run('', cmd, '', options, args)
50
+ end
51
+
52
+ def run(prefix, cmd, postfix, options, args)
47
53
  timeout = options.delete(:timeout)
48
54
  timeout = true if timeout.nil?
49
55
 
50
56
  opt_args = transform_options(options)
51
57
  ext_args = args.map { |a| a == '--' ? a : "'#{a}'" }
52
58
 
53
- call = "#{Git.git_binary} --git-dir='#{self.git_dir}' #{cmd.to_s.gsub(/_/, '-')} #{(opt_args + ext_args).join(' ')}"
54
- puts call if Grit.debug
55
- response = timeout ? sh(call) : wild_sh(call)
56
- puts response if Grit.debug
59
+ call = "#{prefix}#{Git.git_binary} --git-dir='#{self.git_dir}' #{cmd.to_s.gsub(/_/, '-')} #{(opt_args + ext_args).join(' ')}#{postfix}"
60
+ Grit.log(call) if Grit.debug
61
+ response, err = timeout ? sh(call) : wild_sh(call)
62
+ Grit.log(response) if Grit.debug
63
+ Grit.log(err) if Grit.debug
57
64
  response
58
65
  end
59
66
 
60
67
  def sh(command)
61
- pid, _, io, _ = Open4.popen4(command)
62
- ret = Timeout.timeout(self.class.git_timeout) { io.read }
63
- @bytes_read += ret.size
68
+ ret, pid, err = nil, nil, nil
69
+ Open4.popen4(command) do |id, _, stdout, stderr|
70
+ pid = id
71
+ ret = Timeout.timeout(self.class.git_timeout) { stdout.read }
72
+ err = stderr.read
73
+ @bytes_read += ret.size
64
74
 
65
- if @bytes_read > 5242880 # 5.megabytes
66
- bytes = @bytes_read
67
- @bytes_read = 0
68
- raise GitTimeout.new(command, bytes)
75
+ if @bytes_read > 5242880 # 5.megabytes
76
+ bytes = @bytes_read
77
+ @bytes_read = 0
78
+ raise GitTimeout.new(command, bytes)
79
+ end
69
80
  end
70
-
71
- ret
81
+ [ret, err]
82
+ rescue Errno::ECHILD
83
+ [ret, err]
72
84
  rescue Object => e
73
85
  Process.kill('KILL', pid) rescue nil
74
86
  bytes = @bytes_read
@@ -77,8 +89,14 @@ module Grit
77
89
  end
78
90
 
79
91
  def wild_sh(command)
80
- pid, _, io, _ = Open4.popen4(command)
81
- io.read
92
+ ret, err = nil, nil
93
+ Open4.popen4(command) {|pid, _, stdout, stderr|
94
+ ret = stdout.read
95
+ err = stderr.read
96
+ }
97
+ [ret, err]
98
+ rescue Errno::ECHILD
99
+ [ret, err]
82
100
  end
83
101
 
84
102
  # Transform Ruby style options into git command line options
data/lib/grit/head.rb CHANGED
@@ -29,8 +29,11 @@ module Grit
29
29
  #
30
30
  # Returns Grit::Head[] (baked)
31
31
  def self.find_all(repo, options = {})
32
- default_options = {:sort => "committerdate",
33
- :format => "%(refname)%00%(objectname)"}
32
+ default_options = {
33
+ :sort => "committerdate",
34
+ :format => "%(refname)%00%(objectname)",
35
+ :timeout => false
36
+ }
34
37
 
35
38
  actual_options = default_options.merge(options)
36
39
 
@@ -38,19 +41,7 @@ module Grit
38
41
 
39
42
  self.list_from_string(repo, output)
40
43
  end
41
-
42
- # Get the HEAD revision of the repo.
43
- # +repo+ is the Repo
44
- # +options+ is a Hash of options
45
- #
46
- # Returns Grit::Head (baked)
47
- def self.current(repo, options = {})
48
- head = File.open(File.join(repo.path, 'HEAD')).read.chomp
49
- if /ref: refs\/heads\/(.*)/.match(head)
50
- self.new($1, repo.git.rev_parse(options, 'HEAD'))
51
- end
52
- end
53
-
44
+
54
45
  # Parse out head information into an array of baked head objects
55
46
  # +repo+ is the Repo
56
47
  # +text+ is the text output from the git command
data/lib/grit/index.rb ADDED
@@ -0,0 +1,121 @@
1
+ module Grit
2
+
3
+ class Index
4
+ attr_accessor :repo, :tree, :current_tree
5
+
6
+ def initialize(repo)
7
+ self.repo = repo
8
+ self.tree = {}
9
+ self.current_tree = nil
10
+ end
11
+
12
+ # Add a file to the index
13
+ # +path+ is the path (including filename)
14
+ # +data+ is the binary contents of the file
15
+ #
16
+ # Returns nothing
17
+ def add(file_path, data)
18
+ path = file_path.split('/')
19
+ filename = path.pop
20
+
21
+ current = self.tree
22
+
23
+ path.each do |dir|
24
+ current[dir] ||= {}
25
+ node = current[dir]
26
+ current = node
27
+ end
28
+
29
+ current[filename] = data
30
+ end
31
+
32
+ def read_tree(tree)
33
+ self.current_tree = self.repo.tree(tree)
34
+ end
35
+
36
+ # Commit the contents of the index
37
+ # +message+ is the commit message
38
+ #
39
+ # Returns a String of the SHA1 of the commit
40
+ def commit(message, parents = nil, actor = nil, last_tree = nil, head = 'master')
41
+ tree_sha1 = write_tree(self.tree, self.current_tree)
42
+ return false if tree_sha1 == last_tree # don't write identical commits
43
+
44
+ contents = []
45
+ contents << ['tree', tree_sha1].join(' ')
46
+ parents.each do |p|
47
+ contents << ['parent', p].join(' ') if p
48
+ end if parents
49
+
50
+ if actor
51
+ name = actor.name
52
+ email = actor.email
53
+ else
54
+ config = Config.new(self.repo)
55
+ name = config['user.name']
56
+ email = config['user.email']
57
+ end
58
+
59
+ author_string = "#{name} <#{email}> #{Time.now.to_i} -0700" # !! TODO : gotta fix this
60
+ contents << ['author', author_string].join(' ')
61
+ contents << ['committer', author_string].join(' ')
62
+ contents << ''
63
+ contents << message
64
+
65
+ commit_sha1 = self.repo.git.ruby_git.put_raw_object(contents.join("\n"), 'commit')
66
+
67
+ # self.repo.git.update_ref({}, 'HEAD', commit_sha1)
68
+ ref_heads = File.join(self.repo.path, 'refs', 'heads')
69
+ FileUtils.mkdir_p(ref_heads)
70
+ File.open(File.join(ref_heads, head), 'w') do |f|
71
+ f.write(commit_sha1)
72
+ end if commit_sha1
73
+
74
+ commit_sha1
75
+ end
76
+
77
+ # Recursively write a tree to the index
78
+ # +tree+ is the tree
79
+ #
80
+ # Returns the SHA1 String of the tree
81
+ def write_tree(tree, now_tree = nil)
82
+ tree_contents = {}
83
+
84
+ # fill in original tree
85
+ now_tree.contents.each do |obj|
86
+ sha = [obj.id].pack("H*")
87
+ k = obj.name
88
+ k += '/' if (obj.class == Grit::Tree)
89
+ tree_contents[k] = "%s %s\0%s" % [obj.mode.to_s, obj.name, sha]
90
+ end if now_tree
91
+
92
+ # overwrite with new tree contents
93
+ tree.each do |k, v|
94
+ case v
95
+ when String:
96
+ sha = write_blob(v)
97
+ sha = [sha].pack("H*")
98
+ str = "%s %s\0%s" % ['100644', k, sha]
99
+ tree_contents[k] = str
100
+ when Hash:
101
+ ctree = now_tree/k if now_tree
102
+ sha = write_tree(v, ctree)
103
+ sha = [sha].pack("H*")
104
+ str = "%s %s\0%s" % ['040000', k, sha]
105
+ tree_contents[k + '/'] = str
106
+ end
107
+ end
108
+ tr = tree_contents.sort.map { |k, v| v }.join('')
109
+ self.repo.git.ruby_git.put_raw_object(tr, 'tree')
110
+ end
111
+
112
+ # Write the blob to the index
113
+ # +data+ is the data to write
114
+ #
115
+ # Returns the SHA1 String of the blob
116
+ def write_blob(data)
117
+ self.repo.git.ruby_git.put_raw_object(data, 'blob')
118
+ end
119
+ end # Index
120
+
121
+ end # Grit
data/lib/grit/ref.rb ADDED
@@ -0,0 +1,95 @@
1
+ module Grit
2
+
3
+ class Ref
4
+
5
+ class << self
6
+
7
+ # Find all Refs
8
+ # +repo+ is the Repo
9
+ # +options+ is a Hash of options
10
+ #
11
+ # Returns Grit::Ref[] (baked)
12
+ def find_all(repo, options = {})
13
+ refs = []
14
+
15
+ Dir.chdir(repo.path) do
16
+ if File.file?('packed-refs')
17
+ File.readlines('packed-refs').each do |line|
18
+ if m = /^(\w{40}) (.*?)$/.match(line)
19
+ next if !Regexp.new('^' + prefix).match(m[2])
20
+ name = m[2].sub("#{prefix}/", '')
21
+ commit = Commit.create(repo, :id => m[1])
22
+ refs << self.new(name, commit)
23
+ end
24
+ end
25
+ end
26
+
27
+ files = Dir.glob(prefix + '/**/*')
28
+ files.each do |ref|
29
+ next if !File.file?(ref)
30
+ id = File.read(ref).chomp
31
+ name = ref.sub("#{prefix}/", '')
32
+ commit = Commit.create(repo, :id => id)
33
+ refs << self.new(name, commit)
34
+ end
35
+ end
36
+
37
+ refs
38
+ end
39
+
40
+ protected
41
+
42
+ def prefix
43
+ "refs/#{name.to_s.gsub(/^.*::/, '').downcase}s"
44
+ end
45
+
46
+ end
47
+
48
+ attr_reader :name
49
+ attr_reader :commit
50
+
51
+ # Instantiate a new Head
52
+ # +name+ is the name of the head
53
+ # +commit+ is the Commit that the head points to
54
+ #
55
+ # Returns Grit::Head (baked)
56
+ def initialize(name, commit)
57
+ @name = name
58
+ @commit = commit
59
+ end
60
+
61
+ # Pretty object inspection
62
+ def inspect
63
+ %Q{#<#{self.class.name} "#{@name}">}
64
+ end
65
+ end # Ref
66
+
67
+ # A Head is a named reference to a Commit. Every Head instance contains a name
68
+ # and a Commit object.
69
+ #
70
+ # r = Grit::Repo.new("/path/to/repo")
71
+ # h = r.heads.first
72
+ # h.name # => "master"
73
+ # h.commit # => #<Grit::Commit "1c09f116cbc2cb4100fb6935bb162daa4723f455">
74
+ # h.commit.id # => "1c09f116cbc2cb4100fb6935bb162daa4723f455"
75
+ class Head < Ref
76
+
77
+ # Get the HEAD revision of the repo.
78
+ # +repo+ is the Repo
79
+ # +options+ is a Hash of options
80
+ #
81
+ # Returns Grit::Head (baked)
82
+ def self.current(repo, options = {})
83
+ head = File.open(File.join(repo.path, 'HEAD')).read.chomp
84
+ if /ref: refs\/heads\/(.*)/.match(head)
85
+ self.new($1, repo.git.rev_parse(options, 'HEAD'))
86
+ end
87
+ end
88
+
89
+ end # Head
90
+
91
+ class Tag < Ref ; end
92
+
93
+ class Remote < Ref; end
94
+
95
+ end # Grit
data/lib/grit/repo.rb CHANGED
@@ -5,6 +5,7 @@ module Grit
5
5
 
6
6
  # The path of the git repo as a String
7
7
  attr_accessor :path
8
+ attr_accessor :working_dir
8
9
  attr_reader :bare
9
10
 
10
11
  # The git command line interface object
@@ -18,13 +19,14 @@ module Grit
18
19
  # g = Repo.new("/Users/tom/public/grit.git")
19
20
  #
20
21
  # Returns Grit::Repo
21
- def initialize(path)
22
+ def initialize(path, options = {})
22
23
  epath = File.expand_path(path)
23
24
 
24
25
  if File.exist?(File.join(epath, '.git'))
26
+ self.working_dir = epath
25
27
  self.path = File.join(epath, '.git')
26
28
  @bare = false
27
- elsif File.exist?(epath) && epath =~ /\.git$/
29
+ elsif File.exist?(epath) && (epath =~ /\.git$/ || options[:is_bare])
28
30
  self.path = epath
29
31
  @bare = true
30
32
  elsif File.exist?(epath)
@@ -36,6 +38,13 @@ module Grit
36
38
  self.git = Git.new(self.path)
37
39
  end
38
40
 
41
+ def self.init(path)
42
+ # !! TODO !!
43
+ # create directory
44
+ # generate initial git directory
45
+ # create new Grit::Repo on that dir, return it
46
+ end
47
+
39
48
  # The project's description. Taken verbatim from GIT_REPO/description
40
49
  #
41
50
  # Returns String
@@ -53,6 +62,10 @@ module Grit
53
62
 
54
63
  alias_method :branches, :heads
55
64
 
65
+ def is_head?(head_name)
66
+ heads.find { |h| h.name == head_name }
67
+ end
68
+
56
69
  # Object reprsenting the current repo head.
57
70
  #
58
71
  # Returns Grit::Head (baked)
@@ -60,6 +73,47 @@ module Grit
60
73
  Head.current(self)
61
74
  end
62
75
 
76
+
77
+ # Commits current index
78
+ #
79
+ # Returns true/false if commit worked
80
+ def commit_index(message)
81
+ self.git.commit({}, '-m', message)
82
+ end
83
+
84
+ # Commits all tracked and modified files
85
+ #
86
+ # Returns true/false if commit worked
87
+ def commit_all(message)
88
+ self.git.commit({}, '-a', '-m', message)
89
+ end
90
+
91
+ # Adds files to the index
92
+ def add(*files)
93
+ self.git.add({}, *files.flatten)
94
+ end
95
+
96
+ # Adds files to the index
97
+ def remove(*files)
98
+ self.git.rm({}, *files.flatten)
99
+ end
100
+
101
+
102
+ def blame_tree(commit, path = nil)
103
+ commit_array = self.git.blame_tree(commit, path)
104
+
105
+ final_array = {}
106
+ commit_array.each do |file, sha|
107
+ final_array[file] = commit(sha)
108
+ end
109
+ final_array
110
+ end
111
+
112
+ def status
113
+ Status.new(self)
114
+ end
115
+
116
+
63
117
  # An array of Tag objects that are available in this repo
64
118
  #
65
119
  # Returns Grit::Tag[] (baked)
@@ -67,9 +121,32 @@ module Grit
67
121
  Tag.find_all(self)
68
122
  end
69
123
 
124
+ # An array of Remote objects representing the remote branches in
125
+ # this repo
126
+ #
127
+ # Returns Grit::Remote[] (baked)
128
+ def remotes
129
+ Remote.find_all(self)
130
+ end
131
+
132
+ # An array of Ref objects representing the refs in
133
+ # this repo
134
+ #
135
+ # Returns Grit::Ref[] (baked)
136
+ def refs
137
+ [ Head.find_all(self), Tag.find_all(self), Remote.find_all(self) ].flatten
138
+ end
139
+
140
+ def commit_stats(start = 'master', max_count = 10, skip = 0)
141
+ options = {:max_count => max_count,
142
+ :skip => skip}
143
+
144
+ CommitStats.find_all(self, start, options)
145
+ end
146
+
70
147
  # An array of Commit objects representing the history of a given ref/commit
71
148
  # +start+ is the branch/commit name (default 'master')
72
- # +max_count+ is the maximum number of commits to return (default 10)
149
+ # +max_count+ is the maximum number of commits to return (default 10, use +false+ for all)
73
150
  # +skip+ is the number of commits to skip (default 0)
74
151
  #
75
152
  # Returns Grit::Commit[] (baked)
@@ -176,10 +253,10 @@ module Grit
176
253
  # Grit::Repo.init_bare('/var/git/myrepo.git')
177
254
  #
178
255
  # Returns Grit::Repo (the newly created repo)
179
- def self.init_bare(path, options = {})
256
+ def self.init_bare(path, git_options = {}, repo_options = {})
180
257
  git = Git.new(path)
181
- git.init(options)
182
- self.new(path)
258
+ git.init(git_options)
259
+ self.new(path, repo_options)
183
260
  end
184
261
 
185
262
  # Fork a bare git repository from this repo
@@ -235,6 +312,14 @@ module Grit
235
312
  options[:prefix] = prefix if prefix
236
313
  self.git.archive(options, treeish, "| gzip")
237
314
  end
315
+
316
+ # run archive directly to a file
317
+ def archive_to_file(treeish = 'master', prefix = nil, filename = 'archive.tar.gz')
318
+ options = {}
319
+ options[:prefix] = prefix if prefix
320
+ self.git.archive(options, treeish, "| gzip > #{filename}")
321
+ end
322
+
238
323
 
239
324
  # Enable git-daemon serving of this repository by writing the
240
325
  # git-daemon-export-ok file to its git directory
@@ -289,6 +374,10 @@ module Grit
289
374
  @config ||= Config.new(self)
290
375
  end
291
376
 
377
+ def index
378
+ Index.new(self)
379
+ end
380
+
292
381
  # Pretty object inspection
293
382
  def inspect
294
383
  %Q{#<Grit::Repo "#{@path}">}