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