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