/* * Copyright 2000-2009 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.community.intellij.plugins.communitycase.rollback; import com.intellij.lifecycle.PeriodicalTasksCloser; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.FileStatus; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ContentRevision; import com.intellij.openapi.vcs.rollback.RollbackProgressListener; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import org.community.intellij.plugins.communitycase.Util; import org.community.intellij.plugins.communitycase.commands.Command; import org.community.intellij.plugins.communitycase.commands.FileUtils; import org.community.intellij.plugins.communitycase.commands.SimpleHandler; import org.community.intellij.plugins.communitycase.config.VcsSettings; import org.community.intellij.plugins.communitycase.i18n.Bundle; import org.jetbrains.annotations.NotNull; import java.io.File; import java.util.*; /** * rollback/revert environment */ public class RollbackEnvironment implements com.intellij.openapi.vcs.rollback.RollbackEnvironment { /** * The project */ private final Project myProject; /** * A constructor * * @param project the context project */ public RollbackEnvironment(@NotNull Project project) { myProject = project; } /** * {@inheritDoc} */ @NotNull public String getRollbackOperationName() { return Bundle.getString("revert.action.name"); } /** * {@inheritDoc} */ public void rollbackModifiedWithoutCheckout(@NotNull List<VirtualFile> files, final List<VcsException> exceptions, final RollbackProgressListener listener) { throw new UnsupportedOperationException("Explicit file checkout is not supported by ."); } /** * {@inheritDoc} */ public void rollbackMissingFileDeletion(@NotNull List<FilePath> files, final List<VcsException> exceptions, final RollbackProgressListener listener) { throw new UnsupportedOperationException("Missing file delete is not reported by ."); } /** * {@inheritDoc} */ public void rollbackIfUnchanged(@NotNull VirtualFile file) { // do nothing } /** * {@inheritDoc} */ public void rollbackChanges(@NotNull List<Change> changes, final List<VcsException> exceptions, @NotNull final RollbackProgressListener listener) { HashMap<VirtualFile, List<FilePath>> toUndoHijack = new HashMap<VirtualFile, List<FilePath>>(); HashMap<VirtualFile, List<FilePath>> toUndoCheckout = new HashMap<VirtualFile, List<FilePath>>(); List<FilePath> toDelete = new ArrayList<FilePath>(); listener.determinate(); // collect changes to revert for (Change c : changes) { switch (c.getType()) { //todo wc handle new, moved, deleted case NEW: //undo dir checkout //rmname file //delete file break; case MOVED: //DON'T undo file checkout //move file back //undo dir checkout registerFile(toUndoCheckout, c.getBeforeRevision().getFile(), exceptions); toDelete.add(c.getAfterRevision().getFile()); break; case MODIFICATION: //undo file checkout if(c.getFileStatus()== FileStatus.HIJACKED) registerFile(toUndoHijack,c.getBeforeRevision().getFile(),exceptions); else registerFile(toUndoCheckout,c.getBeforeRevision().getFile(),exceptions); break; case DELETED: //undo dir checkout //undo file checkout //update file registerFile(toUndoCheckout, c.getBeforeRevision().getFile(), exceptions); break; } } /* // delete files for (FilePath file : toDelete) { listener.accept(file); try { final File ioFile = file.getIOFile(); if (ioFile.exists()) { if (!ioFile.delete()) { //noinspection ThrowableInstanceNeverThrown exceptions.add(new VcsException("Unable to delete file: " + file)); } } } catch (Exception e) { //noinspection ThrowableInstanceNeverThrown exceptions.add(new VcsException("Unable to delete file: " + file, e)); } } */ // revert files from HEAD for (Map.Entry<VirtualFile, List<FilePath>> entry : toUndoCheckout.entrySet()) { listener.accept(entry.getValue()); try { undoCheckout(entry.getKey(), entry.getValue()); } catch (VcsException e) { exceptions.add(e); } } // revert files from HEAD for (Map.Entry<VirtualFile, List<FilePath>> entry : toUndoHijack.entrySet()) { listener.accept(entry.getValue()); try { undoHijack(entry.getKey(), entry.getValue()); } catch (VcsException e) { exceptions.add(e); } } LocalFileSystem lfs = LocalFileSystem.getInstance(); HashSet<File> filesToRefresh = new HashSet<File>(); for (Change c : changes) { ContentRevision before = c.getBeforeRevision(); if (before != null) { filesToRefresh.add(new File(before.getFile().getPath())); } ContentRevision after = c.getAfterRevision(); if (after != null) { filesToRefresh.add(new File(after.getFile().getPath())); } } lfs.refreshIoFiles(filesToRefresh); } /** * Reverts the list of files we are passed. * * @param root the VCS root * @param files The array of files for which to undo checkout. * @throws VcsException Id it breaks. */ private void undoHijack(VirtualFile root, List<FilePath> files) throws VcsException { for (List<String> paths : FileUtils.chunkPaths(root, files)) { SimpleHandler handler = new SimpleHandler(myProject, root, Command.CHECKOUT); handler.setRemote(true); handler.addParameters("-unr");//unreserved handler.addParameters("-nc");//no comment handler.addParameters("-use"); //use hijacked file for checkout handler.endOptions(); handler.addParameters(paths); handler.run(); } for (List<String> paths : FileUtils.chunkPaths(root, files)) { SimpleHandler handler = new SimpleHandler(myProject, root, Command.UNDO_CHECKOUT); handler.setRemote(true); VcsSettings settings=VcsSettings.getInstance(myProject); if(settings!=null && !settings.isPreserveKeepFiles()) handler.addParameters("-rm"); else handler.addParameters("-kee"); handler.endOptions(); handler.addParameters(paths); handler.run(); } } /** * Reverts the list of files we are passed. * * @param root the VCS root * @param files The array of files for which to undo checkout. * @throws VcsException Id it breaks. */ public void undoCheckout(final VirtualFile root, final List<FilePath> files) throws VcsException { for (List<String> paths : FileUtils.chunkPaths(root, files)) { SimpleHandler handler = new SimpleHandler(myProject, root, Command.UNDO_CHECKOUT); handler.setRemote(true); VcsSettings settings=VcsSettings.getInstance(myProject); if(settings!=null && !settings.isPreserveKeepFiles()) handler.addParameters("-rm"); else handler.addParameters("-kee"); handler.endOptions(); handler.addParameters(paths); handler.run(); } } /** * Register file in the map under appropriate root * * @param files a map to use * @param file a file to register * @param exceptions the list of exceptions to update */ private static void registerFile(Map<VirtualFile, List<FilePath>> files, FilePath file, List<VcsException> exceptions) { final VirtualFile root; try { root = Util.getRoot(file); } catch (VcsException e) { exceptions.add(e); return; } List<FilePath> paths = files.get(root); if (paths == null) { paths = new ArrayList<FilePath>(); files.put(root, paths); } paths.add(file); } /** * Get instance of the service * * @param project a context project * @return a project-specific instance of the service */ public static RollbackEnvironment getInstance(final Project project) { return PeriodicalTasksCloser.getInstance().safeGetService(project, RollbackEnvironment.class); } }