Mercurial > hg4j
diff src/org/tmatesoft/hg/internal/FileRenameHistory.java @ 691:72fc7774b87e
Fix file.isCopy() for blame/annotate. Refactor status and blame to use newly introduced FileHistory helper that builds file rename history
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Fri, 02 Aug 2013 23:07:23 +0200 |
parents | |
children | 7efabe0cddcf |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/internal/FileRenameHistory.java Fri Aug 02 23:07:23 2013 +0200 @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2013 TMate Software Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For information on how to redistribute this software under + * the terms of a license other than GNU General Public License + * contact TMate Software at support@hg4j.com + */ +package org.tmatesoft.hg.internal; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import org.tmatesoft.hg.core.HgFileRevision; +import org.tmatesoft.hg.core.HgIterateDirection; +import org.tmatesoft.hg.repo.HgDataFile; + +/** + * Traces file renames. Quite similar to HgChangesetFileSneaker, although the latter tries different paths + * to find origin names, while this class traces first renames found only. + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +public final class FileRenameHistory { + + private final int csetFrom; + private final int csetTo; + private final List<Chunk> history; + + public FileRenameHistory(int csetStartIndex, int csetEndIndex) { + csetFrom = csetStartIndex; + csetTo = csetEndIndex; + history = new ArrayList<Chunk>(3); + } + + public int startChangeset() { + return csetFrom; + } + + public int endChangeset() { + return csetTo; + } + + public boolean isOutOfRange(HgDataFile df, int fileRev) { + return df.getChangesetRevisionIndex(fileRev) < csetFrom || df.getChangesetRevisionIndex(0) > csetTo; + } + + public void build(HgDataFile df, int fileRev) { + assert !isOutOfRange(df, fileRev); + LinkedList<Chunk> chunks = new LinkedList<Chunk>(); + int chunkStart = 0, chunkEnd = fileRev; + int csetChunkEnd = -1, csetChunkStart = -1; + while (fileRev >= 0) { + int cset = df.getChangesetRevisionIndex(fileRev); + if (csetChunkEnd == -1) { + csetChunkEnd = cset; + } + if (cset <= csetFrom) { + chunkStart = fileRev; + csetChunkStart = csetFrom; + break; + } + if (cset > csetTo) { + chunkEnd = --fileRev; + csetChunkEnd = -1; + continue; + } + csetChunkStart = cset; + if (df.isCopy(fileRev)) { + chunks.addFirst(new Chunk(df, fileRev, chunkEnd, csetChunkStart, csetChunkEnd)); + HgFileRevision origin = df.getCopySource(fileRev); + df = df.getRepo().getFileNode(origin.getPath()); + fileRev = chunkEnd = df.getRevisionIndex(origin.getRevision()); + chunkStart = 0; + csetChunkEnd = cset - 1; // if df is copy, cset can't be 0 + csetChunkStart = -1; + } else { + fileRev--; + } + } + assert chunkStart >= 0; + assert chunkEnd >= 0; // can be negative only if df.cset(0) > csetTo + assert csetChunkEnd >= 0; + assert csetChunkStart >= 0; + chunks.addFirst(new Chunk(df, chunkStart, chunkEnd, csetChunkStart, csetChunkEnd)); + + history.clear(); + history.addAll(chunks); + } + + public Iterable<Chunk> iterate(HgIterateDirection order) { + if (order == HgIterateDirection.NewToOld) { + return ReverseIterator.reversed(history); + } + assert order == HgIterateDirection.OldToNew; + return Collections.unmodifiableList(history); + } + + public int chunks() { + return history.size(); + } + + public Chunk chunkAt(int cset) { + if (cset < csetFrom || cset > csetTo) { + return null; + } + for (Chunk c : history) { + if (c.firstCset() > cset) { + break; + } + if (cset <= c.lastCset()) { + return c; + } + } + return null; + } + + + /** + * file has changes [firstFileRev..lastFileRev] that have occurred somewhere in [firstCset..lastCset] + */ + public static class Chunk { + private final HgDataFile df; + private final int fileRevFrom; + private final int fileRevTo; + private final int csetFrom; + private final int csetTo; + Chunk(HgDataFile file, int fileRevStart, int fileRevEnd, int csetStart, int csetEnd) { + df = file; + fileRevFrom = fileRevStart; + fileRevTo = fileRevEnd; + csetFrom = csetStart; + csetTo = csetEnd; + } + public HgDataFile file() { + return df; + } + public int firstFileRev() { + return fileRevFrom; + } + public int lastFileRev() { + return fileRevTo; + } + public int firstCset() { + return csetFrom; + } + public int lastCset() { + return csetTo; + } + } +}