added object content caching
[rubygit.git] / lib / git / object.rb
blobafda9234162cccfee17b196f585e5e9bc7d77e6a
1 module Git
2   
3   class GitTagNameDoesNotExist< StandardError 
4   end
5   
6   # represents a git object
7   class Object
8     
9     class AbstractObject
10       attr_accessor :sha, :size, :type, :mode
11     
12       @base = nil
13       @contents = nil
14       
15       def initialize(base, sha)
16         @base = base
17         @sha = sha.to_s
18         @size = @base.lib.object_size(@sha)
19         setup
20       end
21     
22       # caches the contents of this call in memory
23       def contents
24         @contents || @contents = @base.lib.object_contents(@sha)
25       end
26       
27       def contents_array
28         self.contents.split("\n")
29       end
30       
31       def setup
32         raise NotImplementedError
33       end
34       
35       def to_s
36         @sha
37       end
38       
39       def grep(string, path_limiter = nil, opts = {})
40         default = {:object => @sha, :path_limiter => path_limiter}
41         grep_options = default.merge(opts)
42         @base.lib.grep(string, grep_options)
43       end
44       
45       def diff(objectish)
46         Git::Diff.new(@base, @sha, objectish)
47       end
48       
49       def log(count = 30)
50         Git::Log.new(@base, count).object(@sha)
51       end
52       
53       # creates an archive of this object (tree)
54       def archive(file = nil, opts = {})
55         @base.lib.archive(@sha, file, opts)
56       end
57       
58       def tree?
59         @type == 'tree'
60       end
61       
62       def blob?
63         @type == 'blob'
64       end
65       
66       def commit?
67         @type == 'commit'
68       end
69        
70      def tag?
71        @type == 'tag'
72      end
73      
74     end
75   
76     
77     class Blob < AbstractObject
78       
79       def initialize(base, sha, mode = nil)
80         super(base, sha)
81         @mode = mode
82       end
83       
84       private
85       
86         def setup
87           @type = 'blob'
88         end
89     end
90   
91     class Tree < AbstractObject
92       
93       @trees = nil
94       @blobs = nil
95       
96       def initialize(base, sha, mode = nil)
97         super(base, sha)
98         @mode = mode
99       end
100             
101       def children
102         blobs.merge(subtrees)
103       end
104       
105       def blobs
106         check_tree
107         @blobs
108       end
109       alias_method :files, :blobs
110       
111       def trees
112         check_tree
113         @trees
114       end
115       alias_method :subtrees, :trees
116       alias_method :subdirectories, :trees
117        
118       private
119       
120         def setup
121           @type = 'tree'
122         end 
124         # actually run the git command
125         def check_tree
126           if !@trees
127             @trees = {}
128             @blobs = {}
129             data = @base.lib.ls_tree(@sha)
130             data['tree'].each { |k, d| @trees[k] = Tree.new(@base, d[:sha], d[:mode]) }
131             data['blob'].each { |k, d| @blobs[k] = Blob.new(@base, d[:sha], d[:mode]) }
132           end
133         end
134       
135     end
136   
137     class Commit < AbstractObject
138       
139       @tree = nil
140       @parents = nil
141       @author = nil
142       @committer = nil
143       @message = nil
144       
145       def message
146         check_commit
147         @message
148       end
149       
150       def name
151         @base.lib.namerev(@sha)
152       end
153       
154       def gtree
155         check_commit
156         Tree.new(@base, @tree)
157       end
158       
159       def parent
160         parents.first
161       end
162       
163       # array of all parent commits
164       def parents
165         check_commit
166         @parents        
167       end
168       
169       # git author
170       def author     
171         check_commit
172         @author
173       end
174       
175       def author_date
176         author.date
177       end
178       
179       # git author
180       def committer
181         check_commit
182         @committer
183       end
184       
185       def committer_date 
186         committer.date
187       end
188       alias_method :date, :committer_date
190       def diff_parent
191         diff(parent)
192       end
193             
194       private
195       
196         def setup
197           @type = 'commit'
198         end
199   
200         # see if this object has been initialized and do so if not
201         def check_commit
202           if !@tree
203             data = @base.lib.commit_data(@sha)
204             @committer = Git::Author.new(data['committer'])
205             @author = Git::Author.new(data['author'])
206             @tree = Tree.new(@base, data['tree'])
207             @parents = data['parent'].map{ |sha| Commit.new(@base, sha) }
208             @message = data['message'].chomp
209           end
210         end
211       
212     end
213   
214     class Tag < AbstractObject
215       attr_accessor :name
216       
217       def initialize(base, sha, name)
218         super(base, sha)
219         @name = name
220       end
221       
222       private
223         
224         def setup
225           @type = 'tag'
226         end
227         
228     end
229     
230     class << self
231       # if we're calling this, we don't know what type it is yet
232       # so this is our little factory method
233       def new(base, objectish, is_tag = false)
234         if is_tag
235           sha = base.lib.tag_sha(objectish)
236           if sha == ''
237             raise Git::GitTagNameDoesNotExist.new(objectish)
238           end
239           return Tag.new(base, sha, objectish)
240         else
241           sha = base.lib.revparse(objectish)
242           type = base.lib.object_type(sha) 
243         end
244         
245         klass =
246           case type
247           when /blob/:   Blob   
248           when /commit/: Commit
249           when /tree/:   Tree
250           end
251         klass::new(base, sha)
252       end
253     end 
254     
255   end