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