| /* |
| * 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; |
| |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.progress.ProcessCanceledException; |
| import com.intellij.openapi.progress.ProgressIndicator; |
| import com.intellij.openapi.progress.ProgressManager; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Computable; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.util.Ref; |
| import com.intellij.openapi.util.io.FileUtil; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.openapi.vcs.AbstractVcsHelper; |
| import com.intellij.openapi.vcs.VcsException; |
| import com.intellij.openapi.vcs.changes.Change; |
| import com.intellij.openapi.vcs.changes.ChangesUtil; |
| import com.intellij.openapi.vfs.LocalFileSystem; |
| import com.intellij.openapi.vfs.VfsUtilCore; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.openapi.vfs.VirtualFileManager; |
| import com.intellij.openapi.wm.impl.status.StatusBarUtil; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.containers.Convertor; |
| import com.intellij.util.containers.MultiMap; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.idea.svn.api.ClientFactory; |
| import org.jetbrains.idea.svn.branchConfig.SvnBranchConfigurationNew; |
| import org.jetbrains.idea.svn.dialogs.LockDialog; |
| import org.tmatesoft.sqljet.core.SqlJetException; |
| import org.tmatesoft.sqljet.core.table.SqlJetDb; |
| import org.tmatesoft.svn.core.*; |
| import org.tmatesoft.svn.core.internal.util.SVNPathUtil; |
| import org.tmatesoft.svn.core.internal.wc.SVNFileUtil; |
| import org.tmatesoft.svn.core.internal.wc2.SvnWcGeneration; |
| import org.tmatesoft.svn.core.io.SVNCapability; |
| import org.tmatesoft.svn.core.io.SVNRepository; |
| import org.tmatesoft.svn.core.wc.*; |
| import org.tmatesoft.svn.core.wc2.SvnOperationFactory; |
| import org.tmatesoft.svn.core.wc2.SvnTarget; |
| |
| import java.io.File; |
| import java.util.*; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| public class SvnUtil { |
| // TODO: ASP.NET hack behavior should be supported - http://svn.apache.org/repos/asf/subversion/trunk/notes/asp-dot-net-hack.txt |
| // TODO: Remember this when moving out SVNKit classes. |
| @NonNls public static final String SVN_ADMIN_DIR_NAME = SVNFileUtil.getAdminDirectoryName(); |
| @NonNls public static final String ENTRIES_FILE_NAME = "entries"; |
| @NonNls public static final String WC_DB_FILE_NAME = "wc.db"; |
| @NonNls public static final String DIR_PROPS_FILE_NAME = "dir-props"; |
| @NonNls public static final String PATH_TO_LOCK_FILE = SVN_ADMIN_DIR_NAME + "/lock"; |
| private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnUtil"); |
| |
| public static final Pattern ERROR_PATTERN = Pattern.compile("^svn: (E(\\d+)): (.*)$", Pattern.MULTILINE); |
| public static final Pattern WARNING_PATTERN = Pattern.compile("^svn: warning: (W(\\d+)): (.*)$", Pattern.MULTILINE); |
| |
| private SvnUtil() { } |
| |
| @Nullable |
| public static SVNErrorMessage parseWarning(@NotNull String text) { |
| Matcher matcher = WARNING_PATTERN.matcher(text); |
| SVNErrorMessage error = null; |
| |
| // currently treating only first warning |
| if (matcher.find()) { |
| error = SVNErrorMessage |
| .create(SVNErrorCode.getErrorCode(Integer.parseInt(matcher.group(2))), matcher.group(3), SVNErrorMessage.TYPE_WARNING); |
| } |
| |
| return error; |
| } |
| |
| public static boolean isSvnVersioned(final Project project, File parent) { |
| return isSvnVersioned(SvnVcs.getInstance(project), parent); |
| } |
| |
| public static boolean isSvnVersioned(final @NotNull SvnVcs vcs, File parent) { |
| final SVNInfo info = vcs.getInfo(parent); |
| |
| return info != null; |
| } |
| |
| public static Collection<VirtualFile> crawlWCRoots(final Project project, File path, SvnWCRootCrawler callback, ProgressIndicator progress) { |
| final LocalFileSystem lfs = LocalFileSystem.getInstance(); |
| VirtualFile vf = lfs.findFileByIoFile(path); |
| if (vf == null) { |
| vf = lfs.refreshAndFindFileByIoFile(path); |
| } |
| if (vf == null) return Collections.emptyList(); |
| return crawlWCRoots(project, vf, callback, progress); |
| } |
| |
| private static Collection<VirtualFile> crawlWCRoots(final Project project, VirtualFile vf, SvnWCRootCrawler callback, ProgressIndicator progress) { |
| final Collection<VirtualFile> result = new HashSet<VirtualFile>(); |
| final boolean isDirectory = vf.isDirectory(); |
| VirtualFile parent = ! isDirectory || !vf.exists() ? vf.getParent() : vf; |
| |
| final File parentIo = new File(parent.getPath()); |
| if (isSvnVersioned(project, parentIo)) { |
| checkCanceled(progress); |
| File ioFile = new File(vf.getPath()); |
| callback.handleWorkingCopyRoot(ioFile, progress); |
| checkCanceled(progress); |
| result.add(parent); |
| } else if (isDirectory) { |
| checkCanceled(progress); |
| final VirtualFile[] childrenVF = parent.getChildren(); |
| for (VirtualFile file : childrenVF) { |
| checkCanceled(progress); |
| if (file.isDirectory()) { |
| result.addAll(crawlWCRoots(project, file, callback, progress)); |
| } |
| } |
| } |
| return result; |
| } |
| |
| private static void checkCanceled(final ProgressIndicator progress) { |
| if (progress != null && progress.isCanceled()) { |
| throw new ProcessCanceledException(); |
| } |
| } |
| |
| @Nullable |
| public static String getExactLocation(final SvnVcs vcs, File path) { |
| SVNInfo info = vcs.getInfo(path); |
| return info != null && info.getURL() != null ? info.getURL().toString() : null; |
| } |
| |
| public static Map<String, File> getLocationInfoForModule(final SvnVcs vcs, File path, ProgressIndicator progress) { |
| final LocationsCrawler crawler = new LocationsCrawler(vcs); |
| crawlWCRoots(vcs.getProject(), path, crawler, progress); |
| return crawler.getLocationInfos(); |
| } |
| |
| public static void doLockFiles(Project project, final SvnVcs activeVcs, @NotNull final File[] ioFiles) throws VcsException { |
| final String lockMessage; |
| final boolean force; |
| // TODO[yole]: check for shift pressed |
| if (activeVcs.getCheckoutOptions().getValue()) { |
| LockDialog dialog = new LockDialog(project, true, ioFiles.length > 1); |
| dialog.show(); |
| if (!dialog.isOK()) { |
| return; |
| } |
| lockMessage = dialog.getComment(); |
| force = dialog.isForce(); |
| } |
| else { |
| lockMessage = ""; |
| force = false; |
| } |
| |
| final VcsException[] exception = new VcsException[1]; |
| final Collection<String> failedLocks = new ArrayList<String>(); |
| final int[] count = new int[]{ioFiles.length}; |
| final ISVNEventHandler eventHandler = new ISVNEventHandler() { |
| public void handleEvent(SVNEvent event, double progress) { |
| if (event.getAction() == SVNEventAction.LOCK_FAILED) { |
| failedLocks.add(event.getErrorMessage() != null ? |
| event.getErrorMessage().getFullMessage() : |
| event.getFile().getAbsolutePath()); |
| count[0]--; |
| } |
| } |
| |
| public void checkCancelled() { |
| } |
| }; |
| |
| Runnable command = new Runnable() { |
| public void run() { |
| ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator(); |
| |
| try { |
| if (progress != null) { |
| progress.setText(SvnBundle.message("progress.text.locking.files")); |
| } |
| for (File ioFile : ioFiles) { |
| if (progress != null) { |
| progress.checkCanceled(); |
| } |
| if (progress != null) { |
| progress.setText2(SvnBundle.message("progress.text2.processing.file", ioFile.getName())); |
| } |
| activeVcs.getFactory(ioFile).createLockClient().lock(ioFile, force, lockMessage, eventHandler); |
| } |
| } |
| catch (VcsException e) { |
| exception[0] = e; |
| } |
| } |
| }; |
| |
| ProgressManager.getInstance().runProcessWithProgressSynchronously(command, SvnBundle.message("progress.title.lock.files"), false, project); |
| if (!failedLocks.isEmpty()) { |
| String[] failedFiles = ArrayUtil.toStringArray(failedLocks); |
| List<VcsException> exceptions = new ArrayList<VcsException>(); |
| for (String file : failedFiles) { |
| exceptions.add(new VcsException(SvnBundle.message("exception.text.locking.file.failed", file))); |
| } |
| final StringBuilder sb = new StringBuilder(SvnBundle.message("message.text.files.lock.failed", failedFiles.length == 1 ? 0 : 1)); |
| for (VcsException vcsException : exceptions) { |
| if (sb.length() > 0) sb.append('\n'); |
| sb.append(vcsException.getMessage()); |
| } |
| //AbstractVcsHelper.getInstance(project).showErrors(exceptions, SvnBundle.message("message.title.lock.failures")); |
| throw new VcsException(sb.toString()); |
| } |
| |
| StatusBarUtil.setStatusBarInfo(project, SvnBundle.message("message.text.files.locked", count[0])); |
| if (exception[0] != null) { |
| throw exception[0]; |
| } |
| } |
| |
| public static void doUnlockFiles(Project project, final SvnVcs activeVcs, final File[] ioFiles) throws VcsException { |
| final boolean force = true; |
| final VcsException[] exception = new VcsException[1]; |
| final Collection<String> failedUnlocks = new ArrayList<String>(); |
| final int[] count = new int[]{ioFiles.length}; |
| final ISVNEventHandler eventHandler = new ISVNEventHandler() { |
| public void handleEvent(SVNEvent event, double progress) { |
| if (event.getAction() == SVNEventAction.UNLOCK_FAILED) { |
| failedUnlocks.add(event.getErrorMessage() != null ? |
| event.getErrorMessage().getFullMessage() : |
| event.getFile().getAbsolutePath()); |
| count[0]--; |
| } |
| } |
| |
| public void checkCancelled() { |
| } |
| }; |
| |
| Runnable command = new Runnable() { |
| public void run() { |
| ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator(); |
| |
| try { |
| if (progress != null) { |
| progress.setText(SvnBundle.message("progress.text.unlocking.files")); |
| } |
| for (File ioFile : ioFiles) { |
| if (progress != null) { |
| progress.checkCanceled(); |
| } |
| if (progress != null) { |
| progress.setText2(SvnBundle.message("progress.text2.processing.file", ioFile.getName())); |
| } |
| activeVcs.getFactory(ioFile).createLockClient().unlock(ioFile, force, eventHandler); |
| } |
| } |
| catch (VcsException e) { |
| exception[0] = e; |
| } |
| } |
| }; |
| |
| ProgressManager.getInstance().runProcessWithProgressSynchronously(command, SvnBundle.message("progress.title.unlock.files"), false, project); |
| if (!failedUnlocks.isEmpty()) { |
| String[] failedFiles = ArrayUtil.toStringArray(failedUnlocks); |
| List<VcsException> exceptions = new ArrayList<VcsException>(); |
| |
| for (String file : failedFiles) { |
| exceptions.add(new VcsException(SvnBundle.message("exception.text.failed.to.unlock.file", file))); |
| } |
| AbstractVcsHelper.getInstance(project).showErrors(exceptions, SvnBundle.message("message.title.unlock.failures")); |
| } |
| |
| StatusBarUtil.setStatusBarInfo(project, SvnBundle.message("message.text.files.unlocked", count[0])); |
| if (exception[0] != null) { |
| throw new VcsException(exception[0]); |
| } |
| } |
| |
| public static String formatRepresentation(final WorkingCopyFormat format) { |
| if (WorkingCopyFormat.ONE_DOT_EIGHT.equals(format)) { |
| return SvnBundle.message("dialog.show.svn.map.table.version18.text"); |
| } else if (WorkingCopyFormat.ONE_DOT_SEVEN.equals(format)) { |
| return SvnBundle.message("dialog.show.svn.map.table.version17.text"); |
| } else if (WorkingCopyFormat.ONE_DOT_SIX.equals(format)) { |
| return SvnBundle.message("dialog.show.svn.map.table.version16.text"); |
| } else if (WorkingCopyFormat.ONE_DOT_FIVE.equals(format)) { |
| return SvnBundle.message("dialog.show.svn.map.table.version15.text"); |
| } else if (WorkingCopyFormat.ONE_DOT_FOUR.equals(format)) { |
| return SvnBundle.message("dialog.show.svn.map.table.version14.text"); |
| } else if (WorkingCopyFormat.ONE_DOT_THREE.equals(format)) { |
| return SvnBundle.message("dialog.show.svn.map.table.version13.text"); |
| } |
| return ""; |
| } |
| |
| public static Collection<List<Change>> splitChangesIntoWc(final SvnVcs vcs, final List<Change> changes) { |
| return splitIntoRepositories(vcs, changes, new Convertor<Change, File>() { |
| @Override |
| public File convert(Change o) { |
| return ChangesUtil.getFilePath(o).getIOFile(); |
| } |
| }); |
| } |
| |
| public static Collection<List<File>> splitFilesIntoRepositories(final SvnVcs vcs, final List<File> committables) { |
| return splitIntoRepositories(vcs, committables, Convertor.SELF); |
| } |
| |
| public static <T> Collection<List<T>> splitIntoRepositories(final SvnVcs vcs, final List<T> committables, |
| Convertor<T, File> convertor) { |
| if (committables.size() == 1) { |
| return Collections.singletonList(committables); |
| } |
| |
| final MultiMap<Pair<SVNURL, WorkingCopyFormat>, T> result = splitIntoRepositoriesMap(vcs, committables, convertor); |
| |
| if (result.size() == 1) { |
| return Collections.singletonList(committables); |
| } |
| final Collection<List<T>> result2 = new ArrayList<List<T>>(); |
| for (Map.Entry<Pair<SVNURL, WorkingCopyFormat>, Collection<T>> entry : result.entrySet()) { |
| result2.add((List<T>)entry.getValue()); |
| } |
| return result2; |
| } |
| |
| public static <T> MultiMap<Pair<SVNURL, WorkingCopyFormat>, T> splitIntoRepositoriesMap(SvnVcs vcs, |
| List<T> committables, Convertor<T, File> convertor) { |
| final MultiMap<Pair<SVNURL, WorkingCopyFormat>, T> result = new MultiMap<Pair<SVNURL, WorkingCopyFormat>, T>() { |
| @Override |
| protected Collection<T> createCollection() { |
| return new ArrayList<T>(); |
| } |
| }; |
| for (T committable : committables) { |
| final RootUrlInfo path = vcs.getSvnFileUrlMapping().getWcRootForFilePath(convertor.convert(committable)); |
| if (path == null) { |
| result.putValue(new Pair<SVNURL, WorkingCopyFormat>(null, null), committable); |
| } else { |
| result.putValue(new Pair<SVNURL, WorkingCopyFormat>(path.getRepositoryUrlUrl(), path.getFormat()), committable); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Gets working copy internal format. Works for 1.7 and 1.8. |
| * |
| * @param path |
| * @return |
| */ |
| public static WorkingCopyFormat getFormat(final File path) { |
| int format = 0; |
| File dbFile = resolveDatabase(path); |
| |
| if (dbFile != null) { |
| SqlJetDb db = null; |
| try { |
| db = SqlJetDb.open(dbFile, false); |
| format = db.getOptions().getUserVersion(); |
| } |
| catch (SqlJetException e) { |
| LOG.error(e); |
| } finally { |
| if (db != null) { |
| try { |
| db.close(); |
| } |
| catch (SqlJetException e) { |
| LOG.error(e); |
| } |
| } |
| } |
| } |
| |
| return WorkingCopyFormat.getInstance(format); |
| } |
| |
| private static File resolveDatabase(final File path) { |
| File dbFile = getWcDb(path); |
| File result = null; |
| |
| try { |
| if (dbFile.exists() && dbFile.isFile()) { |
| result = dbFile; |
| } |
| } catch (SecurityException e) { |
| LOG.error("Failed to access working copy database", e); |
| } |
| |
| return result; |
| } |
| |
| private static class LocationsCrawler implements SvnWCRootCrawler { |
| private final SvnVcs myVcs; |
| private final Map<String, File> myLocations; |
| |
| public LocationsCrawler(SvnVcs vcs) { |
| myVcs = vcs; |
| myLocations = new HashMap<String, File>(); |
| } |
| |
| public Map<String, File> getLocationInfos() { |
| return Collections.unmodifiableMap(myLocations); |
| } |
| |
| public void handleWorkingCopyRoot(File root, ProgressIndicator progress) { |
| String oldText = null; |
| if (progress != null) { |
| oldText = progress.getText(); |
| progress.setText(SvnBundle.message("progress.text.discovering.location", root.getAbsolutePath())); |
| } |
| SVNInfo info = myVcs.getInfo(root); |
| if (info != null && info.getURL() != null) { |
| myLocations.put(info.getURL().toString(), info.getFile()); |
| } |
| if (progress != null) { |
| progress.setText(oldText); |
| } |
| } |
| } |
| |
| @Nullable |
| public static String getRepositoryUUID(final SvnVcs vcs, final File file) { |
| final SVNInfo info = vcs.getInfo(file); |
| return info != null ? info.getRepositoryUUID() : null; |
| } |
| |
| @Nullable |
| public static String getRepositoryUUID(final SvnVcs vcs, final SVNURL url) { |
| try { |
| final SVNInfo info = vcs.getInfo(url, SVNRevision.UNDEFINED); |
| |
| return (info == null) ? null : info.getRepositoryUUID(); |
| } catch (SVNException e) { |
| return null; |
| } |
| } |
| |
| @Nullable |
| public static SVNURL getRepositoryRoot(final SvnVcs vcs, final File file) { |
| final SVNInfo info = vcs.getInfo(file); |
| return info != null ? info.getRepositoryRootURL() : null; |
| } |
| |
| @Nullable |
| public static SVNURL getRepositoryRoot(final SvnVcs vcs, final String url) { |
| try { |
| return getRepositoryRoot(vcs, SVNURL.parseURIEncoded(url)); |
| } |
| catch (SVNException e) { |
| return null; |
| } |
| } |
| |
| @Nullable |
| public static SVNURL getRepositoryRoot(final SvnVcs vcs, final SVNURL url) throws SVNException { |
| SVNInfo info = vcs.getInfo(url, SVNRevision.HEAD); |
| |
| return (info == null) ? null : info.getRepositoryRootURL(); |
| } |
| |
| public static boolean isWorkingCopyRoot(final File file) { |
| return FileUtil.filesEqual(file, getWorkingCopyRootNew(file)); |
| } |
| |
| @Nullable |
| public static File getWorkingCopyRoot(final File inFile) { |
| File file = inFile; |
| while ((file != null) && (file.isFile() || (! file.exists()))) { |
| file = file.getParentFile(); |
| } |
| |
| if (file == null) { |
| return null; |
| } |
| |
| File workingCopyRoot = null; |
| try { |
| workingCopyRoot = SVNWCUtil.getWorkingCopyRoot(file, true); |
| } catch (SVNException e) { |
| // |
| } |
| if (workingCopyRoot == null) { |
| workingCopyRoot = getWcCopyRootIf17(file, null); |
| } |
| return workingCopyRoot; |
| } |
| |
| public static File fileFromUrl(final File baseDir, final String baseUrl, final String fullUrl) throws SVNException { |
| assert fullUrl.startsWith(baseUrl); |
| |
| final String part = fullUrl.substring(baseUrl.length()).replace('/', File.separatorChar).replace('\\', File.separatorChar); |
| return new File(baseDir, part); |
| } |
| |
| public static VirtualFile getVirtualFile(final String filePath) { |
| @NonNls final String path = VfsUtilCore.pathToUrl(filePath.replace(File.separatorChar, '/')); |
| return ApplicationManager.getApplication().runReadAction(new Computable<VirtualFile>() { |
| @Nullable |
| public VirtualFile compute() { |
| return VirtualFileManager.getInstance().findFileByUrl(path); |
| } |
| }); |
| } |
| |
| @Nullable |
| public static SVNURL getBranchForUrl(final SvnVcs vcs, final VirtualFile vcsRoot, final String urlPath) { |
| final SvnBranchConfigurationNew configuration; |
| try { |
| final SVNURL url = SVNURL.parseURIEncoded(urlPath); |
| configuration = SvnBranchConfigurationManager.getInstance(vcs.getProject()).get(vcsRoot); |
| return (configuration == null) ? null : configuration.getWorkingBranch(url); |
| } |
| catch (SVNException e) { |
| return null; |
| } catch (VcsException e1) { |
| return null; |
| } |
| } |
| |
| @Nullable |
| public static String getPathForProgress(final SVNEvent event) { |
| if (event.getFile() != null) { |
| return event.getFile().getName(); |
| } |
| if (event.getURL() != null) { |
| return event.getURL().toString(); |
| } |
| return null; |
| } |
| |
| @Nullable |
| public static VirtualFile correctRoot(final Project project, final VirtualFile file) { |
| if (file.getPath().length() == 0) { |
| // project root |
| return project.getBaseDir(); |
| } |
| return file; |
| } |
| |
| public static boolean checkRepositoryVersion15(final SvnVcs vcs, final String url) { |
| SVNRepository repository = null; |
| try { |
| repository = vcs.createRepository(url); |
| return repository.hasCapability(SVNCapability.MERGE_INFO); |
| } |
| catch (SVNException e) { |
| return false; |
| } |
| finally { |
| if (repository != null) { |
| repository.closeSession(); |
| } |
| } |
| } |
| |
| @Nullable |
| public static SVNStatus getStatus(@NotNull final SvnVcs vcs, @NotNull final File file) { |
| try { |
| return vcs.getFactory(file).createStatusClient().doStatus(file, false); |
| } |
| catch (SVNException e) { |
| return null; |
| } |
| } |
| |
| public static SVNDepth getDepth(final SvnVcs vcs, final File file) { |
| SVNInfo info = vcs.getInfo(file); |
| |
| return info != null && info.getDepth() != null ? info.getDepth() : SVNDepth.UNKNOWN; |
| } |
| |
| public static boolean seemsLikeVersionedDir(final VirtualFile file) { |
| final String adminName = SVNFileUtil.getAdminDirectoryName(); |
| final VirtualFile child = file.findChild(adminName); |
| return child != null && child.isDirectory(); |
| } |
| |
| public static boolean isAdminDirectory(final VirtualFile file) { |
| return isAdminDirectory(file.getParent(), file.getName()); |
| } |
| |
| public static boolean isAdminDirectory(VirtualFile parent, String name) { |
| // never allow to delete admin directories by themselves (this can happen during VCS undo, |
| // which deletes created directories from bottom to top) |
| if (name.equals(SVN_ADMIN_DIR_NAME)) { |
| return true; |
| } |
| if (parent != null) { |
| if (parent.getName().equals(SVN_ADMIN_DIR_NAME)) { |
| return true; |
| } |
| parent = parent.getParent(); |
| if (parent != null && parent.getName().equals(SVN_ADMIN_DIR_NAME)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public static SVNURL getCommittedURL(final SvnVcs vcs, final File file) { |
| final File root = getWorkingCopyRootNew(file); |
| |
| return root == null ? null : getUrl(vcs, root); |
| } |
| |
| @Nullable |
| public static SVNURL getUrl(final SvnVcs vcs, final File file) { |
| // todo for moved items? |
| final SVNInfo info = vcs.getInfo(file); |
| |
| return info == null ? null : info.getURL(); |
| } |
| |
| public static boolean doesRepositorySupportMergeInfo(final SvnVcs vcs, final SVNURL url) { |
| SVNRepository repository = null; |
| try { |
| repository = vcs.createRepository(url); |
| return repository.hasCapability(SVNCapability.MERGE_INFO); |
| } |
| catch (SVNException e) { |
| return false; |
| } finally { |
| if (repository != null) { |
| repository.closeSession(); |
| } |
| } |
| } |
| |
| public static boolean remoteFolderIsEmpty(final SvnVcs vcs, final String url) throws SVNException { |
| // TODO: Implement with command line client |
| SVNRepository repository = null; |
| try { |
| repository = vcs.createRepository(url); |
| final Ref<Boolean> result = new Ref<Boolean>(true); |
| repository.getDir("", -1, null, new ISVNDirEntryHandler() { |
| public void handleDirEntry(final SVNDirEntry dirEntry) throws SVNException { |
| if (dirEntry != null) { |
| result.set(false); |
| } |
| } |
| }); |
| return result.get(); |
| } finally { |
| if (repository != null) { |
| repository.closeSession(); |
| } |
| } |
| } |
| |
| public static File getWcDb(final File file) { |
| return new File(file, SVN_ADMIN_DIR_NAME + "/wc.db"); |
| } |
| |
| @Nullable |
| public static File getWcCopyRootIf17(final File file, @Nullable final File upperBound) { |
| File current = getParentWithDb(file); |
| if (current == null) return null; |
| |
| while (current != null) { |
| try { |
| final SvnWcGeneration svnWcGeneration = SvnOperationFactory.detectWcGeneration(current, false); |
| if (SvnWcGeneration.V17.equals(svnWcGeneration)) return current; |
| if (SvnWcGeneration.V16.equals(svnWcGeneration)) return null; |
| if (upperBound != null && FileUtil.filesEqual(upperBound, current)) return null; |
| current = current.getParentFile(); |
| } |
| catch (SVNException e) { |
| return null; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Utility method that deals also with 1.8 working copies. |
| * TODO: Should be renamed when all parts updated for 1.8. |
| * |
| * @param file |
| * @return |
| */ |
| @Nullable |
| public static File getWorkingCopyRootNew(final File file) { |
| File current = getParentWithDb(file); |
| if (current == null) return getWorkingCopyRoot(file); |
| |
| WorkingCopyFormat format = getFormat(current); |
| |
| return WorkingCopyFormat.ONE_DOT_EIGHT.equals(format) || WorkingCopyFormat.ONE_DOT_SEVEN.equals(format) |
| ? current |
| : getWorkingCopyRoot(file); |
| } |
| |
| private static File getParentWithDb(File file) { |
| File current = file; |
| boolean wcDbFound = false; |
| while (current != null) { |
| File wcDb; |
| if ((wcDb = getWcDb(current)).exists() && ! wcDb.isDirectory()) { |
| wcDbFound = true; |
| break; |
| } |
| current = current.getParentFile(); |
| } |
| if (! wcDbFound) return null; |
| return current; |
| } |
| |
| public static boolean is17CopyPart(final File file) { |
| try { |
| return SvnWcGeneration.V17.equals(SvnOperationFactory.detectWcGeneration(file, true)); |
| } |
| catch (SVNException e) { |
| return false; |
| } |
| } |
| |
| public static String appendMultiParts(@NotNull final String base, @NotNull final String subPath) throws SVNException { |
| if (StringUtil.isEmpty(subPath)) return base; |
| final List<String> parts = StringUtil.split(subPath.replace('\\', '/'), "/", true); |
| String result = base; |
| for (String part : parts) { |
| result = SVNPathUtil.append(result, part); |
| } |
| return result; |
| } |
| |
| public static SVNURL appendMultiParts(@NotNull final SVNURL base, @NotNull final String subPath) throws SVNException { |
| if (StringUtil.isEmpty(subPath)) return base; |
| final List<String> parts = StringUtil.split(subPath.replace('\\', '/'), "/", true); |
| SVNURL result = base; |
| for (String part : parts) { |
| result = result.appendPath(part, false); |
| } |
| return result; |
| } |
| |
| public static byte[] getFileContents(@NotNull final SvnVcs vcs, |
| @NotNull final SvnTarget target, |
| @Nullable final SVNRevision revision, |
| @Nullable final SVNRevision pegRevision) |
| throws VcsException { |
| ClientFactory factory = target.isFile() ? vcs.getFactory(target.getFile()) : vcs.getFactory(); |
| |
| return factory.createContentClient().getContent(target, revision, pegRevision); |
| } |
| |
| public static SVNURL parseUrl(@NotNull String url) { |
| try { |
| return SVNURL.parseURIEncoded(url); |
| } |
| catch (SVNException e) { |
| IllegalArgumentException runtimeException = new IllegalArgumentException(); |
| runtimeException.initCause(e); |
| throw runtimeException; |
| } |
| } |
| } |