Mercurial > jhg
diff src/org/tmatesoft/hg/internal/DirstateReader.java @ 526:2f9ed6bcefa2
Initial support for Revert command with accompanying minor refactoring
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Tue, 15 Jan 2013 17:07:19 +0100 |
parents | src/org/tmatesoft/hg/repo/HgDirstate.java@0be5be8d57e9 |
children | 47b7bedf0569 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/internal/DirstateReader.java Tue Jan 15 17:07:19 2013 +0100 @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2010-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.core.Nodeid.NULL; +import static org.tmatesoft.hg.repo.HgRepositoryFiles.Dirstate; +import static org.tmatesoft.hg.util.LogFacility.Severity.Debug; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; + +import org.tmatesoft.hg.core.Nodeid; +import org.tmatesoft.hg.repo.HgDirstate; +import org.tmatesoft.hg.repo.HgDirstate.EntryKind; +import org.tmatesoft.hg.repo.HgInvalidControlFileException; +import org.tmatesoft.hg.repo.HgRepository; +import org.tmatesoft.hg.util.LogFacility.Severity; +import org.tmatesoft.hg.util.Pair; +import org.tmatesoft.hg.util.Path; + + +/** + * Parse dirstate file + * + * @see http://mercurial.selenic.com/wiki/DirState + * @see http://mercurial.selenic.com/wiki/FileFormats#dirstate + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +public final class DirstateReader { + // dirstate read code originally lived in org.tmatesoft.hg.repo.HgDirstate + + private final Internals repo; + private final Path.Source pathPool; + private Pair<Nodeid, Nodeid> parents; + + public DirstateReader(Internals hgRepo, Path.Source pathSource) { + repo = hgRepo; + pathPool = pathSource; + } + + public void readInto(HgDirstate.Inspector target) throws HgInvalidControlFileException { + EncodingHelper encodingHelper = repo.buildFileNameEncodingHelper(); + parents = new Pair<Nodeid,Nodeid>(Nodeid.NULL, Nodeid.NULL); + File dirstateFile = getDirstateFile(repo); + if (dirstateFile == null || !dirstateFile.exists()) { + return; + } + DataAccess da = repo.getDataAccess().create(dirstateFile); + try { + if (da.isEmpty()) { + return; + } + parents = internalReadParents(da); + // hg init; hg up produces an empty repository where dirstate has parents (40 bytes) only + while (!da.isEmpty()) { + final byte state = da.readByte(); + final int fmode = da.readInt(); + final int size = da.readInt(); + final int time = da.readInt(); + final int nameLen = da.readInt(); + String fn1 = null, fn2 = null; + byte[] name = new byte[nameLen]; + da.readBytes(name, 0, nameLen); + for (int i = 0; i < nameLen; i++) { + if (name[i] == 0) { + fn1 = encodingHelper.fromDirstate(name, 0, i); + fn2 = encodingHelper.fromDirstate(name, i+1, nameLen - i - 1); + break; + } + } + if (fn1 == null) { + fn1 = encodingHelper.fromDirstate(name, 0, nameLen); + } + HgDirstate.Record r = new HgDirstate.Record(fmode, size, time, pathPool.path(fn1), fn2 == null ? null : pathPool.path(fn2)); + if (state == 'n') { + target.next(EntryKind.Normal, r); + } else if (state == 'a') { + target.next(EntryKind.Added, r); + } else if (state == 'r') { + target.next(EntryKind.Removed, r); + } else if (state == 'm') { + target.next(EntryKind.Merged, r); + } else { + repo.getSessionContext().getLog().dump(getClass(), Severity.Warn, "Dirstate record for file %s (size: %d, tstamp:%d) has unknown state '%c'", r.name(), r.size(), r.modificationTime(), state); + } + } + } catch (IOException ex) { + throw new HgInvalidControlFileException("Dirstate read failed", ex, dirstateFile); + } finally { + da.done(); + } + } + + private static Pair<Nodeid, Nodeid> internalReadParents(DataAccess da) throws IOException { + byte[] parents = new byte[40]; + da.readBytes(parents, 0, 40); + Nodeid n1 = Nodeid.fromBinary(parents, 0); + Nodeid n2 = Nodeid.fromBinary(parents, 20); + parents = null; + return new Pair<Nodeid, Nodeid>(n1, n2); + } + + /** + * @return pair of working copy parents, with {@link Nodeid#NULL} for missing values. + */ + public Pair<Nodeid,Nodeid> parents() { + assert parents != null; // instance not initialized with #read() + return parents; + } + + private static File getDirstateFile(Internals repo) { + return repo.getFileFromRepoDir(Dirstate.getName()); + } + + /** + * @return pair of parents, both {@link Nodeid#NULL} if dirstate is not available + */ + public static Pair<Nodeid, Nodeid> readParents(Internals internalRepo) throws HgInvalidControlFileException { + // do not read whole dirstate if all we need is WC parent information + File dirstateFile = getDirstateFile(internalRepo); + if (dirstateFile == null || !dirstateFile.exists()) { + return new Pair<Nodeid,Nodeid>(NULL, NULL); + } + DataAccess da = internalRepo.getDataAccess().create(dirstateFile); + try { + if (da.isEmpty()) { + return new Pair<Nodeid,Nodeid>(NULL, NULL); + } + return internalReadParents(da); + } catch (IOException ex) { + throw new HgInvalidControlFileException("Error reading working copy parents from dirstate", ex, dirstateFile); + } finally { + da.done(); + } + } + + /** + * TODO [post-1.0] it's really not a proper place for the method, need WorkingCopyContainer or similar + * @return branch associated with the working directory + */ + public static String readBranch(Internals internalRepo) throws HgInvalidControlFileException { + File branchFile = internalRepo.getFileFromRepoDir("branch"); // FIXME constant in the HgRepositoryFiles + String branch = HgRepository.DEFAULT_BRANCH_NAME; + if (branchFile.exists()) { + try { + BufferedReader r = new BufferedReader(new FileReader(branchFile)); + String b = r.readLine(); + if (b != null) { + b = b.trim().intern(); + } + branch = b == null || b.length() == 0 ? HgRepository.DEFAULT_BRANCH_NAME : b; + r.close(); + } catch (FileNotFoundException ex) { + internalRepo.getSessionContext().getLog().dump(HgDirstate.class, Debug, ex, null); // log verbose debug, exception might be legal here + // IGNORE + } catch (IOException ex) { + throw new HgInvalidControlFileException("Error reading file with branch information", ex, branchFile); + } + } + return branch; + } +}