tikhomirov@691: /* tikhomirov@691: * Copyright (c) 2013 TMate Software Ltd tikhomirov@691: * tikhomirov@691: * This program is free software; you can redistribute it and/or modify tikhomirov@691: * it under the terms of the GNU General Public License as published by tikhomirov@691: * the Free Software Foundation; version 2 of the License. tikhomirov@691: * tikhomirov@691: * This program is distributed in the hope that it will be useful, tikhomirov@691: * but WITHOUT ANY WARRANTY; without even the implied warranty of tikhomirov@691: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the tikhomirov@691: * GNU General Public License for more details. tikhomirov@691: * tikhomirov@691: * For information on how to redistribute this software under tikhomirov@691: * the terms of a license other than GNU General Public License tikhomirov@691: * contact TMate Software at support@hg4j.com tikhomirov@691: */ tikhomirov@691: package org.tmatesoft.hg.internal; tikhomirov@691: tikhomirov@694: import static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION; tikhomirov@694: tikhomirov@691: import java.util.ArrayList; tikhomirov@694: import java.util.Arrays; tikhomirov@691: import java.util.Collections; tikhomirov@691: import java.util.LinkedList; tikhomirov@691: import java.util.List; tikhomirov@691: tikhomirov@691: import org.tmatesoft.hg.core.HgFileRevision; tikhomirov@691: import org.tmatesoft.hg.core.HgIterateDirection; tikhomirov@691: import org.tmatesoft.hg.repo.HgDataFile; tikhomirov@694: import org.tmatesoft.hg.repo.HgRepository; tikhomirov@694: import org.tmatesoft.hg.repo.HgRuntimeException; tikhomirov@691: tikhomirov@691: /** tikhomirov@691: * Traces file renames. Quite similar to HgChangesetFileSneaker, although the latter tries different paths tikhomirov@691: * to find origin names, while this class traces first renames found only. tikhomirov@691: * tikhomirov@691: * @author Artem Tikhomirov tikhomirov@691: * @author TMate Software Ltd. tikhomirov@691: */ tikhomirov@691: public final class FileRenameHistory { tikhomirov@691: tikhomirov@691: private final int csetFrom; tikhomirov@691: private final int csetTo; tikhomirov@691: private final List history; tikhomirov@691: tikhomirov@691: public FileRenameHistory(int csetStartIndex, int csetEndIndex) { tikhomirov@691: csetFrom = csetStartIndex; tikhomirov@691: csetTo = csetEndIndex; tikhomirov@691: history = new ArrayList(3); tikhomirov@691: } tikhomirov@691: tikhomirov@691: public int startChangeset() { tikhomirov@691: return csetFrom; tikhomirov@691: } tikhomirov@691: tikhomirov@691: public int endChangeset() { tikhomirov@691: return csetTo; tikhomirov@691: } tikhomirov@691: tikhomirov@691: public boolean isOutOfRange(HgDataFile df, int fileRev) { tikhomirov@691: return df.getChangesetRevisionIndex(fileRev) < csetFrom || df.getChangesetRevisionIndex(0) > csetTo; tikhomirov@691: } tikhomirov@691: tikhomirov@691: public void build(HgDataFile df, int fileRev) { tikhomirov@691: assert !isOutOfRange(df, fileRev); tikhomirov@691: LinkedList chunks = new LinkedList(); tikhomirov@691: int chunkStart = 0, chunkEnd = fileRev; tikhomirov@691: int csetChunkEnd = -1, csetChunkStart = -1; tikhomirov@694: BasicRevMap csetMap = new BasicRevMap(0, fileRev).collect(df); tikhomirov@691: while (fileRev >= 0) { tikhomirov@694: int cset = csetMap.changesetAt(fileRev); tikhomirov@691: if (csetChunkEnd == -1) { tikhomirov@691: csetChunkEnd = cset; tikhomirov@691: } tikhomirov@691: if (cset <= csetFrom) { tikhomirov@691: chunkStart = fileRev; tikhomirov@691: csetChunkStart = csetFrom; tikhomirov@691: break; tikhomirov@691: } tikhomirov@691: if (cset > csetTo) { tikhomirov@691: chunkEnd = --fileRev; tikhomirov@691: csetChunkEnd = -1; tikhomirov@691: continue; tikhomirov@691: } tikhomirov@691: csetChunkStart = cset; tikhomirov@691: if (df.isCopy(fileRev)) { tikhomirov@691: chunks.addFirst(new Chunk(df, fileRev, chunkEnd, csetChunkStart, csetChunkEnd)); tikhomirov@691: HgFileRevision origin = df.getCopySource(fileRev); tikhomirov@691: df = df.getRepo().getFileNode(origin.getPath()); tikhomirov@691: fileRev = chunkEnd = df.getRevisionIndex(origin.getRevision()); tikhomirov@694: csetMap = new BasicRevMap(0, fileRev).collect(df); tikhomirov@691: chunkStart = 0; tikhomirov@691: csetChunkEnd = cset - 1; // if df is copy, cset can't be 0 tikhomirov@691: csetChunkStart = -1; tikhomirov@691: } else { tikhomirov@691: fileRev--; tikhomirov@691: } tikhomirov@691: } tikhomirov@691: assert chunkStart >= 0; tikhomirov@691: assert chunkEnd >= 0; // can be negative only if df.cset(0) > csetTo tikhomirov@691: assert csetChunkEnd >= 0; tikhomirov@691: assert csetChunkStart >= 0; tikhomirov@691: chunks.addFirst(new Chunk(df, chunkStart, chunkEnd, csetChunkStart, csetChunkEnd)); tikhomirov@691: tikhomirov@691: history.clear(); tikhomirov@691: history.addAll(chunks); tikhomirov@691: } tikhomirov@691: tikhomirov@691: public Iterable iterate(HgIterateDirection order) { tikhomirov@691: if (order == HgIterateDirection.NewToOld) { tikhomirov@691: return ReverseIterator.reversed(history); tikhomirov@691: } tikhomirov@691: assert order == HgIterateDirection.OldToNew; tikhomirov@691: return Collections.unmodifiableList(history); tikhomirov@691: } tikhomirov@691: tikhomirov@691: public int chunks() { tikhomirov@691: return history.size(); tikhomirov@691: } tikhomirov@691: tikhomirov@691: public Chunk chunkAt(int cset) { tikhomirov@691: if (cset < csetFrom || cset > csetTo) { tikhomirov@691: return null; tikhomirov@691: } tikhomirov@691: for (Chunk c : history) { tikhomirov@691: if (c.firstCset() > cset) { tikhomirov@691: break; tikhomirov@691: } tikhomirov@691: if (cset <= c.lastCset()) { tikhomirov@691: return c; tikhomirov@691: } tikhomirov@691: } tikhomirov@691: return null; tikhomirov@691: } tikhomirov@691: tikhomirov@691: tikhomirov@691: /** tikhomirov@691: * file has changes [firstFileRev..lastFileRev] that have occurred somewhere in [firstCset..lastCset] tikhomirov@691: */ tikhomirov@694: public static final class Chunk { tikhomirov@691: private final HgDataFile df; tikhomirov@691: private final int fileRevFrom; tikhomirov@691: private final int fileRevTo; tikhomirov@691: private final int csetFrom; tikhomirov@691: private final int csetTo; tikhomirov@691: Chunk(HgDataFile file, int fileRevStart, int fileRevEnd, int csetStart, int csetEnd) { tikhomirov@691: df = file; tikhomirov@691: fileRevFrom = fileRevStart; tikhomirov@691: fileRevTo = fileRevEnd; tikhomirov@691: csetFrom = csetStart; tikhomirov@691: csetTo = csetEnd; tikhomirov@691: } tikhomirov@691: public HgDataFile file() { tikhomirov@691: return df; tikhomirov@691: } tikhomirov@691: public int firstFileRev() { tikhomirov@691: return fileRevFrom; tikhomirov@691: } tikhomirov@691: public int lastFileRev() { tikhomirov@691: return fileRevTo; tikhomirov@691: } tikhomirov@691: public int firstCset() { tikhomirov@691: return csetFrom; tikhomirov@691: } tikhomirov@691: public int lastCset() { tikhomirov@691: return csetTo; tikhomirov@691: } tikhomirov@691: } tikhomirov@694: tikhomirov@694: private static final class BasicRevMap implements HgDataFile.LinkRevisionInspector { tikhomirov@694: private final int[] revs; tikhomirov@694: private final int fromRev; tikhomirov@694: private final int toRev; tikhomirov@694: public BasicRevMap(int startRev, int endRev) { tikhomirov@694: revs = new int[endRev+1]; // for simplicity, just ignore startRev now (it's 0 in local use anyway) tikhomirov@694: fromRev = startRev; tikhomirov@694: toRev = endRev; tikhomirov@694: Arrays.fill(revs, BAD_REVISION); tikhomirov@694: } tikhomirov@694: tikhomirov@694: public BasicRevMap collect(HgDataFile df) { tikhomirov@694: df.indexWalk(fromRev, toRev, this); tikhomirov@694: return this; tikhomirov@694: } tikhomirov@694: tikhomirov@694: public void next(int revisionIndex, int linkedRevisionIndex) throws HgRuntimeException { tikhomirov@694: revs[revisionIndex] = linkedRevisionIndex; tikhomirov@694: } tikhomirov@694: tikhomirov@694: /** tikhomirov@694: * @return {@link HgRepository#BAD_REVISION} if there's no mapping tikhomirov@694: */ tikhomirov@694: public int changesetAt(int rev) { tikhomirov@694: return revs[rev]; tikhomirov@694: } tikhomirov@694: } tikhomirov@691: }