1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 package org.eclipse.jgit.blame;
45
46 import java.io.IOException;
47
48 import org.eclipse.jgit.blame.ReverseWalk.ReverseCommit;
49 import org.eclipse.jgit.diff.Edit;
50 import org.eclipse.jgit.diff.EditList;
51 import org.eclipse.jgit.diff.RawText;
52 import org.eclipse.jgit.errors.MissingObjectException;
53 import org.eclipse.jgit.lib.Constants;
54 import org.eclipse.jgit.lib.ObjectId;
55 import org.eclipse.jgit.lib.ObjectLoader;
56 import org.eclipse.jgit.lib.ObjectReader;
57 import org.eclipse.jgit.lib.PersonIdent;
58 import org.eclipse.jgit.revwalk.RevCommit;
59 import org.eclipse.jgit.revwalk.RevFlag;
60 import org.eclipse.jgit.revwalk.RevWalk;
61 import org.eclipse.jgit.treewalk.filter.PathFilter;
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76 class Candidate {
77
78 Candidate queueNext;
79
80
81 RevCommit sourceCommit;
82
83
84 PathFilter sourcePath;
85
86
87 ObjectId sourceBlob;
88
89
90 RawText sourceText;
91
92
93
94
95
96
97
98 Region regionList;
99
100
101
102
103
104
105
106
107
108
109
110 int renameScore;
111
112 Candidate(RevCommit commit, PathFilter path) {
113 sourceCommit = commit;
114 sourcePath = path;
115 }
116
117 void beginResult(RevWalk rw) throws MissingObjectException, IOException {
118 rw.parseBody(sourceCommit);
119 }
120
121 int getParentCount() {
122 return sourceCommit.getParentCount();
123 }
124
125 RevCommit getParent(int idx) {
126 return sourceCommit.getParent(idx);
127 }
128
129 Candidate getNextCandidate(@SuppressWarnings("unused") int idx) {
130 return null;
131 }
132
133 boolean has(RevFlag flag) {
134 return sourceCommit.has(flag);
135 }
136
137 void add(RevFlag flag) {
138 sourceCommit.add(flag);
139 }
140
141 void remove(RevFlag flag) {
142 sourceCommit.remove(flag);
143 }
144
145 int getTime() {
146 return sourceCommit.getCommitTime();
147 }
148
149 PersonIdent getAuthor() {
150 return sourceCommit.getAuthorIdent();
151 }
152
153 Candidate create(RevCommit commit, PathFilter path) {
154 return new Candidate(commit, path);
155 }
156
157 Candidate copy(RevCommit commit) {
158 Candidate r = create(commit, sourcePath);
159 r.sourceBlob = sourceBlob;
160 r.sourceText = sourceText;
161 r.regionList = regionList;
162 r.renameScore = renameScore;
163 return r;
164 }
165
166 void loadText(ObjectReader reader) throws IOException {
167 ObjectLoader ldr = reader.open(sourceBlob, Constants.OBJ_BLOB);
168 sourceText = new RawText(ldr.getCachedBytes(Integer.MAX_VALUE));
169 }
170
171 void takeBlame(EditList editList, Candidate child) {
172 blame(editList, this, child);
173 }
174
175 private static void blame(EditList editList, Candidate a, Candidate b) {
176 Region r = b.clearRegionList();
177 Region aTail = null;
178 Region bTail = null;
179
180 for (int eIdx = 0; eIdx < editList.size();) {
181
182
183
184 if (r == null)
185 return;
186
187 Edit e = editList.get(eIdx);
188
189
190 if (e.getEndB() <= r.sourceStart) {
191 eIdx++;
192 continue;
193 }
194
195
196
197 if (r.sourceStart < e.getBeginB()) {
198 int d = e.getBeginB() - r.sourceStart;
199 if (r.length <= d) {
200
201 Region next = r.next;
202 r.sourceStart = e.getBeginA() - d;
203 aTail = add(aTail, a, r);
204 r = next;
205 continue;
206 }
207
208
209 aTail = add(aTail, a, r.splitFirst(e.getBeginA() - d, d));
210 r.slideAndShrink(d);
211 }
212
213
214
215
216
217 if (e.getLengthB() == 0) {
218 eIdx++;
219 continue;
220 }
221
222
223 int rEnd = r.sourceStart + r.length;
224 if (rEnd <= e.getEndB()) {
225 Region next = r.next;
226 bTail = add(bTail, b, r);
227 r = next;
228 if (rEnd == e.getEndB())
229 eIdx++;
230 continue;
231 }
232
233
234
235 int len = e.getEndB() - r.sourceStart;
236 bTail = add(bTail, b, r.splitFirst(r.sourceStart, len));
237 r.slideAndShrink(len);
238 eIdx++;
239 }
240
241 if (r == null)
242 return;
243
244
245
246 Edit e = editList.get(editList.size() - 1);
247 int endB = e.getEndB();
248 int d = endB - e.getEndA();
249 if (aTail == null)
250 a.regionList = r;
251 else
252 aTail.next = r;
253 do {
254 if (endB <= r.sourceStart)
255 r.sourceStart -= d;
256 r = r.next;
257 } while (r != null);
258 }
259
260 private static Region add(Region aTail, Candidate a, Region n) {
261
262 if (aTail == null) {
263 a.regionList = n;
264 n.next = null;
265 return n;
266 }
267
268
269
270
271
272
273
274 if (aTail.resultStart + aTail.length == n.resultStart
275 && aTail.sourceStart + aTail.length == n.sourceStart) {
276 aTail.length += n.length;
277 return aTail;
278 }
279
280
281 aTail.next = n;
282 n.next = null;
283 return n;
284 }
285
286 private Region clearRegionList() {
287 Region r = regionList;
288 regionList = null;
289 return r;
290 }
291
292 boolean canMergeRegions(Candidate other) {
293 return sourceCommit == other.sourceCommit
294 && sourcePath.getPath().equals(other.sourcePath.getPath());
295 }
296
297 void mergeRegions(Candidate other) {
298
299
300
301 Region a = clearRegionList();
302 Region b = other.clearRegionList();
303 Region t = null;
304
305 while (a != null && b != null) {
306 if (a.resultStart < b.resultStart) {
307 Region n = a.next;
308 t = add(t, this, a);
309 a = n;
310 } else {
311 Region n = b.next;
312 t = add(t, this, b);
313 b = n;
314 }
315 }
316
317 if (a != null) {
318 Region n = a.next;
319 t = add(t, this, a);
320 t.next = n;
321 } else {
322 Region n = b.next;
323 t = add(t, this, b);
324 t.next = n;
325 }
326 }
327
328 @SuppressWarnings("nls")
329 @Override
330 public String toString() {
331 StringBuilder r = new StringBuilder();
332 r.append("Candidate[");
333 r.append(sourcePath.getPath());
334 if (sourceCommit != null)
335 r.append(" @ ").append(sourceCommit.abbreviate(6).name());
336 if (regionList != null)
337 r.append(" regions:").append(regionList);
338 r.append("]");
339 return r.toString();
340 }
341
342
343
344
345
346
347
348
349
350 static final class ReverseCandidate extends Candidate {
351 ReverseCandidate(ReverseCommit commit, PathFilter path) {
352 super(commit, path);
353 }
354
355 @Override
356 int getParentCount() {
357 return ((ReverseCommit) sourceCommit).getChildCount();
358 }
359
360 @Override
361 RevCommit getParent(int idx) {
362 return ((ReverseCommit) sourceCommit).getChild(idx);
363 }
364
365 @Override
366 int getTime() {
367
368 return -sourceCommit.getCommitTime();
369 }
370
371 @Override
372 Candidate create(RevCommit commit, PathFilter path) {
373 return new ReverseCandidate((ReverseCommit) commit, path);
374 }
375
376 @Override
377 public String toString() {
378 return "Reverse" + super.toString();
379 }
380 }
381
382
383
384
385
386
387
388
389
390 static final class BlobCandidate extends Candidate {
391
392
393
394
395
396
397 Candidate parent;
398
399
400 String description;
401
402 BlobCandidate(String name, PathFilter path) {
403 super(null, path);
404 description = name;
405 }
406
407 @Override
408 void beginResult(RevWalk rw) {
409
410 }
411
412 @Override
413 int getParentCount() {
414 return parent != null ? 1 : 0;
415 }
416
417 @Override
418 RevCommit getParent(int idx) {
419 return null;
420 }
421
422 @Override
423 Candidate getNextCandidate(int idx) {
424 return parent;
425 }
426
427 @Override
428 boolean has(RevFlag flag) {
429 return true;
430 }
431
432 @Override
433 void add(RevFlag flag) {
434
435 }
436
437 @Override
438
439 void remove(RevFlag flag) {
440
441 }
442
443 @Override
444 int getTime() {
445 return Integer.MAX_VALUE;
446 }
447
448 @Override
449 PersonIdent getAuthor() {
450 return new PersonIdent(description, "");
451 }
452 }
453 }