public void buildCompositeCommits() throws IOException { revWalk = new RevWalk(repository); ByteArrayOutputStream diffTexts = new ByteArrayOutputStream(); DiffFormatter df = new DiffFormatter(diffTexts); df.setRepository(repository); df.setDiffComparator(RawTextComparator.WS_IGNORE_ALL); df.setContext(0); df.setDiffAlgorithm(DiffAlgorithm.getAlgorithm(SupportedAlgorithm.HISTOGRAM)); df.setDetectRenames(true); for (int idx = 0; idx < _commits.size(); idx++) { RevCommit commit = revWalk.parseCommit(_commits.get(idx)); int p_count = commit.getParentCount(); if (p_count == 0) { throw new RuntimeException("commit with no parent ?!?!"); } RevCommit p = revWalk.parseCommit(commit.getParent(0).getId()); List<DiffEntry> diffs = df.scan(p.getTree(), commit.getTree()); for (DiffEntry d : diffs) { CompositeDiff cd = new CompositeDiff(d, commit); if (ParsingUtils.isSourceFile(d.getOldPath()) || ParsingUtils.isSourceFile(d.getNewPath())) { extractCodeEdits(diffTexts, df, d, cd); } _diffs.add(cd); } } revWalk.release(); }
private boolean split(Candidate parent, Candidate source) throws IOException { EditList editList = diffAlgorithm.diff(textComparator, parent.sourceText, source.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. parent.regionList = source.regionList; push(parent); return false; } parent.takeBlame(editList, source); if (parent.regionList != null) push(parent); if (source.regionList != null) { if (source instanceof ReverseCandidate) return reverseResult(parent, source); return result(source); } return false; }
/** * @param local * @param inCore */ protected ResolveMerger(Repository local, boolean inCore) { super(local); SupportedAlgorithm diffAlg = local .getConfig() .getEnum( ConfigConstants.CONFIG_DIFF_SECTION, null, ConfigConstants.CONFIG_KEY_ALGORITHM, SupportedAlgorithm.HISTOGRAM); mergeAlgorithm = new MergeAlgorithm(DiffAlgorithm.getAlgorithm(diffAlg)); commitNames = new String[] {"BASE", "OURS", "THEIRS"}; // $NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ this.inCore = inCore; if (inCore) { implicitDirCache = false; dircache = DirCache.newInCore(); } else { implicitDirCache = true; } }
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(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(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; }