tikhomirov@526: /* tikhomirov@526: * Copyright (c) 2013 TMate Software Ltd tikhomirov@526: * tikhomirov@526: * This program is free software; you can redistribute it and/or modify tikhomirov@526: * it under the terms of the GNU General Public License as published by tikhomirov@526: * the Free Software Foundation; version 2 of the License. tikhomirov@526: * tikhomirov@526: * This program is distributed in the hope that it will be useful, tikhomirov@526: * but WITHOUT ANY WARRANTY; without even the implied warranty of tikhomirov@526: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the tikhomirov@526: * GNU General Public License for more details. tikhomirov@526: * tikhomirov@526: * For information on how to redistribute this software under tikhomirov@526: * the terms of a license other than GNU General Public License tikhomirov@526: * contact TMate Software at support@hg4j.com tikhomirov@526: */ tikhomirov@526: package org.tmatesoft.hg.core; tikhomirov@526: tikhomirov@526: import java.io.File; tikhomirov@526: import java.util.Arrays; tikhomirov@526: import java.util.LinkedHashSet; tikhomirov@526: import java.util.Set; tikhomirov@526: tikhomirov@526: import org.tmatesoft.hg.internal.DirstateBuilder; tikhomirov@526: import org.tmatesoft.hg.internal.DirstateReader; tikhomirov@526: import org.tmatesoft.hg.internal.Experimental; tikhomirov@526: import org.tmatesoft.hg.internal.Internals; tikhomirov@526: import org.tmatesoft.hg.repo.HgInvalidRevisionException; tikhomirov@526: import org.tmatesoft.hg.repo.HgManifest; tikhomirov@526: import org.tmatesoft.hg.repo.HgRepository; tikhomirov@526: import org.tmatesoft.hg.repo.HgRuntimeException; tikhomirov@526: import org.tmatesoft.hg.repo.HgManifest.Flags; tikhomirov@526: import org.tmatesoft.hg.util.CancelledException; tikhomirov@526: import org.tmatesoft.hg.util.Path; tikhomirov@526: tikhomirov@526: /** tikhomirov@526: * WORK IN PROGRESS. tikhomirov@526: * tikhomirov@526: * Restore files to their checkout state, 'hg revert' counterpart. tikhomirov@526: * tikhomirov@526: * @author Artem Tikhomirov tikhomirov@526: * @author TMate Software Ltd. tikhomirov@526: */ tikhomirov@526: @Experimental(reason="Work in progress") tikhomirov@526: public class HgRevertCommand extends HgAbstractCommand { tikhomirov@526: tikhomirov@526: private final HgRepository repo; tikhomirov@526: private final Set files = new LinkedHashSet(); tikhomirov@526: private int changesetToCheckout = HgRepository.WORKING_COPY; // XXX WORKING_COPY_PARENT, in fact tikhomirov@526: private boolean keepOriginal = true; tikhomirov@526: tikhomirov@526: public HgRevertCommand(HgRepository hgRepo) { tikhomirov@526: repo = hgRepo; tikhomirov@526: } tikhomirov@526: tikhomirov@526: /** tikhomirov@526: * Additive tikhomirov@526: * tikhomirov@526: * @param paths files to revert tikhomirov@526: * @return this for convenience tikhomirov@526: */ tikhomirov@526: public HgRevertCommand file(Path... paths) { tikhomirov@526: files.addAll(Arrays.asList(paths)); tikhomirov@526: return this; tikhomirov@526: } tikhomirov@526: tikhomirov@526: /** tikhomirov@526: * Revert the given files to their states as of a specific revision tikhomirov@526: * tikhomirov@526: * @param changesetRevIndex tikhomirov@526: * @return this for convenience tikhomirov@526: * @throws HgBadArgumentException tikhomirov@526: */ tikhomirov@526: public HgRevertCommand changeset(int changesetRevIndex) throws HgBadArgumentException { tikhomirov@526: int lastCsetIndex = repo.getChangelog().getLastRevision(); tikhomirov@526: if (changesetRevIndex < 0 || changesetRevIndex > lastCsetIndex) { tikhomirov@526: throw new HgBadArgumentException(String.format("Bad revision index %d, value from [0..%d] expected", changesetRevIndex, lastCsetIndex), null).setRevisionIndex(changesetRevIndex); tikhomirov@526: } tikhomirov@526: changesetToCheckout = changesetRevIndex; tikhomirov@526: return this; tikhomirov@526: } tikhomirov@526: tikhomirov@526: /** tikhomirov@526: * Handy supplement to {@link #changeset(int)} tikhomirov@526: * tikhomirov@526: * @param revision tikhomirov@526: * @return this for convenience tikhomirov@526: * @throws HgBadArgumentException tikhomirov@526: */ tikhomirov@526: public HgRevertCommand changeset(Nodeid revision) throws HgBadArgumentException { tikhomirov@526: try { tikhomirov@526: return changeset(repo.getChangelog().getRevisionIndex(revision)); tikhomirov@526: } catch (HgInvalidRevisionException ex) { tikhomirov@526: throw new HgBadArgumentException("Can't find revision", ex).setRevision(revision); tikhomirov@526: } tikhomirov@526: } tikhomirov@526: tikhomirov@526: // TODO keepOriginal() to save .orig tikhomirov@526: tikhomirov@526: /** tikhomirov@526: * Perform the back out for the given files tikhomirov@526: * tikhomirov@526: * @throws HgIOException tikhomirov@526: * @throws HgException tikhomirov@526: * @throws CancelledException tikhomirov@526: */ tikhomirov@526: public void execute() throws HgException, CancelledException { tikhomirov@526: try { tikhomirov@526: final int csetRevision; tikhomirov@526: if (changesetToCheckout == HgRepository.WORKING_COPY) { tikhomirov@526: csetRevision = repo.getChangelog().getRevisionIndex(repo.getWorkingCopyParents().first()); tikhomirov@526: } else { tikhomirov@526: csetRevision = changesetToCheckout; tikhomirov@526: } tikhomirov@526: Internals implRepo = Internals.getInstance(repo); tikhomirov@526: final DirstateBuilder dirstateBuilder = new DirstateBuilder(implRepo); tikhomirov@526: dirstateBuilder.fillFrom(new DirstateReader(implRepo, new Path.SimpleSource())); tikhomirov@526: final HgCheckoutCommand.CheckoutWorker worker = new HgCheckoutCommand.CheckoutWorker(implRepo); tikhomirov@526: tikhomirov@526: HgManifest.Inspector insp = new HgManifest.Inspector() { tikhomirov@526: tikhomirov@526: public boolean next(Nodeid nid, Path fname, Flags flags) { tikhomirov@526: if (worker.next(nid, fname, flags)) { tikhomirov@526: dirstateBuilder.recordUncertain(fname); tikhomirov@526: return true; tikhomirov@526: } tikhomirov@526: return false; tikhomirov@526: } tikhomirov@526: tikhomirov@526: public boolean end(int manifestRevision) { tikhomirov@526: return false; tikhomirov@526: } tikhomirov@526: tikhomirov@526: public boolean begin(int mainfestRevision, Nodeid nid, int changelogRevision) { tikhomirov@526: return true; tikhomirov@526: } tikhomirov@526: }; tikhomirov@526: tikhomirov@526: for (Path file : files) { tikhomirov@526: File f = new File(repo.getWorkingDir(), file.toString()); tikhomirov@526: if (f.isFile()) { tikhomirov@526: if (keepOriginal) { tikhomirov@526: File copy = new File(f.getParentFile(), f.getName() + ".orig"); tikhomirov@526: if (copy.exists()) { tikhomirov@526: copy.delete(); tikhomirov@526: } tikhomirov@526: f.renameTo(copy); tikhomirov@526: } else { tikhomirov@526: f.delete(); tikhomirov@526: } tikhomirov@526: } tikhomirov@526: repo.getManifest().walkFileRevisions(file, insp, csetRevision); tikhomirov@526: worker.checkFailed(); tikhomirov@526: } tikhomirov@526: dirstateBuilder.serialize(); tikhomirov@526: } catch (HgRuntimeException ex) { tikhomirov@526: throw new HgLibraryFailureException(ex); tikhomirov@526: } tikhomirov@526: } tikhomirov@526: }