added a timer to gitweb, so I can see if i'm speeding it up
[rubygit.git] / lib / git / diff.rb
blobb43ecd9afe6ea7e2ec97c15d9c7acf267163de66
1 module Git
2   
3   # object that holds the last X commits on given branch
4   class Diff
5     include Enumerable
6     
7     @base = nil
8     @from = nil
9     @to = nil
10     @path = nil
11     
12     @full_diff = nil
13     @full_diff_files = nil
14     @stats = nil
15     
16     def initialize(base, from = nil, to = nil)
17       @base = base
18       @from = from.to_s
19       @to = to.to_s
20     end
21     
22     def path(path)
23       @path = path
24       return self
25     end
26     
27     def size
28       cache_stats
29       @stats[:total][:files]
30     end
31     
32     def lines
33       cache_stats
34       @stats[:total][:lines]
35     end
36     
37     def deletions
38       cache_stats
39       @stats[:total][:deletions]
40     end
41     
42     def insertions
43       cache_stats
44       @stats[:total][:insertions]
45     end
46     
47     def stats
48       cache_stats
49       @stats
50     end
51     
52     # if file is provided and is writable, it will write the patch into the file
53     def patch(file = nil)
54       cache_full
55       @full_diff
56     end
57     alias_method :to_s, :patch
58     
59     # enumerable methods
60     
61     def [](key)
62       process_full
63       @full_diff_files.assoc(key)[1]
64     end
65     
66     def each
67       process_full
68       @full_diff_files.each do |file|
69         yield file[1]
70       end
71     end
72     
73     class DiffFile
74       attr_accessor :patch, :path, :mode, :src, :dst, :type
75       @base = nil
76       
77       def initialize(base, hash)
78         @base = base
79         @patch = hash[:patch]
80         @path = hash[:path]
81         @mode = hash[:mode]
82         @src = hash[:src]
83         @dst = hash[:dst]
84         @type = hash[:type]
85       end
86       
87       def blob(type = :dst)
88         if type == :src
89           @base.object(@src) if @src != '0000000'
90         else
91           @base.object(@dst) if @dst != '0000000'
92         end
93       end
94     end
95     
96     private
97     
98       def cache_full
99         if !@full_diff
100           @full_diff = @base.lib.diff_full(@from, @to, {:path_limiter => @path})
101         end
102       end
103       
104       def process_full
105         if !@full_diff_files
106           cache_full
107           @full_diff_files = process_full_diff
108         end
109       end
110       
111       def cache_stats
112         if !@stats
113           @stats = @base.lib.diff_stats(@from, @to, {:path_limiter => @path})
114         end
115       end
116       
117       # break up @diff_full
118       def process_full_diff
119         final = {}
120         current_file = nil
121         @full_diff.split("\n").each do |line|
122           if m = /diff --git a\/(.*?) b\/(.*?)/.match(line)
123             current_file = m[1]
124             final[current_file] = {:patch => line, :path => current_file, 
125                                     :mode => '', :src => '', :dst => '', :type => 'modified'}
126           else
127             if m = /index (.......)\.\.(.......)( ......)*/.match(line)
128               final[current_file][:src] = m[1]
129               final[current_file][:dst] = m[2]
130               final[current_file][:mode] = m[3].strip if m[3]
131             end
132             if m = /(.*?) file mode (......)/.match(line)
133               final[current_file][:type] = m[1]
134               final[current_file][:mode] = m[2]
135             end
136             final[current_file][:patch] << "\n" + line 
137           end
138         end
139         final.map { |e| [e[0], DiffFile.new(@base, e[1])] }
140       end
141       
142   end