diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/CommittedChangesProvider.java b/platform/vcs-api/src/com/intellij/openapi/vcs/CommittedChangesProvider.java index 9aa5d7f829c8..4a035ad82c81 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/CommittedChangesProvider.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/CommittedChangesProvider.java @@ -16,6 +16,7 @@ package com.intellij.openapi.vcs; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.vcs.changes.committed.DecoratorManager; import com.intellij.openapi.vcs.changes.committed.VcsCommittedListsZipper; import com.intellij.openapi.vcs.changes.committed.VcsCommittedViewAuxiliary; @@ -58,6 +59,9 @@ public interface CommittedChangesProvider getOneList(final VirtualFile file, final VcsRevisionNumber number) throws VcsException; } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java index c7cfd812ee67..1b4b3afb7d75 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java @@ -38,6 +38,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; @@ -526,13 +527,16 @@ public class AnnotateToggleAction extends ToggleAction implements DumbAware { if (revisionNumber != null) { final VcsException[] exc = new VcsException[1]; final List changes = new LinkedList(); + final FilePath[] targetPath = new FilePath[1]; ProgressManager.getInstance().run(new Task.Backgroundable(myVcs.getProject(), "Loading revision " + revisionNumber.asString() + " contents", true, BackgroundFromStartOption.getInstance()) { @Override public void run(@NotNull ProgressIndicator indicator) { final CommittedChangesProvider provider = myVcs.getCommittedChangesProvider(); try { - final CommittedChangeList cl = provider.getOneList(myFile, revisionNumber); + final Pair pair = provider.getOneList(myFile, revisionNumber); + targetPath[0] = pair.getSecond() == null ? new FilePathImpl(myFile) : pair.getSecond(); + final CommittedChangeList cl = pair.getFirst(); if (cl == null) { ChangesViewBalloonProblemNotifier.showMe(myVcs.getProject(), "Can not load data for show diff", MessageType.ERROR); return; @@ -550,7 +554,7 @@ public class AnnotateToggleAction extends ToggleAction implements DumbAware { if (exc[0] != null) { ChangesViewBalloonProblemNotifier.showMe(myVcs.getProject(), "Can not show diff: " + exc[0].getMessage(), MessageType.ERROR); } else if (! changes.isEmpty()) { - int idx = findSelfInList(changes); + int idx = findSelfInList(changes, targetPath[0]); final ShowDiffUIContext context = new ShowDiffUIContext(true); context.setDiffNavigationContext(createDiffNavigationContext(actualNumber)); ShowDiffAction.showDiffForChange(changes.toArray(new Change[changes.size()]), idx, myVcs.getProject(), context); @@ -560,9 +564,9 @@ public class AnnotateToggleAction extends ToggleAction implements DumbAware { } } - private int findSelfInList(List changes) { + private int findSelfInList(List changes, final FilePath filePath) { int idx = -1; - final File ioFile = new File(myFile.getPath()); + final File ioFile = filePath.getIOFile(); for (int i = 0; i < changes.size(); i++) { final Change change = changes.get(i); if ((change.getAfterRevision() != null) && (change.getAfterRevision().getFile().getIOFile().equals(ioFile))) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/CompositeCommittedChangesProvider.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/CompositeCommittedChangesProvider.java index 8b4c287f54f9..e5ef4df4c142 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/CompositeCommittedChangesProvider.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/CompositeCommittedChangesProvider.java @@ -18,6 +18,7 @@ package com.intellij.openapi.vcs.changes.committed; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings; @@ -154,7 +155,7 @@ public class CompositeCommittedChangesProvider implements CommittedChangesProvid } @Override - public CommittedChangeList getOneList(VirtualFile file, VcsRevisionNumber number) { + public Pair getOneList(VirtualFile file, VcsRevisionNumber number) { throw new UnsupportedOperationException(); } diff --git a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/changeBrowser/CvsCommittedChangesProvider.java b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/changeBrowser/CvsCommittedChangesProvider.java index 17ce14d514ef..a760cc3ec588 100644 --- a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/changeBrowser/CvsCommittedChangesProvider.java +++ b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/changeBrowser/CvsCommittedChangesProvider.java @@ -30,6 +30,7 @@ import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.changes.ChangesUtil; import com.intellij.openapi.vcs.changes.committed.*; @@ -132,8 +133,10 @@ public class CvsCommittedChangesProvider implements CachingCommittedChangesProvi @Nullable @Override - public CvsChangeList getOneList(VirtualFile file, final VcsRevisionNumber number) throws VcsException { - CvsRepositoryLocation cvsLocation = (CvsRepositoryLocation) getLocationFor(new FilePathImpl(file)); + public Pair getOneList(VirtualFile file, final VcsRevisionNumber number) throws VcsException { + // todo implement in proper way + final FilePathImpl filePath = new FilePathImpl(file); + CvsRepositoryLocation cvsLocation = (CvsRepositoryLocation) getLocationFor(filePath); final String module = cvsLocation.getModuleName(); final CvsEnvironment connectionSettings = cvsLocation.getEnvironment(); if (connectionSettings.isOffline()) { @@ -162,7 +165,7 @@ public class CvsCommittedChangesProvider implements CachingCommittedChangesProvi else if (! executionResult.hasNoErrors()) { throw executionResult.composeError(); } - return result[0]; + return new Pair(result[0], filePath); } public List getCommittedChanges(ChangeBrowserSettings settings, RepositoryLocation location, final int maxCount) throws VcsException { diff --git a/plugins/git4idea/src/git4idea/changes/GitCommittedChangeListProvider.java b/plugins/git4idea/src/git4idea/changes/GitCommittedChangeListProvider.java index 0b15782eb351..3faed6261dc7 100644 --- a/plugins/git4idea/src/git4idea/changes/GitCommittedChangeListProvider.java +++ b/plugins/git4idea/src/git4idea/changes/GitCommittedChangeListProvider.java @@ -17,6 +17,7 @@ package git4idea.changes; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.changes.committed.DecoratorManager; @@ -225,8 +226,10 @@ public class GitCommittedChangeListProvider implements CommittedChangesProvider< } @Override - public CommittedChangeList getOneList(VirtualFile file, final VcsRevisionNumber number) throws VcsException { - final GitRepositoryLocation l = (GitRepositoryLocation) getLocationFor(new FilePathImpl(file)); + public Pair getOneList(VirtualFile file, final VcsRevisionNumber number) throws VcsException { + // todo implement in proper way + final FilePathImpl filePath = new FilePathImpl(file); + final GitRepositoryLocation l = (GitRepositoryLocation) getLocationFor(filePath); VirtualFile root = LocalFileSystem.getInstance().findFileByIoFile(l.getRoot()); if (root == null) { throw new VcsException("The repository does not exists anymore: " + l.getRoot()); @@ -244,7 +247,7 @@ public class GitCommittedChangeListProvider implements CommittedChangesProvider< result[0] = committedChangeList; } }, false); - return result[0]; + return new Pair(result[0], filePath); } public int getFormatVersion() { diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgCachingCommitedChangesProvider.java b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgCachingCommitedChangesProvider.java index 156acceb9274..2356f8afd93f 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgCachingCommitedChangesProvider.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgCachingCommitedChangesProvider.java @@ -13,6 +13,7 @@ package org.zmlx.hg4idea.provider; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.actions.VcsContextFactory; import com.intellij.openapi.vcs.changes.Change; @@ -271,16 +272,17 @@ public class HgCachingCommitedChangesProvider } @Override - public CommittedChangeList getOneList(VirtualFile file, VcsRevisionNumber number) throws VcsException { + public Pair getOneList(VirtualFile file, VcsRevisionNumber number) throws VcsException { final ChangeBrowserSettings settings = createDefaultSettings(); settings.USE_CHANGE_AFTER_FILTER = true; settings.USE_CHANGE_BEFORE_FILTER = true; settings.CHANGE_AFTER = number.asString(); settings.CHANGE_BEFORE = number.asString(); - final List list = getCommittedChanges(settings, getLocationFor( - VcsContextFactory.SERVICE.getInstance().createFilePathOn(file)), 1); + // todo implement in proper way + final FilePath filePath = VcsContextFactory.SERVICE.getInstance().createFilePathOn(file); + final List list = getCommittedChanges(settings, getLocationFor(filePath), 1); if (list.size() == 1) { - return list.get(0); + return new Pair(list.get(0), filePath); } return null; } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedChangesProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedChangesProvider.java index 84f9d8b7530e..71d287c954fa 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedChangesProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedChangesProvider.java @@ -26,6 +26,8 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vcs.*; +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.ContentRevision; import com.intellij.openapi.vcs.changes.committed.*; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings; @@ -39,12 +41,10 @@ import com.intellij.util.ThrowableConsumer; import com.intellij.util.containers.MultiMap; import com.intellij.util.messages.MessageBusConnection; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn.SvnAuthenticationNotifier; -import org.jetbrains.idea.svn.SvnBundle; -import org.jetbrains.idea.svn.SvnUtil; -import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.*; import org.jetbrains.idea.svn.actions.ConfigureBranchesAction; import org.tmatesoft.svn.core.*; +import org.tmatesoft.svn.core.internal.util.SVNPathUtil; import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.wc.SVNInfo; import org.tmatesoft.svn.core.wc.SVNLogClient; @@ -53,6 +53,7 @@ import org.tmatesoft.svn.core.wc.SVNWCClient; import java.io.DataInput; import java.io.DataOutput; +import java.io.File; import java.io.IOException; import java.util.*; @@ -491,7 +492,7 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi } @Override - public SvnChangeList getOneList(final VirtualFile file, VcsRevisionNumber number) throws VcsException { + public Pair getOneList(final VirtualFile file, VcsRevisionNumber number) throws VcsException { final VirtualFile root = ProjectLevelVcsManager.getInstance(myProject).getVcsRootFor(file); final SvnRepositoryLocation svnRootLocation = (SvnRepositoryLocation)getLocationFor(new FilePathImpl(root)); if (svnRootLocation == null) return null; @@ -508,13 +509,15 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi final SVNRevision revisionBefore; final SVNURL repositoryUrl; final SVNURL svnurl; + final SVNInfo targetInfo; try { logger = myVcs.createLogClient(); revisionBefore = SVNRevision.create(revision); svnurl = SVNURL.parseURIEncoded(url); final SVNWCClient client = myVcs.createWCClient(); - SVNInfo info = client.doInfo(svnurl, SVNRevision.UNDEFINED, SVNRevision.HEAD); + final SVNInfo info = client.doInfo(svnurl, SVNRevision.UNDEFINED, SVNRevision.HEAD); + targetInfo = client.doInfo(new File(file.getPath()), SVNRevision.WORKING); if (info == null) { throw new VcsException("Can not get repository URL"); } @@ -528,10 +531,71 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi if (result[0] == null) { tryByRoot(result, logger, revisionBefore, repositoryUrl); if (result[0] == null) { - tryStepByStep(svnRootLocation, result, logger, revisionBefore, repositoryUrl, svnurl); + FilePath path = tryStepByStep(svnRootLocation, result, logger, revisionBefore, targetInfo, svnurl); + path = path == null ? new FilePathImpl(file) : path; + // and pass & take rename context there + return new Pair(result[0], path); } } - return result[0]; + if (result[0].getChanges().size() == 1) { + final Collection changes = result[0].getChanges(); + final Change change = changes.iterator().next(); + final ContentRevision afterRevision = change.getAfterRevision(); + if (afterRevision != null) { + return new Pair(result[0], afterRevision.getFile()); + } else { + return new Pair(result[0], new FilePathImpl(file)); + } + } + String relativePath = SVNPathUtil.getRelativePath(targetInfo.getRepositoryRootURL().toString(), targetInfo.getURL().toString()); + relativePath = relativePath.startsWith("/") ? relativePath : "/" + relativePath; + final Change targetChange = result[0].getByPath(relativePath); + if (targetChange == null) { + FilePath path = tryStepByStep(svnRootLocation, result, logger, revisionBefore, targetInfo, svnurl); + path = path == null ? new FilePathImpl(file) : path; + // and pass & take rename context there + return new Pair(result[0], path); + } + return new Pair(result[0], new FilePathImpl(file)); + } + + private static class RenameContext { + private String myCurrentPath; + private String myRepositoryRoot; + private boolean myHadChanged; + + private RenameContext(final SVNInfo info) { + myRepositoryRoot = info.getRepositoryRootURL().toString(); + myCurrentPath = SVNPathUtil.getRelativePath(myRepositoryRoot, info.getURL().toString()); + myCurrentPath = myCurrentPath.startsWith("/") ? myCurrentPath : ("/" + myCurrentPath); + } + + public void accept(final SVNLogEntry entry) { + final Map changedPaths = entry.getChangedPaths(); + + for (Object o : changedPaths.values()) { + final SVNLogEntryPath entryPath = (SVNLogEntryPath) o; + if ('A' == entryPath.getType()) { + if (myCurrentPath.equals(entryPath.getPath())) { + myHadChanged = true; + myCurrentPath = entryPath.getCopyPath(); + } + } + } + } + + @Nullable + public FilePath getFilePath(final SvnVcs vcs) { + if (! myHadChanged) return null; + final SvnFileUrlMapping svnFileUrlMapping = vcs.getSvnFileUrlMapping(); + final String absolutePath = SVNPathUtil.append(myRepositoryRoot, myCurrentPath); + final String localPath = svnFileUrlMapping.getLocalPath(absolutePath); + if (localPath == null) { + LOG.info("Cannot find local path for url: " + absolutePath); + return null; + } + return new FilePathImpl(new File(localPath), false); + } } private void tryByRoot(SvnChangeList[] result, SVNLogClient logger, SVNRevision revisionBefore, SVNURL repositoryUrl) throws VcsException { @@ -540,13 +604,16 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi tryExactHit(new SvnRepositoryLocation(repositoryUrl.toString()), result, logger, revisionBefore, repositoryUrl, repositoryUrl); } - private void tryStepByStep(final SvnRepositoryLocation svnRepositoryLocation, + // return changed path, if any + private FilePath tryStepByStep(final SvnRepositoryLocation svnRepositoryLocation, final SvnChangeList[] result, SVNLogClient logger, - final SVNRevision revisionBefore, final SVNURL repositoryUrl, SVNURL svnurl) throws VcsException { + final SVNRevision revisionBefore, final SVNInfo info, SVNURL svnurl) throws VcsException { + final String repositoryRoot = info.getRepositoryRootURL().toString(); try { + final RenameContext renameContext = new RenameContext(info); logger.doLog(svnurl, null, SVNRevision.UNDEFINED, SVNRevision.HEAD, revisionBefore, - false, true, true, 0, null, + false, true, false, 0, null, new ISVNLogEntryHandler() { public void handleLogEntry(SVNLogEntry logEntry) { if (myProject.isDisposed()) throw new ProcessCanceledException(); @@ -554,11 +621,13 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi // do not add lists without info - this situation is possible for lists where there are paths that user has no rights to observe return; } + renameContext.accept(logEntry); if (logEntry.getRevision() == revisionBefore.getNumber()) { - result[0] = new SvnChangeList(myVcs, svnRepositoryLocation, logEntry, repositoryUrl.toString()); + result[0] = new SvnChangeList(myVcs, svnRepositoryLocation, logEntry, repositoryRoot); } } }); + return renameContext.getFilePath(myVcs); } catch (SVNException e) { throw new VcsException(e);