mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
VCS: support renames in show diff for annotation - for SVN only for now
This commit is contained in:
@@ -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<T extends CommittedChangeList, U exten
|
||||
*/
|
||||
int getUnlimitedCountValue();
|
||||
|
||||
/**
|
||||
* @return required list and path of the target file in that revision (changes when move/rename)
|
||||
*/
|
||||
@Nullable
|
||||
T getOneList(final VirtualFile file, final VcsRevisionNumber number) throws VcsException;
|
||||
Pair<T, FilePath> getOneList(final VirtualFile file, final VcsRevisionNumber number) throws VcsException;
|
||||
}
|
||||
|
||||
@@ -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<Change> changes = new LinkedList<Change>();
|
||||
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<CommittedChangeList, FilePath> 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<Change> changes) {
|
||||
private int findSelfInList(List<Change> 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))) {
|
||||
|
||||
+2
-1
@@ -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<CommittedChangeList, FilePath> getOneList(VirtualFile file, VcsRevisionNumber number) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
+6
-3
@@ -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<CvsChangeList, FilePath> 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<CvsChangeList, FilePath>(result[0], filePath);
|
||||
}
|
||||
|
||||
public List<CvsChangeList> getCommittedChanges(ChangeBrowserSettings settings, RepositoryLocation location, final int maxCount) throws VcsException {
|
||||
|
||||
@@ -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<CommittedChangeList, FilePath> 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<CommittedChangeList, FilePath>(result[0], filePath);
|
||||
}
|
||||
|
||||
public int getFormatVersion() {
|
||||
|
||||
@@ -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<CommittedChangeList, FilePath> 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<CommittedChangeList> 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<CommittedChangeList> list = getCommittedChanges(settings, getLocationFor(filePath), 1);
|
||||
if (list.size() == 1) {
|
||||
return list.get(0);
|
||||
return new Pair<CommittedChangeList, FilePath>(list.get(0), filePath);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
+81
-12
@@ -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<SvnChangeList, FilePath> 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<SvnChangeList, FilePath>(result[0], path);
|
||||
}
|
||||
}
|
||||
return result[0];
|
||||
if (result[0].getChanges().size() == 1) {
|
||||
final Collection<Change> changes = result[0].getChanges();
|
||||
final Change change = changes.iterator().next();
|
||||
final ContentRevision afterRevision = change.getAfterRevision();
|
||||
if (afterRevision != null) {
|
||||
return new Pair<SvnChangeList, FilePath>(result[0], afterRevision.getFile());
|
||||
} else {
|
||||
return new Pair<SvnChangeList, FilePath>(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<SvnChangeList, FilePath>(result[0], path);
|
||||
}
|
||||
return new Pair<SvnChangeList, FilePath>(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);
|
||||
|
||||
Reference in New Issue
Block a user