in org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java [772:928]
private boolean processMerge(Candidate n) throws IOException {
int pCnt = n.getParentCount();
// If any single parent exactly matches the merge, follow only
// that one parent through history.
ObjectId[] ids = null;
for (int pIdx = 0; pIdx < pCnt; pIdx++) {
RevCommit parent = n.getParent(pIdx);
revPool.parseHeaders(parent);
if (!find(parent, n.sourcePath))
continue;
if (!(n instanceof ReverseCandidate) && idBuf.equals(n.sourceBlob))
return blameEntireRegionOnParent(n, parent);
if (ids == null)
ids = new ObjectId[pCnt];
ids[pIdx] = idBuf.toObjectId();
}
// If rename detection is enabled, search for any relevant names.
DiffEntry[] renames = null;
if (renameDetector != null) {
renames = new DiffEntry[pCnt];
for (int pIdx = 0; pIdx < pCnt; pIdx++) {
RevCommit parent = n.getParent(pIdx);
if (ids != null && ids[pIdx] != null)
continue;
DiffEntry r = findRename(parent, n.sourceCommit, n.sourcePath);
if (r == null)
continue;
if (n instanceof ReverseCandidate) {
if (ids == null)
ids = new ObjectId[pCnt];
ids[pCnt] = r.getOldId().toObjectId();
} else if (0 == r.getOldId().prefixCompare(n.sourceBlob)) {
// A 100% rename without any content change can also
// skip directly to the parent. Note this bypasses an
// earlier parent that had the path (above) but did not
// have an exact content match. For performance reasons
// we choose to follow the one parent over trying to do
// possibly both parents.
n.sourcePath = PathFilter.create(r.getOldPath());
return blameEntireRegionOnParent(n, parent);
}
renames[pIdx] = r;
}
}
// Construct the candidate for each parent.
Candidate[] parents = new Candidate[pCnt];
for (int pIdx = 0; pIdx < pCnt; pIdx++) {
RevCommit parent = n.getParent(pIdx);
Candidate p;
if (renames != null && renames[pIdx] != null) {
p = n.create(getRepository(), parent,
PathFilter.create(renames[pIdx].getOldPath()));
p.renameScore = renames[pIdx].getScore();
p.sourceBlob = renames[pIdx].getOldId().toObjectId();
} else if (ids != null && ids[pIdx] != null) {
p = n.create(getRepository(), parent, n.sourcePath);
p.sourceBlob = ids[pIdx];
} else {
continue;
}
EditList editList;
if (n instanceof ReverseCandidate
&& p.sourceBlob.equals(n.sourceBlob)) {
// This special case happens on ReverseCandidate forks.
p.sourceText = n.sourceText;
editList = new EditList(0);
} else {
p.loadText(reader);
editList = diffAlgorithm.diff(textComparator,
p.sourceText, n.sourceText);
}
if (editList.isEmpty()) {
// Ignoring whitespace (or some other special comparator) can
// cause non-identical blobs to have an empty edit list. In
// a case like this push the parent alone.
if (n instanceof ReverseCandidate) {
parents[pIdx] = p;
continue;
}
p.regionList = n.regionList;
n.regionList = null;
parents[pIdx] = p;
break;
}
p.takeBlame(editList, n);
// Only remember this parent candidate if there is at least
// one region that was blamed on the parent.
if (p.regionList != null) {
// Reverse blame requires inverting the regions. This puts
// the regions the parent deleted from us into the parent,
// and retains the common regions to look at other parents
// for deletions.
if (n instanceof ReverseCandidate) {
Region r = p.regionList;
p.regionList = n.regionList;
n.regionList = r;
}
parents[pIdx] = p;
}
}
if (n instanceof ReverseCandidate) {
// On a reverse blame report all deletions found in the children,
// and pass on to them a copy of our region list.
Candidate resultHead = null;
Candidate resultTail = null;
for (int pIdx = 0; pIdx < pCnt; pIdx++) {
Candidate p = parents[pIdx];
if (p == null)
continue;
if (p.regionList != null) {
Candidate r = p.copy(p.sourceCommit);
if (resultTail != null) {
resultTail.queueNext = r;
resultTail = r;
} else {
resultHead = r;
resultTail = r;
}
}
if (n.regionList != null) {
p.regionList = n.regionList.deepCopy();
push(p);
}
}
if (resultHead != null)
return result(resultHead);
return false;
}
// Push any parents that are still candidates.
for (int pIdx = 0; pIdx < pCnt; pIdx++) {
if (parents[pIdx] != null)
push(parents[pIdx]);
}
if (n.regionList != null)
return result(n);
return false;
}