Mercurial > jhg
view src/org/tmatesoft/hg/internal/FileRenameHistory.java @ 694:7efabe0cddcf
Speed up (a) file rename history to minimize file reads; (b) file.isCopy(int) to read metadata for few revisions at once (use pattern assumes earlier revisions are likely to be queried, too); (c) HgIgnore.isIgnored by caching matched initial fragments (to substitute more expensive Matcher.matches with cheaper HashMap.contains)
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Mon, 05 Aug 2013 17:42:10 +0200 |
parents | 72fc7774b87e |
children |
line wrap: on
line source
/* * 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 static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION; import java.util.ArrayList; import java.util.Arrays; 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; import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.repo.HgRuntimeException; /** * 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; BasicRevMap csetMap = new BasicRevMap(0, fileRev).collect(df); while (fileRev >= 0) { int cset = csetMap.changesetAt(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()); csetMap = new BasicRevMap(0, fileRev).collect(df); 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 final 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; } } private static final class BasicRevMap implements HgDataFile.LinkRevisionInspector { private final int[] revs; private final int fromRev; private final int toRev; public BasicRevMap(int startRev, int endRev) { revs = new int[endRev+1]; // for simplicity, just ignore startRev now (it's 0 in local use anyway) fromRev = startRev; toRev = endRev; Arrays.fill(revs, BAD_REVISION); } public BasicRevMap collect(HgDataFile df) { df.indexWalk(fromRev, toRev, this); return this; } public void next(int revisionIndex, int linkedRevisionIndex) throws HgRuntimeException { revs[revisionIndex] = linkedRevisionIndex; } /** * @return {@link HgRepository#BAD_REVISION} if there's no mapping */ public int changesetAt(int rev) { return revs[rev]; } } }