2 * Copyright 2000-2007 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package org
.jetbrains
.idea
.svn
.actions
;
19 import com
.intellij
.openapi
.actionSystem
.AnAction
;
20 import com
.intellij
.openapi
.actionSystem
.AnActionEvent
;
21 import com
.intellij
.openapi
.actionSystem
.PlatformDataKeys
;
22 import com
.intellij
.openapi
.application
.ApplicationManager
;
23 import com
.intellij
.openapi
.diagnostic
.Logger
;
24 import com
.intellij
.openapi
.diff
.DiffManager
;
25 import com
.intellij
.openapi
.diff
.FileContent
;
26 import com
.intellij
.openapi
.diff
.SimpleContent
;
27 import com
.intellij
.openapi
.diff
.SimpleDiffRequest
;
28 import com
.intellij
.openapi
.progress
.ProgressIndicator
;
29 import com
.intellij
.openapi
.progress
.ProgressManager
;
30 import com
.intellij
.openapi
.project
.Project
;
31 import com
.intellij
.openapi
.ui
.Messages
;
32 import com
.intellij
.openapi
.util
.Ref
;
33 import com
.intellij
.openapi
.util
.io
.FileUtil
;
34 import com
.intellij
.openapi
.vcs
.AbstractVcsHelper
;
35 import com
.intellij
.openapi
.vcs
.FileStatus
;
36 import com
.intellij
.openapi
.vcs
.FileStatusManager
;
37 import com
.intellij
.openapi
.vcs
.changes
.Change
;
38 import com
.intellij
.openapi
.vfs
.VirtualFile
;
39 import org
.jetbrains
.annotations
.Nullable
;
40 import org
.jetbrains
.idea
.svn
.*;
41 import org
.jetbrains
.idea
.svn
.status
.SvnDiffEditor
;
42 import org
.tmatesoft
.svn
.core
.*;
43 import org
.tmatesoft
.svn
.core
.internal
.util
.SVNPathUtil
;
44 import org
.tmatesoft
.svn
.core
.internal
.wc
.SVNCancellableEditor
;
45 import org
.tmatesoft
.svn
.core
.internal
.wc
.SVNErrorManager
;
46 import org
.tmatesoft
.svn
.core
.internal
.wc
.admin
.SVNAdminAreaInfo
;
47 import org
.tmatesoft
.svn
.core
.internal
.wc
.admin
.SVNEntry
;
48 import org
.tmatesoft
.svn
.core
.internal
.wc
.admin
.SVNReporter
;
49 import org
.tmatesoft
.svn
.core
.internal
.wc
.admin
.SVNWCAccess
;
50 import org
.tmatesoft
.svn
.core
.io
.SVNRepository
;
51 import org
.tmatesoft
.svn
.core
.wc
.SVNRevision
;
52 import org
.tmatesoft
.svn
.core
.wc
.SVNWCClient
;
53 import org
.tmatesoft
.svn
.util
.SVNLogType
;
54 import org
.tmatesoft
.svn
.util
.SVNDebugLog
;
56 import java
.io
.ByteArrayOutputStream
;
58 import java
.nio
.ByteBuffer
;
59 import java
.util
.ArrayList
;
60 import java
.util
.List
;
65 public class CompareWithBranchAction
extends AnAction
{
66 private static final Logger LOG
= Logger
.getInstance("#org.jetbrains.idea.svn.actions.CompareWithBranchAction");
68 public void actionPerformed(AnActionEvent e
) {
69 Project project
= e
.getData(PlatformDataKeys
.PROJECT
);
70 assert project
!= null;
71 final VirtualFile virtualFile
= e
.getData(PlatformDataKeys
.VIRTUAL_FILE
);
73 SelectBranchPopup
.show(project
, virtualFile
, new SelectBranchPopup
.BranchSelectedCallback() {
74 public void branchSelected(final Project project
, final SvnBranchConfiguration configuration
, final String url
, final long revision
) {
75 new CompareWithBranchOperation(project
, virtualFile
, configuration
).compareWithBranch(url
, revision
);
77 }, SvnBundle
.message("compare.with.branch.popup.title"));
81 public void update(final AnActionEvent e
) {
82 Project project
= e
.getData(PlatformDataKeys
.PROJECT
);
83 VirtualFile virtualFile
= e
.getData(PlatformDataKeys
.VIRTUAL_FILE
);
84 e
.getPresentation().setEnabled(isEnabled(project
, virtualFile
));
87 private static boolean isEnabled(final Project project
, final VirtualFile virtualFile
) {
88 if (project
== null || virtualFile
== null) {
91 final FileStatus fileStatus
= FileStatusManager
.getInstance(project
).getStatus(virtualFile
);
92 if (fileStatus
== FileStatus
.UNKNOWN
|| fileStatus
== FileStatus
.ADDED
) {
99 private class CompareWithBranchOperation
{
100 private final Project myProject
;
101 private final VirtualFile myVirtualFile
;
102 private final SvnBranchConfiguration myConfiguration
;
104 public CompareWithBranchOperation(final Project project
, final VirtualFile virtualFile
, final SvnBranchConfiguration config
) {
106 myVirtualFile
= virtualFile
;
107 myConfiguration
= config
;
110 public void compareWithBranch(final String baseUrl
, final long revision
) {
111 if (myVirtualFile
.isDirectory()) {
112 compareDirectoryWithBranch(baseUrl
, revision
);
115 compareFileWithBranch(baseUrl
, revision
);
118 final StringBuilder titleBuilder
= new StringBuilder();
120 public void compareDirectoryWithBranch(final String baseUrl
, final long revision
) {
121 final List
<Change
> changes
= new ArrayList
<Change
>();
122 ProgressManager
.getInstance().runProcessWithProgressSynchronously(new Runnable() {
125 final SvnVcs vcs
= SvnVcs
.getInstance(myProject
);
126 final SVNURL url
= getURLInBranch(vcs
, baseUrl
);
127 if (url
== null) return;
128 titleBuilder
.append(SvnBundle
.message("repository.browser.compare.title",
130 FileUtil
.toSystemDependentName(myVirtualFile
.getPresentableUrl())));
133 SVNWCAccess wcAccess
= vcs
.createWCAccess();
135 SVNAdminAreaInfo info
= wcAccess
.openAnchor(new File(myVirtualFile
.getPath()), false, SVNWCAccess
.INFINITE_DEPTH
);
136 File anchorPath
= info
.getAnchor().getRoot();
137 String target
= "".equals(info
.getTargetName()) ?
null : info
.getTargetName();
139 SVNEntry anchorEntry
= info
.getAnchor().getEntry("", false);
140 if (anchorEntry
== null) {
141 SVNErrorMessage err
=
142 SVNErrorMessage
.create(SVNErrorCode
.ENTRY_NOT_FOUND
, "''{0}'' is not under version control", anchorPath
);
143 SVNErrorManager
.error(err
, SVNLogType
.WC
);
145 else if (anchorEntry
.getURL() == null) {
146 SVNErrorMessage err
= SVNErrorMessage
.create(SVNErrorCode
.ENTRY_MISSING_URL
, "''{0}'' has no URL", anchorPath
);
147 SVNErrorManager
.error(err
, SVNLogType
.WC
);
150 SVNURL anchorURL
= anchorEntry
.getSVNURL();
151 SVNRepository repository
= vcs
.createRepository(anchorURL
.toString());
152 SVNReporter reporter
= new SVNReporter(info
, info
.getAnchor().getFile(info
.getTargetName()), false, true, SVNDepth
.INFINITY
,
153 false, false, true, SVNDebugLog
.getDefaultLog());
154 long rev
= repository
.getLatestRevision();
155 SvnDiffEditor diffEditor
= new SvnDiffEditor((target
== null) ? myVirtualFile
: myVirtualFile
.getParent(),
156 vcs
.createRepository((target
== null) ? url
.toString() : url
.removePathTail().toString()), rev
, true);
157 repository
.diff(url
, rev
, rev
, target
, true, true, false, reporter
,
158 SVNCancellableEditor
.newInstance(diffEditor
, new SvnProgressCanceller(), null));
159 changes
.addAll(diffEditor
.getChangesMap().values());
165 catch(SVNCancelException ex
) {
168 catch (SVNException ex
) {
169 reportException(ex
, baseUrl
);
172 }, SvnBundle
.message("progress.computing.difference"), true, myProject
);
173 if (!changes
.isEmpty()) {
174 AbstractVcsHelper
.getInstance(myProject
).showWhatDiffersBrowser(null, changes
, titleBuilder
.toString());
178 public void compareFileWithBranch(final String baseUrl
, final long revision
) {
179 final ByteArrayOutputStream baos
= new ByteArrayOutputStream();
180 final StringBuilder remoteTitleBuilder
= new StringBuilder();
181 final Ref
<Boolean
> success
= new Ref
<Boolean
>();
182 ProgressManager
.getInstance().runProcessWithProgressSynchronously(new Runnable() {
185 final ProgressIndicator indicator
= ProgressManager
.getInstance().getProgressIndicator();
186 if (indicator
!= null) {
187 indicator
.setIndeterminate(true);
189 final SvnVcs vcs
= SvnVcs
.getInstance(myProject
);
190 SVNURL svnurl
= getURLInBranch(vcs
, baseUrl
);
191 if (svnurl
== null) return;
192 remoteTitleBuilder
.append(svnurl
.toString());
193 SVNWCClient client
= vcs
.createWCClient();
194 client
.doGetFileContents(svnurl
, SVNRevision
.UNDEFINED
, SVNRevision
.HEAD
, true, baos
);
197 catch (SVNException ex
) {
198 reportException(ex
, baseUrl
);
201 }, SvnBundle
.message("compare.with.branch.progress.loading.content"), true, myProject
);
202 if (success
.isNull()) {
205 ByteBuffer contents
= ByteBuffer
.wrap(baos
.toByteArray());
206 SimpleDiffRequest req
= new SimpleDiffRequest(myProject
, SvnBundle
.message("compare.with.branch.diff.title"));
207 req
.setContents(new SimpleContent(myVirtualFile
.getCharset().decode(contents
).toString()),
208 new FileContent(myProject
, myVirtualFile
));
209 req
.setContentTitles(remoteTitleBuilder
.toString(), myVirtualFile
.getPresentableUrl());
210 DiffManager
.getInstance().getDiffTool().show(req
);
214 private SVNURL
getURLInBranch(final SvnVcs vcs
, final String baseUrl
) throws SVNException
{
215 final SvnFileUrlMapping urlMapping
= vcs
.getSvnFileUrlMapping();
216 final File file
= new File(myVirtualFile
.getPath());
217 final SVNURL fileUrl
= urlMapping
.getUrlForFile(file
);
218 if (fileUrl
== null) {
222 final String fileUrlString
= fileUrl
.toString();
223 final RootUrlInfo rootMixed
= urlMapping
.getWcRootForUrl(fileUrlString
);
224 if (rootMixed
== null) {
228 final SVNURL thisBranchForUrl
= SvnUtil
.getBranchForUrl(vcs
, rootMixed
.getVirtualFile(), fileUrlString
);
229 if (thisBranchForUrl
== null) {
233 final String relativePath
= SVNPathUtil
.getRelativePath(thisBranchForUrl
.toString(), fileUrlString
);
234 return SVNURL
.parseURIEncoded(SVNPathUtil
.append(baseUrl
, relativePath
));
237 private void reportException(final SVNException ex
, final String baseUrl
) {
238 final SVNErrorCode errorCode
= ex
.getErrorMessage().getErrorCode();
239 if (errorCode
.equals(SVNErrorCode
.RA_ILLEGAL_URL
) ||
240 errorCode
.equals(SVNErrorCode
.CLIENT_UNRELATED_RESOURCES
) ||
241 errorCode
.equals(SVNErrorCode
.RA_DAV_PATH_NOT_FOUND
) ||
242 errorCode
.equals(SVNErrorCode
.FS_NOT_FOUND
)) {
243 reportNotFound(baseUrl
);
246 ApplicationManager
.getApplication().invokeLater(new Runnable() {
248 Messages
.showMessageDialog(myProject
, ex
.getMessage(),
249 SvnBundle
.message("compare.with.branch.error.title"), Messages
.getErrorIcon());
256 private void reportNotFound(final String baseUrl
) {
257 ApplicationManager
.getApplication().invokeLater(new Runnable() {
259 Messages
.showMessageDialog(myProject
,
260 SvnBundle
.message("compare.with.branch.location.error", myVirtualFile
.getPresentableUrl(), baseUrl
),
261 SvnBundle
.message("compare.with.branch.error.title"), Messages
.getErrorIcon());