IDEADEV-30823 (Commit Changes button produces a window for each hit while Updating...
[fedora-idea.git] / plugins / svn4idea / src / org / jetbrains / idea / svn / integrate / SvnIntegrateChangesTask.java
blob6d980f69b2d192a6e94cb14cd9fc09d4c7890294
1 package org.jetbrains.idea.svn.integrate;
3 import com.intellij.openapi.application.ApplicationManager;
4 import com.intellij.openapi.progress.ProgressIndicator;
5 import com.intellij.openapi.progress.ProgressManager;
6 import com.intellij.openapi.progress.Task;
7 import com.intellij.openapi.ui.Messages;
8 import com.intellij.openapi.vcs.*;
9 import com.intellij.openapi.vcs.changes.ChangeListManager;
10 import com.intellij.openapi.vcs.changes.VcsDirtyScopeImpl;
11 import com.intellij.openapi.vcs.changes.ui.CommitChangeListDialog;
12 import com.intellij.openapi.vcs.ex.ProjectLevelVcsManagerEx;
13 import com.intellij.openapi.vcs.update.*;
14 import com.intellij.util.NotNullFunction;
15 import org.jetbrains.annotations.NotNull;
16 import org.jetbrains.annotations.Nullable;
17 import org.jetbrains.idea.svn.*;
18 import org.jetbrains.idea.svn.update.RefreshVFsSynchronously;
19 import org.jetbrains.idea.svn.update.SvnStatusWorker;
20 import org.jetbrains.idea.svn.update.UpdateEventHandler;
21 import org.tmatesoft.svn.core.SVNException;
22 import org.tmatesoft.svn.core.SVNURL;
23 import org.tmatesoft.svn.core.wc.SVNStatus;
24 import org.tmatesoft.svn.core.wc.SVNStatusType;
26 import java.io.File;
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.List;
31 public class SvnIntegrateChangesTask extends Task.Backgroundable {
32 private final ProjectLevelVcsManagerEx myProjectLevelVcsManager;
33 private final SvnVcs myVcs;
34 private final WorkingCopyInfo myInfo;
36 private final UpdatedFilesReverseSide myAccomulatedFiles;
37 private UpdatedFiles myRecentlyUpdatedFiles;
39 private final List<VcsException> myExceptions;
41 private final UpdateEventHandler myHandler;
42 private final Merger myMerger;
43 private final ResolveWorker myResolveWorker;
44 private FilePathImpl myMergeTarget;
46 public SvnIntegrateChangesTask(final SvnVcs vcs, final WorkingCopyInfo info, final MergerFactory mergerFactory,
47 final SVNURL currentBranchUrl) {
48 super(vcs.getProject(), SvnBundle.message("action.Subversion.integrate.changes.messages.title"), true,
49 VcsConfiguration.getInstance(vcs.getProject()).getUpdateOption());
51 myProjectLevelVcsManager = ProjectLevelVcsManagerEx.getInstanceEx(myProject);
52 myVcs = vcs;
54 myInfo = info;
56 myAccomulatedFiles = new UpdatedFilesReverseSide(UpdatedFiles.create());
57 myExceptions = new ArrayList<VcsException>();
59 myHandler = new IntegrateEventHandler(myVcs, ProgressManager.getInstance().getProgressIndicator());
60 myMerger = mergerFactory.createMerger(myVcs, new File(info.getLocalPath()), myHandler, currentBranchUrl);
61 myResolveWorker = new ResolveWorker(myInfo.isUnderProjectRoot(), myProject);
64 private void indicatorOnStart() {
65 final ProgressIndicator ind = ProgressManager.getInstance().getProgressIndicator();
66 if (ind != null) {
67 ind.setIndeterminate(true);
69 if (ind != null) {
70 ind.setText(SvnBundle.message("action.Subversion.integrate.changes.progress.integrating.text"));
74 public void run(@NotNull final ProgressIndicator indicator) {
75 BlockReloadingUtil.block();
76 myProjectLevelVcsManager.startBackgroundVcsOperation();
78 myRecentlyUpdatedFiles = UpdatedFiles.create();
79 myHandler.setUpdatedFiles(myRecentlyUpdatedFiles);
81 indicatorOnStart();
83 // try to do multiple under single progress
84 while (true) {
85 if (indicator.isCanceled()) {
86 createMessage(false, true, SvnBundle.message("action.Subversion.integrate.changes.message.canceled.text"));
87 return;
90 doMerge();
92 RefreshVFsSynchronously.updateAllChanged(myRecentlyUpdatedFiles);
93 indicator.setText(VcsBundle.message("progress.text.updating.done"));
95 if (myResolveWorker.needsInteraction(myRecentlyUpdatedFiles) || (! myMerger.hasNext()) ||
96 (! myExceptions.isEmpty()) || UpdatedFilesReverseSide.containErrors(myRecentlyUpdatedFiles)) {
97 break;
99 accomulate();
103 private void createMessage(final boolean getLatest, final boolean warning, final String firstString) {
104 final List<String> messages = new ArrayList<String>();
105 messages.add(firstString);
106 myMerger.getInfo(new NotNullFunction<String, Boolean>() {
107 @NotNull
108 public Boolean fun(final String s) {
109 messages.add(s);
110 return Boolean.TRUE;
112 }, getLatest);
113 final VcsException result = new VcsException(messages);
114 result.setIsWarning(warning);
115 myExceptions.add(result);
118 private void doMerge() {
119 try {
120 myMerger.mergeNext();
121 } catch (SVNException e) {
122 createMessage(true, false, e.getMessage());
126 public void onCancel() {
127 onSuccess();
130 public void onSuccess() {
131 ApplicationManager.getApplication().invokeLater(new Runnable() {
132 public void run() {
133 try {
134 if (myProject.isDisposed()) return;
135 afterExecution();
136 } finally {
137 BlockReloadingUtil.unblock();
138 myProjectLevelVcsManager.stopBackgroundVcsOperation();
145 private void accomulate() {
146 myAccomulatedFiles.accomulateFiles(myRecentlyUpdatedFiles, UpdatedFilesReverseSide.DuplicateLevel.DUPLICATE_ERRORS);
149 private void afterExecution() {
150 if (! myRecentlyUpdatedFiles.isEmpty()) {
151 myResolveWorker.execute(myRecentlyUpdatedFiles);
153 accomulate();
155 if ((! myMerger.hasNext()) || (! myExceptions.isEmpty()) || myAccomulatedFiles.containErrors()) {
156 initMergeTarget();
157 if (myAccomulatedFiles.isEmpty() && myExceptions.isEmpty() && (myMergeTarget == null)) {
158 Messages.showMessageDialog(SvnBundle.message("action.Subversion.integrate.changes.message.files.up.to.date.text"),
159 SvnBundle.message("action.Subversion.integrate.changes.messages.title"),
160 Messages.getInformationIcon());
161 } else {
162 finishActions();
164 myMerger.afterProcessing();
165 } else {
166 stepToNextChangeList();
170 private void finishActions() {
171 if ((! SvnConfiguration.getInstance(myProject).MERGE_DRY_RUN) && (myExceptions.isEmpty()) && (! myAccomulatedFiles.containErrors()) &&
172 ((! myAccomulatedFiles.isEmpty()) || (myMergeTarget != null))) {
173 if (myInfo.isUnderProjectRoot()) {
174 showLocalCommit();
175 } else {
176 showAlienCommit();
180 if ((! myInfo.isUnderProjectRoot()) || (myAccomulatedFiles.isEmpty())) {
181 prepareAndShowResults();
185 // no remote operations
186 private void prepareAndShowResults() {
187 if (! myExceptions.isEmpty()) {
188 AbstractVcsHelper.getInstance(myProject).showErrors(myExceptions, VcsBundle.message("message.title.vcs.update.errors"));
189 } else if (! myAccomulatedFiles.isEmpty()) {
190 if (SvnConfiguration.getInstance(myVcs.getProject()).UPDATE_RUN_STATUS) {
191 final UpdatedFiles statusFiles = doStatus();
192 myAccomulatedFiles.accomulateFiles(statusFiles, UpdatedFilesReverseSide.DuplicateLevel.DUPLICATE_ERRORS_LOCALS);
194 showUpdateTree();
198 private void showUpdateTree() {
199 RestoreUpdateTree restoreUpdateTree = RestoreUpdateTree.getInstance(myProject);
200 // action info is actually NOT used
201 restoreUpdateTree.registerUpdateInformation(myAccomulatedFiles.getUpdatedFiles(), ActionInfo.INTEGRATE);
202 myProjectLevelVcsManager.showUpdateProjectInfo(myAccomulatedFiles.getUpdatedFiles(),
203 SvnBundle.message("action.Subversion.integrate.changes.messages.title"), ActionInfo.INTEGRATE);
206 private UpdatedFiles doStatus() {
207 final UpdatedFiles statusFiles = UpdatedFiles.create();
208 final SvnStatusWorker statusWorker = new SvnStatusWorker(myVcs, new ArrayList<File>(), new File(myInfo.getLocalPath()),
209 statusFiles, false, myExceptions);
210 statusWorker.doStatus();
211 return statusFiles;
214 private void stepToNextChangeList() {
215 ApplicationManager.getApplication().invokeLater(new Runnable() {
216 public void run() {
217 ProgressManager.getInstance().run(SvnIntegrateChangesTask.this);
223 * folder that is going to keep merge info record should also be changed
225 @Nullable
226 private void initMergeTarget() {
227 final File mergeInfoHolder = myMerger.getMergeInfoHolder();
228 if (mergeInfoHolder != null) {
229 final SVNStatus svnStatus = SvnUtil.getStatus(myVcs, mergeInfoHolder);
230 if ((svnStatus != null) && (SVNStatusType.STATUS_MODIFIED.equals(svnStatus.getPropertiesStatus()))) {
231 myMergeTarget = FilePathImpl.create(mergeInfoHolder);
236 private void showLocalCommit() {
237 final Collection<FilePath> files = new ArrayList<FilePath>();
238 UpdateFilesHelper.iterateFileGroupFiles(myAccomulatedFiles.getUpdatedFiles(), new UpdateFilesHelper.Callback() {
239 public void onFile(final String filePath, final String groupId) {
240 final FilePath file = FilePathImpl.create(new File(filePath));
241 files.add(file);
244 if (myMergeTarget != null) {
245 files.add(myMergeTarget);
248 // for changes to be detected, we need switch to background change list manager update thread and back to dispatch thread
249 // so callback is used; ok to be called after VCS update markup closed: no remote operations
250 ChangeListManager.getInstance(myProject).invokeAfterUpdate(new Runnable() {
251 public void run() {
252 CommitChangeListDialog.commitPaths(myProject, files, null, null, myMerger.getComment());
253 prepareAndShowResults();
255 }, true, false, SvnBundle.message("action.Subversion.integrate.changes.messages.title"), true);
258 private void showAlienCommit() {
259 final VcsDirtyScopeImpl dirtyScope = new VcsDirtyScopeImpl(myVcs, myProject);
261 UpdateFilesHelper.iterateFileGroupFiles(myAccomulatedFiles.getUpdatedFiles(), new UpdateFilesHelper.Callback() {
262 public void onFile(final String filePath, final String groupId) {
263 final FilePath file = FilePathImpl.create(new File(filePath));
264 dirtyScope.addDirtyFile(file);
267 if (myMergeTarget != null) {
268 dirtyScope.addDirtyFile(myMergeTarget);
271 final SvnChangeProvider provider = new SvnChangeProvider(myVcs);
272 final GatheringChangelistBuilder clb = new GatheringChangelistBuilder();
273 try {
274 provider.getChanges(dirtyScope, clb, ProgressManager.getInstance().getProgressIndicator());
275 } catch (VcsException e) {
276 Messages.showErrorDialog(SvnBundle.message("action.Subversion.integrate.changes.error.unable.to.collect.changes.text",
277 e.getMessage()), SvnBundle.message("action.Subversion.integrate.changes.alien.commit.changelist.title"));
278 return;
281 if (! clb.getChanges().isEmpty()) {
282 CommitChangeListDialog.commitAlienChanges(myProject, clb.getChanges(), myVcs,
283 SvnBundle.message("action.Subversion.integrate.changes.alien.commit.changelist.title"), myMerger.getComment());