| /* |
| * Copyright 2000-2012 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 git4idea.util; |
| |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.progress.ProgressIndicator; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Clock; |
| import com.intellij.openapi.util.Computable; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.openapi.vcs.VcsException; |
| import com.intellij.openapi.vcs.VcsNotifier; |
| import com.intellij.openapi.vcs.history.VcsRevisionNumber; |
| import com.intellij.openapi.vcs.merge.MergeDialogCustomizer; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.util.text.DateFormatUtil; |
| import git4idea.GitPlatformFacade; |
| import git4idea.GitUtil; |
| import git4idea.commands.Git; |
| import git4idea.merge.GitConflictResolver; |
| import git4idea.repo.GitRepository; |
| import git4idea.stash.GitStashChangesSaver; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.Collection; |
| |
| import static com.intellij.openapi.util.text.StringUtil.join; |
| |
| /** |
| * Executes a Git operation on a number of repositories surrounding it by stash-unstash procedure. |
| * I.e. stashes changes, executes the operation and then unstashes it. |
| * |
| * @author Kirill Likhodedov |
| */ |
| public class GitPreservingProcess { |
| |
| private static final Logger LOG = Logger.getInstance(GitPreservingProcess.class); |
| |
| @NotNull private final Project myProject; |
| @NotNull private final GitPlatformFacade myFacade; |
| @NotNull private final Git myGit; |
| @NotNull private final Collection<GitRepository> myRepositories; |
| @NotNull private final String myOperationTitle; |
| @NotNull private final String myDestinationName; |
| @NotNull private final ProgressIndicator myProgressIndicator; |
| @NotNull private final Runnable myOperation; |
| @NotNull private final String myStashMessage; |
| |
| // suppressed, because only the load() method needs to be synchronized not to load twice |
| @SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized") private GitStashChangesSaver mySaver; |
| private boolean myLoaded; |
| private final Object LOAD_LOCK = new Object(); |
| |
| public GitPreservingProcess(@NotNull Project project, @NotNull GitPlatformFacade facade, @NotNull Git git, |
| @NotNull Collection<GitRepository> repositories, |
| @NotNull String operationTitle, @NotNull String destinationName, |
| @NotNull ProgressIndicator indicator, @NotNull Runnable operation) { |
| myProject = project; |
| myFacade = facade; |
| myGit = git; |
| myRepositories = repositories; |
| myOperationTitle = operationTitle; |
| myDestinationName = destinationName; |
| myProgressIndicator = indicator; |
| myOperation = operation; |
| myStashMessage = String.format("%s %s at %s", StringUtil.capitalize(myOperationTitle), myDestinationName, |
| DateFormatUtil.formatDateTime(Clock.getTime())); |
| } |
| |
| public void execute() { |
| execute(null); |
| } |
| |
| public void execute(@Nullable final Computable<Boolean> autoLoadDecision) { |
| Runnable operation = new Runnable() { |
| @Override |
| public void run() { |
| LOG.debug("starting"); |
| mySaver = configureSaver(); |
| boolean savedSuccessfully = save(); |
| LOG.debug("save result: " + savedSuccessfully); |
| if (savedSuccessfully) { |
| try { |
| LOG.debug("running operation"); |
| myOperation.run(); |
| LOG.debug("operation completed."); |
| } |
| finally { |
| if (autoLoadDecision == null || autoLoadDecision.compute()) { |
| LOG.debug("loading"); |
| load(); |
| } |
| } |
| } |
| LOG.debug("finished."); |
| } |
| }; |
| |
| new GitFreezingProcess(myProject, myFacade, myOperationTitle, operation).execute(); |
| } |
| |
| /** |
| * Configures the saver, actually notifications and texts in the GitConflictResolver used inside. |
| */ |
| private GitStashChangesSaver configureSaver() { |
| GitStashChangesSaver saver = new GitStashChangesSaver(myProject, myFacade, myGit, myProgressIndicator, myStashMessage); |
| MergeDialogCustomizer mergeDialogCustomizer = new MergeDialogCustomizer() { |
| @Override |
| public String getMultipleFileMergeDescription(Collection<VirtualFile> files) { |
| return String.format( |
| "<html>Uncommitted changes that were saved before %s have conflicts with files from <code>%s</code></html>", |
| myOperationTitle, myDestinationName); |
| } |
| |
| @Override |
| public String getLeftPanelTitle(VirtualFile file) { |
| return "Uncommitted changes from stash"; |
| } |
| |
| @Override |
| public String getRightPanelTitle(VirtualFile file, VcsRevisionNumber lastRevisionNumber) { |
| return String.format("<html>Changes from <b><code>%s</code></b></html>", myDestinationName); |
| } |
| }; |
| |
| GitConflictResolver.Params params = new GitConflictResolver.Params(). |
| setReverse(true). |
| setMergeDialogCustomizer(mergeDialogCustomizer). |
| setErrorNotificationTitle("Local changes were not restored"); |
| |
| saver.setConflictResolverParams(params); |
| return saver; |
| } |
| |
| /** |
| * Saves local changes. In case of error shows a notification and returns false. |
| */ |
| private boolean save() { |
| try { |
| mySaver.saveLocalChanges(GitUtil.getRootsFromRepositories(myRepositories)); |
| return true; |
| } catch (VcsException e) { |
| LOG.info("Couldn't save local changes", e); |
| VcsNotifier.getInstance(myProject).notifyError( |
| "Couldn't save uncommitted changes.", |
| String.format("Tried to save uncommitted changes in stash before %s, but failed with an error.<br/>%s", |
| myOperationTitle, join(e.getMessages()))); |
| return false; |
| } |
| } |
| |
| public void load() { |
| synchronized (LOAD_LOCK) { |
| if (myLoaded) { |
| return; |
| } |
| try { |
| mySaver.load(); |
| myLoaded = true; |
| } |
| catch (VcsException e) { |
| LOG.info("Couldn't load local changes", e); |
| VcsNotifier.getInstance(myProject).notifyError("Couldn't restore uncommitted changes", |
| String.format("Tried to unstash uncommitted changes, but failed with error.<br/>%s", |
| join(e.getMessages()))); |
| } |
| } |
| } |
| |
| } |