IDEA-26360 (Performance and inconsistency issues with svn:externals and "Detect neste...
[fedora-idea.git] / plugins / svn4idea / src / org / jetbrains / idea / svn / checkin / SvnCheckinEnvironment.java
blob64e66c9da683283c224600b6b99f8c3821ba196a
1 /*
2 * Copyright 2000-2009 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.
16 package org.jetbrains.idea.svn.checkin;
18 import com.intellij.openapi.application.ApplicationManager;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.diff.impl.patch.formove.FilePathComparator;
21 import com.intellij.openapi.progress.ProcessCanceledException;
22 import com.intellij.openapi.progress.ProgressIndicator;
23 import com.intellij.openapi.progress.ProgressManager;
24 import com.intellij.openapi.ui.MessageType;
25 import com.intellij.openapi.util.Computable;
26 import com.intellij.openapi.util.io.FileUtil;
27 import com.intellij.openapi.vcs.CheckinProjectPanel;
28 import com.intellij.openapi.vcs.FilePath;
29 import com.intellij.openapi.vcs.VcsException;
30 import com.intellij.openapi.vcs.changes.Change;
31 import com.intellij.openapi.vcs.changes.ChangeList;
32 import com.intellij.openapi.vcs.changes.ChangesUtil;
33 import com.intellij.openapi.vcs.changes.ContentRevision;
34 import com.intellij.openapi.vcs.changes.ui.ChangesViewBalloonProblemNotifier;
35 import com.intellij.openapi.vcs.checkin.CheckinEnvironment;
36 import com.intellij.openapi.vcs.ui.Refreshable;
37 import com.intellij.openapi.vcs.ui.RefreshableOnComponent;
38 import com.intellij.openapi.vfs.VirtualFile;
39 import com.intellij.openapi.vfs.VirtualFileManager;
40 import org.jetbrains.annotations.NonNls;
41 import org.jetbrains.annotations.Nullable;
42 import org.jetbrains.idea.svn.SvnBundle;
43 import org.jetbrains.idea.svn.SvnConfiguration;
44 import org.jetbrains.idea.svn.SvnUtil;
45 import org.jetbrains.idea.svn.SvnVcs;
46 import org.tmatesoft.svn.core.SVNCancelException;
47 import org.tmatesoft.svn.core.SVNCommitInfo;
48 import org.tmatesoft.svn.core.SVNException;
49 import org.tmatesoft.svn.core.wc.*;
51 import javax.swing.*;
52 import java.awt.*;
53 import java.io.File;
54 import java.util.*;
55 import java.util.List;
57 public class SvnCheckinEnvironment implements CheckinEnvironment {
58 private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.checkin.SvnCheckinEnvironment");
59 private final SvnVcs mySvnVcs;
61 public SvnCheckinEnvironment(SvnVcs svnVcs) {
62 mySvnVcs = svnVcs;
65 public RefreshableOnComponent createAdditionalOptionsPanel(CheckinProjectPanel panel) {
66 return new KeepLocksComponent(panel);
69 @Nullable
70 public String getDefaultMessageFor(FilePath[] filesToCheckin) {
71 return null;
74 @Nullable
75 public String getHelpId() {
76 return null;
80 private List<VcsException> commitInt(List<File> paths, final String comment, final boolean force, final boolean recursive) {
81 final List<VcsException> exception = new ArrayList<VcsException>();
82 final Collection<File> committables = getCommitables(paths);
84 final SVNCommitClient committer = mySvnVcs.createCommitClient();
86 final ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
87 final Collection<VirtualFile> deletedFiles = new ArrayList<VirtualFile>();
88 if (progress != null) {
89 committer.setEventHandler(new ISVNEventHandler() {
90 public void handleEvent(final SVNEvent event, double p) {
91 final String path = SvnUtil.getPathForProgress(event);
92 if (path == null) {
93 return;
95 if (event.getAction() == SVNEventAction.COMMIT_ADDED) {
96 progress.setText2(SvnBundle.message("progress.text2.adding", path));
98 else if (event.getAction() == SVNEventAction.COMMIT_DELETED) {
99 @NonNls final String filePath = "file://" + event.getFile().getAbsolutePath().replace(File.separatorChar, '/');
100 VirtualFile vf = ApplicationManager.getApplication().runReadAction(new Computable<VirtualFile>() {
101 @Nullable public VirtualFile compute() {
102 return VirtualFileManager.getInstance().findFileByUrl(filePath);
105 if (vf != null) {
106 deletedFiles.add(vf);
108 progress.setText2(SvnBundle.message("progress.text2.deleting", path));
110 else if (event.getAction() == SVNEventAction.COMMIT_MODIFIED) {
111 progress.setText2(SvnBundle.message("progress.text2.sending", path));
113 else if (event.getAction() == SVNEventAction.COMMIT_REPLACED) {
114 progress.setText2(SvnBundle.message("progress.text2.replacing", path));
116 else if (event.getAction() == SVNEventAction.COMMIT_DELTA_SENT) {
117 progress.setText2(SvnBundle.message("progress.text2.transmitting.delta", path));
119 // do not need COMMIT_COMPLETED: same info is get another way
122 public void checkCancelled() throws SVNCancelException {
123 try {
124 progress.checkCanceled();
126 catch(ProcessCanceledException ex) {
127 throw new SVNCancelException();
133 if (progress != null) {
134 doCommit(committables, progress, committer, comment, force, recursive, exception);
136 else if (ApplicationManager.getApplication().isDispatchThread()) {
137 ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
138 public void run() {
139 ProgressIndicator p = ProgressManager.getInstance().getProgressIndicator();
140 doCommit(committables, p, committer, comment, force, recursive, exception);
142 }, SvnBundle.message("progress.title.commit"), false, mySvnVcs.getProject());
144 else {
145 doCommit(committables, progress, committer, comment, force, recursive, exception);
148 for(VirtualFile f : deletedFiles) {
149 f.putUserData(VirtualFile.REQUESTOR_MARKER, this);
151 return exception;
154 private void doCommit(Collection<File> committables,
155 ProgressIndicator progress,
156 SVNCommitClient committer,
157 String comment,
158 boolean force,
159 boolean recursive,
160 List<VcsException> exception) {
161 if (committables.isEmpty()) {
162 return;
164 File[] pathsToCommit = (File[])committables.toArray(new File[committables.size()]);
165 boolean keepLocks = SvnConfiguration.getInstanceChecked(mySvnVcs.getProject()).isKeepLocks();
166 SVNCommitPacket[] commitPackets = null;
167 SVNCommitInfo[] results;
168 try {
169 commitPackets = committer.doCollectCommitItems(pathsToCommit, keepLocks, force, recursive, true);
170 results = committer.doCommit(commitPackets, keepLocks, comment);
171 commitPackets = null;
173 catch (SVNException e) {
174 // exception on collecting commitables.
175 exception.add(new VcsException(e));
176 LOG.info(e);
177 return;
179 finally {
180 if (commitPackets != null) {
181 for (int i = 0; i < commitPackets.length; i++) {
182 SVNCommitPacket commitPacket = commitPackets[i];
183 try {
184 commitPacket.dispose();
186 catch (SVNException e) {
192 final StringBuffer committedRevisions = new StringBuffer();
193 for (SVNCommitInfo result : results) {
194 if (result.getErrorMessage() != null) {
195 exception.add(new VcsException(result.getErrorMessage().getFullMessage()));
197 else if (result != SVNCommitInfo.NULL && result.getNewRevision() > 0) {
198 if (committedRevisions.length() > 0) {
199 committedRevisions.append(", ");
201 committedRevisions.append(result.getNewRevision());
204 if (committedRevisions.length() > 0) {
205 ApplicationManager.getApplication().invokeLater(new Runnable() {
206 public void run() {
207 new ChangesViewBalloonProblemNotifier(mySvnVcs.getProject(),
208 SvnBundle.message("status.text.comitted.revision", committedRevisions),
209 MessageType.INFO).run();
215 private static class Adder {
216 private final Collection<File> myResult = new ArrayList<File>();
217 private final Set<String> myDuplicatesControlSet = new HashSet<String>();
219 public void add(final File file) {
220 final String path = file.getAbsolutePath();
221 if (! myDuplicatesControlSet.contains(path)) {
222 myResult.add(file);
223 myDuplicatesControlSet.add(path);
227 public Collection<File> getResult() {
228 return myResult;
232 private Collection<File> getCommitables(List<File> paths) {
233 final Adder adder = new Adder();
235 SVNStatusClient statusClient = mySvnVcs.createStatusClient();
236 for (File path : paths) {
237 File file = path.getAbsoluteFile();
238 adder.add(file);
239 if (file.getParentFile() != null) {
240 addParents(statusClient, file.getParentFile(), adder);
243 return adder.getResult();
246 private static void addParents(SVNStatusClient statusClient, File file, final Adder adder) {
247 SVNStatus status;
248 try {
249 status = statusClient.doStatus(file, false);
251 catch (SVNException e) {
252 return;
254 if (status != null &&
255 (status.getContentsStatus() == SVNStatusType.STATUS_ADDED ||
256 status.getContentsStatus() == SVNStatusType.STATUS_REPLACED)) {
257 // file should be added
258 adder.add(file);
259 file = file.getParentFile();
260 if (file != null) {
261 addParents(statusClient, file, adder);
266 private static List<File> collectPaths(final List<Change> changes) {
267 // case sensitive..
268 ArrayList<File> result = new ArrayList<File>();
270 final Set<String> pathesSet = new HashSet<String>();
271 for (Change change : changes) {
272 final ContentRevision beforeRevision = change.getBeforeRevision();
273 final ContentRevision afterRevision = change.getAfterRevision();
274 if (beforeRevision != null) {
275 pathesSet.add(beforeRevision.getFile().getIOFile().getAbsolutePath());
277 if (afterRevision != null) {
278 pathesSet.add(afterRevision.getFile().getIOFile().getAbsolutePath());
282 for (String s : pathesSet) {
283 result.add(new File(s));
285 return result;
288 public String getCheckinOperationName() {
289 return SvnBundle.message("checkin.operation.name");
292 public List<VcsException> commit(List<Change> changes, String preparedComment) {
293 return commitInt(collectPaths(changes), preparedComment, true, false);
296 public List<VcsException> commit(List<Change> changes, String preparedComment, Object parameters) {
297 return commit(changes, preparedComment);
300 public List<VcsException> scheduleMissingFileForDeletion(List<FilePath> filePaths) {
301 List<VcsException> exceptions = new ArrayList<VcsException>();
302 final SVNWCClient wcClient = mySvnVcs.createWCClient();
304 List<File> files = ChangesUtil.filePathsToFiles(filePaths);
305 for (File file : files) {
306 try {
307 wcClient.doDelete(file, true, false);
309 catch (SVNException e) {
310 exceptions.add(new VcsException(e));
314 return exceptions;
317 public List<VcsException> scheduleUnversionedFilesForAddition(List<VirtualFile> files) {
318 final List<VcsException> result = new ArrayList<VcsException>();
319 final SVNWCClient wcClient = mySvnVcs.createWCClient();
321 final List<SVNException> exceptionList = scheduleUnversionedFilesForAddition(wcClient, files);
322 for (SVNException svnException : exceptionList) {
323 result.add(new VcsException(svnException));
325 return result;
328 public static List<SVNException> scheduleUnversionedFilesForAddition(SVNWCClient wcClient, List<VirtualFile> files) {
329 return scheduleUnversionedFilesForAddition(wcClient, files, false);
332 public static List<SVNException> scheduleUnversionedFilesForAddition(SVNWCClient wcClient, List<VirtualFile> files, final boolean recursive) {
333 List<SVNException> exceptions = new ArrayList<SVNException>();
335 Collections.sort(files, FilePathComparator.getInstance());
337 for (VirtualFile file : files) {
338 try {
339 wcClient.doAdd(new File(FileUtil.toSystemDependentName(file.getPath())), true, false, true, recursive);
341 catch (SVNException e) {
342 exceptions.add(e);
346 return exceptions;
349 public boolean keepChangeListAfterCommit(ChangeList changeList) {
350 return false;
353 private class KeepLocksComponent implements RefreshableOnComponent {
354 private final JCheckBox myKeepLocksBox;
355 private boolean myIsKeepLocks;
356 private final JPanel myPanel;
358 public KeepLocksComponent(final Refreshable panel) {
360 myPanel = new JPanel(new BorderLayout());
361 myKeepLocksBox = new JCheckBox(SvnBundle.message("checkbox.chckin.keep.files.locked"));
362 myKeepLocksBox.setSelected(myIsKeepLocks);
364 myPanel.add(myKeepLocksBox, BorderLayout.CENTER);
367 public JComponent getComponent() {
368 return myPanel;
371 public boolean isKeepLocks() {
372 return myKeepLocksBox != null && myKeepLocksBox.isSelected();
375 public void refresh() {
378 public void saveState() {
379 mySvnVcs.getSvnConfiguration().setKeepLocks(isKeepLocks());
382 public void restoreState() {
383 myIsKeepLocks = mySvnVcs.getSvnConfiguration().isKeepLocks();