Blob of vendor/grit/lib/grit/commit.rb (raw blob data)

1 module Grit
2
3 class Commit
4 attr_reader :id
5 lazy_reader :parents
6 lazy_reader :tree
7 lazy_reader :author
8 lazy_reader :authored_date
9 lazy_reader :committer
10 lazy_reader :committed_date
11 lazy_reader :message
12 lazy_reader :short_message
13
14 # Instantiate a new Commit
15 # +id+ is the id of the commit
16 # +parents+ is an array of commit ids (will be converted into Commit instances)
17 # +tree+ is the correspdonding tree id (will be converted into a Tree object)
18 # +author+ is the author string
19 # +authored_date+ is the authored Time
20 # +committer+ is the committer string
21 # +committed_date+ is the committed Time
22 # +message+ is an array of commit message lines
23 #
24 # Returns Grit::Commit (baked)
25 def initialize(repo, id, parents, tree, author, authored_date, committer, committed_date, message)
26 @repo = repo
27 @id = id
28 @parents = parents.map { |p| Commit.create(repo, :id => p) }
29 @tree = Tree.create(repo, :id => tree)
30 @author = author
31 @authored_date = authored_date
32 @committer = committer
33 @committed_date = committed_date
34 @message = message.join("\n")
35 @short_message = message[0] || ''
36 end
37
38 def id_abbrev
39 @id[0,7]
40 end
41
42 # Create an unbaked Commit containing just the specified attributes
43 # +repo+ is the Repo
44 # +atts+ is a Hash of instance variable data
45 #
46 # Returns Grit::Commit (unbaked)
47 def self.create(repo, atts)
48 self.allocate.create_initialize(repo, atts)
49 end
50
51 # Initializer for Commit.create
52 # +repo+ is the Repo
53 # +atts+ is a Hash of instance variable data
54 #
55 # Returns Grit::Commit (unbaked)
56 def create_initialize(repo, atts)
57 @repo = repo
58 atts.each do |k, v|
59 instance_variable_set("@#{k}", v)
60 end
61 self
62 end
63
64 def lazy_source
65 self.class.find_all(@repo, @id, {:max_count => 1}).first
66 end
67
68 # Count the number of commits reachable from this ref
69 # +repo+ is the Repo
70 # +ref+ is the ref from which to begin (SHA1 or name)
71 #
72 # Returns Integer
73 def self.count(repo, ref)
74 repo.git.rev_list({}, ref).strip.split("\n").size
75 end
76
77 # Find all commits matching the given criteria.
78 # +repo+ is the Repo
79 # +ref+ is the ref from which to begin (SHA1 or name) or nil for --all
80 # +options+ is a Hash of optional arguments to git
81 # :max_count is the maximum number of commits to fetch
82 # :skip is the number of commits to skip
83 #
84 # Returns Grit::Commit[] (baked)
85 def self.find_all(repo, ref, options = {})
86 allowed_options = [:max_count, :skip, :since]
87
88 default_options = {:pretty => "raw"}
89 actual_options = default_options.merge(options)
90
91 if ref
92 output = repo.git.rev_list(actual_options, ref)
93 else
94 output = repo.git.rev_list(actual_options.merge(:all => true))
95 end
96
97 self.list_from_string(repo, output)
98 end
99
100 # Parse out commit information into an array of baked Commit objects
101 # +repo+ is the Repo
102 # +text+ is the text output from the git command (raw format)
103 #
104 # Returns Grit::Commit[] (baked)
105 def self.list_from_string(repo, text)
106 lines = text.split("\n")
107
108 commits = []
109
110 while !lines.empty?
111 id = lines.shift.split.last
112 tree = lines.shift.split.last
113
114 parents = []
115 parents << lines.shift.split.last while lines.first =~ /^parent/
116
117 author, authored_date = self.actor(lines.shift)
118 committer, committed_date = self.actor(lines.shift)
119
120 lines.shift
121
122 message_lines = []
123 message_lines << lines.shift[4..-1] while lines.first =~ /^ {4}/
124
125 lines.shift while lines.first && lines.first.empty?
126
127 commits << Commit.new(repo, id, parents, tree, author, authored_date, committer, committed_date, message_lines)
128 end
129
130 commits
131 end
132
133 # Show diffs between two trees:
134 # +repo+ is the Repo
135 # +a+ is a named commit
136 # +b+ is an optional named commit. Passing an array assumes you
137 # wish to omit the second named commit and limit the diff to the
138 # given paths.
139 # +paths* is an array of paths to limit the diff.
140 #
141 # Returns Grit::Diff[] (baked)
142 def self.diff(repo, a, b = nil, paths = [])
143 if b.is_a?(Array)
144 paths = b
145 b = nil
146 end
147 paths.unshift("--") unless paths.empty?
148 paths.unshift(b) unless b.nil?
149 paths.unshift(a)
150 text = repo.git.diff({:full_index => true}, *paths)
151 Diff.list_from_string(repo, text)
152 end
153
154 def diffs
155 if parents.empty?
156 diff = @repo.git.show({:full_index => true, :pretty => 'raw'}, @id)
157 if diff =~ /diff --git a/
158 diff = diff.sub(/.+?(diff --git a)/m, '\1')
159 else
160 diff = ''
161 end
162 Diff.list_from_string(@repo, diff)
163 else
164 self.class.diff(@repo, parents.first.id, @id)
165 end
166 end
167
168 def stats
169 if parents.empty?
170 text = @repo.git.diff({:numstat => true}, @id)
171 text2 = ""
172 text.each_line do |line|
173 (insertions, deletions, filename) = line.split("\t")
174 text2 << "#{deletions}\t#{insertions}\t#{filename}"
175 end
176 text = text2
177 else
178 text = @repo.git.diff({:numstat => true}, parents.first.id, @id)
179 end
180 Stats.list_from_string(@repo, text)
181 end
182
183 # Convert this Commit to a String which is just the SHA1 id
184 def to_s
185 @id
186 end
187
188 # Pretty object inspection
189 def inspect
190 %Q{#<Grit::Commit "#{@id}">}
191 end
192
193 # private
194
195 # Parse out the actor (author or committer) info
196 #
197 # Returns [String (actor name and email), Time (acted at time)]
198 def self.actor(line)
199 m, actor, epoch = *line.match(/^.+? (.*) (\d+) .*$/)
200 [Actor.from_string(actor), Time.at(epoch.to_i)]
201 end
202
203 def to_hash
204 {
205 'id' => id,
206 'parents' => parents.map { |p| { 'id' => p.id } },
207 'tree' => tree.id,
208 'message' => message,
209 'author' => {
210 'name' => author.name,
211 'email' => author.email
212 },
213 'committer' => {
214 'name' => committer.name,
215 'email' => committer.email
216 },
217 'authored_date' => authored_date.xmlschema,
218 'committed_date' => committed_date.xmlschema,
219 }
220 end
221 end # Commit
222
223 end # Grit