IDEA-60323 Support editing subversion commit messages

from Repository tab and from Browse Changes results right after that (server is not accessed, just files rewritten)

also corrected loading of incoming changes on project start
This commit is contained in:
irengrig
2012-10-24 20:02:05 +04:00
parent e55f96812a
commit 4234c1c7b4
22 changed files with 354 additions and 77 deletions
@@ -24,6 +24,7 @@ import org.jetbrains.annotations.NotNull;
public class ProgressManagerQueue extends AbstractTaskQueue<Runnable> {
private final ProgressManager myProgressManager;
private final Task.Backgroundable myTask;
private volatile boolean myIsStarted;
public ProgressManagerQueue(final Project project, final String title) {
myProgressManager = ProgressManager.getInstance();
@@ -34,7 +35,13 @@ public class ProgressManagerQueue extends AbstractTaskQueue<Runnable> {
};
}
public void start() {
myIsStarted = true;
runMe();
}
protected void runMe() {
if (! myIsStarted) return;
final Application app = ApplicationManager.getApplication();
if (app.isDispatchThread()) {
if (myTask.myProject != null && myTask.myProject.isDisposed()) return;
@@ -60,4 +60,6 @@ public interface VcsDataKeys {
DataKey<Object> LABEL_AFTER = DataKey.create("LABEL_AFTER");
DataKey<String> PRESET_COMMIT_MESSAGE = DataKey.create("PRESET_COMMIT_MESSAGE");
DataKey<CommitMessageI> COMMIT_MESSAGE_CONTROL = DataKey.create("COMMIT_MESSAGE_CONTROL");
DataKey<Runnable> REMOTE_HISTORY_CHANGED_LISTENER = DataKey.create("REMOTE_HISTORY_CHANGED_LISTENER");
DataKey<RepositoryLocation> REMOTE_HISTORY_LOCATION = DataKey.create("REMOTE_HISTORY_LOCATION");
}
@@ -45,4 +45,6 @@ public interface CommittedChangeList extends ChangeList {
* @return true if this change list can be modified, for example, by reverting some of the changes.
*/
boolean isModifiable();
void setDescription(final String newMessage);
}
@@ -34,7 +34,7 @@ public class CommittedChangeListImpl implements CommittedChangeList {
private final String myCommitterName;
private final Date myCommitDate;
private final String myName;
private final String myComment;
private String myComment;
private final long myNumber;
protected ArrayList<Change> myChanges;
@@ -73,6 +73,11 @@ public class CommittedChangeListImpl implements CommittedChangeList {
return true;
}
@Override
public void setDescription(String newMessage) {
myComment = newMessage;
}
public static Collection<Change> getChangesWithMovedTreesImpl(final CommittedChangeList list) {
return list.getChanges();
}
@@ -148,4 +148,14 @@ public class CachesHolder {
throw new RuntimeException(e);
}
}
public ChangesCacheFile haveCache(RepositoryLocation location) {
String key = location.getKey();
if (myCacheFiles.containsKey(key)) return myCacheFiles.get(key);
key = key.endsWith("/") ? key : (key + "/");
for (String s : myCacheFiles.keySet()) {
if (key.startsWith(s) || s.startsWith(key)) return myCacheFiles.get(s);
}
return null;
}
}
@@ -56,21 +56,18 @@ public class ChangesCacheFile {
private final RepositoryLocation myLocation;
private Date myFirstCachedDate;
private Date myLastCachedDate;
private long myFirstCachedChangelist = Long.MAX_VALUE;
private long myLastCachedChangelist = -1;
private int myIncomingCount = 0;
private boolean myHaveCompleteHistory = false;
private boolean myHeaderLoaded = false;
private long myFirstCachedChangelist;
private long myLastCachedChangelist;
private int myIncomingCount;
private boolean myHaveCompleteHistory;
private boolean myHeaderLoaded;
@NonNls private static final String INDEX_EXTENSION = ".index";
private static final int INDEX_ENTRY_SIZE = 3*8+2;
private static final int HEADER_SIZE = 46;
public ChangesCacheFile(Project project, File path, AbstractVcs vcs, VirtualFile root, RepositoryLocation location) {
final Calendar date = Calendar.getInstance();
date.set(2020, Calendar.FEBRUARY, 2);
myFirstCachedDate = date.getTime();
date.set(1970, Calendar.FEBRUARY, 2);
myLastCachedDate = date.getTime();
reset();
myProject = project;
myPath = path;
myIndexPath = new File(myPath.toString() + INDEX_EXTENSION);
@@ -81,6 +78,19 @@ public class ChangesCacheFile {
myLocation = location;
}
private void reset() {
final Calendar date = Calendar.getInstance();
date.set(2020, Calendar.FEBRUARY, 2);
myFirstCachedDate = date.getTime();
date.set(1970, Calendar.FEBRUARY, 2);
myLastCachedDate = date.getTime();
myIncomingCount = 0;
myLastCachedChangelist = -1;
myFirstCachedChangelist = Long.MAX_VALUE;
myHaveCompleteHistory = false;
myHeaderLoaded = false;
}
public RepositoryLocation getLocation() {
return myLocation;
}
@@ -113,9 +123,27 @@ public class ChangesCacheFile {
public void delete() {
FileUtil.delete(myPath);
FileUtil.delete(myIndexPath);
try {
closeStreams();
}
catch (IOException e) {
//
}
}
public List<CommittedChangeList> writeChanges(final List<CommittedChangeList> changes) throws IOException {
// the list and index are sorted in direct chronological order
Collections.sort(changes, new Comparator<CommittedChangeList>() {
public int compare(final CommittedChangeList o1, final CommittedChangeList o2) {
return Comparing.compare(o1.getCommitDate(), o2.getCommitDate());
}
});
return writeChanges(changes, null);
}
public List<CommittedChangeList> writeChanges(final List<CommittedChangeList> changes, @Nullable final List<Boolean> present) throws IOException {
assert present == null || present.size() == changes.size();
List<CommittedChangeList> result = new ArrayList<CommittedChangeList>(changes.size());
boolean wasEmpty = isEmpty();
openStreams();
@@ -126,12 +154,8 @@ public class ChangesCacheFile {
}
myStream.seek(myStream.length());
IndexEntry[] entries = readLastIndexEntries(0, changes.size());
// the list and index are sorted in direct chronological order
Collections.sort(changes, new Comparator<CommittedChangeList>() {
public int compare(final CommittedChangeList o1, final CommittedChangeList o2) {
return Comparing.compare(o1.getCommitDate(), o2.getCommitDate());
}
});
final Iterator<Boolean> iterator = present == null ? null : present.iterator();
for(CommittedChangeList list: changes) {
boolean duplicate = false;
for(IndexEntry entry: entries) {
@@ -150,7 +174,7 @@ public class ChangesCacheFile {
//noinspection unchecked
myChangesProvider.writeChangeList(myStream, list);
updateCachedRange(list);
writeIndexEntry(list.getNumber(), list.getCommitDate().getTime(), position, false);
writeIndexEntry(list.getNumber(), list.getCommitDate().getTime(), position, present == null ? false : iterator.next());
myIncomingCount++;
}
writeHeader();
@@ -318,6 +342,44 @@ public class ChangesCacheFile {
return new BackIterator(bunchSize);
}
private List<Boolean> loadAllData(final List<CommittedChangeList> lists) throws IOException {
List<Boolean> idx = new ArrayList<Boolean>();
openStreams();
try {
loadHeader();
final long length = myIndexStream.length();
long totalCount = length / INDEX_ENTRY_SIZE;
for(int i=0; i<totalCount; i++) {
final long indexOffset = length - (i + 1) * INDEX_ENTRY_SIZE;
myIndexStream.seek(indexOffset);
IndexEntry e = new IndexEntry();
readIndexEntry(e);
final CommittedChangeList list = loadChangeListAt(e.offset);
lists.add(list);
idx.add(e.completelyDownloaded);
}
} finally {
closeStreams();
}
return idx;
}
public void editChangelist(long number, String message) throws IOException {
final List<CommittedChangeList> lists = new ArrayList<CommittedChangeList>();
final List<Boolean> present = loadAllData(lists);
for (CommittedChangeList list : lists) {
if (list.getNumber() == number) {
list.setDescription(message);
break;
}
}
delete();
Collections.reverse(lists);
Collections.reverse(present);
writeChanges(lists, present);
}
private class BackIterator implements Iterator<ChangesBunch> {
private final int bunchSize;
private long myOffset;
@@ -36,6 +36,10 @@ public class CommittedChangesAdapter implements CommittedChangesListener {
public void changesCleared() {
}
@Override
public void presentationChanged() {
}
public void refreshErrorStatusChanged(@Nullable VcsException lastError) {
}
}
@@ -22,17 +22,20 @@ import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.components.*;
import com.intellij.openapi.components.StoragePathMacros;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressManagerQueue;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl;
import com.intellij.openapi.vcs.impl.VcsInitObject;
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
import com.intellij.openapi.vcs.update.UpdatedFiles;
import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings;
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
@@ -138,6 +141,12 @@ public class CommittedChangesCache implements PersistentStateComponent<Committed
myLocationCache = new RepositoryLocationCache(project);
myCachesHolder = new CachesHolder(project, myLocationCache);
myTaskQueue = new ProgressManagerQueue(project, VcsBundle.message("committed.changes.refresh.progress"));
((ProjectLevelVcsManagerImpl) vcsManager).addInitializationRequest(VcsInitObject.COMMITTED_CHANGES_CACHE, new Runnable() {
@Override
public void run() {
myTaskQueue.start();
}
});
myVcsManager = vcsManager;
Disposer.register(project, new Disposable() {
public void dispose() {
@@ -645,6 +654,28 @@ public class CommittedChangesCache implements PersistentStateComponent<Committed
}
}
public void commitMessageChanged(final AbstractVcs vcs,
final RepositoryLocation location, final long number, final String newMessage) {
myTaskQueue.run(new Runnable() {
@Override
public void run() {
final ChangesCacheFile file = myCachesHolder.haveCache(location);
if (file != null) {
try {
if (file.isEmpty()) return;
file.editChangelist(number, newMessage);
loadIncomingChanges(false);
myBus.syncPublisher(COMMITTED_TOPIC).changesLoaded(location, Collections.<CommittedChangeList>emptyList());
}
catch (IOException e) {
VcsBalloonProblemNotifier.showOverChangesView(myProject, "Didn't update Repository changes with new message due to error: " + e.getMessage(),
MessageType.ERROR);
}
}
}
});
}
public void loadIncomingChangesAsync(@Nullable final Consumer<List<CommittedChangeList>> consumer, final boolean inBackground) {
debug("Loading incoming changes");
final Runnable task = new Runnable() {
@@ -663,7 +694,7 @@ public class CommittedChangesCache implements PersistentStateComponent<Committed
@Override
public void run() {
myCachesHolder.clearAllCaches();
myCachedIncomingChangeLists.clear();
myCachedIncomingChangeLists = null;
continuation.run();
myBus.syncPublisher(COMMITTED_TOPIC).changesCleared();
}
@@ -29,5 +29,6 @@ public interface CommittedChangesListener {
void changesLoaded(RepositoryLocation location, List<CommittedChangeList> changes);
void incomingChangesUpdated(@Nullable final List<CommittedChangeList> receivedChanges);
void changesCleared();
void presentationChanged();
void refreshErrorStatusChanged(@Nullable VcsException lastError);
}
@@ -69,6 +69,7 @@ public class CommittedChangesPanel extends JPanel implements TypeSafeDataProvide
private final List<Runnable> myShouldBeCalledOnDispose;
private volatile boolean myDisposed;
private volatile boolean myInLoad;
private Runnable myIfNotCachedReloader;
public CommittedChangesPanel(Project project, final CommittedChangesProvider provider, final ChangeBrowserSettings settings,
@Nullable final RepositoryLocation location, @Nullable ActionGroup extraActions) {
@@ -108,6 +109,12 @@ public class CommittedChangesPanel extends JPanel implements TypeSafeDataProvide
final AnAction anAction = ActionManager.getInstance().getAction("CommittedChanges.Refresh");
anAction.registerCustomShortcutSet(CommonShortcuts.getRerun(), this);
myBrowser.addFilter(myFilterComponent);
myIfNotCachedReloader = myLocation == null ? null : new Runnable() {
@Override
public void run() {
refreshChanges(false);
}
};
}
public RepositoryLocation getRepositoryLocation() {
@@ -278,9 +285,14 @@ public class CommittedChangesPanel extends JPanel implements TypeSafeDataProvide
}
public void calcData(DataKey key, DataSink sink) {
if (key.equals(VcsDataKeys.CHANGES) || key.equals(VcsDataKeys.CHANGE_LISTS)) {
myBrowser.calcData(key, sink);
if (key.equals(VcsDataKeys.REMOTE_HISTORY_CHANGED_LISTENER)) {
sink.put(VcsDataKeys.REMOTE_HISTORY_CHANGED_LISTENER, myIfNotCachedReloader);
} else if (VcsDataKeys.REMOTE_HISTORY_LOCATION.equals(key)) {
sink.put(VcsDataKeys.REMOTE_HISTORY_LOCATION, myLocation);
}
//if (key.equals(VcsDataKeys.CHANGES) || key.equals(VcsDataKeys.CHANGE_LISTS)) {
myBrowser.calcData(key, sink);
//}
}
public void dispose() {
@@ -225,8 +225,10 @@ public class CommittedChangesTreeBrowser extends JPanel implements TypeSafeDataP
private void updateModel() {
final List<CommittedChangeList> filteredChangeLists = myFilteringStrategy.filterChangeLists(myChangeLists);
final TreePath[] paths = myChangesTree.getSelectionPaths();
myChangesTree.setModel(buildTreeModel(filteredChangeLists));
TreeUtil.expandAll(myChangesTree);
myChangesTree.setSelectionPaths(paths);
}
public void setGroupingStrategy(ChangeListGroupingStrategy strategy) {
@@ -116,6 +116,11 @@ public class CommittedChangesViewManager implements ChangesViewContentProvider {
private class MyCommittedChangesListener extends CommittedChangesAdapter {
public void changesLoaded(RepositoryLocation location, List<CommittedChangeList> changes) {
presentationChanged();
}
@Override
public void presentationChanged() {
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
if (myComponent != null && !myProject.isDisposed()) {
@@ -47,10 +47,18 @@ public class IncomingChangesViewProvider implements ChangesViewContentProvider {
private final MessageBus myBus;
private CommittedChangesTreeBrowser myBrowser;
private MessageBusConnection myConnection;
private Consumer<List<CommittedChangeList>> myListConsumer;
public IncomingChangesViewProvider(final Project project, final MessageBus bus) {
myProject = project;
myBus = bus;
myListConsumer = new Consumer<List<CommittedChangeList>>() {
@Override
public void consume(List<CommittedChangeList> lists) {
myBrowser.getEmptyText().setText(VcsBundle.message("incoming.changes.empty.message"));
myBrowser.setItems(lists, CommittedChangesBrowserUseCase.INCOMING);
}
};
}
public JComponent initContent() {
@@ -62,7 +70,7 @@ public class IncomingChangesViewProvider implements ChangesViewContentProvider {
myBrowser.setTableContextMenu(group, Collections.<AnAction>emptyList());
myConnection = myBus.connect();
myConnection.subscribe(CommittedChangesCache.COMMITTED_TOPIC, new MyCommittedChangesListener());
loadChangesToBrowser(false);
loadChangesToBrowser(false, true);
JPanel contentPane = new JPanel(new BorderLayout());
contentPane.add(myBrowser, BorderLayout.CENTER);
@@ -75,18 +83,18 @@ public class IncomingChangesViewProvider implements ChangesViewContentProvider {
myBrowser = null;
}
private void updateModel(final boolean inBackground) {
private void updateModel(final boolean inBackground, final boolean refresh) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
if (myProject.isDisposed()) return;
if (myBrowser != null) {
loadChangesToBrowser(inBackground);
loadChangesToBrowser(inBackground, refresh);
}
}
});
}
private void loadChangesToBrowser(final boolean inBackground) {
private void loadChangesToBrowser(final boolean inBackground, final boolean refresh) {
final CommittedChangesCache cache = CommittedChangesCache.getInstance(myProject);
cache.hasCachesForAnyRoot(new Consumer<Boolean>() {
public void consume(final Boolean notEmpty) {
@@ -95,9 +103,11 @@ public class IncomingChangesViewProvider implements ChangesViewContentProvider {
if (list != null) {
myBrowser.getEmptyText().setText(VcsBundle.message("incoming.changes.empty.message"));
myBrowser.setItems(list, CommittedChangesBrowserUseCase.INCOMING);
}
else {
cache.loadIncomingChangesAsync(null, inBackground);
} else if (refresh) {
cache.loadIncomingChangesAsync(myListConsumer, inBackground);
} else {
myBrowser.getEmptyText().setText(VcsBundle.message("incoming.changes.empty.message"));
myBrowser.setItems(Collections.<CommittedChangeList>emptyList(), CommittedChangesBrowserUseCase.INCOMING);
}
}
}
@@ -106,11 +116,16 @@ public class IncomingChangesViewProvider implements ChangesViewContentProvider {
private class MyCommittedChangesListener extends CommittedChangesAdapter {
public void changesLoaded(final RepositoryLocation location, final List<CommittedChangeList> changes) {
updateModel(true);
updateModel(true, true);
}
public void incomingChangesUpdated(final List<CommittedChangeList> receivedChanges) {
updateModel(true);
updateModel(true, true);
}
@Override
public void presentationChanged() {
updateModel(true, false);
}
@Override
@@ -61,6 +61,11 @@ public class ReceivedChangeList extends CommittedChangeListImpl {
return myBaseList;
}
@Override
public void setDescription(String newMessage) {
myBaseList.setDescription(newMessage);
}
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@@ -27,13 +27,13 @@ import com.intellij.cvsSupport2.connections.CvsEnvironment;
import com.intellij.cvsSupport2.cvsoperations.dateOrRevision.SimpleRevision;
import com.intellij.cvsSupport2.history.CvsRevisionNumber;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.AbstractVcs;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ContentRevision;
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeListImpl;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.io.IOUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -125,6 +125,11 @@ public class CvsChangeList implements CommittedChangeList {
return true;
}
@Override
public void setDescription(String newMessage) {
myDescription = newMessage;
}
@Nullable
public String getBranch() {
if (myRevisions.size() > 0) {
+4
View File
@@ -39,6 +39,10 @@
<action id="PropertiesDiff" class="org.jetbrains.idea.svn.actions.ShowPropertiesDiffAction" popup="true" icon="SvnIcons.PropertiesDiff">
<add-to-group group-id="RepositoryChangesBrowserToolbar" anchor="last"/>
</action>
<action id="EditCommitMessage" class="org.jetbrains.idea.svn.history.SvnEditCommitMessageAction" popup="true" icon="AllIcons.Actions.Edit"
text="Edit Revision Comment" description="Edit revision comment. Previous message is rewritten.">
<add-to-group group-id="CommittedChangesToolbar" anchor="last"/>
</action>
<action id="AlienPropertiesLocalDiff" class="org.jetbrains.idea.svn.actions.ShowPropertiesDiffWithLocalAlienAction" popup="true">
<add-to-group group-id="AlienCommitChangesDialog.AdditionalActions" anchor="last"/>
@@ -18,7 +18,6 @@ package org.jetbrains.idea.svn;
import com.intellij.lifecycle.PeriodicalTasksCloser;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.*;
import com.intellij.openapi.components.StoragePathMacros;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.DumbAwareRunnable;
@@ -44,10 +43,14 @@ import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.util.SVNURLUtil;
import org.tmatesoft.svn.core.wc.*;
import org.tmatesoft.svn.core.wc.SVNInfo;
import org.tmatesoft.svn.core.wc.SVNStatus;
import java.io.File;
import java.util.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@State(
name = "SvnFileUrlMappingImpl",
@@ -395,10 +398,16 @@ public class SvnFileUrlMappingImpl implements SvnFileUrlMapping, PersistentState
return root;
}
}
final SVNURL newUrl = SvnUtil.getRepositoryRoot(myVcs, url);
if (newUrl != null) {
myRoots.add(newUrl);
return newUrl;
final SVNURL newUrl;
try {
newUrl = SvnUtil.getRepositoryRoot(myVcs, url);
if (newUrl != null) {
myRoots.add(newUrl);
return newUrl;
}
}
catch (SVNException e) {
//
}
return null;
}
@@ -366,14 +366,10 @@ public class SvnUtil {
}
@Nullable
public static SVNURL getRepositoryRoot(final SvnVcs vcs, final SVNURL url) {
public static SVNURL getRepositoryRoot(final SvnVcs vcs, final SVNURL url) throws SVNException {
final SVNWCClient client = vcs.createWCClient();
try {
SVNInfo info = client.doInfo(url, SVNRevision.UNDEFINED, SVNRevision.HEAD);
return (info == null) ? null : info.getRepositoryRootURL();
} catch (SVNException e) {
return null;
}
SVNInfo info = client.doInfo(url, SVNRevision.UNDEFINED, SVNRevision.HEAD);
return (info == null) ? null : info.getRepositoryRootURL();
}
public static boolean isWorkingCopyRoot(final File file) {
@@ -65,6 +65,10 @@ public class LoadedRevisionsCache implements Disposable {
public void changesCleared() {
}
@Override
public void presentationChanged() {
}
public void incomingChangesUpdated(@Nullable final List<CommittedChangeList> receivedChanges) {
}
@@ -599,6 +599,11 @@ public class SvnChangeList implements CommittedChangeList {
return true;
}
@Override
public void setDescription(String newMessage) {
myMessage = newMessage;
}
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@@ -17,6 +17,31 @@ package org.jetbrains.idea.svn.history;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vcs.AbstractVcsHelper;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.VcsDataKeys;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.ChangeList;
import com.intellij.openapi.vcs.changes.committed.CommittedChangesCache;
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnUtil;
import org.jetbrains.idea.svn.SvnVcs;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNPropertyValue;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNWCClient;
/**
* Created with IntelliJ IDEA.
@@ -25,13 +50,104 @@ import com.intellij.openapi.actionSystem.AnActionEvent;
* Time: 7:23 PM
*/
public class SvnEditCommitMessageAction extends AnAction {
// todo we need:
// todo repo url
// todo revision number, and current text
@Override
public void actionPerformed(AnActionEvent e) {
//To change body of implemented methods use File | Settings | File Templates.
final DataContext dc = e.getDataContext();
final ChangeList[] lists = VcsDataKeys.CHANGE_LISTS.getData(dc);
final boolean enabled = lists != null && lists.length == 1 && lists[0] instanceof SvnChangeList;
if (! enabled) return;
final SvnChangeList svnList = (SvnChangeList) lists[0];
Project project = PlatformDataKeys.PROJECT.getData(dc);
project = project == null ? ProjectManager.getInstance().getDefaultProject() : project;
final String edited = Messages.showMultilineInputDialog(project, "Attention! Previous message will be lost!\n\nNew revision comment:",
"Edit Revision # " + svnList.getNumber() + " Comment", svnList.getComment(), Messages.getInformationIcon(), null);
if (edited == null || edited.trim().equals(svnList.getComment().trim())) return;
final Runnable listener = VcsDataKeys.REMOTE_HISTORY_CHANGED_LISTENER.getData(dc);
ProgressManager.getInstance().run(new EditMessageTask(project, edited, svnList, listener));
}
@Override
public void update(AnActionEvent e) {
final DataContext dc = e.getDataContext();
final ChangeList[] lists = VcsDataKeys.CHANGE_LISTS.getData(dc);
final boolean enabled = lists != null && lists.length == 1 && lists[0] instanceof SvnChangeList;
boolean visible = enabled;
Project project = PlatformDataKeys.PROJECT.getData(dc);
if (project == null) {
visible = VcsDataKeys.REMOTE_HISTORY_LOCATION.getData(dc) instanceof SvnRepositoryLocation;
} else {
visible = ProjectLevelVcsManager.getInstance(project).checkVcsIsActive(SvnVcs.VCS_NAME);
}
e.getPresentation().setVisible(visible);
e.getPresentation().setEnabled(enabled);
}
/*private boolean anyChangeUnderSvn(ChangeList[] lists) {
for (ChangeList list : lists) {
final Collection<Change> changes = list.getChanges();
for (Change change : changes) {
if (isSvn(change.getBeforeRevision()) || isSvn(change.getAfterRevision())) {
return true;
}
}
}
return false;
}
private boolean isSvn(ContentRevision cr) {
return cr instanceof MarkerVcsContentRevision && SvnVcs.getKey().equals(((MarkerVcsContentRevision) cr).getVcsKey());
}*/
private static class EditMessageTask extends Task.Backgroundable {
private final String myNewMessage;
private final SvnChangeList myChangeList;
private final Runnable myListener;
private VcsException myException;
private final SvnVcs myVcs;
private EditMessageTask(@Nullable Project project, final String newMessage, final SvnChangeList changeList, Runnable listener) {
super(project, "Edit Revision Comment");
myNewMessage = newMessage;
myChangeList = changeList;
myListener = listener;
myVcs = SvnVcs.getInstance(myProject);
}
@Override
public void run(@NotNull ProgressIndicator indicator) {
final SVNWCClient client = myVcs.createWCClient();
final String url = myChangeList.getLocation().getURL();
final SVNURL root;
try {
root = SvnUtil.getRepositoryRoot(myVcs, SVNURL.parseURIEncoded(url));
if (root == null) {
myException = new VcsException("Can not determine repository root for URL: " + url);
return;
}
client.doSetRevisionProperty(root, SVNRevision.create(myChangeList.getNumber()), "svn:log",
SVNPropertyValue.create(myNewMessage), false, null);
}
catch (SVNException e) {
myException = new VcsException(e);
}
}
@Override
public void onSuccess() {
if (myException != null) {
AbstractVcsHelper.getInstance(myProject).showError(myException, myTitle);
} else {
if (myListener != null) {
myListener.run();
}
if (! myProject.isDefault()) {
CommittedChangesCache.getInstance(myProject).commitMessageChanged(myVcs,
myChangeList.getLocation(), myChangeList.getNumber(), myNewMessage);
}
VcsBalloonProblemNotifier.showOverChangesView(myProject, "Revision #" + myChangeList.getNumber() + " comment " +
"changed to:\n'" + myNewMessage + "'", MessageType.INFO);
}
}
}
}
@@ -1,25 +0,0 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.svn.history;
/**
* Created with IntelliJ IDEA.
* User: Irina.Chernushina
* Date: 10/23/12
* Time: 7:24 PM
*/
public class SvnRevisionComment {
}