tikhomirov@539: /* tikhomirov@539: * Copyright (c) 2013 TMate Software Ltd tikhomirov@539: * tikhomirov@539: * This program is free software; you can redistribute it and/or modify tikhomirov@539: * it under the terms of the GNU General Public License as published by tikhomirov@539: * the Free Software Foundation; version 2 of the License. tikhomirov@539: * tikhomirov@539: * This program is distributed in the hope that it will be useful, tikhomirov@539: * but WITHOUT ANY WARRANTY; without even the implied warranty of tikhomirov@539: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the tikhomirov@539: * GNU General Public License for more details. tikhomirov@539: * tikhomirov@539: * For information on how to redistribute this software under tikhomirov@539: * the terms of a license other than GNU General Public License tikhomirov@539: * contact TMate Software at support@hg4j.com tikhomirov@539: */ tikhomirov@539: package org.tmatesoft.hg.internal; tikhomirov@539: tikhomirov@647: import static org.tmatesoft.hg.repo.HgRepositoryFiles.FNCache; tikhomirov@647: tikhomirov@539: import java.io.File; tikhomirov@539: import java.io.FileOutputStream; tikhomirov@539: import java.io.IOException; tikhomirov@616: import java.nio.ByteBuffer; tikhomirov@616: import java.nio.CharBuffer; tikhomirov@616: import java.nio.channels.FileChannel; tikhomirov@539: import java.util.ArrayList; tikhomirov@559: import java.util.List; tikhomirov@539: tikhomirov@664: import org.tmatesoft.hg.core.HgIOException; tikhomirov@539: import org.tmatesoft.hg.util.Path; tikhomirov@539: tikhomirov@539: /** tikhomirov@559: * Append-only fncache support tikhomirov@559: * tikhomirov@539: *
tikhomirov@539: * The fncache file contains the paths of all filelog files in the store as encoded by mercurial.filelog.encodedir. The paths are separated by '\n' (LF). tikhomirov@539: *
tikhomirov@539: * @see http://mercurial.selenic.com/wiki/fncacheRepoFormat tikhomirov@559: * tikhomirov@559: * tikhomirov@539: * @author Artem Tikhomirov tikhomirov@539: * @author TMate Software Ltd. tikhomirov@539: */ tikhomirov@539: public class FNCacheFile { tikhomirov@539: tikhomirov@539: private final Internals repo; tikhomirov@559: // private final List files; tikhomirov@616: private final List addedDotI; tikhomirov@616: private final List addedDotD; tikhomirov@616: private final FNCachePathHelper pathHelper; tikhomirov@539: tikhomirov@539: public FNCacheFile(Internals internalRepo) { tikhomirov@539: repo = internalRepo; tikhomirov@559: // files = new ArrayList(); tikhomirov@616: pathHelper = new FNCachePathHelper(); tikhomirov@616: addedDotI = new ArrayList(5); tikhomirov@616: addedDotD = new ArrayList(5); tikhomirov@539: } tikhomirov@539: tikhomirov@559: /* tikhomirov@559: * For append-only option, we don't care reading the original content tikhomirov@539: public void read(Path.Source pathFactory) throws IOException { tikhomirov@539: File f = fncacheFile(); tikhomirov@539: files.clear(); tikhomirov@539: if (!f.exists()) { tikhomirov@539: return; tikhomirov@539: } tikhomirov@539: ArrayList entries = new ArrayList(); tikhomirov@539: // names in fncache are in local encoding, shall translate to unicode tikhomirov@539: new LineReader(f, repo.getSessionContext().getLog(), repo.getFilenameEncoding()).read(new LineReader.SimpleLineCollector(), entries); tikhomirov@539: for (String e : entries) { tikhomirov@624: // XXX plain wrong, need either to decode paths and strip off .i/.d or (if keep names as is) change write() tikhomirov@539: files.add(pathFactory.path(e)); tikhomirov@539: } tikhomirov@539: } tikhomirov@559: */ tikhomirov@539: tikhomirov@664: public void write(Transaction tr) throws HgIOException { tikhomirov@616: if (addedDotI.isEmpty() && addedDotD.isEmpty()) { tikhomirov@539: return; tikhomirov@539: } tikhomirov@647: File f = repo.getRepositoryFile(FNCache); tikhomirov@539: f.getParentFile().mkdirs(); tikhomirov@667: final EncodingHelper fnEncoder = repo.buildFileNameEncodingHelper(); tikhomirov@616: ArrayList added = new ArrayList(); tikhomirov@616: for (Path p : addedDotI) { tikhomirov@616: added.add(CharBuffer.wrap(pathHelper.rewrite(p))); tikhomirov@616: } tikhomirov@616: for (Path p : addedDotD) { tikhomirov@616: // XXX FNCachePathHelper always return name of an index file, need to change it into a name of data file, tikhomirov@616: // although the approach (to replace last char) is depressingly awful tikhomirov@616: CharSequence cs = pathHelper.rewrite(p); tikhomirov@616: CharBuffer cb = CharBuffer.allocate(cs.length()); tikhomirov@616: cb.append(cs); tikhomirov@616: cb.put(cs.length()-1, 'd'); tikhomirov@616: cb.flip(); tikhomirov@616: added.add(cb); tikhomirov@616: } tikhomirov@664: FileOutputStream fos = null; tikhomirov@664: f = tr.prepare(f); tikhomirov@664: try { tikhomirov@664: fos = new FileOutputStream(f, true); tikhomirov@664: FileChannel fncacheFile = fos.getChannel(); tikhomirov@664: ByteBuffer lf = ByteBuffer.wrap(new byte[] { 0x0A }); tikhomirov@664: for (CharBuffer b : added) { tikhomirov@667: fncacheFile.write(fnEncoder.toFNCache(b)); tikhomirov@664: fncacheFile.write(lf); tikhomirov@664: lf.rewind(); tikhomirov@664: } tikhomirov@664: fncacheFile.force(true); tikhomirov@664: tr.done(f); tikhomirov@664: } catch (IOException ex) { tikhomirov@664: tr.failure(f, ex); tikhomirov@664: throw new HgIOException("Failed to write fncache", ex, f); tikhomirov@664: } finally { tikhomirov@664: new FileUtils(repo.getLog(), this).closeQuietly(fos, f); tikhomirov@539: } tikhomirov@664: tikhomirov@539: } tikhomirov@539: tikhomirov@616: public void addIndex(Path p) { tikhomirov@616: addedDotI.add(p); tikhomirov@616: } tikhomirov@616: tikhomirov@616: public void addData(Path p) { tikhomirov@616: addedDotD.add(p); tikhomirov@539: } tikhomirov@663: tikhomirov@663: /** tikhomirov@663: * Register new files with fncache if one is enabled for the repo, do nothing otherwise tikhomirov@663: */ tikhomirov@663: public static class Mediator { tikhomirov@663: private final Internals repo; tikhomirov@663: private FNCacheFile fncache; tikhomirov@664: private final Transaction tr; tikhomirov@663: tikhomirov@664: public Mediator(Internals internalRepo, Transaction transaction) { tikhomirov@663: repo = internalRepo; tikhomirov@664: tr = transaction; tikhomirov@663: } tikhomirov@663: tikhomirov@663: public void registerNew(Path f, RevlogStream rs) { tikhomirov@663: if (fncache != null || repo.fncacheInUse()) { tikhomirov@663: if (fncache == null) { tikhomirov@663: fncache = new FNCacheFile(repo); tikhomirov@663: } tikhomirov@663: fncache.addIndex(f); tikhomirov@663: if (!rs.isInlineData()) { tikhomirov@663: fncache.addData(f); tikhomirov@663: } tikhomirov@663: } tikhomirov@663: } tikhomirov@663: tikhomirov@664: public void complete() throws HgIOException { tikhomirov@663: if (fncache != null) { tikhomirov@664: fncache.write(tr); tikhomirov@663: } tikhomirov@663: } tikhomirov@663: } tikhomirov@539: }