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@617: import org.tmatesoft.hg.internal.COWTransaction; tikhomirov@565: import org.tmatesoft.hg.internal.CsetParamKeeper; tikhomirov@526: import org.tmatesoft.hg.internal.DirstateBuilder; tikhomirov@526: import org.tmatesoft.hg.internal.DirstateReader; tikhomirov@526: import org.tmatesoft.hg.internal.Internals; tikhomirov@617: import org.tmatesoft.hg.internal.Transaction; tikhomirov@526: import org.tmatesoft.hg.repo.HgManifest; tikhomirov@565: import org.tmatesoft.hg.repo.HgManifest.Flags; tikhomirov@526: import org.tmatesoft.hg.repo.HgRepository; tikhomirov@617: import org.tmatesoft.hg.repo.HgRepositoryLock; tikhomirov@526: import org.tmatesoft.hg.repo.HgRuntimeException; tikhomirov@581: import org.tmatesoft.hg.util.CancelSupport; tikhomirov@526: import org.tmatesoft.hg.util.CancelledException; tikhomirov@526: import org.tmatesoft.hg.util.Path; tikhomirov@581: import org.tmatesoft.hg.util.ProgressSupport; tikhomirov@526: tikhomirov@526: /** tikhomirov@526: * Restore files to their checkout state, 'hg revert' counterpart. tikhomirov@526: * tikhomirov@613: * @since 1.1 tikhomirov@526: * @author Artem Tikhomirov tikhomirov@526: * @author TMate Software Ltd. tikhomirov@526: */ tikhomirov@526: public class HgRevertCommand extends HgAbstractCommand { tikhomirov@526: tikhomirov@526: private final HgRepository repo; tikhomirov@526: private final Set files = new LinkedHashSet(); tikhomirov@565: private CsetParamKeeper changesetToCheckout; tikhomirov@526: private boolean keepOriginal = true; tikhomirov@526: tikhomirov@526: public HgRevertCommand(HgRepository hgRepo) { tikhomirov@526: repo = hgRepo; tikhomirov@565: changesetToCheckout = new CsetParamKeeper(hgRepo); tikhomirov@565: changesetToCheckout.doSet(HgRepository.WORKING_COPY); // XXX WORKING_COPY_PARENT, in fact 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@565: changesetToCheckout.set(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@565: changesetToCheckout.set(revision); tikhomirov@565: return this; tikhomirov@526: } tikhomirov@526: tikhomirov@565: // TODO keepOriginal() to save .orig (with tests!) tikhomirov@526: tikhomirov@526: /** tikhomirov@526: * Perform the back out for the given files tikhomirov@526: * tikhomirov@581: * @throws HgIOException to indicate troubles updating files in working copy tikhomirov@581: * @throws HgException subclass thereof to indicate specific issue with the command arguments or repository state tikhomirov@581: * @throws CancelledException if execution of the command was cancelled tikhomirov@526: */ tikhomirov@526: public void execute() throws HgException, CancelledException { tikhomirov@617: final HgRepositoryLock wdLock = repo.getWorkingDirLock(); tikhomirov@617: wdLock.acquire(); tikhomirov@526: try { tikhomirov@581: final ProgressSupport progress = getProgressSupport(null); tikhomirov@581: final CancelSupport cancellation = getCancelSupport(null, true); tikhomirov@581: cancellation.checkCancelled(); tikhomirov@581: progress.start(files.size() + 2); tikhomirov@526: final int csetRevision; tikhomirov@565: if (changesetToCheckout.get() == HgRepository.WORKING_COPY) { tikhomirov@526: csetRevision = repo.getChangelog().getRevisionIndex(repo.getWorkingCopyParents().first()); tikhomirov@526: } else { tikhomirov@565: csetRevision = changesetToCheckout.get(); 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@581: progress.worked(1); tikhomirov@581: cancellation.checkCancelled(); tikhomirov@581: 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@581: progress.worked(1); tikhomirov@581: cancellation.checkCancelled(); tikhomirov@526: } tikhomirov@617: Transaction.Factory trFactory = new COWTransaction.Factory(); tikhomirov@617: Transaction tr = trFactory.create(repo); tikhomirov@617: try { tikhomirov@617: // TODO same code in HgAddRemoveCommand and similar in HgCommitCommand tikhomirov@617: dirstateBuilder.serialize(tr); tikhomirov@617: tr.commit(); tikhomirov@617: } catch (RuntimeException ex) { tikhomirov@617: tr.rollback(); tikhomirov@617: throw ex; tikhomirov@617: } catch (HgException ex) { tikhomirov@617: tr.rollback(); tikhomirov@617: throw ex; tikhomirov@617: } tikhomirov@581: progress.worked(1); tikhomirov@581: progress.done(); tikhomirov@526: } catch (HgRuntimeException ex) { tikhomirov@526: throw new HgLibraryFailureException(ex); tikhomirov@617: } finally { tikhomirov@617: wdLock.release(); tikhomirov@526: } tikhomirov@526: } tikhomirov@526: }