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
;
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
);
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();
67 ind
.setIndeterminate(true);
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
);
83 // try to do multiple under single progress
85 if (indicator
.isCanceled()) {
86 createMessage(false, true, SvnBundle
.message("action.Subversion.integrate.changes.message.canceled.text"));
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
)) {
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
>() {
108 public Boolean
fun(final String s
) {
113 final VcsException result
= new VcsException(messages
);
114 result
.setIsWarning(warning
);
115 myExceptions
.add(result
);
118 private void doMerge() {
120 myMerger
.mergeNext();
121 } catch (SVNException e
) {
122 createMessage(true, false, e
.getMessage());
126 public void onCancel() {
130 public void onSuccess() {
131 ApplicationManager
.getApplication().invokeLater(new Runnable() {
134 if (myProject
.isDisposed()) return;
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
);
155 if ((! myMerger
.hasNext()) || (! myExceptions
.isEmpty()) || myAccomulatedFiles
.containErrors()) {
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());
164 myMerger
.afterProcessing();
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()) {
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
);
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();
214 private void stepToNextChangeList() {
215 ApplicationManager
.getApplication().invokeLater(new Runnable() {
217 ProgressManager
.getInstance().run(SvnIntegrateChangesTask
.this);
223 * folder that is going to keep merge info record should also be changed
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
));
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() {
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();
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"));
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());