blob: 601c486a9578f930a25d2ed530e975017b043d40 [file] [log] [blame]
/*
* 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 com.intellij.openapi.vcs.changes.ui;
import com.intellij.history.LocalHistory;
import com.intellij.history.LocalHistoryAction;
import com.intellij.lifecycle.PeriodicalTasksCloser;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.progress.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.changes.*;
import com.intellij.openapi.vcs.rollback.DefaultRollbackEnvironment;
import com.intellij.openapi.vcs.rollback.RollbackEnvironment;
import com.intellij.openapi.vcs.update.RefreshVFsSynchronously;
import com.intellij.util.WaitForProgressToShow;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class RollbackWorker {
private final Project myProject;
private final String myOperationName;
private final List<VcsException> myExceptions;
public RollbackWorker(final Project project) {
this(project, DefaultRollbackEnvironment.ROLLBACK_OPERATION_NAME);
}
public RollbackWorker(final Project project, final String operationName) {
myProject = project;
myOperationName = operationName;
myExceptions = new ArrayList<VcsException>(0);
}
public void doRollback(final Collection<Change> changes,
final boolean deleteLocallyAddedFiles,
@Nullable final Runnable afterVcsRefreshInAwt,
@Nullable final String localHistoryActionName) {
final ChangeListManager changeListManager = ChangeListManager.getInstance(myProject);
final Runnable notifier = changeListManager.prepareForChangeDeletion(changes);
final Runnable afterRefresh = new Runnable() {
public void run() {
changeListManager.invokeAfterUpdate(new Runnable() {
public void run() {
notifier.run();
if (afterVcsRefreshInAwt != null) {
afterVcsRefreshInAwt.run();
}
}
}, InvokeAfterUpdateMode.SILENT, "Refresh change lists after update", ModalityState.current());
}
};
final Runnable rollbackAction = new MyRollbackRunnable(changes, deleteLocallyAddedFiles, afterRefresh, localHistoryActionName);
if (ApplicationManager.getApplication().isDispatchThread()) {
ProgressManager.getInstance()
.run(new Task.Backgroundable(myProject, myOperationName, true,
new PerformInBackgroundOption() {
public boolean shouldStartInBackground() {
return VcsConfiguration.getInstance(myProject).PERFORM_ROLLBACK_IN_BACKGROUND;
}
public void processSentToBackground() {
VcsConfiguration.getInstance(myProject).PERFORM_ROLLBACK_IN_BACKGROUND = true;
}
}) {
public void run(@NotNull ProgressIndicator indicator) {
rollbackAction.run();
}
});
}
else {
rollbackAction.run();
}
((ChangeListManagerImpl) changeListManager).showLocalChangesInvalidated();
}
private class MyRollbackRunnable implements Runnable {
private final Collection<Change> myChanges;
private final boolean myDeleteLocallyAddedFiles;
private final Runnable myAfterRefresh;
private final String myLocalHistoryActionName;
private ProgressIndicator myIndicator;
private MyRollbackRunnable(final Collection<Change> changes,
final boolean deleteLocallyAddedFiles,
final Runnable afterRefresh,
final String localHistoryActionName) {
myChanges = changes;
myDeleteLocallyAddedFiles = deleteLocallyAddedFiles;
myAfterRefresh = afterRefresh;
myLocalHistoryActionName = localHistoryActionName;
}
public void run() {
ChangesUtil.markInternalOperation(myChanges, true);
try {
doRun();
}
finally {
ChangesUtil.markInternalOperation(myChanges, false);
}
}
private void doRun() {
myIndicator = ProgressManager.getInstance().getProgressIndicator();
final List<Change> changesToRefresh = new ArrayList<Change>();
try {
ChangesUtil.processChangesByVcs(myProject, myChanges, new ChangesUtil.PerVcsProcessor<Change>() {
public void process(AbstractVcs vcs, List<Change> changes) {
final RollbackEnvironment environment = vcs.getRollbackEnvironment();
if (environment != null) {
changesToRefresh.addAll(changes);
if (myIndicator != null) {
myIndicator.setText(vcs.getDisplayName() + ": performing " + StringUtil.toLowerCase(myOperationName) + "...");
myIndicator.setIndeterminate(false);
myIndicator.checkCanceled();
}
environment.rollbackChanges(changes, myExceptions, new RollbackProgressModifier(changes.size(), myIndicator));
if (myIndicator != null) {
myIndicator.setText2("");
myIndicator.checkCanceled();
}
if (myExceptions.isEmpty() && myDeleteLocallyAddedFiles) {
deleteAddedFilesLocally(changes);
}
}
}
});
}
catch (ProcessCanceledException e) {
// still do refresh
}
if (myIndicator != null) {
myIndicator.startNonCancelableSection();
myIndicator.setIndeterminate(true);
myIndicator.setText2("");
myIndicator.setText(VcsBundle.message("progress.text.synchronizing.files"));
}
doRefresh(myProject, changesToRefresh);
AbstractVcsHelper.getInstance(myProject).showErrors(myExceptions, myOperationName);
}
private void doRefresh(final Project project, final List<Change> changesToRefresh) {
final LocalHistoryAction action = LocalHistory.getInstance().startAction(myOperationName);
final Runnable forAwtThread = new Runnable() {
public void run() {
action.finish();
LocalHistory.getInstance().putSystemLabel(myProject, (myLocalHistoryActionName == null) ?
myOperationName : myLocalHistoryActionName, -1);
final VcsDirtyScopeManager manager = PeriodicalTasksCloser.getInstance().safeGetComponent(project, VcsDirtyScopeManager.class);
for (Change change : changesToRefresh) {
final ContentRevision beforeRevision = change.getBeforeRevision();
final ContentRevision afterRevision = change.getAfterRevision();
if ((!change.isIsReplaced()) && beforeRevision != null && Comparing.equal(beforeRevision, afterRevision)) {
manager.fileDirty(beforeRevision.getFile());
}
else {
if (beforeRevision != null) {
final FilePath parent = beforeRevision.getFile().getParentPath();
if (parent != null) {
manager.dirDirtyRecursively(parent);
}
}
if (afterRevision != null) {
final FilePath parent = afterRevision.getFile().getParentPath();
if (parent != null) {
manager.dirDirtyRecursively(parent);
}
}
}
}
myAfterRefresh.run();
}
};
RefreshVFsSynchronously.updateChangesForRollback(changesToRefresh);
WaitForProgressToShow.runOrInvokeLaterAboveProgress(forAwtThread, null, project);
}
private void deleteAddedFilesLocally(final List<Change> changes) {
if (myIndicator != null) {
myIndicator.setText("Deleting added files locally...");
myIndicator.setFraction(0);
}
final int changesSize = changes.size();
for (int i = 0; i < changesSize; i++) {
final Change c = changes.get(i);
if (c.getType() == Change.Type.NEW) {
ContentRevision rev = c.getAfterRevision();
assert rev != null;
final File ioFile = rev.getFile().getIOFile();
if (myIndicator != null) {
myIndicator.setText2(ioFile.getAbsolutePath());
myIndicator.setFraction(((double) i) / changesSize);
}
FileUtil.delete(ioFile);
}
}
if (myIndicator != null) {
myIndicator.setText2("");
}
}
}
}