tikhomirov@525: /* tikhomirov@525: * Copyright (c) 2012-2013 TMate Software Ltd tikhomirov@525: * tikhomirov@525: * This program is free software; you can redistribute it and/or modify tikhomirov@525: * it under the terms of the GNU General Public License as published by tikhomirov@525: * the Free Software Foundation; version 2 of the License. tikhomirov@525: * tikhomirov@525: * This program is distributed in the hope that it will be useful, tikhomirov@525: * but WITHOUT ANY WARRANTY; without even the implied warranty of tikhomirov@525: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the tikhomirov@525: * GNU General Public License for more details. tikhomirov@525: * tikhomirov@525: * For information on how to redistribute this software under tikhomirov@525: * the terms of a license other than GNU General Public License tikhomirov@525: * contact TMate Software at support@hg4j.com tikhomirov@525: */ tikhomirov@525: package org.tmatesoft.hg.core; tikhomirov@525: tikhomirov@525: import java.io.File; tikhomirov@525: import java.io.FileOutputStream; tikhomirov@525: import java.io.IOException; tikhomirov@525: import java.nio.channels.FileChannel; tikhomirov@525: tikhomirov@525: import org.tmatesoft.hg.internal.DirstateBuilder; tikhomirov@525: import org.tmatesoft.hg.internal.Experimental; tikhomirov@525: import org.tmatesoft.hg.internal.Internals; tikhomirov@525: import org.tmatesoft.hg.internal.WorkingDirFileWriter; tikhomirov@525: import org.tmatesoft.hg.repo.HgDataFile; tikhomirov@525: import org.tmatesoft.hg.repo.HgInvalidRevisionException; tikhomirov@525: import org.tmatesoft.hg.repo.HgInvalidStateException; tikhomirov@525: import org.tmatesoft.hg.repo.HgManifest; tikhomirov@525: import org.tmatesoft.hg.repo.HgRepository; tikhomirov@525: import org.tmatesoft.hg.repo.HgRuntimeException; tikhomirov@525: import org.tmatesoft.hg.repo.HgManifest.Flags; tikhomirov@525: import org.tmatesoft.hg.util.CancelledException; tikhomirov@525: import org.tmatesoft.hg.util.Path; tikhomirov@525: tikhomirov@525: /** tikhomirov@525: * WORK IN PROGRESS. tikhomirov@525: * tikhomirov@525: * Update working directory to specific state, 'hg checkout' counterpart. tikhomirov@525: * For the time being, only 'clean' checkout is supported ('hg co --clean') tikhomirov@525: * tikhomirov@525: * @since 1.1 tikhomirov@525: * @author Artem Tikhomirov tikhomirov@525: * @author TMate Software Ltd. tikhomirov@525: */ tikhomirov@525: @Experimental(reason="Work in progress") tikhomirov@525: public class HgCheckoutCommand extends HgAbstractCommand{ tikhomirov@525: tikhomirov@525: private final HgRepository repo; tikhomirov@525: private int revisionToCheckout = HgRepository.BAD_REVISION; tikhomirov@525: tikhomirov@525: public HgCheckoutCommand(HgRepository hgRepo) { tikhomirov@525: repo = hgRepo; tikhomirov@525: } tikhomirov@525: tikhomirov@525: /** tikhomirov@525: * Select revision to check out tikhomirov@525: * tikhomirov@525: * @param nodeid revision tikhomirov@525: * @return this for convenience tikhomirov@525: * @throws HgBadArgumentException if failed to find supplied changeset tikhomirov@525: */ tikhomirov@525: public HgCheckoutCommand changeset(Nodeid nodeid) throws HgBadArgumentException { tikhomirov@525: try { tikhomirov@525: return changeset(repo.getChangelog().getRevisionIndex(nodeid)); tikhomirov@525: } catch (HgInvalidRevisionException ex) { tikhomirov@525: throw new HgBadArgumentException("Can't find revision", ex).setRevision(nodeid); tikhomirov@525: } tikhomirov@525: } tikhomirov@525: tikhomirov@525: /** tikhomirov@525: * Select revision to check out using local revision index tikhomirov@525: * tikhomirov@525: * @param changesetIndex local revision index tikhomirov@525: * @return this for convenience tikhomirov@525: * @throws HgBadArgumentException if failed to find supplied changeset tikhomirov@525: */ tikhomirov@525: public HgCheckoutCommand changeset(int changesetIndex) throws HgBadArgumentException { tikhomirov@525: int lastCsetIndex = repo.getChangelog().getLastRevision(); tikhomirov@525: if (changesetIndex < 0 || changesetIndex > lastCsetIndex) { tikhomirov@525: throw new HgBadArgumentException(String.format("Bad revision index %d, value from [0..%d] expected", changesetIndex, lastCsetIndex), null).setRevisionIndex(changesetIndex); tikhomirov@525: } tikhomirov@525: revisionToCheckout = changesetIndex; tikhomirov@525: return this; tikhomirov@525: } tikhomirov@525: tikhomirov@525: /** tikhomirov@525: * tikhomirov@525: * @throws HgIOException to indicate troubles updating files in working copy tikhomirov@525: * @throws HgException tikhomirov@525: * @throws CancelledException tikhomirov@525: */ tikhomirov@525: public void execute() throws HgException, CancelledException { tikhomirov@525: Internals internalRepo = Internals.getInstance(repo); tikhomirov@525: // remove tracked files from wd (perhaps, just forget 'Added'?) tikhomirov@525: // TODO tikhomirov@525: final DirstateBuilder dirstateBuilder = new DirstateBuilder(internalRepo.buildFileNameEncodingHelper()); tikhomirov@525: final Exception[] failure = new Exception[1]; tikhomirov@525: HgManifest.Inspector worker = new HgManifest.Inspector() { tikhomirov@525: tikhomirov@525: public boolean next(Nodeid nid, Path fname, Flags flags) { tikhomirov@525: try { tikhomirov@525: HgDataFile df = repo.getFileNode(fname); tikhomirov@525: int fileRevIndex = df.getRevisionIndex(nid); tikhomirov@525: // check out files based on manifest tikhomirov@525: // FIXME links! tikhomirov@525: WorkingDirFileWriter workingDirWriter = new WorkingDirFileWriter(repo); tikhomirov@525: workingDirWriter.processFile(df, fileRevIndex); tikhomirov@525: // new dirstate based on manifest tikhomirov@525: dirstateBuilder.recordNormal(fname, flags, workingDirWriter.bytesWritten()); tikhomirov@525: return true; tikhomirov@525: } catch (IOException ex) { tikhomirov@525: failure[0] = ex; tikhomirov@525: } catch (HgRuntimeException ex) { tikhomirov@525: failure[0] = ex; tikhomirov@525: } tikhomirov@525: return false; tikhomirov@525: } tikhomirov@525: tikhomirov@525: public boolean end(int manifestRevision) { tikhomirov@525: return false; tikhomirov@525: } tikhomirov@525: tikhomirov@525: public boolean begin(int mainfestRevision, Nodeid nid, int changelogRevision) { tikhomirov@525: return true; tikhomirov@525: } tikhomirov@525: }; tikhomirov@525: dirstateBuilder.parents(repo.getChangelog().getRevision(revisionToCheckout), null); tikhomirov@525: repo.getManifest().walk(revisionToCheckout, revisionToCheckout, worker); tikhomirov@525: if (failure[0] != null) { tikhomirov@525: if (failure[0] instanceof IOException) { tikhomirov@525: throw new HgIOException("Failed to write down file revision", failure[0], /*FIXME file*/null); tikhomirov@525: } tikhomirov@525: if (failure[0] instanceof HgRuntimeException) { tikhomirov@525: throw new HgLibraryFailureException((HgRuntimeException) failure[0]); tikhomirov@525: } tikhomirov@525: HgInvalidStateException e = new HgInvalidStateException("Unexpected exception"); tikhomirov@525: e.initCause(failure[0]); tikhomirov@525: throw e; tikhomirov@525: } tikhomirov@525: File dirstateFile = internalRepo.getFileFromRepoDir("dirstate"); tikhomirov@525: try { tikhomirov@525: FileChannel dirstate = new FileOutputStream(dirstateFile).getChannel(); tikhomirov@525: dirstateBuilder.serialize(dirstate); tikhomirov@525: dirstate.close(); tikhomirov@525: } catch (IOException ex) { tikhomirov@525: throw new HgIOException("Can't write down new directory state", ex, dirstateFile); tikhomirov@525: } tikhomirov@525: // FIXME write down branch file tikhomirov@525: } tikhomirov@525: }