svn 1.7 as additional plugin

This commit is contained in:
irengrig
2011-11-17 13:30:51 +04:00
parent a327c351ea
commit 078cf050b5
815 changed files with 54781 additions and 89 deletions
+9
View File
@@ -52,6 +52,15 @@
</layout>
</appender>
<category name="#org.jetbrains.idea.svn.dialogs.SvnBranchPointsCalculator">
<level value="DEBUG"/>
<appender-ref ref="FILE"/>
</category>
<category name="#org.jetbrains.idea.svn.history.FirstInBranch">
<level value="DEBUG"/>
<appender-ref ref="FILE"/>
</category>
<root>
<priority value="INFO"/>
<appender-ref ref="DIALOG"/>
+3
View File
@@ -240,6 +240,9 @@ public def layoutCommunityPlugins(String home) {
}
}
layoutPlugin("svn4ideaOld") {
fileset(dir: "$home/plugins/svn4ideaOld/lib", excludes: "**/svnkitsrc.zip")
}
layoutPlugin("svn4idea") {
fileset(dir: "$home/plugins/svn4idea/lib", excludes: "**/svnkitsrc.zip")
}
-24
View File
@@ -1,24 +0,0 @@
This Idea plugin integrates Idea with Subversion.
The plugin tries to be as tightly integrated with Idea as possible.
Like the CVS integration it uses Idea's project view to display the workspace.
Changed files are color coded in the project view and the editor just as in the CVS integration.
All files & directories in the project directory, that are in the subversion repository are tracked during
move and rename operations.
Files & directories created through Idea in the projects source path are automatically tracked.
You can configure whether the new files shall be added to the
subversion repository (Always, Ask, Never).
Additional directories to be tracked for newly created files can be entered in
the "Additional Path" list on the configuration page.
Files & directories in a subversion repository which are deleted through Idea are automatically tracked.
You can configure whether the deleted files shall also be deleted from the subversion repository (Always, Ask, Never).
Subversion operations can be trieggered from the Subversion menu (tools/Subversion) or from the context menu.
For exended subversion operations (administration, blaming, properties etc) you can start the SvnUp
dialog from the Subversion menu (tools/Subversion/SvnUp).
+26
View File
@@ -0,0 +1,26 @@
[The "BSD licence"]
Copyright (c) 2003-2008 Terence Parr
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+55
View File
@@ -0,0 +1,55 @@
This license applies to all portions of Subversion which are not
externally-maintained libraries (e.g. apr/, apr-util/, and neon/).
Such libraries have their own licenses; we recommend you read them, as
their terms may differ from the terms below.
This is version 1 of this license. It is also available online at
http://subversion.tigris.org/license-1.html. If newer versions of
this license are posted there (the same URL, but with the version
number incremented: .../license-2.html, .../license-3.html, and so
on), you may use a newer version instead, at your option.
====================================================================
Copyright (c) 2000-2005 CollabNet. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The end-user documentation included with the redistribution, if
any, must include the following acknowledgment: "This product includes
software developed by CollabNet (http://www.Collab.Net/)."
Alternately, this acknowledgment may appear in the software itself, if
and wherever such third-party acknowledgments normally appear.
4. The hosted project names must not be used to endorse or promote
products derived from this software without prior written
permission. For written permission, please contact info@collab.net.
5. Products derived from this software may not use the "Tigris" name
nor may "Tigris" appear in their names without prior written
permission of CollabNet.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL COLLABNET OR ITS CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
====================================================================
This software consists of voluntary contributions made by many
individuals on behalf of CollabNet.
+11
View File
@@ -0,0 +1,11 @@
Copyright (c) 2008 Timothy Wall, All Rights Reserved
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
+48
View File
@@ -0,0 +1,48 @@
Sequence Library License
This license applies to all portions of the Sequence library, which
are not externally-maintained libraries (e.g. junit or jsch).
====================================================================
Copyright (c) 2000-2008 SyntEvo GmbH, Ainring, GERMANY.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The end-user documentation included with the redistribution, if
any, must include the following acknowledgment: "This product includes
software developed by SyntEvo GmbH, Ainring, GERMANY."
Alternately, this acknowledgment may appear in the software itself, if
and wherever such third-party acknowledgments normally appear.
4. The hosted project names must not be used to endorse or promote
products derived from this software without prior written
permission. For written permission, please contact
info@syntevo.com.
5. Neither the name of SyntEvo GmbH nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL SyntEvo GmbH OR HIS CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
====================================================================
+19
View File
@@ -0,0 +1,19 @@
Copyright (C) 2009-2010 TMate Software Ltd
As part of SVNKit library, SQLJet library is distributed under
the terms of TMate Open Source License (see http://svnkit.com/license.html).
Otherwise, outside of SVNKit library, the following licensing terms
are applicable for SQLJet library:
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
For information on how to redistribute this software under
the terms of a license other than GNU General Public License
contact TMate Software at support@sqljet.com
+88
View File
@@ -0,0 +1,88 @@
Copyright (c) 2007-2008 Trilead AG (http://www.trilead.com)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
a.) Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
b.) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
c.) Neither the name of Trilead nor the names of its contributors may
be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Trilead SSH-2 for Java includes code that was written by Dr. Christian Plattner
during his PhD at ETH Zurich. The license states the following:
Copyright (c) 2005 - 2006 Swiss Federal Institute of Technology (ETH Zurich),
Department of Computer Science (http://www.inf.ethz.ch),
Christian Plattner. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
a.) Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
b.) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
c.) Neither the name of ETH Zurich nor the names of its contributors may
be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
The Java implementations of the AES, Blowfish and 3DES ciphers have been
taken (and slightly modified) from the cryptography package released by
"The Legion Of The Bouncy Castle".
Their license states the following:
Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
(http://www.bouncycastle.org)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+58 -58
View File
@@ -1,139 +1,139 @@
<idea-plugin version="2">
<name>Subversion Integration</name>
<id>Subversion</id>
<name>Subversion 1.7 Experimental Integration</name>
<id>Subversion 1.7</id>
<version>1.0</version>
<category>VCS Integration</category>
<depends>com.intellij.modules.vcs</depends>
<vendor logo="/general/ijLogo.png">JetBrains</vendor>
<resource-bundle>org.jetbrains.idea.svn.SvnBundle</resource-bundle>
<resource-bundle>org.jetbrains.idea.svn17.SvnBundle</resource-bundle>
<project-components>
<component>
<implementation-class>org.jetbrains.idea.svn.SvnFileUrlMappingImpl</implementation-class>
<implementation-class>org.jetbrains.idea.svn17.SvnFileUrlMappingImpl17</implementation-class>
<option name="workspace" value="true"/>
</component>
</project-components>
<actions>
<action id="Subversion.ImportToSVNRepository" class="org.jetbrains.idea.svn.actions.ImportToRepositoryAction">
<action id="Subversion17.ImportToSVNRepository" class="org.jetbrains.idea.svn17.actions.ImportToRepositoryAction">
<add-to-group group-id="Vcs.Import"/>
</action>
<action id="Subversion.BrowseSVNRepository" class="org.jetbrains.idea.svn.actions.BrowseRepositoryAction">
<action id="Subversion17.BrowseSVNRepository" class="org.jetbrains.idea.svn17.actions.BrowseRepositoryAction">
<add-to-group group-id="Vcs.Browse"/>
</action>
<action id="Subversion.ShareWholeProject" class="org.jetbrains.idea.svn.actions.ShareWholeProject">
<action id="Subversion17.ShareWholeProject" class="org.jetbrains.idea.svn17.actions.ShareWholeProject">
<add-to-group group-id="Vcs.Import"/>
</action>
<group id="SubversionFilePopupGroup" popup="true" class="org.jetbrains.idea.svn.actions.SubversionGroup">
<group id="SubversionFilePopupGroup17" popup="true" class="org.jetbrains.idea.svn17.actions.SubversionGroup">
<add-to-group group-id="VcsGroup" anchor="last"/>
<add-to-group group-id="VcsGlobalGroup" anchor="after" relative-to-action="Vcs.Specific"/>
</group>
<action id="IntegrateChangeSetAction" class="org.jetbrains.idea.svn.actions.IntegrateChangeSetAction" popup="true" icon="/icons/IntegrateToBranch.png">
<action id="IntegrateChangeSetAction17" class="org.jetbrains.idea.svn17.actions.IntegrateChangeSetAction" popup="true" icon="/icons/IntegrateToBranch.png">
<add-to-group group-id="RepositoryChangesBrowserToolbar" anchor="last"/>
</action>
<action id="PropertiesDiff" class="org.jetbrains.idea.svn.actions.ShowPropertiesDiffAction" popup="true" icon="/icons/PropertiesDiff.png">
<action id="PropertiesDiff17" class="org.jetbrains.idea.svn17.actions.ShowPropertiesDiffAction" popup="true" icon="/icons/PropertiesDiff.png">
<add-to-group group-id="RepositoryChangesBrowserToolbar" anchor="last"/>
</action>
<action id="AlienPropertiesLocalDiff" class="org.jetbrains.idea.svn.actions.ShowPropertiesDiffWithLocalAlienAction" popup="true">
<action id="AlienPropertiesLocalDiff17" class="org.jetbrains.idea.svn17.actions.ShowPropertiesDiffWithLocalAlienAction" popup="true">
<add-to-group group-id="AlienCommitChangesDialog.AdditionalActions" anchor="last"/>
</action>
<group id="SubversionGroup">
<action id="Subversion.Share" class="org.jetbrains.idea.svn.actions.ShareProjectAction"/>
<group id="SubversionGroup17">
<action id="Subversion17.Share" class="org.jetbrains.idea.svn17.actions.ShareProjectAction"/>
<separator/>
<action id="Subversion.Add" class="org.jetbrains.idea.svn.actions.AddAction"/>
<action id="Subversion17.Add" class="org.jetbrains.idea.svn17.actions.AddAction"/>
<reference id="CheckinFiles"/>
<reference id="UpdateFiles"/>
<reference id="IntegrateFiles"/>
<reference id="CheckStatusForFiles"/>
<separator/>
<action id="Subversion.ShowProperties" class="org.jetbrains.idea.svn.actions.ShowPropertiesAction"/>
<action id="Subversion.SetProperty" class="org.jetbrains.idea.svn.actions.SetPropertyAction"/>
<action id="ShowPropertiesDiffVsLocal" class="org.jetbrains.idea.svn.actions.ShowPropertiesDiffWithLocalAction" icon="/icons/PropertiesDiffWithLocal.png">
<action id="Subversion17.ShowProperties" class="org.jetbrains.idea.svn17.actions.ShowPropertiesAction"/>
<action id="Subversion17.SetProperty" class="org.jetbrains.idea.svn17.actions.SetPropertyAction"/>
<action id="ShowPropertiesDiffVsLocal17" class="org.jetbrains.idea.svn17.actions.ShowPropertiesDiffWithLocalAction" icon="/icons/PropertiesDiffWithLocal.png">
</action>
<reference id="ChangesView.Rollback"/>
<action id="Subversion.Resolve" class="org.jetbrains.idea.svn.actions.ResolveAction"/>
<action id="Subversion.MarkResolved" class="org.jetbrains.idea.svn.actions.MarkResolvedAction"/>
<action id="Subversion.MarkTreeResolved" class="org.jetbrains.idea.svn.actions.MarkTreeConflictResolvedAction"/>
<action id="Subversion.Clenaup" class="org.jetbrains.idea.svn.actions.CleanupAction"/>
<action id="Subversion17.Resolve" class="org.jetbrains.idea.svn17.actions.ResolveAction"/>
<action id="Subversion17.MarkResolved" class="org.jetbrains.idea.svn17.actions.MarkResolvedAction"/>
<action id="Subversion17.MarkTreeResolved" class="org.jetbrains.idea.svn17.actions.MarkTreeConflictResolvedAction"/>
<action id="Subversion17.Clenaup" class="org.jetbrains.idea.svn17.actions.CleanupAction"/>
<separator/>
<reference id="Show.Current.Revision"/>
<reference id="Compare.SameVersion" text="Compare with BASE revision"/>
<reference id="Compare.LastVersion" text="Compare with revision at HEAD"/>
<reference id="Compare.Selected"/>
<action id="Subversion.CompareWithBranch" class="org.jetbrains.idea.svn.actions.CompareWithBranchAction"/>
<action id="Subversion17.CompareWithBranch" class="org.jetbrains.idea.svn17.actions.CompareWithBranchAction"/>
<reference id="Vcs.ShowTabbedFileHistory"/>
<reference id="Vcs.ShowHistoryForBlock"/>
<reference id="Annotate"/>
<action id="Subversion.Copy" class="org.jetbrains.idea.svn.actions.CopyAction"/>
<action id="Subversion17.Copy" class="org.jetbrains.idea.svn17.actions.CopyAction"/>
<separator/>
<action id="Subversion.Lock" class="org.jetbrains.idea.svn.actions.LockAction"/>
<action id="Subversion.Unlock" class="org.jetbrains.idea.svn.actions.UnlockAction"/>
<action id="Subversion.Relocate" class="org.jetbrains.idea.svn.actions.RelocateAction"/>
<action id="Subversion17.Lock" class="org.jetbrains.idea.svn17.actions.LockAction"/>
<action id="Subversion17.Unlock" class="org.jetbrains.idea.svn17.actions.UnlockAction"/>
<action id="Subversion17.Relocate" class="org.jetbrains.idea.svn17.actions.RelocateAction"/>
<separator/>
<reference id="ChangesView.Browse"/>
<separator/>
<add-to-group group-id="SubversionFilePopupGroup" anchor="last"/>
<add-to-group group-id="SubversionFilePopupGroup17" anchor="last"/>
</group>
<action id="Subversion.MarkLocallyDeletedTreeResolved" class="org.jetbrains.idea.svn.actions.MarkLocallyDeletedTreeConflictResolvedAction">
<action id="Subversion17.MarkLocallyDeletedTreeResolved" class="org.jetbrains.idea.svn17.actions.MarkLocallyDeletedTreeConflictResolvedAction">
<add-to-group group-id="ChangesViewPopupMenu" anchor="after" relative-to-action="ChangesView.Diff"/>
</action>
<reference id="Subversion.MarkTreeResolved">
<reference id="Subversion17.MarkTreeResolved">
<add-to-group group-id="ChangesViewPopupMenu" anchor="after" relative-to-action="ChangesView.Diff"/>
</reference>
<group id="SubversionUpdateActionGroup" class="org.jetbrains.idea.svn.actions.SubversionGroup">
<reference id="Subversion.Add"/>
<group id="SubversionUpdateActionGroup17" class="org.jetbrains.idea.svn17.actions.SubversionGroup">
<reference id="Subversion17.Add"/>
<reference id="ChangesView.Rollback"/>
<reference id="Subversion.Resolve"/>
<reference id="Subversion.MarkResolved"/>
<reference id="Subversion17.Resolve"/>
<reference id="Subversion17.MarkResolved"/>
<separator/>
<add-to-group group-id="UpdateActionGroup" anchor="first"/>
</group>
<group id="IgnoreChoicesGroup" popup="true" class="org.jetbrains.idea.svn.actions.IgnoreActionGroup">
<add-to-group group-id="SubversionGroup" relative-to-action="Subversion.Add" anchor="after"/>
<group id="IgnoreChoicesGroup17" popup="true" class="org.jetbrains.idea.svn17.actions.IgnoreActionGroup">
<add-to-group group-id="SubversionGroup17" relative-to-action="Subversion17.Add" anchor="after"/>
</group>
<action class="org.jetbrains.idea.svn.dialogs.MergeFromAction" id="Subversion.MergeFrom">
<action class="org.jetbrains.idea.svn17.dialogs.MergeFromAction" id="Subversion17.MergeFrom">
<add-to-group relative-to-action="Vcs.IntegrateProject" anchor="after" group-id="VcsGlobalGroup"/>
</action>
<action class="org.jetbrains.idea.svn.actions.CleanupProjectAction" id="Subversion.CleanupProject">
<add-to-group group-id="VcsGlobalGroup" anchor="after" relative-to-action="Subversion.MergeFrom"/>
<action class="org.jetbrains.idea.svn17.actions.CleanupProjectAction" id="Subversion17.CleanupProject">
<add-to-group group-id="VcsGlobalGroup" anchor="after" relative-to-action="Subversion17.MergeFrom"/>
</action>
</actions>
<extensions defaultExtensionNs="com.intellij">
<errorHandler implementation="com.intellij.diagnostic.ITNReporter"/>
<checkoutProvider implementation="org.jetbrains.idea.svn.checkout.SvnCheckoutProvider"/>
<applicationService serviceInterface="org.jetbrains.idea.svn.SvnApplicationSettings"
serviceImplementation="org.jetbrains.idea.svn.SvnApplicationSettings"/>
<projectService serviceImplementation="org.jetbrains.idea.svn.SvnConfiguration"/>
<projectService serviceInterface="org.jetbrains.idea.svn.SvnBranchConfigurationManager"
serviceImplementation="org.jetbrains.idea.svn.SvnBranchConfigurationManager"/>
<projectService serviceInterface="org.jetbrains.idea.svn.history.LoadedRevisionsCache"
serviceImplementation="org.jetbrains.idea.svn.history.LoadedRevisionsCache"/>
<projectService serviceInterface="org.jetbrains.idea.svn.mergeinfo.SvnMergeInfoCache"
serviceImplementation="org.jetbrains.idea.svn.mergeinfo.SvnMergeInfoCache"/>
<projectService serviceInterface="org.jetbrains.idea.svn.SvnLoadedBrachesStorage"
serviceImplementation="org.jetbrains.idea.svn.SvnLoadedBrachesStorage"/>
<applicationService serviceInterface="org.jetbrains.idea.svn.SvnBranchMapperManager"
serviceImplementation="org.jetbrains.idea.svn.SvnBranchMapperManager"/>
<applicationService serviceInterface="org.jetbrains.idea.svn.dialogs.browserCache.SvnRepositoryCache"
serviceImplementation="org.jetbrains.idea.svn.dialogs.browserCache.SvnRepositoryCache"/>
<applicationService serviceInterface="org.jetbrains.idea.svn.dialogs.browserCache.Loader"
serviceImplementation="org.jetbrains.idea.svn.dialogs.browserCache.CacheLoader"/>
<checkoutProvider implementation="org.jetbrains.idea.svn17.checkout.SvnCheckoutProvider"/>
<applicationService serviceInterface="org.jetbrains.idea.svn17.SvnApplicationSettings17"
serviceImplementation="org.jetbrains.idea.svn17.SvnApplicationSettings17"/>
<projectService serviceImplementation="org.jetbrains.idea.svn17.SvnConfiguration17"/>
<projectService serviceInterface="org.jetbrains.idea.svn17.SvnBranchConfigurationManager17"
serviceImplementation="org.jetbrains.idea.svn17.SvnBranchConfigurationManager17"/>
<projectService serviceInterface="org.jetbrains.idea.svn17.history.LoadedRevisionsCache17"
serviceImplementation="org.jetbrains.idea.svn17.history.LoadedRevisionsCache17"/>
<projectService serviceInterface="org.jetbrains.idea.svn17.mergeinfo.SvnMergeInfoCache17"
serviceImplementation="org.jetbrains.idea.svn17.mergeinfo.SvnMergeInfoCache17"/>
<projectService serviceInterface="org.jetbrains.idea.svn17.SvnLoadedBrachesStorage17"
serviceImplementation="org.jetbrains.idea.svn17.SvnLoadedBrachesStorage17"/>
<applicationService serviceInterface="org.jetbrains.idea.svn17.SvnBranchMapperManager17"
serviceImplementation="org.jetbrains.idea.svn17.SvnBranchMapperManager17"/>
<applicationService serviceInterface="org.jetbrains.idea.svn17.dialogs.browserCache.SvnRepositoryCache17"
serviceImplementation="org.jetbrains.idea.svn17.dialogs.browserCache.SvnRepositoryCache17"/>
<applicationService serviceInterface="org.jetbrains.idea.svn17.dialogs.browserCache.Loader17"
serviceImplementation="org.jetbrains.idea.svn17.dialogs.browserCache.CacheLoader17"/>
<vcs name="svn" vcsClass="org.jetbrains.idea.svn.SvnVcs" displayName="Subversion" administrativeAreaName=".svn, _svn"/>
<vcs name="svn17" vcsClass="org.jetbrains.idea.svn17.SvnVcs17" displayName="Subversion 1.7 Experimental" administrativeAreaName=".svn, _svn"/>
<ComponentRoamingType component="SvnConfiguration" type="DISABLED"/>
<vcsPopupProvider implementation="org.jetbrains.idea.svn.actions.SvnQuickListContentProvider"/>
<vcsPopupProvider implementation="org.jetbrains.idea.svn17.actions.SvnQuickListContentProvider"/>
</extensions>
</idea-plugin>
@@ -0,0 +1,119 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.util.IconLoader;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
public enum ConflictState {
none(false, false, false, null),
tree(true, false, false, "/icons/conflictc.png"),
text(false, true, false, "/icons/conflictt.png"),
prop(false, false, true, "/icons/conflictp.png"),
tree_text(true, true, false, "/icons/conflictct.png"), // ? -
tree_prop(true, false, true, "/icons/conflictcp.png"), // now falls but marked
text_prop(false, true, true, "/icons/conflicttp.png"),
all3(true, true, true, "/icons/conflictctp.png"); // ? -
private final boolean myTree;
private final boolean myText;
private final boolean myProperty;
@Nullable
private final Icon myIcon;
private final String myDescription;
private ConflictState(final boolean tree, final boolean text, final boolean property, final String iconPath) {
myTree = tree;
myText = text;
myProperty = property;
if (iconPath != null) {
myIcon = IconLoader.getIcon(iconPath);
} else {
myIcon = null;
}
myDescription = createDescription();
}
@Nullable
private String createDescription() {
int cnt = 0;
final StringBuilder sb = new StringBuilder();
cnt = checkOne(myTree, cnt, sb, "tree");
cnt = checkOne(myText, cnt, sb, "text");
cnt = checkOne(myProperty, cnt, sb, "property");
if (cnt == 0) {
return null;
}
return sb.toString();
}
private int checkOne(final boolean value, final int init, final StringBuilder sb, final String text) {
if (value) {
if (sb.length() > 0) {
sb.append(", ");
}
sb.append(text);
return init + 1;
}
return init;
}
public boolean isTree() {
return myTree;
}
public boolean isText() {
return myText;
}
public boolean isProperty() {
return myProperty;
}
public boolean isConflict() {
return myProperty || myText || myTree;
}
@Nullable
public Icon getIcon() {
return myIcon;
}
public String getDescription() {
return myDescription;
}
public static ConflictState mergeState(final ConflictState leftState, final ConflictState rightState) {
return getInstance(leftState.myTree | rightState.myTree, leftState.myText | rightState.myText,
leftState.myProperty | rightState.myProperty);
}
public static ConflictState getInstance(final boolean tree, final boolean text, final boolean property) {
final ConflictState[] conflictStates = values();
for (ConflictState state : conflictStates) {
if ((state.isTree() == tree) && (state.isText() == text) && (state.isProperty() == property)) {
return state;
}
}
// all combinations are defined
assert false;
return null;
}
}
@@ -0,0 +1,65 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.FileStatus;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ContentRevision;
import javax.swing.*;
public class ConflictedSvnChange extends Change {
private final ConflictState myConflictState;
// +-
private final FilePath myTreeConflictMarkHolder;
public ConflictedSvnChange(ContentRevision beforeRevision, ContentRevision afterRevision, final ConflictState conflictState,
final FilePath treeConflictMarkHolder) {
super(beforeRevision, afterRevision);
myConflictState = conflictState;
myTreeConflictMarkHolder = treeConflictMarkHolder;
}
public ConflictedSvnChange(ContentRevision beforeRevision, ContentRevision afterRevision, FileStatus fileStatus,
final ConflictState conflictState, final FilePath treeConflictMarkHolder) {
super(beforeRevision, afterRevision, fileStatus);
myConflictState = conflictState;
myTreeConflictMarkHolder = treeConflictMarkHolder;
}
public ConflictState getConflictState() {
return myConflictState;
}
@Override
public Icon getAdditionalIcon() {
return myConflictState.getIcon();
}
@Override
public String getDescription() {
final String description = myConflictState.getDescription();
if (description != null) {
return SvnBundle.message("svn.changeview.item.in.conflict.text", description);
}
return description;
}
public FilePath getTreeConflictMarkHolder() {
return myTreeConflictMarkHolder;
}
}
@@ -0,0 +1,22 @@
/*
* Copyright 2000-2009 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.svn17;
public interface CopiesRefresh {
void ensureInit();
void asynchRequest();
void synchRequest();
}
@@ -0,0 +1,97 @@
/*
* Copyright 2000-2009 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.svn17;
public class DefendedCopiesRefreshProxy implements CopiesRefresh {
private final ReentranceDefence myDefence;
private final MyEnsureInit myEnsureInit;
private final MyAsynchRequest myAsynchRequest;
private final MySynchRequest mySynchRequest;
public DefendedCopiesRefreshProxy(final CopiesRefresh refresh) {
myDefence = new ReentranceDefence();
myEnsureInit = new MyEnsureInit(refresh);
myAsynchRequest = new MyAsynchRequest(refresh);
mySynchRequest = new MySynchRequest(refresh);
}
public void ensureInit() {
ReentranceDefence.executeReentrant(myDefence, myEnsureInit);
}
public void asynchRequest() {
ReentranceDefence.executeReentrant(myDefence, myAsynchRequest);
}
public void synchRequest() {
ReentranceDefence.executeReentrant(myDefence, mySynchRequest);
}
public Runnable proxyRefresher(final Runnable runnable) {
return new Runnable() {
public void run() {
myDefence.executeOtherDefended(runnable);
}
};
}
private static class MyAsynchRequest extends MyBase {
private MyAsynchRequest(final CopiesRefresh delegate) {
super(delegate);
}
public Boolean executeMe() {
myDelegate.asynchRequest();
return null;
}
}
private static class MySynchRequest extends MyBase {
private MySynchRequest(final CopiesRefresh delegate) {
super(delegate);
}
public Boolean executeMe() {
myDelegate.synchRequest();
return null;
}
}
private static class MyEnsureInit extends MyBase {
private MyEnsureInit(final CopiesRefresh delegate) {
super(delegate);
}
public Boolean executeMe() {
myDelegate.ensureInit();
return null;
}
}
private abstract static class MyBase implements ReentranceDefence.MyControlled<Boolean> {
protected final CopiesRefresh myDelegate;
protected MyBase(final CopiesRefresh delegate) {
myDelegate = delegate;
}
public Boolean executeMeSimple() {
return null;
}
}
}
@@ -0,0 +1,64 @@
/*
* Copyright 2000-2009 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.svn17;
import org.tmatesoft.svn.core.SVNDepth;
import javax.swing.*;
public class DepthCombo extends JComboBox {
public DepthCombo(final boolean forUpdate) {
super(forUpdate ? ourForUpdate : ourForCheckout);
setSelectedIndex(forUpdate ? 0 : 3);
setEditable(false);
setToolTipText(SvnBundle.message("label.depth.description"));
}
public SVNDepth getDepth() {
return ((SVNDepthWithName) super.getSelectedItem()).getDepth();
}
private final static SVNDepthWithName [] ourForUpdate = {new SVNDepthWithName(SVNDepth.UNKNOWN, "working copy"),
new SVNDepthWithName(SVNDepth.EMPTY), new SVNDepthWithName(SVNDepth.FILES), new SVNDepthWithName(SVNDepth.IMMEDIATES),
new SVNDepthWithName(SVNDepth.INFINITY)};
private final static SVNDepthWithName [] ourForCheckout = {
new SVNDepthWithName(SVNDepth.EMPTY), new SVNDepthWithName(SVNDepth.FILES), new SVNDepthWithName(SVNDepth.IMMEDIATES),
new SVNDepthWithName(SVNDepth.INFINITY)};
private static class SVNDepthWithName {
private final SVNDepth myDepth;
private final String myName;
private SVNDepthWithName(SVNDepth depth) {
myDepth = depth;
myName = myDepth.toString();
}
private SVNDepthWithName(SVNDepth depth, String name) {
myDepth = depth;
myName = name;
}
@Override
public String toString() {
return myName;
}
public SVNDepth getDepth() {
return myDepth;
}
}
}
@@ -0,0 +1,42 @@
/*
* Copyright 2000-2010 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.svn17;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.FilePath;
import java.io.File;
import java.util.Collection;
public class FilePathUtil {
private FilePathUtil() {
}
public static boolean isNested(final Collection<FilePath> roots, final FilePath root) {
return isNested(roots, root.getIOFile());
}
public static boolean isNested(final Collection<FilePath> roots, final File root) {
for (FilePath filePath : roots) {
final File ioFile = filePath.getIOFile();
if (ioFile.equals(root)) continue;
if (FileUtil.isAncestor(ioFile, root, true)) {
return true;
}
}
return false;
}
}
@@ -0,0 +1,209 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.util.Getter;
import com.intellij.openapi.vcs.impl.VcsRootIterator;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.Nullable;
import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.wc.SVNInfo;
import org.tmatesoft.svn.core.wc.SVNWCClient;
import java.util.*;
public class ForNestedRootChecker {
static class DirInfo {
private final VirtualFile myFile;
private final String myUrl;
DirInfo(final VirtualFile file, final String url) {
myFile = file;
myUrl = url;
}
public VirtualFile getFile() {
return myFile;
}
public String getUrl() {
return myUrl;
}
}
private static class UrlConstructor {
final SvnVcs17 myVcs;
final SVNWCClient myClient;
private UrlConstructor(final SvnVcs17 vcs) {
myVcs = vcs;
myClient = myVcs.createWCClient();
}
@Nullable
public Real createReal(final VirtualFile file, final VirtualFile vcsRoot) {
final SVNInfo info = myVcs.getInfo(file);
if (info == null || info.getRepositoryRootURL() == null || info.getURL() == null) {
return null;
}
return new Real(file, info, vcsRoot);
}
public Node createReplaceable(final VirtualFile file) {
return new Node(file, null);
}
public Node createSupposed(final Real parent, final VirtualFile child) {
return new Node(child, getForChild(parent.getUrl(), child.getName()));
}
public boolean replaceWithReal(final Real real, final Node supposed) {
return supposed.getUrl() == null || ((supposed.getUrl() != null) && (! supposed.getUrl().equals(real.getUrl())));
}
@Nullable
private String getSupposedUrl(final String parentUrl, final List<String> subpath) {
if (parentUrl == null) return null;
final StringBuilder sb = new StringBuilder();
for (String s : subpath) {
sb.append(s).append('/');
}
if (sb.length() > 0) {
sb.deleteCharAt(sb.length() - 1);
}
return SVNPathUtil.append(parentUrl, sb.toString());
}
public String getForChild(final String parentUrl, final String childName) {
return parentUrl == null ? null : SVNPathUtil.append(parentUrl, SVNEncodingUtil.uriEncode(childName));
}
}
public static<T extends RootUrlPair> void filterOutSuperfluousChildren(final SvnVcs17 vcs, final List<T> list,
final List<T> result) {
final UrlConstructor constructor = new UrlConstructor(vcs);
Collections.sort(list, new Comparator<RootUrlPair>() {
public int compare(final RootUrlPair o1, final RootUrlPair o2) {
return o1.getVirtualFile().getPath().compareTo(o2.getVirtualFile().getPath());
}
});
for (int i = 0; i < list.size(); i++) {
final T child = list.get(i);
boolean add = true;
for (T parent : result) {
if (parent.getVirtualFile().getPath().equals(child.getVirtualFile().getPath())) {
add = false;
break;
}
final List<String> subpath = subpathIfAncestor(parent.getVirtualFile(), child.getVirtualFile());
if (subpath != null) {
// get child's supposed and real urls
final String supposed = constructor.getSupposedUrl(parent.getUrl(), subpath);
if (supposed.equals(child.getUrl())) {
add = false;
break;
}
}
}
if (add) {
result.add(child);
}
}
}
@Nullable
private static List<String> subpathIfAncestor(final VirtualFile parent, final VirtualFile child) {
if (! VfsUtil.isAncestor(parent, child, true)) return null;
final List<String> result = new ArrayList<String>();
VirtualFile tmp = child;
final String parentPath = parent.getPath();
while ((tmp != null) && (! tmp.getPath().equals(parentPath))) {
result.add(tmp.getName());
tmp = tmp.getParent();
}
Collections.reverse(result);
return result;
}
/*public static List<Real> getAllNestedWorkingCopies(final VirtualFile[] roots, final SvnVcs vcs, final boolean goIntoNested) {
if (goIntoNested) {
FilterDescendantVirtualFiles.filter(Arrays.asList(roots));
}
final VcsRootIterator rootIterator = new VcsRootIterator(vcs.getProject(), vcs);
final List<Real> result = new ArrayList<Real>();
for (VirtualFile root : roots) {
result.addAll(getForOne(root, vcs, goIntoNested, rootIterator));
}
if (! goIntoNested) {
final List<Real> filtered = new ArrayList<Real>(result.size());
filterOutSuperfluousChildren(vcs, result, filtered);
return filtered;
}
return result;
}*/
public static List<Real> getAllNestedWorkingCopies(final VirtualFile root, final SvnVcs17 vcs, final boolean goIntoNested, final Getter<Boolean> cancelledGetter) {
final VcsRootIterator rootIterator = new VcsRootIterator(vcs.getProject(), vcs);
return getForOne(root, vcs, goIntoNested, rootIterator, cancelledGetter);
}
private static List<Real> getForOne(final VirtualFile root, final SvnVcs17 vcs, final boolean goIntoNested,
final VcsRootIterator rootIterator, final Getter<Boolean> cancelledGetter) {
final UrlConstructor constructor = new UrlConstructor(vcs);
final LinkedList<Node> queue = new LinkedList<Node>();
final LinkedList<Real> result = new LinkedList<Real>();
queue.add(constructor.createReplaceable(root));
while (! queue.isEmpty()) {
final Node node = queue.removeFirst();
if (Boolean.TRUE.equals(cancelledGetter.get())) throw new ProcessCanceledException();
// check self
final Real real = constructor.createReal(node.getFile(), root);
if (real != null) {
if (constructor.replaceWithReal(real, node)) {
result.add(real);
if (! goIntoNested) continue;
}
}
// for next step
final VirtualFile file = node.getFile();
if (file.isDirectory() && (! SvnUtil.isAdminDirectory(file))) {
for (VirtualFile child : file.getChildren()) {
if (Boolean.TRUE.equals(cancelledGetter.get())) throw new ProcessCanceledException();
if (rootIterator.acceptFolderUnderVcs(root, child)) {
if (real == null) {
queue.add(constructor.createReplaceable(child));
} else {
queue.add(constructor.createSupposed(real, child));
}
}
}
}
}
return result;
}
}
@@ -0,0 +1,46 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.util.Factory;
import com.intellij.util.Consumer;
public class FragmentsMerger<U, T extends Consumer<U>> {
private T myData;
private final Object myLock;
private final Factory<T> myFactory;
public FragmentsMerger(final Factory<T> factory) {
myFactory = factory;
myLock = new Object();
myData = myFactory.create();
}
public void add(final U data) {
synchronized (myLock) {
// only T decides what to do with input, not the external code
myData.consume(data);
}
}
public T receive() {
synchronized (myLock) {
final T copy = myData;
myData = myFactory.create();
return copy;
}
}
}
@@ -0,0 +1,107 @@
/*
* Copyright 2000-2009 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.svn17;
import org.jetbrains.idea.svn17.config.DefaultProxyGroup;
import org.jetbrains.idea.svn17.config.ProxyGroup;
import org.tmatesoft.svn.core.internal.wc.SVNConfigFile;
import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class IdeaSVNConfigFile {
private final Map<String, String> myPatternsMap;
private final long myLatestUpdate;
private final File myFile;
private Map myDefaultProperties;
private final SVNConfigFile mySVNConfigFile;
private static final String GROUPS_GROUP_NAME = "groups";
public IdeaSVNConfigFile(final File file) {
mySVNConfigFile = new SVNConfigFile(file);
myFile = file;
myLatestUpdate = -1;
myPatternsMap = new HashMap<String, String>();
}
public void updateGroups() {
if (myLatestUpdate != myFile.lastModified()) {
myPatternsMap.clear();
myPatternsMap.putAll(mySVNConfigFile.getProperties(GROUPS_GROUP_NAME));
myDefaultProperties = mySVNConfigFile.getProperties(DefaultProxyGroup.DEFAULT_GROUP_NAME);
}
}
public Map<String, ProxyGroup> getAllGroups() {
final Map<String, ProxyGroup> result = new HashMap<String, ProxyGroup>(myPatternsMap.size());
for (Map.Entry<String, String> entry : myPatternsMap.entrySet()) {
final String groupName = entry.getKey();
result.put(groupName, new ProxyGroup(groupName, entry.getValue(), mySVNConfigFile.getProperties(groupName)));
}
return result;
}
public DefaultProxyGroup getDefaultGroup() {
return new DefaultProxyGroup(myDefaultProperties);
}
public void deleteGroup(final String name) {
// remove all properties
final Map<String, String> properties = mySVNConfigFile.getProperties(name);
for (String propertyName : properties.keySet()) {
mySVNConfigFile.setPropertyValue(name, propertyName, null, false);
}
if (DefaultProxyGroup.DEFAULT_GROUP_NAME.equals(name)) {
myDefaultProperties.clear();
}
// remove group from groups
mySVNConfigFile.setPropertyValue(GROUPS_GROUP_NAME, name, null, false);
mySVNConfigFile.deleteGroup(name, false);
}
public void addGroup(final String name, final String patterns, final Map<String, String> properties) {
mySVNConfigFile.setPropertyValue(GROUPS_GROUP_NAME, name, patterns, false);
addProperties(name, properties);
}
private void addProperties(final String groupName, final Map<String, String> properties) {
for (Map.Entry<String, String> entry : properties.entrySet()) {
mySVNConfigFile.setPropertyValue(groupName, entry.getKey(), entry.getValue(), false);
}
}
public void modifyGroup(final String name, final String patterns, final Collection<String> delete, final Map<String, String> addOrModify,
final boolean isDefault) {
if (! isDefault) {
mySVNConfigFile.setPropertyValue(GROUPS_GROUP_NAME, name, patterns, false);
}
final Map<String,String> deletedPrepared = new HashMap<String, String>(delete.size());
for (String property : delete) {
deletedPrepared.put(property, null);
}
addProperties(name, deletedPrepared);
addProperties(name, addOrModify);
}
public void save() {
mySVNConfigFile.save();
}
}
@@ -0,0 +1,34 @@
/*
* Copyright 2000-2010 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.svn17;
import com.intellij.openapi.util.SystemInfo;
import org.tmatesoft.svn.core.wc.SVNWCUtil;
import java.io.File;
public class IdeaSubversionConfigurationDirectory {
private IdeaSubversionConfigurationDirectory() {
}
public static String getPath() {
final File standard = SVNWCUtil.getDefaultConfigurationDirectory();
if (SystemInfo.isWindows) {
return standard.getAbsolutePath();
}
return standard.getParent() + File.separator + standard.getName() + "_IDEA";
}
}
@@ -0,0 +1,110 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.diagnostic.Logger;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
import org.tmatesoft.svn.core.wc.ISVNStatusHandler;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNStatus;
import org.tmatesoft.svn.core.wc.SVNStatusType;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class IgnoredFileInfo {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.IgnoredFileInfo");
// directory for which properties are collected
private final File myFile;
private final List<String> myPatterns;
private final Set<String> myFileNames;
private final Set<String> myOldPatterns;
public IgnoredFileInfo(final File file, final Set<String> oldPatterns) {
myFile = file;
myPatterns = new ArrayList<String>();
myFileNames = new HashSet<String>();
myOldPatterns = oldPatterns;
}
public void addFileName(final String name) {
myFileNames.add(name);
}
public void addPattern(final String value) {
myPatterns.add(value);
}
public void calculatePatterns(final SvnVcs17 vcs) {
final List<String> names = new ArrayList<String>();
try {
vcs.createStatusClient().doStatus(myFile, SVNRevision.WORKING, SVNDepth.IMMEDIATES, false, true, true, false, new ISVNStatusHandler() {
public void handleStatus(SVNStatus status) throws SVNException {
if (SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_IGNORED)) {
final String name = status.getFile().getName();
if (! myFileNames.contains(name)) {
names.add(name);
}
}
}
}, null);
}
catch (SVNException e) {
LOG.info(e);
}
for (String pattern : myOldPatterns) {
boolean usedSomewhereElse = false;
for (String name : names) {
if (DefaultSVNOptions.matches(pattern, name)) {
usedSomewhereElse = true;
break;
}
}
if (! usedSomewhereElse) {
for (String name : myFileNames) {
if (DefaultSVNOptions.matches(pattern, name)) {
myPatterns.add(pattern);
break;
}
}
}
}
}
public File getFile() {
return myFile;
}
public List<String> getPatterns() {
return myPatterns;
}
public Set<String> getFileNames() {
return myFileNames;
}
public Set<String> getOldPatterns() {
return myOldPatterns;
}
}
@@ -0,0 +1,48 @@
/*
* Copyright 2000-2009 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.svn17;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class LimitedStringsList {
private static final int ourMaxTypedUrls = 20;
private final List<String> myList;
public LimitedStringsList(final List<String> list) {
myList = new ArrayList<String>((list.size() > ourMaxTypedUrls ? list.subList(0, ourMaxTypedUrls) : list));
}
public void add(final String value) {
myList.remove(value);
if (myList.size() >= ourMaxTypedUrls) {
// leave space for 1 to be added
final int numToRemove = myList.size() - ourMaxTypedUrls + 1;
for (int i = 0; i < numToRemove; i++) {
myList.remove(0);
}
}
// more recent first
myList.add(0, value);
}
public List<String> getList() {
return Collections.unmodifiableList(myList);
}
}
@@ -0,0 +1,26 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.vcs.changes.Change;
public class MoveRenameReplaceCheck {
public static boolean check(final Change c) {
if (c.getAfterRevision() == null || c.getBeforeRevision() == null) return false;
return c.isIsReplaced() || c.isMoved() || c.isRenamed() || (! Comparing.equal(c.getBeforeRevision().getFile(), c.getAfterRevision().getFile()));
}
}
@@ -0,0 +1,128 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.wc.SVNStatus;
import org.tmatesoft.svn.core.wc.SVNStatusType;
import java.util.HashSet;
import java.util.Set;
public class NestedCopiesBuilder implements StatusReceiver {
private final Set<MyPointInfo> mySet;
public NestedCopiesBuilder() {
mySet = new HashSet<MyPointInfo>();
}
public void process(final FilePath path, final SVNStatus status, final boolean isInnerCopyRoot) throws SVNException {
if ((path.getVirtualFile() != null) && SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_EXTERNAL)) {
final MyPointInfo info = new MyPointInfo(path.getVirtualFile(), null, WorkingCopyFormat.UNKNOWN, NestedCopyType.external);
mySet.add(info);
return;
}
if ((path.getVirtualFile() == null) || (status.getURL() == null)) return;
final NestedCopyType type;
if (SvnVcs17.svnStatusIsUnversioned(status)) {
return;
} else if (status.isSwitched()) {
type = NestedCopyType.switched;
} else if (isInnerCopyRoot) {
// will not be changed or modified; can't be switched or external
type = NestedCopyType.inner;
} else {
return;
}
final MyPointInfo info = new MyPointInfo(path.getVirtualFile(), status.getURL(),
WorkingCopyFormat.getInstance(status.getWorkingCopyFormat()), type);
mySet.add(info);
}
public void processIgnored(final VirtualFile vFile) {
}
public void processUnversioned(final VirtualFile vFile) {
}
static class MyPointInfo {
private final VirtualFile myFile;
private SVNURL myUrl;
private WorkingCopyFormat myFormat;
private final NestedCopyType myType;
MyPointInfo(@NotNull final VirtualFile file, final SVNURL url, final WorkingCopyFormat format, final NestedCopyType type) {
myFile = file;
myUrl = url;
myFormat = format;
myType = type;
}
public void setUrl(SVNURL url) {
myUrl = url;
}
public void setFormat(WorkingCopyFormat format) {
myFormat = format;
}
public VirtualFile getFile() {
return myFile;
}
public SVNURL getUrl() {
return myUrl;
}
public WorkingCopyFormat getFormat() {
return myFormat;
}
public NestedCopyType getType() {
return myType;
}
private String key(final VirtualFile file) {
return file.getPath();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyPointInfo info = (MyPointInfo)o;
if (! key(myFile).equals(key(info.myFile))) return false;
return true;
}
@Override
public int hashCode() {
return key(myFile).hashCode();
}
}
public Set<MyPointInfo> getSet() {
return mySet;
}
}
@@ -0,0 +1,38 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.util.Consumer;
import java.util.HashSet;
import java.util.Set;
public class NestedCopiesData implements Consumer<Set<NestedCopiesBuilder.MyPointInfo>> {
// we can keep the type here also, but
private final Set<NestedCopiesBuilder.MyPointInfo> mySet;
public NestedCopiesData() {
mySet = new HashSet<NestedCopiesBuilder.MyPointInfo>();
}
public void consume(final Set<NestedCopiesBuilder.MyPointInfo> nestedCopyTypeSet) {
mySet.addAll(nestedCopyTypeSet);
}
public Set<NestedCopiesBuilder.MyPointInfo> getSet() {
return mySet;
}
}
@@ -0,0 +1,30 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.util.Factory;
import java.util.Set;
public class NestedCopiesSink extends FragmentsMerger<Set<NestedCopiesBuilder.MyPointInfo>, NestedCopiesData> {
public NestedCopiesSink() {
super(new Factory<NestedCopiesData>() {
public NestedCopiesData create() {
return new NestedCopiesData();
}
});
}
}
@@ -0,0 +1,32 @@
/*
* Copyright 2000-2009 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.svn17;
public enum NestedCopyType {
external("External"),
switched("Switched"),
inner("Nested");
private final String myName;
NestedCopyType(String name) {
myName = name;
}
public String getName() {
return myName;
}
}
@@ -0,0 +1,36 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.vfs.VirtualFile;
class Node {
private final VirtualFile myFile;
private final String myUrl;
Node(final VirtualFile file, final String url) {
myFile = file;
myUrl = url;
}
public VirtualFile getFile() {
return myFile;
}
public String getUrl() {
return myUrl;
}
}
@@ -0,0 +1,58 @@
/*
* Copyright 2000-2011 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.svn17;
import org.tmatesoft.svn.core.auth.*;
/**
* @author irengrig
* Date: 1/24/11
* Time: 6:14 PM
*/
public class ProxySvnAuthentication {
private ProxySvnAuthentication() {
}
public static SVNAuthentication proxy(final SVNAuthentication in, final boolean storeAuth) {
if (in.isStorageAllowed() == storeAuth || (! in.isStorageAllowed())) return in;
final String userName = in.getUserName();
if (in instanceof SVNPasswordAuthentication) {
return new SVNPasswordAuthentication(userName, ((SVNPasswordAuthentication)in).getPassword(),
storeAuth, in.getURL(), in.isPartial());
} else if (in instanceof SVNSSHAuthentication) {
final SVNSSHAuthentication svnsshAuthentication = (SVNSSHAuthentication)in;
if (svnsshAuthentication.hasPrivateKey()) {
return new SVNSSHAuthentication(userName, svnsshAuthentication.getPrivateKeyFile(), svnsshAuthentication.getPassphrase(),
svnsshAuthentication.getPortNumber(), storeAuth, in.getURL(), in.isPartial());
} else {
return new SVNSSHAuthentication(userName, svnsshAuthentication.getPassword(), svnsshAuthentication.getPortNumber(),
storeAuth, in.getURL(), in.isPartial());
}
} else if (in instanceof SVNSSLAuthentication) {
final SVNSSLAuthentication svnsslAuthentication = (SVNSSLAuthentication)in;
if (SVNSSLAuthentication.MSCAPI.equals(svnsslAuthentication.getSSLKind())) {
return new SVNSSLAuthentication(SVNSSLAuthentication.MSCAPI, svnsslAuthentication.getAlias(), storeAuth, in.getURL(), in.isPartial());
} else {
return new SVNSSLAuthentication(svnsslAuthentication.getCertificateFile(), svnsslAuthentication.getPassword(),
storeAuth, svnsslAuthentication.getURL(), svnsslAuthentication.isPartial());
}
} else if (in instanceof SVNUserNameAuthentication) {
return new SVNUserNameAuthentication(in.getUserName(), storeAuth, in.getURL(), in.isPartial());
}
return in;
}
}
@@ -0,0 +1,42 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.vfs.VirtualFile;
import org.tmatesoft.svn.core.wc.SVNInfo;
class Real extends Node implements RootUrlPair {
private final SVNInfo myInfo;
private final VirtualFile myVcsRoot;
Real(final VirtualFile file, final SVNInfo info, VirtualFile vcsRoot) {
super(file, info.getURL().toString());
myInfo = info;
myVcsRoot = vcsRoot;
}
public SVNInfo getInfo() {
return myInfo;
}
public VirtualFile getVcsRoot() {
return myVcsRoot;
}
public VirtualFile getVirtualFile() {
return getFile();
}
}
@@ -0,0 +1,58 @@
/*
* Copyright 2000-2009 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.svn17;
public class ReentranceDefence {
protected final ThreadLocal<Boolean> myThreadLocal;
protected ReentranceDefence() {
myThreadLocal = new ThreadLocal<Boolean>() {
@Override
protected Boolean initialValue() {
return Boolean.TRUE;
}
};
}
public static <T> T executeReentrant(final ReentranceDefence defence, final MyControlled<T> controlled) {
if (defence.isInside()) {
return controlled.executeMeSimple();
}
return controlled.executeMe();
}
public boolean isInside() {
return ! Boolean.TRUE.equals(myThreadLocal.get());
}
public boolean isOutside() {
return ! isInside();
}
public void executeOtherDefended(final Runnable runnable) {
try {
myThreadLocal.set(Boolean.FALSE);
runnable.run();
} finally {
myThreadLocal.set(Boolean.TRUE);
}
}
public interface MyControlled<T> {
T executeMe();
T executeMeSimple();
}
}
@@ -0,0 +1,55 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.vfs.VirtualFile;
import org.tmatesoft.svn.core.SVNURL;
public class RootMixedInfo {
/**
* working copy root path
*/
private final String myFilePath;
private final VirtualFile myFile;
/**
* url corresponding to working copy root
*/
private final SVNURL myUrl;
private final VirtualFile myParentVcsRoot;
public RootMixedInfo(final String filePath, final VirtualFile file, final SVNURL url, final VirtualFile parentVcsRoot) {
myFilePath = filePath;
myFile = file;
myUrl = url;
myParentVcsRoot = parentVcsRoot;
}
public String getFilePath() {
return myFilePath;
}
public VirtualFile getFile() {
return myFile;
}
public SVNURL getUrl() {
return myUrl;
}
public VirtualFile getParentVcsRoot() {
return myParentVcsRoot;
}
}
@@ -0,0 +1,92 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.vfs.VirtualFile;
import org.tmatesoft.svn.core.SVNURL;
import java.io.File;
public class RootUrlInfo implements RootUrlPair {
private final SVNURL myRepositoryUrlUrl;
private final String myRepositoryUrl;
private final SVNURL myAbsoluteUrlAsUrl;
private final WorkingCopyFormat myFormat;
private final File myIoFile;
private final VirtualFile myVfile;
// vcs root
private final VirtualFile myRoot;
private NestedCopyType myType;
public RootUrlInfo(final SVNURL repositoryUrl, final SVNURL absoluteUrlAsUrl, final WorkingCopyFormat format, final VirtualFile vfile,
final VirtualFile root) {
myRepositoryUrlUrl = repositoryUrl;
myFormat = format;
myVfile = vfile;
myRoot = root;
myIoFile = new File(myVfile.getPath());
final String asString = repositoryUrl.toString();
myRepositoryUrl = asString.endsWith("/") ? asString.substring(0, asString.length() - 1) : asString;
myAbsoluteUrlAsUrl = absoluteUrlAsUrl;
//myType = NestedCopyType.inner; // default
}
public String getRepositoryUrl() {
return myRepositoryUrl;
}
public SVNURL getRepositoryUrlUrl() {
return myRepositoryUrlUrl;
}
public String getAbsoluteUrl() {
return myAbsoluteUrlAsUrl.toString();
}
public SVNURL getAbsoluteUrlAsUrl() {
return myAbsoluteUrlAsUrl;
}
public WorkingCopyFormat getFormat() {
return myFormat;
}
public File getIoFile() {
return myIoFile;
}
// vcs root
public VirtualFile getRoot() {
return myRoot;
}
public VirtualFile getVirtualFile() {
return myVfile;
}
public String getUrl() {
return myAbsoluteUrlAsUrl.toString();
}
public NestedCopyType getType() {
return myType;
}
public void setType(NestedCopyType type) {
myType = type;
}
}
@@ -0,0 +1,23 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.vfs.VirtualFile;
public interface RootUrlPair {
VirtualFile getVirtualFile();
String getUrl();
}
@@ -0,0 +1,174 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.progress.BackgroundTaskQueue;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.CalledInBackground;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.VcsListener;
import com.intellij.openapi.vcs.changes.BackgroundFromStartOption;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.internal.util.SVNURLUtil;
import org.tmatesoft.svn.core.internal.wc.admin.SVNEntry;
import org.tmatesoft.svn.core.internal.wc.admin.SVNWCAccess;
import org.tmatesoft.svn.core.wc.SVNInfo;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNWCUtil;
import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
// 1. listen to roots changes
// 2. - possibly - to deletion/checkouts??? what if WC roots can be
public class RootsToWorkingCopies implements VcsListener {
private final Object myLock;
private final Map<VirtualFile, WorkingCopy> myRootMapping;
private final Set<VirtualFile> myUnversioned;
private final BackgroundTaskQueue myQueue;
private final Project myProject;
public RootsToWorkingCopies(final Project project) {
myProject = project;
myQueue = new BackgroundTaskQueue(project, "SVN VCS roots authorization checker");
myLock = new Object();
myRootMapping = new HashMap<VirtualFile, WorkingCopy>();
myUnversioned = new HashSet<VirtualFile>();
}
public void addRoot(final VirtualFile root) {
myQueue.run(new Task.Backgroundable(myProject, "Looking for '" + root.getPath() + "' working copy root", false,
BackgroundFromStartOption.getInstance()) {
public void run(@NotNull ProgressIndicator indicator) {
calculateRoot(root);
}
});
}
@Nullable
@CalledInBackground
public WorkingCopy getMatchingCopy(final SVNURL url) {
assert ! ApplicationManager.getApplication().isDispatchThread();
final VirtualFile[] roots = ProjectLevelVcsManager.getInstance(myProject).getRootsUnderVcs(SvnVcs17.getInstance(myProject));
synchronized (myLock) {
for (VirtualFile root : roots) {
final WorkingCopy wcRoot = getWcRoot(root);
if (wcRoot != null) {
final SVNURL common = SVNURLUtil.getCommonURLAncestor(wcRoot.getUrl(), url);
if (wcRoot.getUrl().equals(common) || url.equals(common)) {
return wcRoot;
}
}
}
}
return null;
}
@CalledInBackground
@Nullable
public WorkingCopy getWcRoot(final VirtualFile root) {
assert ! ApplicationManager.getApplication().isDispatchThread();
synchronized (myLock) {
if (myUnversioned.contains(root)) return null;
final WorkingCopy existing = myRootMapping.get(root);
if (existing != null) return existing;
}
return calculateRoot(root);
}
@Nullable
private WorkingCopy calculateRoot(final VirtualFile root) {
WorkingCopy workingCopy = null;
final File ioFile = new File(root.getPath());
File workingCopyRoot = null;
try {
workingCopyRoot = SVNWCUtil.getWorkingCopyRoot(ioFile, true);
if (workingCopyRoot != null) {
SVNWCAccess wcAccess = SVNWCAccess.newInstance(null);
try {
wcAccess.probeOpen(workingCopyRoot, false, 0);
SVNEntry entry = wcAccess.getVersionedEntry(workingCopyRoot, false);
final SVNURL url = entry.getSVNURL();
if (url != null) {
workingCopy = new WorkingCopy(workingCopyRoot, url);
}
} finally {
wcAccess.close();
}
}
}
catch (SVNException e) {
//
}
if (workingCopyRoot == null) {
workingCopyRoot = SvnUtil.getWcCopyRootIf17(ioFile, null);
if (workingCopyRoot != null) {
final SVNInfo svnInfo;
try {
svnInfo = SvnVcs17.getInstance(myProject).createWCClient().doInfo(workingCopyRoot, SVNRevision.UNDEFINED);
workingCopy = new WorkingCopy(workingCopyRoot, svnInfo.getURL());
}
catch (SVNException e) {
//
}
}
}
synchronized (myLock) {
if (workingCopy == null) {
myRootMapping.remove(root);
myUnversioned.add(root);
} else {
myUnversioned.remove(root);
myRootMapping.put(root, workingCopy);
}
}
return workingCopy;
}
public void clear() {
synchronized (myLock) {
myRootMapping.clear();
myUnversioned.clear();
}
}
public void directoryMappingChanged() {
final SvnVcs17 svnVcs = SvnVcs17.getInstance(myProject);
// todo +- here... shouldnt be
svnVcs.getAuthNotifier().clear();
final VirtualFile[] roots = ProjectLevelVcsManager.getInstance(myProject).getRootsUnderVcs(svnVcs);
synchronized (myLock) {
clear();
for (VirtualFile root : roots) {
addRoot(root);
}
}
}
}
@@ -0,0 +1,29 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vfs.VirtualFile;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.wc.SVNStatus;
import java.util.EventListener;
public interface StatusReceiver extends EventListener {
void process(final FilePath path, final SVNStatus status, final boolean isInnerCopyRoot) throws SVNException;
void processIgnored(final VirtualFile vFile);
void processUnversioned(final VirtualFile vFile);
}
@@ -0,0 +1,26 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.vfs.VirtualFile;
import org.tmatesoft.svn.core.wc.SVNStatusClient;
public interface StatusWalkerPartner {
SVNStatusClient createStatusClient();
void checkCanceled();
boolean isExcluded(final VirtualFile vFile);
boolean isIgnoredIdeaLevel(final VirtualFile vFile);
}
@@ -0,0 +1,78 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.lifecycle.PeriodicalTasksCloser;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.roots.FileIndexFacade;
import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vfs.VirtualFile;
import org.tmatesoft.svn.core.SVNCancelException;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.ISVNStatusFileProvider;
import org.tmatesoft.svn.core.wc.SVNEvent;
import org.tmatesoft.svn.core.wc.SVNStatusClient;
public class StatusWalkerPartnerImpl implements StatusWalkerPartner {
private final SvnVcs17 myVcs;
private final ChangeListManager myClManager;
private final FileIndexFacade myExcludedFileIndex;
private final ProgressIndicator myIndicator;
private ISVNStatusFileProvider myFileProvider;
public StatusWalkerPartnerImpl(final SvnVcs17 vcs, final ProgressIndicator pi) {
myVcs = vcs;
myClManager = ChangeListManager.getInstance(myVcs.getProject());
myExcludedFileIndex = PeriodicalTasksCloser.getInstance().safeGetService(myVcs.getProject(), FileIndexFacade.class);
myIndicator = pi;
}
public void setFileProvider(final ISVNStatusFileProvider fileProvider) {
myFileProvider = fileProvider;
}
public SVNStatusClient createStatusClient() {
final SVNStatusClient result = myVcs.createStatusClient();
result.setFilesProvider(myFileProvider);
result.setEventHandler(new ISVNEventHandler() {
public void handleEvent(SVNEvent event, double progress) throws SVNException {
//
}
public void checkCancelled() throws SVNCancelException {
if (myIndicator != null) {
myIndicator.checkCanceled();
}
}
});
return result;
}
public void checkCanceled() {
if (myIndicator != null) {
myIndicator.checkCanceled();
}
}
public boolean isExcluded(VirtualFile vFile) {
return myExcludedFileIndex.isExcludedFile(vFile);
}
public boolean isIgnoredIdeaLevel(VirtualFile vFile) {
return myClManager.isIgnoredFile(vFile);
}
}
@@ -0,0 +1,160 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.project.Project;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@State(
name="SvnApplicationSettings17",
storages = {
@Storage(
file="$APP_CONFIG$/other.xml"
)}
)
public class SvnApplicationSettings17 implements PersistentStateComponent<SvnApplicationSettings17.ConfigurationBean> {
private SvnFileSystemListenerWrapper myVFSHandler;
private int mySvnProjectCount;
private LimitedStringsList myLimitedStringsList;
public static class ConfigurationBean {
public List<String> myCheckoutURLs = new ArrayList<String>();
public List<String> myTypedURLs = new ArrayList<String>();
}
private ConfigurationBean myConfigurationBean;
public static SvnApplicationSettings17 getInstance() {
return ServiceManager.getService(SvnApplicationSettings17.class);
}
public SvnApplicationSettings17() {
myConfigurationBean = new ConfigurationBean();
}
public ConfigurationBean getState() {
myConfigurationBean.myTypedURLs.clear();
myConfigurationBean.myTypedURLs.addAll(getTypedList().getList());
return myConfigurationBean;
}
public void loadState(ConfigurationBean object) {
myConfigurationBean = object;
getTypedList();
}
private LimitedStringsList getTypedList() {
if (myLimitedStringsList == null) {
checkFillTypedFromCheckout();
myLimitedStringsList = new LimitedStringsList(myConfigurationBean.myTypedURLs);
}
return myLimitedStringsList;
}
private void checkFillTypedFromCheckout() {
if (myConfigurationBean.myTypedURLs.isEmpty() && (! myConfigurationBean.myCheckoutURLs.isEmpty())) {
myConfigurationBean.myTypedURLs.addAll(myConfigurationBean.myCheckoutURLs);
}
}
public void svnActivated() {
if (myVFSHandler == null) {
myVFSHandler = new SvnFileSystemListenerWrapper(new SvnFileSystemListener());
myVFSHandler.registerSelf();
}
mySvnProjectCount++;
}
public void svnDeactivated() {
mySvnProjectCount--;
if (mySvnProjectCount == 0) {
myVFSHandler.unregisterSelf();
myVFSHandler = null;
// todo what should be done instead?
//SVNSSHSession.shutdown();
}
}
private static File getCommonPath() {
File file = new File(PathManager.getSystemPath());
file = new File(file, "plugins");
file = new File(file, "svn4idea17");
file.mkdirs();
return file;
}
public static File getCredentialsFile() {
return new File(getCommonPath(), "credentials.xml");
}
private static final String LOADED_REVISIONS_DIR = "loadedRevisions";
public static File getLoadedRevisionsDir(final Project project) {
File file = getCommonPath();
file = new File(file, LOADED_REVISIONS_DIR);
file = new File(file, project.getLocationHash());
file.mkdirs();
return file;
}
public Collection<String> getCheckoutURLs() {
return myConfigurationBean.myCheckoutURLs;
}
public void addCheckoutURL(String url) {
if (myConfigurationBean.myCheckoutURLs.contains(url)) {
return;
}
myConfigurationBean.myCheckoutURLs.add(0, url);
}
public void removeCheckoutURL(String url) {
if (myConfigurationBean.myCheckoutURLs != null) {
// 'url' is not necessary an exact match for some of the urls in collection - it has been parsed and then converted back to string
for(String oldUrl: myConfigurationBean.myCheckoutURLs) {
try {
if (url.equals(oldUrl) || SVNURL.parseURIEncoded(url).equals(SVNURL.parseURIEncoded(oldUrl))) {
myConfigurationBean.myCheckoutURLs.remove(oldUrl);
break;
}
}
catch (SVNException e) {
// ignore
}
}
}
}
public List<String> getTypedUrlsListCopy() {
return new ArrayList<String>(getTypedList().getList());
}
public void addTypedUrl(final String url) {
getTypedList().add(url);
}
}
@@ -0,0 +1,58 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.util.Comparing;
import org.tmatesoft.svn.core.auth.*;
public class SvnAuthEquals {
private SvnAuthEquals() {
}
public static boolean equals(final SVNAuthentication a1, final SVNAuthentication a2) {
if (a1 == a2) return true;
if (a1 == null || a2 == null) return false;
if (! Comparing.equal(a1.getKind(), a2.getKind())) return false;
if (! Comparing.equal(a1.getUserName(), a2.getUserName())) return false;
final Class<? extends SVNAuthentication> a1Class = a1.getClass();
if (! a1Class.equals(a2.getClass())) return false;
if (SVNUserNameAuthentication.class.equals(a1Class)) return true;
if (SVNPasswordAuthentication.class.equals(a1Class)) {
return Comparing.equal(((SVNPasswordAuthentication) a1).getPassword(), ((SVNPasswordAuthentication) a2).getPassword());
}
if (SVNSSLAuthentication.class.equals(a1Class)) {
if (! Comparing.equal(((SVNSSLAuthentication) a1).getCertificateFile(), ((SVNSSLAuthentication) a2).getCertificateFile())) return false;
return Comparing.equal(((SVNSSLAuthentication) a1).getPassword(), ((SVNSSLAuthentication) a2).getPassword());
}
if (SVNSSHAuthentication.class.equals(a1Class)) {
if (! Comparing.equal(((SVNSSHAuthentication) a1).getPrivateKeyFile(), ((SVNSSHAuthentication) a2).getPrivateKeyFile())) return false;
if (! Comparing.equal(((SVNSSHAuthentication) a1).getPassphrase(), ((SVNSSHAuthentication) a2).getPassphrase())) return false;
if (! Comparing.equal(((SVNSSHAuthentication) a1).getPortNumber(), ((SVNSSHAuthentication) a2).getPortNumber())) return false;
return Comparing.equal(((SVNSSHAuthentication) a1).getPassword(), ((SVNSSHAuthentication) a2).getPassword());
}
return false;
}
public static int hashCode(final SVNAuthentication a) {
int result = a.getKind().hashCode();
if (a.getUserName() != null) {
result = (31 * result) + a.getUserName().hashCode();
}
return result;
}
}
@@ -0,0 +1,825 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Getter;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.CalledInAwt;
import com.intellij.openapi.vcs.changes.committed.AbstractCalledLater;
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
import com.intellij.util.EventDispatcher;
import com.intellij.util.net.HttpConfigurable;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn17.auth.ProviderType;
import org.jetbrains.idea.svn17.auth.SvnAuthenticationInteraction;
import org.jetbrains.idea.svn17.auth.SvnAuthenticationListener;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNProperties;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.*;
import org.tmatesoft.svn.core.internal.wc.*;
import org.tmatesoft.svn.core.io.SVNRepository;
import java.io.File;
import java.io.IOException;
import java.util.*;
/**
* @author alex
*/
public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager implements SvnAuthenticationListener {
// while Mac storage not working for IDEA, we use this key to check whether to prompt abt plaintext or just store
private static final Logger LOG = Logger.getInstance(SvnAuthenticationManager.class.getName());
public static final String SVN_SSH = "svn+ssh";
public static final String HTTP = "http";
public static final String HTTPS = "https";
private final Project myProject;
private File myConfigDirectory;
private PersistentAuthenticationProviderProxy myPersistentAuthenticationProviderProxy;
private SvnConfiguration17 myConfig;
private static final ThreadLocal<Boolean> ourJustEntered = new ThreadLocal<Boolean>();
private SvnAuthenticationInteraction myInteraction;
private EventDispatcher<SvnAuthenticationListener> myListener;
private IdeaSVNHostOptionsProvider myLocalHostOptionsProvider;
private final ThreadLocalSavePermissions mySavePermissions;
private final Map<Thread, String> myKeyAlgorithm;
public SvnAuthenticationManager(final Project project, final File configDirectory) {
super(configDirectory, true, null, null);
myProject = project;
myConfigDirectory = configDirectory;
myKeyAlgorithm = new HashMap<Thread, String>();
ensureListenerCreated();
mySavePermissions = new ThreadLocalSavePermissions();
myConfig = SvnConfiguration17.getInstance(myProject);
if (myPersistentAuthenticationProviderProxy != null) {
myPersistentAuthenticationProviderProxy.setProject(myProject);
}
myInteraction = new MySvnAuthenticationInteraction(myProject);
}
private void ensureListenerCreated() {
if (myListener == null) {
myListener = EventDispatcher.create(SvnAuthenticationListener.class);
}
}
@Override
public IdeaSVNHostOptionsProvider getHostOptionsProvider() {
if (myLocalHostOptionsProvider == null) {
myLocalHostOptionsProvider = new IdeaSVNHostOptionsProvider();
}
return myLocalHostOptionsProvider;
}
public void addListener(final SvnAuthenticationListener listener) {
myListener.addListener(listener);
}
@Override
public void actualSaveWillBeTried(ProviderType type, SVNURL url, String realm, String kind) {
myListener.getMulticaster().actualSaveWillBeTried(type, url, realm, kind);
}
@Override
public void saveAttemptStarted(ProviderType type, SVNURL url, String realm, String kind) {
myListener.getMulticaster().saveAttemptStarted(type, url, realm, kind);
}
@Override
public void saveAttemptFinished(ProviderType type, SVNURL url, String realm, String kind) {
myListener.getMulticaster().saveAttemptFinished(type, url, realm, kind);
}
@Override
public void requested(ProviderType type, SVNURL url, String realm, String kind, boolean canceled) {
if (ProviderType.interactive.equals(type) && (! canceled)) {
ourJustEntered.set(true);
}
myListener.getMulticaster().requested(type, url, realm, kind, canceled);
}
@Override
protected ISVNAuthenticationProvider createCacheAuthenticationProvider(File authDir, String userName) {
// this is a hack due to the fact this method is called from super() constructor
myConfigDirectory = new File(authDir.getParent());
myPersistentAuthenticationProviderProxy = new PersistentAuthenticationProviderProxy(authDir, userName);
return myPersistentAuthenticationProviderProxy;
}
@Override
public void acknowledgeAuthentication(boolean accepted,
String kind,
String realm,
SVNErrorMessage errorMessage,
SVNAuthentication authentication) throws SVNException {
try {
final boolean authStorageEnabled = getHostOptionsProvider().getHostOptions(authentication.getURL()).isAuthStorageEnabled();
final SVNAuthentication proxy = ProxySvnAuthentication.proxy(authentication, authStorageEnabled);
super.acknowledgeAuthentication(accepted, kind, realm, errorMessage, proxy);
} finally {
mySavePermissions.remove();
}
}
private class PersistentAuthenticationProviderProxy implements ISVNAuthenticationProvider, ISVNPersistentAuthenticationProvider {
private final ISVNAuthenticationProvider myDelegate;
private final ISVNGnomeKeyringPasswordProvider myISVNGnomeKeyringPasswordProvider;
private final File myAuthDir;
private Project myProject;
private PersistentAuthenticationProviderProxy(File authDir, String userName) {
myISVNGnomeKeyringPasswordProvider = new MyKeyringMasterKeyProvider(myProject);
ISVNAuthenticationStorageOptions delegatingOptions = new ISVNAuthenticationStorageOptions() {
public boolean isNonInteractive() throws SVNException {
return getAuthenticationStorageOptions().isNonInteractive();
}
public ISVNAuthStoreHandler getAuthStoreHandler() throws SVNException {
return getAuthenticationStorageOptions().getAuthStoreHandler();
}
@Override
public ISVNGnomeKeyringPasswordProvider getGnomeKeyringPasswordProvider() {
return myISVNGnomeKeyringPasswordProvider;
}
@Override
public boolean isSSLPassphrasePromptSupported() {
return false;
}
};
ensureListenerCreated();
myDelegate = new DefaultSVNPersistentAuthenticationProvider(authDir, userName, delegatingOptions, getDefaultOptions(), getHostOptionsProvider()) {
@Override
protected IPasswordStorage[] createPasswordStorages(DefaultSVNOptions options) {
final IPasswordStorage[] passwordStorages = super.createPasswordStorages(options);
final IPasswordStorage[] proxied = new IPasswordStorage[passwordStorages.length];
for (int i = 0; i < passwordStorages.length; i++) {
final IPasswordStorage storage = passwordStorages[i];
proxied[i] = new ProxyPasswordStorageForDebug(storage, myListener);
}
return proxied;
}
};
myAuthDir = authDir;
}
public void setProject(Project project) {
myProject = project;
}
public SVNAuthentication requestClientAuthentication(final String kind, final SVNURL url, final String realm, final SVNErrorMessage errorMessage,
final SVNAuthentication previousAuth, final boolean authMayBeStored) {
final SVNAuthentication svnAuthentication =
myDelegate.requestClientAuthentication(kind, url, realm, errorMessage, previousAuth, authMayBeStored);
myListener.getMulticaster().requested(ProviderType.persistent, url, realm, kind, svnAuthentication == null);
return svnAuthentication;
}
public int acceptServerAuthentication(final SVNURL url, final String realm, final Object certificate, final boolean resultMayBeStored) {
return ACCEPTED_TEMPORARY;
}
private void actualSavePermissions(String realm, SVNAuthentication auth) {
final String actualKind = auth.getKind();
File dir = new File(myAuthDir, actualKind);
String fileName = SVNFileUtil.computeChecksum(realm);
File authFile = new File(dir, fileName);
try {
((ISVNPersistentAuthenticationProvider) myDelegate).saveAuthentication(auth, actualKind, realm);
}
catch (SVNException e) {
if (myProject != null) {
ApplicationManager.getApplication().invokeLater(new VcsBalloonProblemNotifier(myProject,
"<b>Problem when storing Subversion credentials:</b>&nbsp;" + e.getMessage(), MessageType.ERROR));
}
}
finally {
// do not make password file readonly
setWriteable(authFile);
}
}
public void saveAuthentication(final SVNAuthentication auth, final String kind, final String realm) throws SVNException {
final Boolean fromInteractive = ourJustEntered.get();
ourJustEntered.set(null);
if (! Boolean.TRUE.equals(fromInteractive)) {
// not what user entered
return;
}
myListener.getMulticaster().saveAttemptStarted(ProviderType.persistent, auth.getURL(), realm, auth.getKind());
((ISVNPersistentAuthenticationProvider) myDelegate).saveAuthentication(auth, kind, realm);
myListener.getMulticaster().saveAttemptFinished(ProviderType.persistent, auth.getURL(), realm, auth.getKind());
}
@Override
public void saveFingerprints(String realm, byte[] fingerprints) {
((ISVNPersistentAuthenticationProvider) myDelegate).saveFingerprints(realm, fingerprints);
}
@Override
public byte[] loadFingerprints(String realm) {
return ((ISVNPersistentAuthenticationProvider) myDelegate).loadFingerprints(realm);
}
private final static int maxAttempts = 10;
private void setWriteable(final File file) {
if (! file.exists()) return;
if (file.getParentFile() == null) {
return;
}
for (int i = 0; i < maxAttempts; i++) {
final File parent = file.getParentFile();
try {
final File tempFile = FileUtil.createTempFile(parent, "123", "1", true);
FileUtil.delete(tempFile);
if (! file.renameTo(tempFile)) continue;
if (! file.createNewFile()) continue;
FileUtil.copy(tempFile, file);
FileUtil.delete(tempFile);
return;
}
catch (IOException e) {
//
}
}
}
}
@Override
public void verifyHostKey(String hostName, int port, String keyAlgorithm, byte[] hostKey) throws SVNException {
myKeyAlgorithm.put(Thread.currentThread(), keyAlgorithm);
try {
super.verifyHostKey(hostName, port, keyAlgorithm, hostKey);
} finally {
myKeyAlgorithm.remove(Thread.currentThread());
}
}
@Nullable
public String getSSHKeyAlgorithm() {
return myKeyAlgorithm.get(Thread.currentThread());
}
public ISVNProxyManager getProxyManager(SVNURL url) throws SVNException {
// this code taken from default manager (changed for system properties reading)
String host = url.getHost();
String proxyHost = getServersPropertyIdea(host, "http-proxy-host");
if ((proxyHost == null) || "".equals(proxyHost.trim())) {
if (myConfig.isIsUseDefaultProxy()) {
// ! use common proxy if it is set
final HttpConfigurable httpConfigurable = HttpConfigurable.getInstance();
final String ideaWideProxyHost = httpConfigurable.PROXY_HOST;
String ideaWideProxyPort = String.valueOf(httpConfigurable.PROXY_PORT);
if (ideaWideProxyPort == null) {
ideaWideProxyPort = "3128";
}
if ((ideaWideProxyHost != null) && (! "".equals(ideaWideProxyHost.trim()))) {
return new MyPromptingProxyManager(ideaWideProxyHost, ideaWideProxyPort);
}
}
return null;
}
String proxyExceptions = getServersPropertyIdea(host, "http-proxy-exceptions");
String proxyExceptionsSeparator = ",";
if (proxyExceptions == null) {
proxyExceptions = System.getProperty("http.nonProxyHosts");
proxyExceptionsSeparator = "|";
}
if (proxyExceptions != null) {
for(StringTokenizer exceptions = new StringTokenizer(proxyExceptions, proxyExceptionsSeparator); exceptions.hasMoreTokens();) {
String exception = exceptions.nextToken().trim();
if (DefaultSVNOptions.matches(exception, host)) {
return null;
}
}
}
String proxyPort = getServersPropertyIdea(host, "http-proxy-port");
String proxyUser = getServersPropertyIdea(host, "http-proxy-username");
String proxyPassword = getServersPropertyIdea(host, "http-proxy-password");
return new MySimpleProxyManager(proxyHost, proxyPort, proxyUser, proxyPassword);
}
private static class MyPromptingProxyManager extends MySimpleProxyManager {
private static final String ourPrompt = "Proxy authentication";
private MyPromptingProxyManager(final String host, final String port) {
super(host, port, null, null);
}
@Override
public String getProxyUserName() {
if (myProxyUser != null) {
return myProxyUser;
}
final HttpConfigurable httpConfigurable = HttpConfigurable.getInstance();
if (httpConfigurable.PROXY_AUTHENTICATION && (! httpConfigurable.KEEP_PROXY_PASSWORD)) {
httpConfigurable.getPromptedAuthentication(myProxyHost, ourPrompt);
}
myProxyUser = httpConfigurable.PROXY_LOGIN;
return myProxyUser;
}
@Override
public String getProxyPassword() {
if (myProxyPassword != null) {
return myProxyPassword;
}
final HttpConfigurable httpConfigurable = HttpConfigurable.getInstance();
if (httpConfigurable.PROXY_AUTHENTICATION && (! httpConfigurable.KEEP_PROXY_PASSWORD)) {
httpConfigurable.getPromptedAuthentication(myProxyUser, ourPrompt);
}
myProxyPassword = httpConfigurable.getPlainProxyPassword();
return myProxyPassword;
}
}
private static class MySimpleProxyManager implements ISVNProxyManager {
protected String myProxyHost;
private final String myProxyPort;
protected String myProxyUser;
protected String myProxyPassword;
public MySimpleProxyManager(String host, String port, String user, String password) {
myProxyHost = host;
myProxyPort = port == null ? "3128" : port;
myProxyUser = user;
myProxyPassword = password;
}
public String getProxyHost() {
return myProxyHost;
}
public int getProxyPort() {
try {
return Integer.parseInt(myProxyPort);
} catch (NumberFormatException nfe) {
//
}
return 3128;
}
public String getProxyUserName() {
return myProxyUser;
}
public String getProxyPassword() {
return myProxyPassword;
}
public void acknowledgeProxyContext(boolean accepted, SVNErrorMessage errorMessage) {
}
}
// 30 seconds
private final static int DEFAULT_READ_TIMEOUT = 30 * 1000;
@Override
public int getReadTimeout(final SVNRepository repository) {
String protocol = repository.getLocation().getProtocol();
if (HTTP.equals(protocol) || HTTPS.equals(protocol)) {
String host = repository.getLocation().getHost();
String timeout = getServersPropertyIdea(host, "http-timeout");
if (timeout != null) {
try {
return Integer.parseInt(timeout)*1000;
} catch (NumberFormatException nfe) {
// use default
}
}
return DEFAULT_READ_TIMEOUT;
}
if (SVN_SSH.equals(protocol)) {
return (int) myConfig.mySSHReadTimeout;
}
return 0;
}
@Override
public int getConnectTimeout(SVNRepository repository) {
String protocol = repository.getLocation().getProtocol();
if (SVN_SSH.equals(protocol)) {
return (int) myConfig.mySSHConnectionTimeout;
}
final int connectTimeout = super.getConnectTimeout(repository);
if ((HTTP.equals(protocol) || HTTPS.equals(protocol)) && (connectTimeout <= 0)) {
return DEFAULT_READ_TIMEOUT;
}
return connectTimeout;
}
// taken from default manager as is
private String getServersPropertyIdea(String host, final String name) {
final SVNCompositeConfigFile serversFile = getHostOptionsProvider().getServersFile();
return getPropertyIdea(host, serversFile, name);
}
private String getPropertyIdea(String host, SVNCompositeConfigFile serversFile, final String name) {
String groupName = getGroupName(serversFile.getProperties("groups"), host);
if (groupName != null) {
Map hostProps = serversFile.getProperties(groupName);
final String value = (String)hostProps.get(name);
if (value != null) {
return value;
}
}
Map globalProps = serversFile.getProperties("global");
return (String) globalProps.get(name);
}
public static boolean checkHostGroup(final String url, final String patterns, final String exceptions) {
final SVNURL svnurl;
try {
svnurl = SVNURL.parseURIEncoded(url);
}
catch (SVNException e) {
return false;
}
final String host = svnurl.getHost();
return matches(patterns, host) && (! matches(exceptions, host));
}
private static boolean matches(final String pattern, final String host) {
final StringTokenizer tokenizer = new StringTokenizer(pattern, ",");
while(tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
if (DefaultSVNOptions.matches(token, host)) {
return true;
}
}
return false;
}
// taken from default manager as is
private static String getGroupName(Map groups, String host) {
for (Iterator names = groups.keySet().iterator(); names.hasNext();) {
String name = (String) names.next();
String pattern = (String) groups.get(name);
for(StringTokenizer tokens = new StringTokenizer(pattern, ","); tokens.hasMoreTokens();) {
String token = tokens.nextToken();
if (DefaultSVNOptions.matches(token, host)) {
return name;
}
}
}
return null;
}
// default = yes
private static boolean isTurned(final String value) {
return value == null || "yes".equalsIgnoreCase(value) || "on".equalsIgnoreCase(value) || "true".equalsIgnoreCase(value);
}
private ModalityState getCurrent() {
if (ApplicationManager.getApplication().isDispatchThread()) {
return ModalityState.current();
}
final ProgressIndicator pi = ProgressManager.getInstance().getProgressIndicator();
if (pi == null) {
return ModalityState.defaultModalityState();
}
return pi.getModalityState();
}
/**
* Shows a yes/no question whether user wants to store his password in plain text and returns his answer.
* @param title title of the questioning dialog.
* @param message questioning message to be displayed.
* @return true if user agrees to store his password in plaintext, false if he doesn't.
*/
@CalledInAwt
private boolean askToStoreUnencrypted(String title, String message) {
final int answer = Messages.showYesNoDialog(myProject, message, title, Messages.getQuestionIcon());
return answer == 0;
}
public void setInteraction(SvnAuthenticationInteraction interaction) {
myInteraction = interaction;
}
private static class MySvnAuthenticationInteraction implements SvnAuthenticationInteraction {
private final Project myProject;
private MySvnAuthenticationInteraction(Project project) {
myProject = project;
}
@Override
public void warnOnAuthStorageDisabled(SVNURL url) {
VcsBalloonProblemNotifier.showOverChangesView(myProject, "Cannot store credentials: forbidden by \"store-auth-creds=no\"", MessageType.ERROR);
}
@Override
public void warnOnPasswordStorageDisabled(SVNURL url) {
VcsBalloonProblemNotifier.showOverChangesView(myProject, "Cannot store password: forbidden by \"store-passwords=no\"", MessageType.ERROR);
}
@Override
public void warnOnSSLPassphraseStorageDisabled(SVNURL url) {
VcsBalloonProblemNotifier.showOverChangesView(myProject, "Cannot store passphrase: forbidden by \"store-ssl-client-cert-pp=no\"", MessageType.ERROR);
}
@Override
public boolean promptForPlaintextPasswordSaving(SVNURL url, String realm) {
final int answer = Messages.showYesNoDialog(myProject, String.format("Your password for authentication realm:\n" +
"%s\ncan only be stored to disk unencrypted. Would you like to store it in plaintext?", realm),
"Store the password in plaintext?", Messages.getQuestionIcon());
return answer == 0;
}
@Override
public boolean promptInAwt() {
return true;
}
@Override
public boolean promptForSSLPlaintextPassphraseSaving(SVNURL url, String realm, File certificateFile, String certificateName) {
final int answer = Messages.showYesNoDialog(myProject,
String.format("Your passphrase for " + certificateName + ":\n%s\ncan only be stored to disk unencrypted. Would you like to store it in plaintext?",
certificateFile.getPath()),
"Store the passphrase in plaintext?", Messages.getQuestionIcon());
return answer == 0;
}
}
private static boolean isLion() {
return SystemInfo.isMac && SystemInfo.isMacOSSnowLeopard && ! SystemInfo.OS_VERSION.startsWith("10.6");
}
public class IdeaSVNHostOptionsProvider extends DefaultSVNHostOptionsProvider {
public IdeaSVNHostOptionsProvider() {
super(myConfigDirectory);
}
@Override
public SVNCompositeConfigFile getServersFile() {
return super.getServersFile();
}
@Override
public ISVNHostOptions getHostOptions(SVNURL url) {
return new IdeaSVNHostOptions(getServersFile(), url);
}
}
private static class ThreadLocalSavePermissions {
private final Map<Thread, Boolean> myPlainTextAllowed;
private ThreadLocalSavePermissions() {
myPlainTextAllowed = Collections.synchronizedMap(new HashMap<Thread, Boolean>());
}
public void put(final boolean value) {
myPlainTextAllowed.put(Thread.currentThread(), value);
}
public boolean have() {
return myPlainTextAllowed.containsKey(Thread.currentThread());
}
public void remove() {
myPlainTextAllowed.remove(Thread.currentThread());
}
public boolean allowed() {
return Boolean.TRUE.equals(myPlainTextAllowed.get(Thread.currentThread()));
}
}
private class IdeaSVNHostOptions extends DefaultSVNHostOptions {
private SVNCompositeConfigFile myConfigFile;
private final SVNURL myUrl;
private IdeaSVNHostOptions(SVNCompositeConfigFile serversFile, SVNURL url) {
super(serversFile, url);
myUrl = url;
}
@Override
public boolean isStorePlainTextPasswords(final String realm, SVNAuthentication auth) throws SVNException {
if (USERNAME.equals(auth.getKind())) return true;
final boolean superValue = super.isStorePlainTextPasswords(realm, auth);
final boolean value = mySavePermissions.allowed() || superValue;
if ((! value) && (! mySavePermissions.have())) {
promptAndSaveWhenWeLackEncryption(realm, auth, new Getter<Boolean>() {
@Override
public Boolean get() {
return myInteraction.promptForPlaintextPasswordSaving(myUrl, realm);
}
});
}
return value;
}
@Override
public boolean isStorePlainTextPassphrases(final String realm, final SVNAuthentication auth) throws SVNException {
if (USERNAME.equals(auth.getKind())) return true;
final boolean value = mySavePermissions.allowed() || super.isStorePlainTextPassphrases(realm, auth);
if ((! value) && (! mySavePermissions.have())) {
promptAndSaveWhenWeLackEncryption(realm, auth, new Getter<Boolean>() {
@Override
public Boolean get() {
File file = null;
String certificateName = null;
if (auth instanceof SVNSSLAuthentication) {
file = ((SVNSSLAuthentication) auth).getCertificateFile();
certificateName = "client certificate";
} else if (auth instanceof SVNSSHAuthentication) {
file = ((SVNSSHAuthentication) auth).getPrivateKeyFile();
certificateName = "private key file";
} else {
assert false;
}
return myInteraction.promptForSSLPlaintextPassphraseSaving(myUrl, realm,
file, certificateName);
}
});
}
return value;
}
@Override
public boolean isAuthStorageEnabled() {
final boolean value;
if (hasAuthStorageEnabledOption()) {
value = super.isAuthStorageEnabled();
} else {
value = isTurned(getConfigFile().getPropertyValue("auth", "store-auth-creds"));
}
if (! value) {
myInteraction.warnOnAuthStorageDisabled(myUrl);
}
return value;
}
@Override
public boolean isStorePasswords() {
final String storePasswords = getStorePasswords();
final boolean value;
if (storePasswords != null) {
value = isTurned(storePasswords);
} else {
final String configValue = getConfigFile().getPropertyValue("auth", "store-passwords");
value = isTurned(configValue);
}
if (! value) {
myInteraction.warnOnPasswordStorageDisabled(myUrl);
}
return value;
}
@Override
public boolean isStoreSSLClientCertificatePassphrases() {
final boolean value = super.isStoreSSLClientCertificatePassphrases();
if (! value) {
myInteraction.warnOnSSLPassphraseStorageDisabled(myUrl);
}
return value;
}
public String getStorePasswords() {
return getServersPropertyIdea(getHost(), "store-passwords");
}
private SVNCompositeConfigFile getConfigFile() {
if (myConfigFile == null) {
final File config = new File(myConfigDirectory, "config");
SVNConfigFile.createDefaultConfiguration(myConfigDirectory);
SVNConfigFile userConfig = new SVNConfigFile(config);
SVNConfigFile systemConfig = new SVNConfigFile(new File(SVNFileUtil.getSystemConfigurationDirectory(), "config"));
myConfigFile = new SVNCompositeConfigFile(systemConfig, userConfig);
}
return myConfigFile;
}
private void promptAndSaveWhenWeLackEncryption(final String realm, final SVNAuthentication auth, final Getter<Boolean> prompt) {
final Boolean[] saveOnce = new Boolean[1];
final Runnable actualSave = new Runnable() {
@Override
public void run() {
mySavePermissions.put(Boolean.TRUE.equals(saveOnce[0]));
try {
myPersistentAuthenticationProviderProxy.actualSavePermissions(realm, auth);
}
finally {
mySavePermissions.remove();
}
}
};
if (myInteraction.promptInAwt()) {
new AbstractCalledLater(myProject, getCurrent()) {
@Override
public void run() {
saveOnce[0] = Boolean.TRUE.equals(prompt.get());
ApplicationManager.getApplication().executeOnPooledThread(actualSave);
}
}.callMe();
} else {
saveOnce[0] = Boolean.TRUE.equals(prompt.get());
actualSave.run();
}
}
}
private static class ProxyPasswordStorageForDebug implements DefaultSVNPersistentAuthenticationProvider.IPasswordStorage {
private final DefaultSVNPersistentAuthenticationProvider.IPasswordStorage myDelegate;
private final EventDispatcher<SvnAuthenticationListener> myListener;
public ProxyPasswordStorageForDebug(final DefaultSVNPersistentAuthenticationProvider.IPasswordStorage delegate,
final EventDispatcher<SvnAuthenticationListener> listener) {
myDelegate = delegate;
myListener = listener;
}
@Override
public String getPassType() {
return myDelegate.getPassType();
}
@Override
public boolean savePassword(String realm, String password, SVNAuthentication auth, SVNProperties authParameters) throws SVNException {
final boolean saved = myDelegate.savePassword(realm, password, auth, authParameters);
if (saved) {
myListener.getMulticaster().actualSaveWillBeTried(ProviderType.persistent, auth.getURL(), realm, auth.getKind()
);
}
return saved;
}
@Override
public String readPassword(String realm, String userName, SVNProperties authParameters) throws SVNException {
return myDelegate.readPassword(realm, userName, authParameters);
}
@Override
public boolean savePassphrase(String realm, String passphrase, SVNAuthentication auth, SVNProperties authParameters, boolean force)
throws SVNException {
final boolean saved = myDelegate.savePassphrase(realm, passphrase, auth, authParameters, force);
if (saved) {
myListener.getMulticaster().actualSaveWillBeTried(ProviderType.persistent, auth.getURL(), realm, auth.getKind()
);
}
return saved;
}
@Override
public String readPassphrase(String realm, SVNProperties authParameters) throws SVNException {
return myDelegate.readPassphrase(realm, authParameters);
}
}
private static class MyKeyringMasterKeyProvider implements ISVNGnomeKeyringPasswordProvider {
private final Project myProject;
public MyKeyringMasterKeyProvider(Project project) {
myProject = project;
}
@Override
public String getKeyringPassword(final String keyringName) throws SVNException {
final String[] s = new String[1];
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
s[0] = Messages.showInputDialog(myProject, "Please provide '" + keyringName + "' GNOME Keyring password", "Subversion", Messages.getQuestionIcon());
}
});
return s[0];
}
}
}
@@ -0,0 +1,339 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.impl.GenericNotifierImpl;
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.IdeFrame;
import com.intellij.openapi.wm.ex.WindowManagerEx;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.util.ThreeState;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn17.dialogs.SvnInteractiveAuthenticationProvider;
import org.tmatesoft.svn.core.SVNAuthenticationException;
import org.tmatesoft.svn.core.SVNCancelException;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.SVNAuthentication;
import org.tmatesoft.svn.core.internal.util.SVNURLUtil;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNWCClient;
import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.Timer;
public class SvnAuthenticationNotifier extends GenericNotifierImpl<SvnAuthenticationNotifier.AuthenticationRequest, SVNURL> {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnAuthenticationNotifier");
private final SvnVcs17 myVcs;
private final RootsToWorkingCopies myRootsToWorkingCopies;
private final Map<SVNURL, Boolean> myCopiesPassiveResults;
private Timer myTimer;
private volatile boolean myVerificationInProgress;
public SvnAuthenticationNotifier(final SvnVcs17 svnVcs) {
super(svnVcs.getProject(), svnVcs.getDisplayName(), "Not Logged In to Subversion", NotificationType.ERROR);
myVcs = svnVcs;
myRootsToWorkingCopies = myVcs.getRootsToWorkingCopies();
myCopiesPassiveResults = Collections.synchronizedMap(new HashMap<SVNURL, Boolean>());
myVerificationInProgress = false;
}
public void init() {
if (myTimer != null) {
stop();
}
myTimer = new Timer("SVN authentication timer");
// every 10 minutes
myTimer.schedule(new TimerTask() {
@Override
public void run() {
myCopiesPassiveResults.clear();
}
}, 10000, 10 * 60 * 1000);
}
public void stop() {
myTimer.cancel();
myTimer = null;
}
@Override
protected boolean ask(final AuthenticationRequest obj, String description) {
if (myVerificationInProgress) {
return showAlreadyChecking();
}
myVerificationInProgress = true;
final Ref<Boolean> resultRef = new Ref<Boolean>();
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
public void run() {
try {
final boolean result =
interactiveValidation(obj.myProject, obj.getUrl(), obj.getRealm(), obj.getKind());
log("ask result for: " + obj.getUrl() + " is: " + result);
resultRef.set(result);
if (result) {
onStateChangedToSuccess(obj);
}
} finally {
myVerificationInProgress = false;
}
}
});
return false;
}
private boolean showAlreadyChecking() {
final IdeFrame frameFor = WindowManagerEx.getInstanceEx().findFrameFor(myProject);
if (frameFor != null) {
final JComponent component = frameFor.getComponent();
Point point = component.getMousePosition();
if (point == null) {
point = new Point((int)(component.getWidth() * 0.7), 0);
}
SwingUtilities.convertPointToScreen(point, component);
JBPopupFactory.getInstance().createHtmlTextBalloonBuilder("Already checking...", MessageType.WARNING, null).
createBalloon().show(new RelativePoint(point), Balloon.Position.below);
}
return false;
}
private void onStateChangedToSuccess(final AuthenticationRequest obj) {
myCopiesPassiveResults.put(getKey(obj), true);
myVcs.invokeRefreshSvnRoots(false);
final List<SVNURL> outdatedRequests = new LinkedList<SVNURL>();
final Collection<SVNURL> keys = getAllCurrentKeys();
for (SVNURL key : keys) {
final SVNURL commonURLAncestor = SVNURLUtil.getCommonURLAncestor(key, obj.getUrl());
if ((commonURLAncestor != null) && (! StringUtil.isEmptyOrSpaces(commonURLAncestor.getHost())) &&
(! StringUtil.isEmptyOrSpaces(commonURLAncestor.getPath()))) {
//final AuthenticationRequest currObj = getObj(key);
//if ((currObj != null) && passiveValidation(myVcs.getProject(), key, true, currObj.getRealm(), currObj.getKind())) {
outdatedRequests.add(key);
//}
}
}
log("on state changed ");
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
for (SVNURL key : outdatedRequests) {
removeLazyNotificationByKey(key);
}
}
}, ModalityState.NON_MODAL);
}
@Override
public void ensureNotify(AuthenticationRequest obj) {
final SVNURL key = getKey(obj);
myCopiesPassiveResults.remove(key);
/*VcsBalloonProblemNotifier.showOverChangesView(myVcs.getProject(), "You are not authenticated to '" + obj.getRealm() + "'." +
"To login, see pending notifications.", MessageType.ERROR);*/
super.ensureNotify(obj);
}
@NotNull
@Override
public SVNURL getKey(final AuthenticationRequest obj) {
// !!! wc's URL
return obj.getWcUrl();
}
@Nullable
public SVNURL getWcUrl(final AuthenticationRequest obj) {
if (obj.isOutsideCopies()) return null;
if (obj.getWcUrl() != null) return obj.getWcUrl();
final WorkingCopy copy = myRootsToWorkingCopies.getMatchingCopy(obj.getUrl());
if (copy != null) {
obj.setOutsideCopies(false);
obj.setWcUrl(copy.getUrl());
} else {
obj.setOutsideCopies(true);
}
return copy == null ? null : copy.getUrl();
}
/**
* Bases on presence of notifications!
*/
public ThreeState isAuthenticatedFor(final VirtualFile vf) {
final WorkingCopy wcCopy = myRootsToWorkingCopies.getWcRoot(vf);
if (wcCopy == null) return ThreeState.UNSURE;
// check there's no cancellation yet
final boolean haveCancellation = getStateFor(wcCopy.getUrl());
if (haveCancellation) return ThreeState.NO;
final Boolean keptResult = myCopiesPassiveResults.get(wcCopy.getUrl());
if (Boolean.TRUE.equals(keptResult)) return ThreeState.YES;
if (Boolean.FALSE.equals(keptResult)) return ThreeState.NO;
// check have credentials
final boolean calculatedResult = passiveValidation(myVcs.getProject(), wcCopy.getUrl());
myCopiesPassiveResults.put(wcCopy.getUrl(), calculatedResult);
return calculatedResult ? ThreeState.YES : ThreeState.NO;
}
@NotNull
@Override
protected String getNotificationContent(AuthenticationRequest obj) {
return "<a href=\"\">Click to fix.</a> Not logged In to Subversion '" + obj.getRealm() + "' (" + obj.getUrl().toDecodedString() + ")";
}
@NotNull
@Override
protected String getToString(AuthenticationRequest obj) {
return "Click to fix. Not logged In to Subversion '" + obj.getRealm() + "' (" + obj.getUrl().toDecodedString() + ")";
}
public static class AuthenticationRequest {
private final Project myProject;
private final String myKind;
private final SVNURL myUrl;
private final String myRealm;
private SVNURL myWcUrl;
private boolean myOutsideCopies;
public AuthenticationRequest(Project project, String kind, SVNURL url, String realm) {
myProject = project;
myKind = kind;
myUrl = url;
myRealm = realm;
}
public boolean isOutsideCopies() {
return myOutsideCopies;
}
public void setOutsideCopies(boolean outsideCopies) {
myOutsideCopies = outsideCopies;
}
public SVNURL getWcUrl() {
return myWcUrl;
}
public void setWcUrl(SVNURL wcUrl) {
myWcUrl = wcUrl;
}
public String getKind() {
return myKind;
}
public SVNURL getUrl() {
return myUrl;
}
public String getRealm() {
return myRealm;
}
}
static void log(final Throwable t) {
LOG.debug(t);
}
static void log(final String s) {
LOG.debug(s);
}
public static boolean passiveValidation(final Project project, final SVNURL url) {
final SvnConfiguration17 configuration = SvnConfiguration17.getInstance(project);
final SvnAuthenticationManager passiveManager = configuration.getPassiveAuthenticationManager();
return validationImpl(project, url, configuration, passiveManager, false, null, null, false);
}
public static boolean interactiveValidation(final Project project, final SVNURL url, final String realm, final String kind) {
final SvnConfiguration17 configuration = SvnConfiguration17.getInstance(project);
final SvnAuthenticationManager passiveManager = configuration.getInteractiveManager(SvnVcs17.getInstance(project));
return validationImpl(project, url, configuration, passiveManager, true, realm, kind, true);
}
private static boolean validationImpl(final Project project, final SVNURL url,
final SvnConfiguration17 configuration, final SvnAuthenticationManager manager,
final boolean checkWrite,
final String realm,
final String kind, boolean interactive) {
SvnInteractiveAuthenticationProvider.clearCallState();
try {
new SVNWCClient(manager, configuration.getOptions(project)).doInfo(url, SVNRevision.UNDEFINED, SVNRevision.HEAD);
} catch (SVNAuthenticationException e) {
log(e);
return false;
} catch (SVNCancelException e) {
log(e); // auth canceled
return false;
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode().isAuthentication()) {
log(e);
return false;
}
LOG.info("some other exc", e);
if (interactive) {
VcsBalloonProblemNotifier.showOverChangesView(project, "Authentication failed: " + e.getMessage(), MessageType.ERROR);
}
return false; /// !!!! any exception means user should be notified that authorization failed
}
if (! checkWrite) {
return true;
}
/*if (passive) {
return SvnInteractiveAuthenticationProvider.wasCalled();
}*/
if (SvnInteractiveAuthenticationProvider.wasCalled() && SvnInteractiveAuthenticationProvider.wasCancelled()) return false;
if (SvnInteractiveAuthenticationProvider.wasCalled()) return true;
final SvnVcs17 svnVcs = SvnVcs17.getInstance(project);
final SvnInteractiveAuthenticationProvider provider = new SvnInteractiveAuthenticationProvider(svnVcs, manager);
final SVNAuthentication svnAuthentication = provider.requestClientAuthentication(kind, url, realm, null, null, true);
if (svnAuthentication != null) {
configuration.acknowledge(kind, realm, svnAuthentication);
try {
configuration.getAuthenticationManager(svnVcs).acknowledgeAuthentication(true, kind, realm, null, svnAuthentication);
}
catch (SVNException e) {
LOG.info(e);
}
return true;
}
return false;
}
}
@@ -0,0 +1,49 @@
/*
* Copyright 2000-2011 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.svn17;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.BinaryContentRevision;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.tmatesoft.svn.core.wc.SVNRevision;
/**
* @author yole
*/
class SvnBinaryContentRevision extends SvnContentRevision implements BinaryContentRevision {
public SvnBinaryContentRevision(@NotNull SvnVcs17 vcs, @NotNull final FilePath file, final SVNRevision revision, final boolean useBaseRevision) {
super(vcs, file, revision, useBaseRevision);
}
@Nullable
public byte[] getBinaryContent() throws VcsException {
try {
return getUpToDateBinaryContent();
}
catch(Exception ex) {
throw new VcsException(ex);
}
}
@NonNls
public String toString() {
return "SvnBinaryContentRevision:" + myFile;
}
}
@@ -0,0 +1,59 @@
/*
* Copyright 2000-2009 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.svn17;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author yole
*/
public class SvnBranchConfiguration {
private String myTrunkUrl;
private List<String> myBranchUrls;
private boolean myUserinfoInUrl;
public SvnBranchConfiguration() {
myBranchUrls = new ArrayList<String>();
}
public boolean isUserinfoInUrl() {
return myUserinfoInUrl;
}
public void setUserinfoInUrl(final boolean userinfoInUrl) {
myUserinfoInUrl = userinfoInUrl;
}
public void setBranchUrls(final List<String> branchUrls) {
myBranchUrls = branchUrls;
Collections.sort(myBranchUrls);
}
public void setTrunkUrl(final String trunkUrl) {
myTrunkUrl = trunkUrl;
}
public String getTrunkUrl() {
return myTrunkUrl;
}
public List<String> getBranchUrls() {
return myBranchUrls;
}
}
@@ -0,0 +1,328 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.lifecycle.PeriodicalTasksCloser;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.committed.VcsConfigurationChangeListener;
import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl;
import com.intellij.openapi.vcs.impl.VcsInitObject;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.PairConsumer;
import com.intellij.util.messages.MessageBus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn17.branchConfig.*;
import org.jetbrains.idea.svn17.integrate.SvnBranchItem;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import java.io.File;
import java.util.*;
/**
* @author yole
*/
@State(
name = "SvnBranchConfigurationManager17",
storages = {
@Storage(
file = "$PROJECT_FILE$"
)}
)
public class SvnBranchConfigurationManager17 implements PersistentStateComponent<SvnBranchConfigurationManager17.ConfigurationBean> {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnBranchConfigurationManager");
private final Project myProject;
private final ProjectLevelVcsManager myVcsManager;
private final SvnLoadedBrachesStorage17 myStorage;
public SvnBranchConfigurationManager17(final Project project,
final ProjectLevelVcsManager vcsManager,
final SvnLoadedBrachesStorage17 storage) {
myProject = project;
myVcsManager = vcsManager;
myStorage = storage;
myBunch = new NewRootBunch(project);
}
public static SvnBranchConfigurationManager17 getInstance(final Project project) {
return PeriodicalTasksCloser.getInstance().safeGetService(project, SvnBranchConfigurationManager17.class);
}
public static class ConfigurationBean {
public Map<String, SvnBranchConfiguration> myConfigurationMap = new TreeMap<String, SvnBranchConfiguration>();
/**
* version of "support SVN in IDEA". for features tracking. should grow
*/
public Long myVersion;
public boolean mySupportsUserInfoFilter;
}
public Long getSupportValue() {
return myConfigurationBean.myVersion;
}
private ConfigurationBean myConfigurationBean = new ConfigurationBean();
private final SvnBranchConfigManager myBunch;
public SvnBranchConfigurationNew get(@NotNull final VirtualFile vcsRoot) throws VcsException {
return myBunch.getConfig(vcsRoot);
}
public SvnBranchConfigManager getSvnBranchConfigManager() {
return myBunch;
}
public void setConfiguration(final VirtualFile vcsRoot, final SvnBranchConfigurationNew configuration) {
myBunch.updateForRoot(vcsRoot, new InfoStorage<SvnBranchConfigurationNew>(configuration, InfoReliability.setByUser),
new BranchesPreloader(myProject, myBunch, vcsRoot));
SvnBranchMapperManager17.getInstance().notifyBranchesChanged(myProject, vcsRoot, configuration);
final MessageBus messageBus = myProject.getMessageBus();
messageBus.syncPublisher(VcsConfigurationChangeListener.BRANCHES_CHANGED).execute(myProject, vcsRoot);
}
public ConfigurationBean getState() {
final ConfigurationBean result = new ConfigurationBean();
result.myVersion = myConfigurationBean.myVersion;
final UrlSerializationHelper helper = new UrlSerializationHelper(SvnVcs17.getInstance(myProject));
for (VirtualFile root : myBunch.getMapCopy().keySet()) {
final String key = root.getPath();
final SvnBranchConfigurationNew configOrig = myBunch.getConfig(root);
final SvnBranchConfiguration configuration = new SvnBranchConfiguration();
configuration.setTrunkUrl(configOrig.getTrunkUrl());
configuration.setUserinfoInUrl(configOrig.isUserinfoInUrl());
configuration.setBranchUrls(configOrig.getBranchUrls());
final HashMap<String, List<SvnBranchItem>> map = new HashMap<String, List<SvnBranchItem>>();
final Map<String, InfoStorage<List<SvnBranchItem>>> origMap = configOrig.getBranchMap();
for (String origKey : origMap.keySet()) {
map.put(origKey, origMap.get(origKey).getValue());
}
result.myConfigurationMap.put(key, helper.prepareForSerialization(configuration));
}
result.mySupportsUserInfoFilter = true;
return result;
}
private static class BranchesPreloader implements PairConsumer<SvnBranchConfigurationNew, SvnBranchConfigurationNew> {
private final Project myProject;
private final VirtualFile myRoot;
private final SvnBranchConfigManager myBunch;
private boolean myAll;
public BranchesPreloader(Project project, @NotNull final SvnBranchConfigManager bunch, VirtualFile root) {
myBunch = bunch;
myProject = project;
myRoot = root;
}
public void consume(final SvnBranchConfigurationNew prev, final SvnBranchConfigurationNew next) {
final Application application = ApplicationManager.getApplication();
application.executeOnPooledThread(new Runnable() {
public void run() {
loadImpl(prev, next);
}
});
}
protected void loadImpl(final SvnBranchConfigurationNew prev, final SvnBranchConfigurationNew next) {
final Set<String> oldUrls = (prev == null) ? Collections.<String>emptySet() : new HashSet<String>(prev.getBranchUrls());
final SvnVcs17 vcs = SvnVcs17.getInstance(myProject);
if (! vcs.isVcsBackgroundOperationsAllowed(myRoot)) return;
for (String newBranchUrl : next.getBranchUrls()) {
// check if cancel had been put
if (! vcs.isVcsBackgroundOperationsAllowed(myRoot)) return;
if (myAll || (! oldUrls.contains(newBranchUrl))) {
new NewRootBunch.BranchesLoadRunnable(myProject, myBunch, newBranchUrl, InfoReliability.defaultValues, myRoot, null, true).run();
}
}
}
public void setAll(boolean all) {
myAll = all;
}
}
public void loadState(final ConfigurationBean object) {
final UrlSerializationHelper helper = new UrlSerializationHelper(SvnVcs17.getInstance(myProject));
final Map<String, SvnBranchConfiguration> map = object.myConfigurationMap;
final Map<String, SvnBranchConfiguration> newMap = new HashMap<String, SvnBranchConfiguration>(map.size(), 1);
final LocalFileSystem lfs = LocalFileSystem.getInstance();
final Set<Pair<VirtualFile, SvnBranchConfigurationNew>> whatToInit = new HashSet<Pair<VirtualFile, SvnBranchConfigurationNew>>();
for (Map.Entry<String, SvnBranchConfiguration> entry : map.entrySet()) {
final SvnBranchConfiguration configuration = entry.getValue();
final VirtualFile root = lfs.refreshAndFindFileByIoFile(new File(entry.getKey()));
if (root == null) {
LOG.info("root not found: " + entry.getKey());
continue;
}
final SvnBranchConfiguration configToConvert;
if ((! myConfigurationBean.mySupportsUserInfoFilter) || configuration.isUserinfoInUrl()) {
configToConvert = helper.afterDeserialization(entry.getKey(), configuration);
} else {
configToConvert = configuration;
}
final SvnBranchConfigurationNew newConfig = new SvnBranchConfigurationNew();
newConfig.setTrunkUrl(configToConvert.getTrunkUrl());
newConfig.setUserinfoInUrl(configToConvert.isUserinfoInUrl());
for (String branchUrl : configToConvert.getBranchUrls()) {
List<SvnBranchItem> stored = getStored(branchUrl);
if (stored != null && ! stored.isEmpty()) {
newConfig.addBranches(branchUrl, new InfoStorage<List<SvnBranchItem>>(stored, InfoReliability.setByUser));
} else {
whatToInit.add(new Pair<VirtualFile, SvnBranchConfigurationNew>(root, newConfig));
newConfig.addBranches(branchUrl, new InfoStorage<List<SvnBranchItem>>(new ArrayList<SvnBranchItem>(), InfoReliability.empty));
}
}
myBunch.updateForRoot(root, new InfoStorage<SvnBranchConfigurationNew>(newConfig, InfoReliability.setByUser), null);
}
((ProjectLevelVcsManagerImpl) myVcsManager).addInitializationRequest(VcsInitObject.BRANCHES, new Runnable() {
public void run() {
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
public void run() {
try {
for (Pair<VirtualFile, SvnBranchConfigurationNew> pair : whatToInit) {
final BranchesPreloader branchesPreloader = new BranchesPreloader(myProject, myBunch, pair.getFirst());
branchesPreloader.setAll(true);
branchesPreloader.loadImpl(null, pair.getSecond());
}
}
catch (ProcessCanceledException e) {
//
}
}
});
}
});
object.myConfigurationMap.clear();
object.myConfigurationMap.putAll(newMap);
myConfigurationBean = object;
}
private List<SvnBranchItem> getStored(String branchUrl) {
Collection<SvnBranchItem> collection = myStorage.get(branchUrl);
if (collection == null) return null;
final List<SvnBranchItem> items = new ArrayList<SvnBranchItem>(collection);
Collections.sort(items);
return items;
}
private static class UrlSerializationHelper {
private final SvnVcs17 myVcs;
private UrlSerializationHelper(final SvnVcs17 vcs) {
myVcs = vcs;
}
public SvnBranchConfiguration prepareForSerialization(final SvnBranchConfiguration configuration) {
final Ref<Boolean> withUserInfo = new Ref<Boolean>();
final String trunkUrl = serializeUrl(configuration.getTrunkUrl(), withUserInfo);
if (Boolean.FALSE.equals(withUserInfo.get())) {
return configuration;
}
final List<String> branches = configuration.getBranchUrls();
final List<String> newBranchesList = new ArrayList<String>(branches.size());
for (String s : branches) {
newBranchesList.add(serializeUrl(s, withUserInfo));
}
final SvnBranchConfiguration result = new SvnBranchConfiguration();
result.setTrunkUrl(trunkUrl);
result.setBranchUrls(newBranchesList);
result.setUserinfoInUrl(withUserInfo.isNull() ? false : withUserInfo.get());
return result;
}
public SvnBranchConfiguration afterDeserialization(final String path, final SvnBranchConfiguration configuration) {
if (! configuration.isUserinfoInUrl()) {
return configuration;
}
final String userInfo = getUserInfo(path);
if (userInfo == null) {
return configuration;
}
final String newTrunkUrl = deserializeUrl(configuration.getTrunkUrl(), userInfo);
final List<String> branches = configuration.getBranchUrls();
final List<String> newBranchesList = new ArrayList<String>(branches.size());
for (String s : branches) {
newBranchesList.add(deserializeUrl(s, userInfo));
}
final SvnBranchConfiguration result = new SvnBranchConfiguration();
result.setTrunkUrl(newTrunkUrl);
result.setBranchUrls(newBranchesList);
result.setUserinfoInUrl(userInfo != null && userInfo.length() > 0);
return result;
}
private String serializeUrl(final String url, final Ref<Boolean> withUserInfo) {
if (Boolean.FALSE.equals(withUserInfo.get())) {
return url;
}
try {
final SVNURL svnurl = SVNURL.parseURIEncoded(url);
if (withUserInfo.isNull()) {
final String userInfo = svnurl.getUserInfo();
withUserInfo.set((userInfo != null) && (userInfo.length() > 0));
}
if (withUserInfo.get()) {
return SVNURL.create(svnurl.getProtocol(), null, svnurl.getHost(), svnurl.getPort(), svnurl.getURIEncodedPath(), true).toString();
}
}
catch (SVNException e) {
//
}
return url;
}
@Nullable
private String getUserInfo(final String path) {
final SVNURL svnurl = myVcs.getSvnFileUrlMapping().getUrlForFile(new File(path));
return svnurl != null ? svnurl.getUserInfo() : null;
}
private String deserializeUrl(final String url, final String userInfo) {
try {
final SVNURL svnurl = SVNURL.parseURIEncoded(url);
return SVNURL.create(svnurl.getProtocol(), userInfo, svnurl.getHost(), svnurl.getPort(), svnurl.getURIEncodedPath(), true).toString();
} catch (SVNException e) {
return url;
}
}
}
}
@@ -0,0 +1,117 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.messages.Topic;
import org.jetbrains.idea.svn17.branchConfig.SvnBranchConfigurationNew;
import java.io.File;
import java.util.*;
/**
* Holds what working copies we have for URLs
*/
@State(
name = "SvnBranchMapperManager17",
storages = {
@Storage(
file = "$APP_CONFIG$/other.xml"
)}
)
public class SvnBranchMapperManager17 implements PersistentStateComponent<SvnBranchMapperManager17.SvnBranchMapperHolder> {
private SvnBranchMapperHolder myStateHolder;
public static SvnBranchMapperManager17 getInstance() {
return ServiceManager.getService(SvnBranchMapperManager17.class);
}
public SvnBranchMapperManager17() {
myStateHolder = new SvnBranchMapperHolder();
}
public SvnBranchMapperHolder getState() {
return myStateHolder;
}
public void loadState(final SvnBranchMapperHolder state) {
myStateHolder = state;
}
public void put(final String url, final String value) {
myStateHolder.put(url, value);
notifyWcRootsChanged(url, Collections.unmodifiableCollection(myStateHolder.get(url)));
}
public void remove(final String url, final File value) {
final Set<String> set = myStateHolder.get(url);
if (set != null) {
set.remove(value.getAbsolutePath());
}
notifyWcRootsChanged(url, Collections.unmodifiableCollection(set));
}
private static void notifyWcRootsChanged(final String url, final Collection<String> roots) {
ApplicationManager.getApplication().getMessageBus().syncPublisher(WC_ROOTS_CHANGED).rootsChanged(url, roots);
}
public void notifyBranchesChanged(final Project project, final VirtualFile vcsRoot, final SvnBranchConfigurationNew configuration) {
final Map<String, String> map = configuration.getUrl2FileMappings(project, vcsRoot);
if (map != null) {
for (Map.Entry<String, String> entry : map.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
}
public Set<String> get(final String key) {
return myStateHolder.get(key);
}
public static class SvnBranchMapperHolder {
public Map<String, Set<String>> myMapping;
public SvnBranchMapperHolder() {
myMapping = new HashMap<String, Set<String>>();
}
public void put(final String key, final String value) {
Set<String> files = myMapping.get(key);
if (files == null) {
files = new HashSet<String>();
myMapping.put(key, files);
}
files.add(value);
}
public Set<String> get(final String key) {
return myMapping.get(key);
}
}
public static interface WcRootsChangeConsumer {
void rootsChanged(final String url, final Collection<String> roots);
}
public static final Topic<WcRootsChangeConsumer> WC_ROOTS_CHANGED =
new Topic<WcRootsChangeConsumer>("SVN_WC_ROOTS_CHANGED", WcRootsChangeConsumer.class);
}
@@ -0,0 +1,51 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.CommonBundle;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.PropertyKey;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.util.ResourceBundle;
public class SvnBundle {
private static Reference<ResourceBundle> ourBundle;
@NonNls private static final String BUNDLE = "org.jetbrains.idea.svn17.SvnBundle";
private SvnBundle() {
}
public static String message(@PropertyKey(resourceBundle = BUNDLE)String key, Object... params) {
return CommonBundle.message(getBundle(), key, params);
}
public static String getString(@PropertyKey(resourceBundle = BUNDLE)String key) {
return getBundle().getString(key);
}
private static ResourceBundle getBundle() {
ResourceBundle bundle = null;
if (ourBundle != null) bundle = ourBundle.get();
if (bundle == null) {
bundle = ResourceBundle.getBundle(BUNDLE);
ourBundle = new SoftReference<ResourceBundle>(bundle);
}
return bundle;
}
}
@@ -0,0 +1,635 @@
#Actions&Plugin info
group.SubversionFileGroup.text=SVN
action.Subversion.BrowseSVNRepository.text=Bro_wse Subversion Repository...
action.Subversion.BrowseSVNRepository.description=Browse a Subversion repository
group.SubversionFilePopupGroup.text=_Subversion
action.Subversion.Add.text=_Add
action.Subversion.Add.description=Add the file to Subversion
group.IgnoreChoicesGroup.text=Ignore
group.RevertIgnoreChoicesGroup.text=Remove from ignore list
action.Subversion.UndoIgnore.text=Selected file names
action.Subversion.UndoIgnore.description=Remove file name/mask from folder ignore list
action.Subversion.Ignore.ExactMatch.text=Selected file names
action.Subversion.Ignore.ExactMatch.description=Adds names of all selected files into folder ignore list
action.Subversion.Ignore.MatchExtension.text=Extension mask
action.Subversion.Ignore.MatchExtension.description=Adds {0} mask into folder ignore list
action.Subversion.SetProperty.text=Set Proper_ty...
action.Subversion.SetProperty.description=Set versioned property on file or directory
action.Subversion.Revert.text=_Revert...
action.Subversion.Revert.description=Revert file to pristine state
action.Subversion.Resolve.text=Re_solve Text Conflict...
action.Subversion.Resolve.description=Resolve text conflict on file
action.Subversion.MarkResolved.text=_Mark Resolved...
action.Subversion.MarkResolved.description=Mark text and properties conflicts as resolved
action.Subversion.Clenaup.text=Cl_eanup
action.Subversion.Clenaup.description=Unlock locked directories and run all remaining incompleted operations
action.Subversion.Copy.text=_Branch or Tag...
action.Subversion.Copy.description=Copy selected file or directory to the new repository location
action.Subversion.Lock.text=_Lock...
action.Subversion.Lock.description=Lock files
action.Subversion.Unlock.text=Unl_ock
action.Subversion.Unlock.description=Unlock files
action.SvnChangesBrowser.text=Bro_wse Changes...
plugin.Subversion.description=Provides integration with Subversion version control system
action.name.refresh=Refresh
action.Subversion.ImportToSVNRepository.text=I_mport into Subversion...
action.Subversion.Share.text=_Share Directory...
action.Subversion.ShowProperties.text=Edit _Properties
action.Subversion.ConfigureBranches.text=Configure Branches...
action.Subversion.CompareWithBranch.text=Compare with Branch...
action.Subversion.Relocate.text=Relocate...
action.Subversion.Relocate.description=Relocate working copy to a different URL
action.Subversion.CleanupProject.text=Cleanup Project
action.Subversion.CleanupProject.description=Perform cleanup for all working copy directories in the project
#Copy dialog
dialog.title.branch=Create Branch or Tag
label.copy.from=&From:
label.copy.to=&To:
label.copy.from.revision=Copy from revision:
radio.copy.working.copy=&Working copy
radio.copy.head=&HEAD revision
radio.copy.specific=Specific &revision:
label.copy.comment=&Comment
label.copy.select.location.dialog.copy.as=Copy &as:
#Locl dialog
dialog.title.lock.files=Lock Files
dialog.title.lock.file=Lock File
label.lock.comment=&Lock comment:
label.locl.steal.existing=&Steal existing lock
#SSHCredentialsDialog
label.ssh.authentication.realm=Authentication realm: ''{0}''
dialog.title.openssh.v2.private.key=OpenSSH v2 Private Key
dialog.description.openssh.v2.private.key=Select private key file for SSH authentication
checkbox.ssh.keep.for.current.session=&Save Credentials
label.ssh.user.name=&User name:
radio.ssh.authentication.with.password=Authentication &with password
label.ssh.password=&Password:
radio.ssh.authentication.private.key=Authentication with private &key
label.ssh.key.file=Key &File:
label.ssh.passphrase=Passph&rase:
label.ssh.port=Port &Number:
label.ssl.certificate.path=Path to certificate:
label.ssl.certificate.password=Certificate passphrase:
checkbox.ssl.keep.for.current.session=&Save Credentials
progress.text.create.remote.folder=Create Remote Folder
message.text.error=Error
#Repository Browser
dialog.title.browser.copy.or.move=Copy or Move
dialog.title.browser.delete=Delete
dialog.title.browser.new.folder=New Folder
label.browser.url1.delete=Delete
label.browser.url1.create=Create
label.browser.url1.from=From
label.browser.to=To
label.browser.name.copy.as=Copy &as:
label.browser.name.name=&Name:
checkbox.browser.move.source.to.target=&Move source to target
label.browser.comment=&Comment:
label.browser.select.location=&Select location in repository:
node.text.browser.type.valid.url=Type valid URL and click 'Refresh' to select location
dialog.title.browser.browse.repository=Browse Subversion Repository
label.text.browser.repository.url=Repository &URL:
button.text.new.folder=&New Folder...
button.text.delete=&Delete...
button.text.copy=&Copy
button.text.paste=&Paste...
progres.text.deleting=Deleting {0}
progress.title.browser.delete=Delete
progress.text.browser.creating=Creating {0}
progress.text.browser.moving=Moving {0}
progress.text.browser.copying=Copying {0}
progress.text.browser.remote.destination=To {0}
progress.title.browser.move=Move
progress.title.browser.copy=Copy
node.text.browser.cannot.access.repository=Cannot access repository at ''{0}''
progress.text.browser.establishing.connection=Establishing connection to Subversion repository
progress.title.browser.establishing.connection=Establishing Connection...
node.text.browser.cannot.establish.connection=Cannot establish connection to ''{0}''
#authentication
label.auth.authentication.realm=Authentication realm: ''{0}''
label.auth.user.name=&User name:
label.auth.password=&Password:
checkbox.auth.keep.for.current.session=&Save credentials
label.credentials.user.name.required=User name is required for ''{0}''
label.credentials.user.name=User name:
checkbox.credentials.save.for.future=Save for future use
dialog.title.authentication.required=Authentication Required
dialog.title.authentication.required.was.failed=Authentication Required (Authentication Failed)
#history
progress.title.revisions.history=Revisions History
progress.text2.collecting.history=Collecting revisions history for ''{0}''
checkbox.changes.filter.filter.by.author=A&uthor:
progress.text2.processing.revision=Processing revision ''{0}''
progress.text.changes.collecting.changes=Collecting information on changes
progress.text2.changes.establishing.connection=Establishing connection to ''{0}''
progress.text2.revision.processed=Revision ''{0}'' processed
#status
status.configurable.display.name=Status
status.configurable.get.out.of.date.information=Check status against se&rver
progress.text.status.analyzing.local=Analyzing status of ''{0}''
progress.text.status.analizyng.remote=Discovering remote status of ''{0}''
status.group.name.missing=Missing
status.group.name.replaced=Replaced
status.group.name.obstructed=Obstructed
status.group.name.externals=Externals
status.group.name.switched=Switched
status.group.name.locked=Locked
#Configuration
checkbox.configure.use.system.default.configuration.directory=&Use system default Subversion configuration directory
label.configuration.configuration.directory=Subversion configuration &directory:
dialog.title.select.configuration.directory=Select Configuration Directory
dialog.description.select.configuration.directory=Select Subversion configuration directory or create new one
button.text.clear.authentication.cache=&Clear Auth Cache
label.text.delete.stored.credentials=Delete all stored credentials for 'http', 'svn' and 'svn+ssh' protocols
button.text.edit.proxies=Edit Network &Options...
label.text.edit.proxies=Edit 'servers' Subversion runtime configuration file
dialog.title.edit.http.proxies.settings = Edit Subversion options related to the network layers
dialog.edit.http.proxies.settings.error.same.group.names.text=Groups with identical names detected: ''{0}''
dialog.edit.http.proxies.settings.error.ambiguous.group.patterns.text=Found repository urls that match to several groups: {0}
dialog.edit.http.proxies.settings.error.ambiguous.group.patterns.to.text=url {0} matches to {1}\n
dialog.edit.http.proxies.settings.tab.edit.user.file.title=User file
dialog.edit.http.proxies.settings.tab.edit.system.file.title=System file
dialog.edit.http.proxies.settings.port.must.be.number.error=Port must be a number (group ''{0}'')
dialog.edit.http.proxies.settings.timeout.must.be.number.error=HTTP timeout must be a number (group ''{0}'')
dialog.edit.http.proxies.settings.dialog.select.ssl.client.certificate.path.title=Select path to your certificate
dialog.edit.http.proxies.settings.dialog.select.ssl.client.certificate.path.text=Select path to your certificate
dialog.edit.http.proxies.settings.panel.proxy.title=HTTP proxy settings
dialog.edit.http.proxies.settings.panel.ssl.title=SSL settings
dialog.edit.http.proxies.settings.panel.repositories.title=Repositories
dialog.edit.http.proxies.settings.patterns.text=U&RL patterns:
dialog.edit.http.proxies.settings.exceptions.text=E&xceptions:
dialog.edit.http.proxies.settings.server.text=&Server:
dialog.edit.http.proxies.settings.user.text=&User:
dialog.edit.http.proxies.settings.port.text=P&ort:
dialog.edit.http.proxies.settings.password.text=Pass&word:
dialog.edit.http.proxies.settings.connection.timeout.text=Connection t&imeout:
dialog.edit.http.proxies.settings.seconds.text=seconds
dialog.edit.http.proxies.settings.paths.to.authority.certificates.text=Comma separated &paths to CAs certificate files:
dialog.edit.http.proxies.settings.ssl.client.certificate.file.text=SSL &client certificate file:
dialog.edit.http.proxies.settings.client.certificate.passphrase.text=SSL client certificate passp&hrase:
dialog.edit.http.proxies.settings.trust.default.cas.text=Trust default C&As
dialog.edit.http.proxies.settings.test.connection.button.text=&Test connection
dialog.edit.http.proxies.settings.test.connection.settings.will.be.stored.title=Warning
dialog.edit.http.proxies.settings.test.connection.settings.will.be.stored.text=Settings will be saved
dialog.edit.http.proxies.settings.test.connection.error.title=Connection test failed
dialog.edit.http.proxies.settings.test.connection.succes.title=Connection test successful
dialog.edit.http.proxies.settings.test.connection.succes.text=Connection test successful
confirmation.text.delete.stored.authentication.information=You are about to delete all stored Subversion authentication information.\nWould you like to proceed with deletion?
confirmation.title.clear.authentication.cache=Clear Authentication Cache
label.configure.upgrade.label=Working Copy upgrade strategy:
radio.configure.upgrade.ask=As&k me on attempt to upgrade
radio.configure.upgrade.none=&Never upgrade (keep Subversion 1.3)
radio.configure.upgrade.auto=Upgrade &automatically (use Subversion 1.4)
radio.configure.upgrade.auto.15format=Upgrade automatically (use Subversion 1.&5)
radio.configure.upgrade.auto.16format=Upgrade automatically (use Subversion 1.&6)
label.configure.create.label={0} is about to create new Subversion working copy,\n\
please, select desired working copy format:
label.configure.create.strategy=Create working copy directory in:
radio.configure.create.none=1.&3 format
radio.configure.create.auto=1.&4 format
radio.configure.create.auto.15format=1.&5 format
radio.configure.create.auto.16format=1.&6 format
radio.configure.create.auto.17format=1.&7 format
label.configure.change.label=Change working copy ''{0}'' format to:
radio.configure.change.none=1.&3 format
radio.configure.change.auto=1.&4 format
radio.configure.change.auto.15format=1.&5 format
radio.configure.change.auto.16format=1.&6 format
radio.configure.change.auto.17format=1.&7 format
label.where.svn.format.settings.text=To change above setting later, visit 'File | Settings | Version Control'
label.where.svn.format.can.be.changed.text=''Version Control | Subversion | {0}''
dialog.upgrade.wcopy.format.title=Subversion Working Copy Format
label.working.copy.root.outside.text=Working copy root is outside the directory specified in Project Settings.\nThe whole working copy will be converted.
progress.text.loading.contents=Loading contents of ''{0}''
progress.text2.revision.information=Revision {0}
progress.title.loading.file.content=Loading Remote File Content
exception.text.file.miss.svn=File ''{0}'' is readonly, but miss svn:needs-lock property
confirmation.text.edit.file=File(s) you're are going to edit needs to be locked before editing
#File statuses
file.status.external=External (svn)
file.status.obstructed=Obstructed (svn)
file.status.replaced=Replaced (svn)
file.status.switched=Switched (svn)
#Adding
confirmation.text.add.file=Do you want to schedule the following file for addition to Subversion?\n{0}
confirmation.text.add.dir=Do you want to schedule the following directory for addition to Subversion?\n{0}
confirmation.title.add.file=Schedule for Addition
confirmation.title.add.multiple.files=Select Files to Add to Subversion
action.name.add.files=Adding files to {0}
#Ignore
action.name.ignore.files=Adding ignore file(s) mask to {0}
action.name.undo.ignore.files=Removing file names(or mask) from ignore list from {0}
# Delete
confirmation.text.delete.file=Do you want to schedule the following file for deletion from Subversion?\n{0}
confirmation.text.delete.dir=Do you want to schedule the following directory for deletion from Subversion?\n{0}\nNOTE: The directory will be deleted from disk when the change is committed.
confirmation.title.delete.file=Schedule for Deletion
confirmation.title.delete.multiple.files=Select Files to Delete from Subversion
progress.text.discovering.location=Discovering location for ''{0}''
#Lock/Unlock
progress.text.locking.files=Locking files in repository...
progress.text2.processing.file=Processing file {0}
progress.title.lock.files=Lock Files
exception.text.locking.file.failed=Failed to lock file: {0}
message.title.lock.failures=Lock Failures
message.text.files.locked={0,choice, 0#Failed to lock files.|1#1 file locked|2#{0,number} files locked}
progress.text.unlocking.files=Unlocking files in repository...
progress.title.unlock.files=Unlock Files
exception.text.failed.to.unlock.file=Failed to unlock file: {0}
message.title.unlock.failures=Unlock Failures
message.text.files.unlocked={0,choice, 0#Failed to unlock files.|1#1 file unlocked|2#{0,number} files unlocked}
progress.text.collection.locations=Collection Subversion Repository Locations
message.text.no.working.copies.found=No subversion working copies found in project
message.title.no.working.copies.found=Not working copies found
progress.text.collecting.roots.in.module=Collecting working copy roots in module ''{0}''
dialog.title.select.location=Select Location
border.select.location.repository.location=Repository Location
radio.select.location.use.project=Use project location:
radio.select.location.use.custom=Use custom location:
exception.text.cleanupaction.batchperform.not.implemented=CleanupAction.batchPerform not implemented
progress.text2.adding=Adding ''{0}''
progress.text2.deleting=Deleting ''{0}''
progress.text2.sending=Sending ''{0}''
progress.text2.replacing=Replacing ''{0}''
progress.text2.transmitting.delta=Transmitting delta for ''{0}''
progress.title.copy=Subversion Copy
status.text.comitted.revision=Committed revision {0}.
progress.text.copy.to=Copy to ''{0}''
message.text.no.conflicts.found=No conflicts found
message.title.no.conflicts.found=No Conflicts
label.select.files.and.directories.to.mark.resolved=Select files and directories to mark resolved:
dialog.title.mark.resolved=Mark Resolved
action.name.mark.resolved=Mark Resolved
action.name.resolve.conflict=Resolve Conflict
message.text.revert.no.modifications.found=No modifications to revert found
label.select.files.and.directories.to.revert=Select files and directories to revert:
dialog.title.revert.files=Revert Files
action.name.revert=&Revert
message.title.revert.no.modifications.found=No Modifications
action.name.revert.files=Revert files
action.name.set.property=Set Property
action.name.show.all.paths.affected=Show all paths affected in selected transaction
dialog.title.affected.paths=Paths Affected in Transaction ''{0}''
progress.text.loading.log=Loading revision log...
exception.text.cannot.load.version=Cannot load repository version {0}
progress.text.processing.changes=Processing changes...
message.text.cannot.load.version=Cannot load repository version {0} :{1}
message.title.error.fetching.affected.paths=Error Fetching Affected Paths
exception.text.cannot.annotate.directory=Annotation operation only makes sence for files
progress.text.computing.annotation=Computing annotation for ''{0}''
action.text.annotate=Annotate
checkbox.chckin.keep.files.locked=&Keep files locked
border.show.changes.dialog.subversion.group=Subversion
progress.title.commit=Commit
progress.text.committing.changes.below=Creating commit packets...
status.text.committed.revision=Committed revision {0}.
checkin.operation.name=Comm_it
progress.text.collecting.commitables=Collecting commitables at ''{0}''
progress.text2.fetching.external.location=Fetching external location to ''{0}''
progress.text2.checked.out=Checked out {1} files, checking out {0}
progress.text2.exported=Exported {1} files, exporting {0}
progress.text2.checked.out.revision=Checked out revision {0}.
progress.text2.exported.revision=Exported revision {0}.
status.text.checked.out.revision=Checked out revision {0}.
message.text.cannot.checkout=Cannot checkout from svn: {0}
message.text.cannot.export=Cannot export from svn: {0}
message.title.check.out=Check Out from Subversion
message.title.export=Export from Subversion
progress.text.checking.out=Checking out files to ''{0}''
progress.text.export=Exporting files to ''{0}''
dialog.text.check.out=Check Out from Repository
label.text.checkout.repository.url=Repository &URL:
label.text.checkout.checkout.from=Checkout From:
label.checkout.checkout.to=Checkout &To:
dialof.title.select.checkout.destination=Select Checkout Destination
dialog.description.select.checkout.destination=Select directory to checkout remote directory into or create new one
button.text.select.all=Select &All
button.text.deselect.all=&Deselect All
dialog.title.select.repository.location=Select Repository Location
button.text.ssl.accept=_Accept
button.text.ssl.reject=_Reject
button.text.ssh.accept=_Yes
button.text.ssh.reject=_No
dialog.title.ssl.examine.server.crertificate=Examine Server Certificate
dialog.title.ssh.examine.server.fingerprints=Verify Server Key Fingerprint
label.ssl.server.provided.certificate=Server provided the following certificate:
label.ssh.server.provided.fingerprints=The authenticity of host {0} can''t be established.
label.ssh.server.provided.fingerprints2={0} key fingerprint is
label.ssh.server.provided.fingerprints3=Are you sure you want to continue connecting?
checkbox.svn.ssh.cache.fingerprint=&Add key to Subversion cache
dialog.title.set.property=Set Property
label.set.property.property.name=Property &name:
radio.set.property.set.property.value=&Set property value:
radio.set.property.delete.property=&Delete property
checkbox.set.property.update.properties.recursively=Update properties &recursively
exception.text.invalid.revision=Invalid revision ''{0}''
#update
update.configurable.name=Update
checkbox.update.configurable.descend.into.child.directories=&Descend into child directories
checkbox.update.configurable.run.status=Run &status after update
update.switch.configurable.name=Update/Switch
radio.update.switch.configurable.update.or.switch=U&pdate or Switch
label.update.switch.configurable.url=&URL:
checkbox.update.switch.configurable.to.specific.revision=Update/Switch to specific &revision:
radio.update.switch.configurable.merge=&Merge
label.update.switch.configurable.url1=URL &1:
label.update.switch.configurable.revision1=Re&vision:
lable.update.switch.configurable.url2=URL &2:
update.switch.configurable.revision2=R&evision:
checkbox.update.switch.configurable.try.merge.without.changes=&Try merge, but make no changes
checkbox.update.switch.configurable.descend.into.child.directories=&Descend into child directories
checkbox.update.switch.configurable.run.status=Run &status after update
progress.text.merging.dry.run.changes=Merging (dry run) changes into ''{0}''
progress.text.merging.changes=Merging changes into ''{0}''
progress.text.updating=Updating ''{0}''
exception.text.root.was.not.properly.updated=svn: {0} was not properly updated; may be it is already removed from the repository along with its parent.
progress.text.update.computing.post.update.status=Computing post-update status of ''{0}''
message.text.update.no.directories.found=No versioned directories to update was found
messate.text.update.error=SVN: Update Error
progress.text2.added=Added {0}
progress.text2.deleted=Deleted {0}
progress.text2.conflicted=Conflicted {0}
progress.text2.treeconflicted=Conflicted {0} (tree conflict)
progres.text2.merged=Merged {0}
progres.text2.updated=Updated {0}
progress.text2.restored.file=Restored {0}
progres.text2.updated.to.revision=Updated to revision {0}.
status.text.updated.to.revision=Updated to revision {0}.
progress.text2.skipped.file=Skipped {0}
update.group.name.skipped=Skipped
progress.text.updating.external.location=Updating external location at ''{0}''
exception.text.update.operation.cancelled=Operation cancelled
server.ssl.accept.temporary.action.name=Accept _Temporary
changes.browser.change.column.name=Change
changes.browser.author.column.name=Author
changes.browser.date.column.name=Date
cannot.mark.file.as.resolved.error.message=Cannot mark file as resolved: {0}
mark.resolved.dialog.title=Mark Resolved
mark.resolved.action.description=Mark resolved
commit.dialog.setings.show.unresolved.checkbox=Show &unresolved
unresolved.difference.type.name=Unresolved
integrate.display.name=Integrate
invalid.svn.revision.error.message=Invalid svn revision: {0}
nulty.roots.update.configuration.affected.roots.label=Affected roots:
nulty.roots.update.configuration.selected.root.setting.group.title=Selected Root Setting:
integrate.configuration.revision1.label=re&vision
integrate.configuration.source2.label=Source 2:
integrate.configuration.revision2.label=&revision
integrate.configuration.source1.label=Source 1:
integrate.configuration.description.label=Integrate differences between sources into working copy (compare Source 2 to Source 1)
source.url.could.not.be.empty.error.message=Source url could not be empty
no.differences.between.sources.error.message=There are no differences between source and the same source
configure.revision.specified.radio=Specified
configure.revision.head.radio=HEAD
update.configuration.specific.url=Update/Switch to specific &url:
progress.text.import=Importing {0}
message.text.cannot.import=Cannot import to svn: {0}
message.title.import=Import to Subversion
revision.title=Revision
checkout.dialog.title=Checkout from Subversion
checkout.dialog.button=Checkout
checkout.directory.chooser.title=Checkout Directory
checkout.directory.chooser.prompt=Select directory to checkout from Subversion
checkout.options.dialog.title=SVN Checkout Options
checkout.options.checkout.label=Checkout:
checkout.options.destination.label=Destination:
checkout.options.recursive.label=Checkout directories recursively
checkout.options.externals.label=Include externals locations
configure.branches.title=Configure Subversion Branches
configure.branches.error.no.connection.title=Repository root was not found
configure.branches.error.wrong.url=Trunk location must be under repository root
configure.branches.trunk.location=Trunk location:
configure.branches.branch.locations=Branch locations:
configure.branches.item=Configure Branches...
refresh.branches.item=Refresh Branches...
copy.column.title=Copy
copy.column.tooltip=Copied from {0}
compare.with.branch.progress.loading.branches=Loading Branches
compare.with.branch.progress.loading.content=Loading Content
compare.with.branch.diff.title=Compare with Branch
compare.with.branch.error.title=Compare with Branch
compare.with.branch.popup.title=Compare with Branch
getting.branch.configuration.error=Error retrieving branch configuration: {0}
compare.with.branch.location.error=Couldn''t find repository location for file ''{0}'' under branch ''{1}''\nPlease check your branch configuration.
compare.with.branch.list.error=Error Loading Branches
create.branch.invalid.revision.error=Invalid revision ''{0}''
create.branch.no.working.copy.error=No working copy found at {0}
create.branch.no.base.location.error=No branch base location selected
import.dialog.title=Import into Subversion
import.dialog.button=Import
repository.browser.add.location.action=Add Repository Location
repository.browser.add.location.prompt=Repository URL:
repository.browser.add.location.title=New Repository Location
repository.browser.details.action=Show/Hide Details
repository.browser.refresh.action=Refresh selected tree node
repository.browser.add.location.menu.item=_Repository Location...
repository.browser.edit.location.menu.item= Edit Location _Url...
repository.browser.discard.location.action=Discard _Location
repository.browser.discard.location.prompt=Would you like to discard the location ''{0}''?
repository.browser.discard.location.title=Discard Location
repository.browser.new.folder.action=Remote _Folder...
repository.browser.history.action=Show History
repository.browser.import.action=_Import...
diff.options.title=Compare With Branch or Tag
diff.options.no.url.error=Please select an URL to compare with
diff.options.same.url.error=Please select different URLs to compare
diff.options.no.patch.file.error=Please specify the path to save the patch file
repository.browser.browse.changes.action=_Browse Changes...
repository.browser.browse.changes.description=View history of changes in the selected node
repository.browser.compare.title=Compare of ''{0} and ''{1}''
repository.browser.compare.no.difference.message=No textual difference found between ''{0} and ''{1}''
repository.browser.compare.no.difference.title=No Difference Found
cleanup.action.name=Cleanup
changes.browser.revision.term=Revision
progress.computing.difference=Computing Difference
progress.searching.for.files.with.conflicts=Searching for files with conflicts
add.files.errors.title=Errors Adding Files
move.files.errors.title=Errors Moving Files
remove.ignore.patterns.errors.title=Errors Removing 'svn:ignore' patterns
gather.ignore.patterns.info.progress.title=Gathering not used 'svn:ignore' patterns
delete.files.errors.title=Errors Deleting Files
copy.dialog.title=Branch or Tag
move.dialog.title=Move or Rename
select.location.invalid.url.message=Invalid Subversion repository URL: ''{0}''
share.directory.action=Share Directory...
share.directory.commit.message=Directory ''{0}'' created by {1}
share.directory.title=Share Directory
share.directory.info.message=To complete share operation commit ''{0}''.
share.directory.create.dir.progress.text=Creating remote folder {0}
share.directory.checkout.back.progress.text=Checking out {0}
share.or.import.add.progress.text=Scheduling {0} for addition
action.share.whole.project.text=Share Project (Subversion)...
action.Subversion.integrate.changes.open.in.changelist.text=Open in changelist:
action.Subversion.integrate.changes.select.working.copy.text=Integrate into working copy:
action.Subversion.integrate.changes.dialog.title=Integrate To Branch
action.Subversion.integrate.changes.message.not.under.control.text=Selected destination is not under Subversion control. Continue anyway?
action.Subversion.integrate.changes.message.another.wc.text=Selected destination is inside working copy of another repository. Continue anyway?
action.Subversion.integrate.changes.branch.info.source.label.text=Source branch URL: {0}
action.Subversion.integrate.changes.branch.info.target.label.text=Target branch URL: {0}
action.Subversion.integrate.changes.actionname=Integrate To Branch...
action.Subversion.integrate.changes.description=Integrates selected changelist(s)/changeset into selected branch
action.Subversion.integrate.changes.messages.title=Integrate To Branch
action.Subversion.integrate.changes.error.no.available.files.text=No files available for integration
action.Subversion.integrate.changes.warning.failed.list.text=Changelist {0} ({1}) integration faced problems
action.Subversion.integrate.changes.warning.skipped.lists.text=Skipped change lists: {0}
action.Subversion.integrate.changes.select.branch.text=Select branch to integrate to...
action.Subversion.integrate.changes.error.source.and.target.same.text=Can not start integration: Target and source branches are the same.
action.Subversion.integrate.changes.error.target.not.dir.text=Can not start integration: target directory does not exist or is not a directory.
action.Subversion.integrate.changes.error.not.versioned.text=Can not start integration: target directory is not under Subversion control or does not belong to the selected branch.
action.Subversion.integrate.changes.alien.commit.changelist.title=Integrated
action.Subversion.integrate.changes.dialog.add.wc.text=Add
action.Subversion.integrate.changes.dialog.remove.wc.text=Remove
action.Subversion.integrate.changes.message.files.up.to.date.text=All files are up to date
action.Subversion.integrate.changes.error.unable.to.collect.changes.text=Error while collecting changes to commit: {0}
error.cannot.load.revisions=Can not load revision list
action.Subversion.integrate.changes.message.canceled.text=Integration was canceled
action.Subversion.integrate.changes.progress.integrating.text=Merging changes
action.Subversion.integrate.changes.progress.integrating.details.text=Merging changelist {0}
update.switch.to.branch.text=Use branch:
select.branch.popup.general.title=Select branch
progress.live.provider.loading.revisions.text=Loading revisions from server
progress.live.provider.loading.revisions.details.text=Loading revisions from server...
action.show.svn.map.text=Show Working Copies...
action.show.svn.map.text.reference=Show Working Copies
action.show.svn.map.description=Shows working copies information: working copy formats and URLs
dialog.show.svn.map.title=Subversion Working Copies Information
dialog.show.svn.map.label.table.header=Detected working copies
dialog.show.svn.map.change.format.button.text=&Change Working Copy Format
dialog.show.svn.map.table.header.column.wcpath.title=Root path
dialog.show.svn.map.table.header.column.wcurl.title=URL
dialog.show.svn.map.table.header.column.wcroot.title=Working copy root
dialog.show.svn.map.table.header.column.format.title=Format
dialog.show.svn.map.table.version13.text=1.3
dialog.show.svn.map.table.version14.text=1.4
dialog.show.svn.map.table.version15.text=1.5
dialog.show.svn.map.table.version16.text=1.6
dialog.show.svn.map.table.version17.text=1.7
action.change.wcopy.format.task.title=Convert working copy format
action.change.wcopy.format.task.progress.text=Converting working copy at {0} format from {1} to {2}
action.change.wcopy.format.after.change.settings=Selected working copy format ({0}) is older than upgrade mode selected in Project Settings ({1}).\
\nChange Project Settings upgrade mode to {0}?
committed.changes.action.enable.merge.highlighting=Highlight integrated
committed.changes.action.enable.merge.highlighting.description.text=Option is available for SVN repositories and working copies with 1.5 version only
committed.changes.action.merge.highlighting.select.root=from:
committed.changes.action.merge.highlighting.select.branch=to:
committed.changes.action.merge.highlighting.refresh.text=Refresh
committed.changes.action.merge.highlighting.refresh.description=Refresh merged revisions info
committed.changes.merge.status.refreshing.text=?
committed.changes.merge.status.integrated.text=+
committed.changes.merge.status.not.integrated.text=-
committed.changes.action.merge.highlighting.select.wcopy=working copy:
dialog.Subversion.select.working.copy.title=Configure working copy paths
dialog.Subversion.select.working.copy.wcopy.list.title=Known working copies
upgrade.format.to15.question.title=Upgrade working copies format
upgrade.format.to15.question.text=Do you want to upgrade Subversion working copies format to 1.5?\n\
Note: Working copy formats can be changed through {0}
upgrade.format.clarify.for.outside.copies.text=Working copy root ''{0}'' is outside the directory specified in Project Settings.\nConvert anyway?
upgrade.format.to16.question.title=Upgrade Subversion working copies format
upgrade.format.to16.question.text=Do you want to upgrade Subversion working copies format to 1.6?\n\
Note: Working copy formats can be changed through {0}
action.Subversion.cleanup.progress.title=Subversion cleanup
action.Subversion.cleanup.progress.text=Performing ''{0}'' cleanup...
action.Subversion.cleanup.project.title=Cleanup Project
action.Subversion.cleanup.error.message=Error performing cleanup for ''{0}'': {1}
action.working.copies.map.correct.roots.button.text=Correct &Roots
action.working.copies.map.correct.warning.text=Some folders marked in 'Project Settings | Version Control' as under Subversion control,\n\
are unversioned (Versioned folders may be under them).\n\
You can use 'Correct Roots' button to perform auto correction.
action.Subversion.properties.diff.with.local.name=Properties Diff with Local
action.Subversion.properties.diff.name=Properties Diff
action.Subversion.properties.difference.diff.title=Subversion properties difference: {0}
action.Subversion.properties.difference.diff.for.move.title=Subversion properties difference: {0} -> {1}
message.Subversion.files.can.not.be.added.to.subversion.are.not.under.working.copy.text=File(s) {0} can not be added to Subversion control since they are not under working copy.
action.Subversion.integrate.difference.option.use.ancestry.text=&Use ancestry
fatal.Subversion.javasvn.jar.library.version.test=javasvn.jar library is old;\nPlease make sure installation was made properly.
subversion.problem.text=Subversion problem
use.idea.proxy.as.default=Use {0} general proxy settings as default for Subversion
use.idea.proxy.as.default.label.text=Use proxy settings from Settings | Plugins | HTTP Proxy Settings as default
action.Subversion.update.use.locks.on.demand.text.configurable=Updat&e administrative information only in changed subtrees
action.Subversion.update.use.locks.on.demand.text=Update administrative information only in &changed subtrees
action.Subversion.update.use.locks.on.demand.description=When enabled, update only locks those parts of working copy that is about to be updated.\n\
As a result administrative information will only be updated for working copy subtrees that has been modified during update.\n\
Enabling this option may improve update operation performance.\n\
Note that under some circumstances, you might have to perform "normal" update.
switch.target.not.copy.current=Switch target URL is not a copy of current.\n Do you want to continue?
switch.target.problem.title=Switch target problem
tab.repository.merge.panel.title=Subversion
tab.repository.merge.panel.root.panel.to=To:
tab.repository.merge.panel.root.panel.from=From:
tab.repository.merge.panel.root.panel.branch=Branch:
tab.repository.merge.panel.root.panel.url=Url:
tab.repository.merge.panel.root.panel.local=Local Path:
tab.repository.merge.panel.root.panel.select.local=Please select path
tab.repository.merge.panel.filter.plus=Filter out integrated
tab.repository.merge.panel.filter.minus=Filter out not integrated
tab.repository.merge.panel.filter.others=Filter out others
browse.changes.settings.stop.on.copy=&Stop On Copy
action.mark.list.as.merged.text=Mark As Merged
action.mark.list.as.merged.description=Mark selected revision as merged, but do not actually merge
action.mark.list.as.not.merged.title=Mark As Not Merged
action.mark.list.as.not.merged.descrition=Mark selected revision as not merged, but do not change anything besides svn:mergeinfo
label.depth.text=&Depth:
label.depth.description=Replaces old "recursive" option. "Recursive"=false is equivalent to "immediates", "recursive"=true is equivalent to "infinity"
undo.integrate.to.branch=Undo Integrate To Branch...
undo.integrate.to.branch.description=Removes changes done in revision from branch
undo.integrate.to.branch.dialog.title=Undo Integrate To Branch
merge.source.details.title=Merge source details for {0} [{1}]
repository.browser.edit.location.dialog.title= Edit Location Url
loading.default.branches.configuration.text=Loading default branches configuration...
loading.data.for.root.text=Loading data for {0}
action.PropertiesDiff.text=Compare Subversion Properties
action.AlienPropertiesLocalDiff.text=Compare Subversion Properties with Branch
action.IntegrateChangeSetAction.text=Integrate to Branch
dialog.map.working.copies.button.refresh.text=Re&fresh
refreshing.working.copies.roots.progress.text=Refreshing working copies roots...
retrieving.subversion.status.text=Retrieving Subversion status...
fetching.properties.contents.progress.title=Fetching properties contents...
show.properties.diff.progress.text.revision.information=Revision {0}
show.properties.diff.progress.text2.property.information=Property {0}
svn.checkout.destination.directory.title=Destination directory
svn.checkout.destination.directory.description=Select checkout destination directory
checkbox.ignore.whitespace.when.merge.text=&Ignore whitespaces
svn.dialog.select.ignore.patterns.to.remove.title=Select svn:ignore patterns to remove
svn.dialog.select.ignore.patterns.to.remove.prompt=Files being deleted are ignored by Subversion.\n\
Some of 'svn:ignore' patterns may become outdated.\n\
Select 'svn:ignore' patterns to be deleted.
detect.nested.working.copies=Detect nested working copies
svn.changeview.item.in.conflict.text=File has {0} conflict(s) and can not be committed
svn.changeview.locally.deleted.item.in.conflict.text=File has {0} conflict(s)
action.mark.tree.conflict.resolved.text=Mark Tree Conflict Resolved...
action.mark.tree.conflict.resolved.confirmation.text=Do you want to mark tree conflict as resolved?
action.mark.tree.conflict.resolved.confirmation.title=Mark Tree Conflict Resolved
svn.integrate.changelist.warning.unresolved.conflicts.text=Integration interrupted. There are unresolved conflicts or skipped items.
svn.option.ignore.whitespace.in.annotate=Ignore whitespace differences in annotations
annotation.show.merge.sources.default.text=Show merge source in annotations
svn.cannot.save.credentials.store-auth-creds=Cannot store credentials: forbidden by "store-auth-creds = no"
annotation.original.revision=Original revision
svn17.plugin.not.ready=<html><body>This plugin provides only basic support for Subversion 1.7.<br/>\
Many operations are not implemented.<br/>\
Subversion 1.7 integration is not ready yet.<br/>\
We do NOT guarantee data safety and consistency.<br/><br/>Also, we do NOT guarantee that plugin correctly works with Subversion 1.6 and lower working copies!</body></html>
@@ -0,0 +1,304 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.actions.VcsContextFactory;
import com.intellij.openapi.vcs.changes.*;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.EventDispatcher;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn17.actions.CleanupWorker;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.wc.ISVNStatusFileProvider;
import org.tmatesoft.svn.core.wc.SVNStatus;
import org.tmatesoft.svn.core.wc.SVNStatusType;
import java.io.File;
import java.util.*;
/**
* @author max
* @author yole
*/
public class SvnChangeProvider implements ChangeProvider {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnChangeProvider");
public static final String ourDefaultListName = VcsBundle.message("changes.default.changlist.name");
private final SvnVcs17 myVcs;
private final VcsContextFactory myFactory;
private final SvnFileUrlMappingImpl17 mySvnFileUrlMapping;
public SvnChangeProvider(final SvnVcs17 vcs) {
myVcs = vcs;
myFactory = VcsContextFactory.SERVICE.getInstance();
mySvnFileUrlMapping = (SvnFileUrlMappingImpl17) vcs.getSvnFileUrlMapping();
}
public void getChanges(final VcsDirtyScope dirtyScope, final ChangelistBuilder builder, ProgressIndicator progress,
final ChangeListManagerGate addGate) throws VcsException {
final SvnScopeZipper zipper = new SvnScopeZipper(dirtyScope);
zipper.run();
final Map<String, SvnScopeZipper.MyDirNonRecursive> nonRecursiveMap = zipper.getNonRecursiveDirs();
final ISVNStatusFileProvider fileProvider = createFileProvider(nonRecursiveMap);
try {
final SvnChangeProviderContext context = new SvnChangeProviderContext(myVcs, builder, progress);
final StatusWalkerPartnerImpl partner = new StatusWalkerPartnerImpl(myVcs, progress);
final NestedCopiesBuilder nestedCopiesBuilder = new NestedCopiesBuilder();
final EventDispatcher<StatusReceiver> statusReceiver = EventDispatcher.create(StatusReceiver.class);
statusReceiver.addListener(context);
statusReceiver.addListener(nestedCopiesBuilder);
final SvnRecursiveStatusWalker walker = new SvnRecursiveStatusWalker(statusReceiver.getMulticaster(), partner);
for (FilePath path : zipper.getRecursiveDirs()) {
walker.go(path, SVNDepth.INFINITY);
}
partner.setFileProvider(fileProvider);
for (SvnScopeZipper.MyDirNonRecursive item : nonRecursiveMap.values()) {
walker.go(item.getDir(), SVNDepth.FILES);
}
// they are taken under non recursive: ENTRIES file is read anyway, so we get to know parent status also for free
/*for (FilePath path : zipper.getSingleFiles()) {
FileStatus status = getParentStatus(context, path);
processFile(path, context, status, false, context.getClient());
}*/
processCopiedAndDeleted(context);
processUnsaved(dirtyScope, addGate, context);
mySvnFileUrlMapping.acceptNestedData(nestedCopiesBuilder.getSet());
}
catch (SVNException e) {
throw new VcsException(e);
}
}
private void processUnsaved(VcsDirtyScope dirtyScope, ChangeListManagerGate addGate, SvnChangeProviderContext context)
throws SVNException {
final FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
final Document[] unsavedDocuments = fileDocumentManager.getUnsavedDocuments();
for (Document unsavedDocument : unsavedDocuments) {
final VirtualFile file = fileDocumentManager.getFile(unsavedDocument);
if (file != null && dirtyScope.belongsTo(new FilePathImpl(file)) && fileDocumentManager.isFileModified(file)) {
final FileStatus status = addGate.getStatus(file);
if (status == null || FileStatus.NOT_CHANGED.equals(status)) {
context.addModifiedNotSavedChange(file);
}
}
}
}
private ISVNStatusFileProvider createFileProvider(Map<String, SvnScopeZipper.MyDirNonRecursive> nonRecursiveMap) {
// translate into terms of File.getAbsolutePath()
final Map<String, Map> preparedMap = new HashMap<String, Map>();
for (SvnScopeZipper.MyDirNonRecursive item : nonRecursiveMap.values()) {
final Map result = new HashMap();
for (FilePath path : item.getChildrenList()) {
result.put(path.getName(), path.getIOFile());
}
preparedMap.put(item.getDir().getIOFile().getAbsolutePath(), result);
}
return new ISVNStatusFileProvider() {
public Map getChildrenFiles(File parent) {
return preparedMap.get(parent.getAbsolutePath());
}
};
}
private void processCopiedAndDeleted(final SvnChangeProviderContext context) throws SVNException {
for(SvnChangedFile copiedFile: context.getCopiedFiles()) {
if (context.isCanceled()) {
throw new ProcessCanceledException();
}
processCopiedFile(copiedFile, context.getBuilder(), context);
}
for(SvnChangedFile deletedFile: context.getDeletedFiles()) {
if (context.isCanceled()) {
throw new ProcessCanceledException();
}
context.processStatus(deletedFile.getFilePath(), deletedFile.getStatus());
}
}
public void getChanges(final FilePath path, final boolean recursive, final ChangelistBuilder builder) throws SVNException {
final SvnChangeProviderContext context = new SvnChangeProviderContext(myVcs, builder, null);
final StatusWalkerPartnerImpl partner = new StatusWalkerPartnerImpl(myVcs, ProgressManager.getInstance().getProgressIndicator());
final SvnRecursiveStatusWalker walker = new SvnRecursiveStatusWalker(context, partner);
walker.go(path, recursive ? SVNDepth.INFINITY : SVNDepth.IMMEDIATES);
processCopiedAndDeleted(context);
}
@Nullable
private String changeListNameFromStatus(final SVNStatus status) {
if (WorkingCopyFormat.getInstance(status.getWorkingCopyFormat()).supportsChangelists()) {
if (SVNNodeKind.FILE.equals(status.getKind())) {
final String clName = status.getChangelistName();
return (clName == null) ? null : clName;
}
}
// always null for earlier versions
return null;
}
private void processCopiedFile(SvnChangedFile copiedFile, ChangelistBuilder builder, SvnChangeProviderContext context) throws SVNException {
boolean foundRename = false;
final SVNStatus copiedStatus = copiedFile.getStatus();
final String copyFromURL = copiedFile.getCopyFromURL();
final FilePath copiedToPath = copiedFile.getFilePath();
// if copy target is _deleted_, treat like deleted, not moved!
/*for (Iterator<SvnChangedFile> iterator = context.getDeletedFiles().iterator(); iterator.hasNext();) {
final SvnChangedFile deletedFile = iterator.next();
final FilePath deletedPath = deletedFile.getFilePath();
if (Comparing.equal(deletedPath, copiedToPath)) {
return;
}
}*/
final Set<SvnChangedFile> deletedToDelete = new HashSet<SvnChangedFile>();
for (Iterator<SvnChangedFile> iterator = context.getDeletedFiles().iterator(); iterator.hasNext();) {
SvnChangedFile deletedFile = iterator.next();
final SVNStatus deletedStatus = deletedFile.getStatus();
if ((deletedStatus != null) && (deletedStatus.getURL() != null) && Comparing.equal(copyFromURL, deletedStatus.getURL().toString())) {
final String clName = changeListNameFromStatus(copiedFile.getStatus());
builder.processChangeInList(context.createMovedChange(createBeforeRevision(deletedFile, true),
CurrentContentRevision.create(copiedFile.getFilePath()), copiedStatus, deletedStatus), clName, SvnVcs17
.getKey());
deletedToDelete.add(deletedFile);
for(Iterator<SvnChangedFile> iterChild = context.getDeletedFiles().iterator(); iterChild.hasNext();) {
SvnChangedFile deletedChild = iterChild.next();
final SVNStatus childStatus = deletedChild.getStatus();
if (childStatus == null) {
continue;
}
final SVNURL childUrl = childStatus.getURL();
if (childUrl == null) {
continue;
}
final String childURL = childUrl.toString();
if (StringUtil.startsWithConcatenationOf(childURL, copyFromURL, "/")) {
String relativePath = childURL.substring(copyFromURL.length());
File newPath = new File(copiedFile.getFilePath().getIOFile(), relativePath);
FilePath newFilePath = myFactory.createFilePathOn(newPath);
if (!context.isDeleted(newFilePath)) {
builder.processChangeInList(context.createMovedChange(createBeforeRevision(deletedChild, true),
CurrentContentRevision.create(newFilePath),
context.getTreeConflictStatus(newPath), childStatus), clName, SvnVcs17.getKey());
deletedToDelete.add(deletedChild);
}
}
}
foundRename = true;
break;
}
}
final List<SvnChangedFile> deletedFiles = context.getDeletedFiles();
for (SvnChangedFile file : deletedToDelete) {
deletedFiles.remove(file);
}
// handle the case when the deleted file wasn't included in the dirty scope - try searching for the local copy
// by building a relative url
if (!foundRename && copiedStatus.getURL() != null) {
File wcPath = guessWorkingCopyPath(copiedStatus.getFile(), copiedStatus.getURL(), copyFromURL);
SVNStatus status;
try {
status = context.getClient().doStatus(wcPath, false);
}
catch(SVNException ex) {
status = null;
}
if (status != null && SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_DELETED)) {
final FilePath filePath = myFactory.createFilePathOnDeleted(wcPath, false);
final SvnContentRevision beforeRevision = SvnContentRevision.createBaseRevision(myVcs, filePath, status.getRevision());
final ContentRevision afterRevision = CurrentContentRevision.create(copiedFile.getFilePath());
builder.processChangeInList(context.createMovedChange(beforeRevision, afterRevision, copiedStatus, status), changeListNameFromStatus(status),
SvnVcs17.getKey());
foundRename = true;
}
}
if (!foundRename) {
// for debug
LOG.info("Rename not found for " + copiedFile.getFilePath().getPresentableUrl());
context.processStatus(copiedFile.getFilePath(), copiedStatus);
}
}
private SvnContentRevision createBeforeRevision(final SvnChangedFile changedFile, final boolean forDeleted) {
return SvnContentRevision.createBaseRevision(myVcs,
forDeleted ? FilePathImpl.createForDeletedFile(changedFile.getStatus().getFile(),
changedFile.getFilePath().isDirectory()) :
changedFile.getFilePath(), changedFile.getStatus().getRevision());
}
private static File guessWorkingCopyPath(final File file, @NotNull final SVNURL url, final String copyFromURL) throws SVNException {
String copiedPath = url.getPath();
String copyFromPath = SVNURL.parseURIEncoded(copyFromURL).getPath();
String commonPathAncestor = SVNPathUtil.getCommonPathAncestor(copiedPath, copyFromPath);
int pathSegmentCount = SVNPathUtil.getSegmentsCount(copiedPath);
int ancestorSegmentCount = SVNPathUtil.getSegmentsCount(commonPathAncestor);
boolean startsWithSlash = file.getAbsolutePath().startsWith("/");
List<String> segments = StringUtil.split(file.getPath(), File.separator);
List<String> copyFromPathSegments = StringUtil.split(copyFromPath, "/");
List<String> resultSegments = new ArrayList<String>();
final int keepSegments = segments.size() - pathSegmentCount + ancestorSegmentCount;
for(int i=0; i< keepSegments; i++) {
resultSegments.add(segments.get(i));
}
for(int i=ancestorSegmentCount; i<copyFromPathSegments.size(); i++) {
resultSegments.add(copyFromPathSegments.get(i));
}
String result = StringUtil.join(resultSegments, "/");
if (startsWithSlash) {
result = "/" + result;
}
return new File(result);
}
public boolean isModifiedDocumentTrackingRequired() {
return true;
}
public void doCleanup(final List<VirtualFile> files) {
new CleanupWorker(VfsUtil.toVirtualFileArray(files), myVcs.getProject(), "action.Subversion.cleanup.progress.title").execute();
}
}
@@ -0,0 +1,354 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.changes.*;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLock;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.wc.*;
import java.io.File;
import java.util.*;
class SvnChangeProviderContext implements StatusReceiver {
private static final Logger LOG = Logger.getInstance("org.jetbrains.idea.svn.SvnChangeProviderContext");
private final ChangelistBuilder myChangelistBuilder;
private final SVNStatusClient myStatusClient;
private List<SvnChangedFile> myCopiedFiles = null;
private final List<SvnChangedFile> myDeletedFiles = new ArrayList<SvnChangedFile>();
// for files moved in a subtree, which were the targets of merge (for instance).
private final Map<String, SVNStatus> myTreeConflicted;
private Map<FilePath, String> myCopyFromURLs = null;
private final SvnVcs17 myVcs;
private final SvnBranchConfigurationManager17 myBranchConfigurationManager;
private final ProgressIndicator myProgress;
public SvnChangeProviderContext(SvnVcs17 vcs, final ChangelistBuilder changelistBuilder, final ProgressIndicator progress) {
myVcs = vcs;
myStatusClient = vcs.createStatusClient();
myChangelistBuilder = changelistBuilder;
myProgress = progress;
myTreeConflicted = new HashMap<String, SVNStatus>();
myBranchConfigurationManager = SvnBranchConfigurationManager17.getInstance(myVcs.getProject());
}
public void process(FilePath path, SVNStatus status, boolean isInnerCopyRoot) throws SVNException {
processStatusFirstPass(path, status);
}
public void processIgnored(VirtualFile vFile) {
myChangelistBuilder.processIgnoredFile(vFile);
}
public void processUnversioned(VirtualFile vFile) {
myChangelistBuilder.processUnversionedFile(vFile);
}
public ChangelistBuilder getBuilder() {
return myChangelistBuilder;
}
public SVNStatusClient getClient() {
return myStatusClient;
}
public void reportTreeConflict(final SVNStatus status) {
myTreeConflicted.put(status.getFile().getAbsolutePath(), status);
}
@Nullable
public SVNStatus getTreeConflictStatus(final File file) {
return myTreeConflicted.get(file.getAbsolutePath());
}
@NotNull
public List<SvnChangedFile> getCopiedFiles() {
if (myCopiedFiles == null) {
return Collections.emptyList();
}
return myCopiedFiles;
}
public List<SvnChangedFile> getDeletedFiles() {
return myDeletedFiles;
}
public boolean isDeleted(final FilePath path) {
for (SvnChangedFile deletedFile : myDeletedFiles) {
if (Comparing.equal(path, deletedFile.getFilePath())) {
return true;
}
}
return false;
}
public boolean isCanceled() {
return (myProgress != null) && myProgress.isCanceled();
}
/**
* If the specified filepath or its parent was added with history, returns the URL of the copy source for this filepath.
*
* @param filePath the original filepath
* @return the copy source url, or null if the file isn't a copy of anything
*/
@Nullable
public String getParentCopyFromURL(FilePath filePath) {
if (myCopyFromURLs == null) {
return null;
}
StringBuilder relPathBuilder = new StringBuilder();
while(filePath != null) {
String copyFromURL = myCopyFromURLs.get(filePath);
if (copyFromURL != null) {
return copyFromURL + relPathBuilder.toString();
}
relPathBuilder.insert(0, "/" + filePath.getName());
filePath = filePath.getParentPath();
}
return null;
}
public void addCopiedFile(final FilePath filePath, final SVNStatus status, final String copyFromURL) {
if (myCopiedFiles == null) {
myCopiedFiles = new ArrayList<SvnChangedFile>();
}
myCopiedFiles.add(new SvnChangedFile(filePath, status, copyFromURL));
final String url = status.getCopyFromURL();
if (url != null) {
addCopyFromURL(filePath, url);
}
}
public void addCopyFromURL(final FilePath filePath, final String url) {
if (myCopyFromURLs == null) {
myCopyFromURLs = new HashMap<FilePath, String>();
}
myCopyFromURLs.put(filePath, url);
}
//
void processStatusFirstPass(final FilePath filePath, final SVNStatus status) throws SVNException {
if (status == null) {
// external to wc
return;
}
if (status.getRemoteLock() != null) {
final SVNLock lock = status.getRemoteLock();
myChangelistBuilder.processLogicallyLockedFolder(filePath.getVirtualFile(),
new LogicalLock(false, lock.getOwner(), lock.getComment(), lock.getCreationDate(), lock.getExpirationDate()));
}
if (status.getLocalLock() != null) {
final SVNLock lock = status.getLocalLock();
myChangelistBuilder.processLogicallyLockedFolder(filePath.getVirtualFile(),
new LogicalLock(true, lock.getOwner(), lock.getComment(), lock.getCreationDate(), lock.getExpirationDate()));
}
if (filePath.isDirectory() && status.isLocked()) {
myChangelistBuilder.processLockedFolder(filePath.getVirtualFile());
}
if (SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_ADDED) && status.getCopyFromURL() != null) {
addCopiedFile(filePath, status, status.getCopyFromURL());
}
else if (SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_DELETED)) {
myDeletedFiles.add(new SvnChangedFile(filePath, status));
}
else {
String parentCopyFromURL = getParentCopyFromURL(filePath);
if (parentCopyFromURL != null) {
addCopiedFile(filePath, status, parentCopyFromURL);
} else {
processStatus(filePath, status);
}
}
}
void processStatus(final FilePath filePath, final SVNStatus status) throws SVNException {
loadEntriesFile(filePath);
if (status != null) {
FileStatus fStatus = SvnStatusConvertor.convertStatus(status);
final SVNStatusType statusType = status.getContentsStatus();
final SVNStatusType propStatus = status.getPropertiesStatus();
if (SvnVcs17.svnStatusIsUnversioned(status) || SvnVcs17.svnStatusIs(status, SVNStatusType.UNKNOWN)) {
final VirtualFile file = filePath.getVirtualFile();
if (file != null) {
myChangelistBuilder.processUnversionedFile(file);
}
}
else if (SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_ADDED)) {
myChangelistBuilder.processChangeInList(createChange(null, CurrentContentRevision.create(filePath), fStatus, status),
changeListNameFromStatus(status), SvnVcs17.getKey());
}
else if (SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_CONFLICTED) ||
SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_MODIFIED) ||
SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_REPLACED) ||
propStatus == SVNStatusType.STATUS_MODIFIED ||
propStatus == SVNStatusType.STATUS_CONFLICTED) {
myChangelistBuilder.processChangeInList(createChange(SvnContentRevision.createBaseRevision(myVcs, filePath, status.getRevision()),
CurrentContentRevision.create(filePath), fStatus, status), changeListNameFromStatus(status), SvnVcs17.getKey());
checkSwitched(filePath, myChangelistBuilder, status, fStatus);
}
else if (SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_DELETED)) {
myChangelistBuilder.processChangeInList(
createChange(SvnContentRevision.createBaseRevision(myVcs, filePath, status.getRevision()), null, fStatus, status),
changeListNameFromStatus(status), SvnVcs17.getKey());
}
else if (SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_MISSING)) {
myChangelistBuilder.processLocallyDeletedFile(createLocallyDeletedChange(filePath, status));
}
else if (SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_IGNORED)) {
myChangelistBuilder.processIgnoredFile(filePath.getVirtualFile());
}
else if (status.isCopied()) {
//
}
else if ((fStatus == FileStatus.NOT_CHANGED || fStatus == FileStatus.SWITCHED) && statusType != SVNStatusType.STATUS_NONE) {
VirtualFile file = filePath.getVirtualFile();
if (file != null && FileDocumentManager.getInstance().isFileModified(file)) {
myChangelistBuilder.processChangeInList(createChange(SvnContentRevision.createBaseRevision(myVcs, filePath, status.getRevision()),
CurrentContentRevision.create(filePath), FileStatus.MODIFIED, status), changeListNameFromStatus(status),
SvnVcs17.getKey());
} else if (status.getTreeConflict() != null) {
myChangelistBuilder.processChange(createChange(SvnContentRevision.createBaseRevision(myVcs, filePath, status.getRevision()),
CurrentContentRevision.create(filePath), FileStatus.MODIFIED, status), SvnVcs17.getKey());
}
checkSwitched(filePath, myChangelistBuilder, status, fStatus);
}
}
}
public void addModifiedNotSavedChange(final VirtualFile file) throws SVNException {
final FilePath filePath = new FilePathImpl(file);
final SVNInfo svnInfo;
try {
svnInfo = myVcs.createWCClient().doInfo(new File(file.getPath()), SVNRevision.UNDEFINED);
}
catch (SVNException e) {
if (SVNErrorCode.WC_PATH_NOT_FOUND.equals(e.getErrorMessage().getErrorCode())) {
return;
}
throw e;
}
final SVNStatus svnStatus = new SVNStatus();
svnStatus.setRevision(svnInfo.getRevision());
myChangelistBuilder.processChangeInList(createChange(SvnContentRevision.createBaseRevision(myVcs, filePath, svnInfo.getRevision()),
CurrentContentRevision.create(filePath), FileStatus.MODIFIED, svnStatus), (String) null,
SvnVcs17.getKey());
}
private void checkSwitched(final FilePath filePath, final ChangelistBuilder builder, final SVNStatus status,
final FileStatus convertedStatus) {
if (status.isSwitched() || (convertedStatus == FileStatus.SWITCHED)) {
final VirtualFile virtualFile = filePath.getVirtualFile();
if (virtualFile == null) return;
final String switchUrl = status.getURL().toString();
final VirtualFile vcsRoot = ProjectLevelVcsManager.getInstance(myVcs.getProject()).getVcsRootFor(virtualFile);
if (vcsRoot != null) { // it will be null if we walked into an excluded directory
String baseUrl = null;
try {
baseUrl = myBranchConfigurationManager.get(vcsRoot).getBaseName(switchUrl);
}
catch (VcsException e) {
LOG.info(e);
}
builder.processSwitchedFile(virtualFile, baseUrl == null ? switchUrl : baseUrl, true);
}
}
}
/**
* Ensures that the contents of the 'entries' file is cached in the VFS, so that the VFS will send
* correct events when the 'entries' file is changed externally (to be received by SvnEntriesFileListener)
*
* @param filePath the path of a changed file.
*/
private static void loadEntriesFile(final FilePath filePath) {
final FilePath parentPath = filePath.getParentPath();
if (parentPath == null) {
return;
}
File svnSubdirectory = new File(parentPath.getIOFile(), SvnUtil.SVN_ADMIN_DIR_NAME);
LocalFileSystem localFileSystem = LocalFileSystem.getInstance();
VirtualFile file = localFileSystem.refreshAndFindFileByIoFile(svnSubdirectory);
if (file != null) {
localFileSystem.refreshAndFindFileByIoFile(new File(svnSubdirectory, SvnUtil.ENTRIES_FILE_NAME));
}
if (filePath.isDirectory()) {
svnSubdirectory = new File(filePath.getPath(), SvnUtil.SVN_ADMIN_DIR_NAME);
file = localFileSystem.refreshAndFindFileByIoFile(svnSubdirectory);
if (file != null) {
localFileSystem.refreshAndFindFileByIoFile(new File(svnSubdirectory, SvnUtil.ENTRIES_FILE_NAME));
}
}
}
// seems here we can only have a tree conflict; which can be marked on either path (?)
// .. ok try to merge states
Change createMovedChange(final ContentRevision before, final ContentRevision after, final SVNStatus copiedStatus,
final SVNStatus deletedStatus) {
return new ConflictedSvnChange(before, after, ConflictState.mergeState(getState(copiedStatus), getState(deletedStatus)),
((copiedStatus != null) && (copiedStatus.getTreeConflict() != null)) ? after.getFile() : before.getFile());
}
private Change createChange(final ContentRevision before, final ContentRevision after, final FileStatus fStatus, final SVNStatus svnStatus) {
return new ConflictedSvnChange(before, after, fStatus, getState(svnStatus), after == null ? before.getFile() : after.getFile());
}
private LocallyDeletedChange createLocallyDeletedChange(@NotNull FilePath filePath, final SVNStatus status) {
return new SvnLocallyDeletedChange(filePath, getState(status));
}
private ConflictState getState(@Nullable final SVNStatus svnStatus) {
if (svnStatus == null) {
return ConflictState.none;
}
final boolean treeConflict = svnStatus.getTreeConflict() != null;
final boolean textConflict = SVNStatusType.STATUS_CONFLICTED == svnStatus.getContentsStatus();
final boolean propertyConflict = SVNStatusType.STATUS_CONFLICTED == svnStatus.getPropertiesStatus();
if (treeConflict) {
reportTreeConflict(svnStatus);
}
return ConflictState.getInstance(treeConflict, textConflict, propertyConflict);
}
@Nullable
private static String changeListNameFromStatus(final SVNStatus status) {
if (WorkingCopyFormat.getInstance(status.getWorkingCopyFormat()).supportsChangelists()) {
if (SVNNodeKind.FILE.equals(status.getKind())) {
final String clName = status.getChangelistName();
return (clName == null) ? null : clName;
}
}
// always null for earlier versions
return null;
}
}
@@ -0,0 +1,73 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.vcs.FilePath;
import org.tmatesoft.svn.core.wc.SVNStatus;
class SvnChangedFile {
private final FilePath myFilePath;
private final SVNStatus myStatus;
private String myCopyFromURL;
public SvnChangedFile(final FilePath filePath, final SVNStatus status) {
myFilePath = filePath;
myStatus = status;
}
public SvnChangedFile(final FilePath filePath, final SVNStatus status, final String copyFromURL) {
myFilePath = filePath;
myStatus = status;
myCopyFromURL = copyFromURL;
}
public FilePath getFilePath() {
return myFilePath;
}
public SVNStatus getStatus() {
return myStatus;
}
public String getCopyFromURL() {
if (myCopyFromURL == null) {
return myStatus.getCopyFromURL();
}
return myCopyFromURL;
}
@Override
public String toString() {
return myFilePath.getPath();
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final SvnChangedFile that = (SvnChangedFile)o;
if (myFilePath != null ? !myFilePath.equals(that.myFilePath) : that.myFilePath != null) return false;
return true;
}
@Override
public int hashCode() {
return myFilePath != null ? myFilePath.hashCode() : 0;
}
}
@@ -0,0 +1,234 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vcs.AbstractVcs;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ChangeList;
import com.intellij.openapi.vcs.changes.ChangeListListener;
import org.jetbrains.annotations.Nullable;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.wc.ISVNChangelistHandler;
import org.tmatesoft.svn.core.wc.SVNChangelistClient;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class SvnChangelistListener implements ChangeListListener {
private final static Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnChangelistListener");
private final Project myProject;
private final SVNChangelistClient myClient;
public SvnChangelistListener(final Project project, final SVNChangelistClient client) {
myProject = project;
myClient = client;
}
public void changeListAdded(final ChangeList list) {
// SVN change list exists only when there are any files in it
}
public void changesRemoved(final Collection<Change> changes, final ChangeList fromList) {
if (SvnChangeProvider.ourDefaultListName.equals(fromList.getName())) {
return;
}
final List<String> paths = getPathsFromChanges(changes);
for (String path : paths) {
try {
myClient.doRemoveFromChangelist(new File[]{new File(path)}, SVNDepth.EMPTY, null);
}
catch (SVNException e) {
LOG.info(e);
}
}
}
public void changesAdded(Collection<Change> changes, ChangeList toList) {
if (toList == null) return;
if (SvnChangeProvider.ourDefaultListName.equals(toList.getName())) {
return;
}
final List<String> paths = getPathsFromChanges(changes);
for (String path : paths) {
try {
myClient.doAddToChangelist(new File[]{new File(path)}, SVNDepth.EMPTY, toList.getName(), null);
}
catch (SVNException e) {
LOG.info(e);
}
}
}
public void changeListRemoved(final ChangeList list) {
final List<String> paths = getPathsFromChanges(list.getChanges());
for (String path : paths) {
try {
myClient.doRemoveFromChangelist(new File[]{new File(path)}, SVNDepth.EMPTY, null);
}
catch (SVNException e) {
LOG.info(e);
}
}
}
private boolean isUnderSvn(final FilePath path) {
final AbstractVcs vcs = ProjectLevelVcsManager.getInstance(myProject).getVcsFor(path);
return ((vcs != null) && (SvnVcs17.VCS_NAME.equals(vcs.getName())));
}
private List<String> getPathsFromChanges(final Collection<Change> changes) {
final List<String> paths = new ArrayList<String>();
for (Change change : changes) {
if ((change.getBeforeRevision() != null) && (isUnderSvn(change.getBeforeRevision().getFile()))) {
final String path = change.getBeforeRevision().getFile().getIOFile().getAbsolutePath();
if (! paths.contains(path)) {
paths.add(path);
}
}
if ((change.getAfterRevision() != null) && (isUnderSvn(change.getAfterRevision().getFile()))) {
final String path = change.getAfterRevision().getFile().getIOFile().getAbsolutePath();
if (! paths.contains(path)) {
paths.add(path);
}
}
}
return paths;
}
public void changeListChanged(final ChangeList list) {
}
public void changeListRenamed(final ChangeList list, final String oldName) {
if (Comparing.equal(list.getName(), oldName)) {
return;
}
if (SvnChangeProvider.ourDefaultListName.equals(list.getName())) {
changeListRemoved(list);
return;
}
final List<String> paths = getPathsFromChanges(list.getChanges());
for (String path : paths) {
try {
myClient.doAddToChangelist(new File[]{new File(path)}, SVNDepth.EMPTY, list.getName(), null);
}
catch (SVNException e) {
LOG.info(e);
}
}
}
public void changeListCommentChanged(final ChangeList list, final String oldComment) {
}
public void changesMoved(final Collection<Change> changes, final ChangeList fromList, final ChangeList toList) {
if (fromList.getName().equals(toList.getName())) {
return;
}
if (SvnChangeProvider.ourDefaultListName.equals(toList.getName())) {
changeListRemoved(toList);
return;
}
final String[] fromLists = SvnChangeProvider.ourDefaultListName.equals(fromList.getName()) ? null : new String[] {fromList.getName()};
final List<String> paths = getPathsFromChanges(changes);
for (final String path : paths) {
try {
myClient.doAddToChangelist(new File[]{new File(path)}, SVNDepth.EMPTY, toList.getName(), fromLists);
}
catch (SVNException e) {
LOG.info(e);
}
}
}
public void defaultListChanged(final ChangeList oldDefaultList, final ChangeList newDefaultList) {
}
public void unchangedFileStatusChanged() {
}
public void changeListUpdateDone() {
}
@Nullable
public static String getCurrentMapping(final Project project, final File file) {
final SvnVcs17 vcs = SvnVcs17.getInstance(project);
final SVNChangelistClient client = vcs.createChangelistClient();
try {
final Ref<String> refResult = new Ref<String>();
final ISVNChangelistHandler handler = new ISVNChangelistHandler() {
public void handle(final File path, final String changelistName) {
if (refResult.isNull() && Comparing.equal(path, file)) {
refResult.set(changelistName);
}
}
};
if (file.exists()) {
client.doGetChangeLists(file, null, SVNDepth.EMPTY, handler);
} else if (file.getParentFile() != null) {
client.doGetChangeLists(file.getParentFile(), null, SVNDepth.IMMEDIATES, handler);
}
return refResult.get();
}
catch (SVNException e) {
final SVNErrorCode errorCode = e.getErrorMessage().getErrorCode();
if (SVNErrorCode.WC_NOT_DIRECTORY.equals(errorCode) || SVNErrorCode.WC_NOT_FILE.equals(errorCode)) {
LOG.debug("Logging only, exception is valid (caught) here", e);
} else {
LOG.info("Logging only, exception is valid (caught) here", e);
}
}
return null;
}
public static void putUnderList(final Project project, final String list, final File after) throws SVNException {
final SvnVcs17 vcs = SvnVcs17.getInstance(project);
final SVNChangelistClient client = vcs.createChangelistClient();
try {
client.doAddToChangelist(new File[]{after}, SVNDepth.EMPTY, list, null);
}
catch (SVNException e) {
LOG.info(e);
if ((! SVNErrorCode.WC_NOT_DIRECTORY.equals(e.getErrorMessage().getErrorCode()) && (! SVNErrorCode.WC_NOT_FILE.equals(e.getErrorMessage().getErrorCode())))) {
throw e;
}
}
}
public static void removeFromList(final Project project, final File after) throws SVNException {
final SvnVcs17 vcs = SvnVcs17.getInstance(project);
final SVNChangelistClient client = vcs.createChangelistClient();
try {
client.doRemoveFromChangelist(new File[]{after}, SVNDepth.EMPTY, null);
}
catch (SVNException e) {
LOG.info(e);
if ((! SVNErrorCode.WC_NOT_DIRECTORY.equals(e.getErrorMessage().getErrorCode()) && (! SVNErrorCode.WC_NOT_FILE.equals(e.getErrorMessage().getErrorCode())))) {
throw e;
}
}
}
}
@@ -0,0 +1,86 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
import com.intellij.openapi.vfs.VirtualFile;
import java.util.ArrayList;
import java.util.List;
public class SvnCompatibilityChecker {
private final Project myProject;
private final static long ourFrequency = 10;
private final static long ourInvocationMax = 10;
private final static long ourInitCounter = 3;
private long myCounter;
private long myDownStartCounter;
private long myInvocationCounter;
private final Object myLock = new Object();
public SvnCompatibilityChecker(Project project) {
myProject = project;
myCounter= 0;
myDownStartCounter = ourInitCounter;
myInvocationCounter = 0;
}
public void reportNoRoots(final List<VirtualFile> result) {
synchronized (myLock) {
if (myInvocationCounter >= ourInvocationMax) return;
++ myCounter;
-- myDownStartCounter;
if ((myCounter > ourFrequency) || (myDownStartCounter >= 0)) {
myCounter = 0;
++ myInvocationCounter;
final Application application = ApplicationManager.getApplication();
application.executeOnPooledThread(new Runnable() {
public void run() {
final List<VirtualFile> suspicious = new ArrayList<VirtualFile>();
for (VirtualFile vf : result) {
if (SvnUtil.seemsLikeVersionedDir(vf)) {
suspicious.add(vf);
}
}
if (! suspicious.isEmpty()) {
final String message = (suspicious.size() == 1) ?
"Root '" + suspicious.get(0).getPresentableName() + "' is likely to be of unsupported Subversion format" :
"Some roots are likely to be of unsupported Subversion format";
application.invokeLater(new Runnable() {
public void run() {
new VcsBalloonProblemNotifier(myProject, message, MessageType.WARNING).run();
}
}, ModalityState.NON_MODAL, new Condition() {
@Override
public boolean value(Object o) {
return (! myProject.isOpen()) || myProject.isDisposed();
}
});
}
}
});
}
}
}
}
@@ -0,0 +1,277 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.idea.svn17.SvnConfigurable">
<grid id="27dc6" binding="myComponent" layout-manager="GridLayoutManager" row-count="22" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="959" height="651"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<vspacer id="76b6e">
<constraints>
<grid row="21" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="5" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<component id="9b7ff" class="javax.swing.JLabel" binding="myConfigurationDirectoryLabel">
<constraints>
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="0" indent="0" use-parent-layout="false">
<preferred-size width="319" height="16"/>
</grid>
</constraints>
<properties>
<text resource-bundle="org/jetbrains/idea/svn17/SvnBundle" key="label.configuration.configuration.directory"/>
</properties>
</component>
<component id="212b3" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myConfigurationDirectoryText">
<constraints>
<grid row="5" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="0" indent="0" use-parent-layout="false">
<preferred-size width="319" height="28"/>
</grid>
</constraints>
<properties>
<editable value="false"/>
</properties>
</component>
<component id="df054" class="javax.swing.JCheckBox" binding="myUseDefaultCheckBox">
<constraints>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
<preferred-size width="319" height="23"/>
</grid>
</constraints>
<properties>
<text resource-bundle="org/jetbrains/idea/svn17/SvnBundle" key="checkbox.configure.use.system.default.configuration.directory"/>
</properties>
</component>
<component id="1283d" class="javax.swing.JCheckBox" binding="myUseCommonProxy">
<constraints>
<grid row="10" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false">
<preferred-size width="319" height="23"/>
</grid>
</constraints>
<properties>
<text value="Use IDEA general proxy settings as default for Subversion" noi18n="true"/>
</properties>
</component>
<component id="3e7c5" class="javax.swing.JButton" binding="myEditProxiesButton">
<constraints>
<grid row="13" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="1" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="org/jetbrains/idea/svn17/SvnBundle" key="button.text.edit.proxies"/>
</properties>
</component>
<component id="decc5" class="javax.swing.JButton" binding="myClearAuthButton">
<constraints>
<grid row="7" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="1" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="org/jetbrains/idea/svn17/SvnBundle" key="button.text.clear.authentication.cache"/>
</properties>
</component>
<component id="23675" class="javax.swing.JLabel" binding="myClearCacheLabel">
<constraints>
<grid row="8" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<enabled value="true"/>
<font/>
<foreground color="-10066330"/>
<text resource-bundle="org/jetbrains/idea/svn17/SvnBundle" key="label.text.delete.stored.credentials"/>
</properties>
</component>
<component id="a9606" class="javax.swing.JLabel" binding="myUseCommonProxyLabel">
<constraints>
<grid row="11" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<enabled value="true"/>
<foreground color="-10066330"/>
<text resource-bundle="org/jetbrains/idea/svn17/SvnBundle" key="use.idea.proxy.as.default.label.text"/>
</properties>
</component>
<component id="a353c" class="javax.swing.JLabel" binding="myEditProxyLabel">
<constraints>
<grid row="14" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<enabled value="true"/>
<foreground color="-10066330"/>
<text resource-bundle="org/jetbrains/idea/svn17/SvnBundle" key="label.text.edit.proxies"/>
</properties>
</component>
<component id="27350" class="com.intellij.ui.TitledSeparator">
<constraints>
<grid row="6" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="2" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="b92b0" class="com.intellij.ui.TitledSeparator">
<constraints>
<grid row="9" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="2" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="14e00" class="com.intellij.ui.TitledSeparator">
<constraints>
<grid row="12" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="2" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="947fd" class="javax.swing.JCheckBox" binding="myLockOnDemand" custom-create="true">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="org/jetbrains/idea/svn17/SvnBundle" key="action.Subversion.update.use.locks.on.demand.text.configurable"/>
<toolTipText resource-bundle="org/jetbrains/idea/svn17/SvnBundle" key="action.Subversion.update.use.locks.on.demand.description"/>
</properties>
</component>
<component id="c2f81" class="javax.swing.JCheckBox" binding="myDetectNestedWorkingCopiesCheckBox" default-binding="true">
<constraints>
<grid row="15" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="org/jetbrains/idea/svn17/SvnBundle" key="detect.nested.working.copies"/>
</properties>
</component>
<component id="f2afc" class="javax.swing.JCheckBox" binding="myIgnoreWhitespaceDifferenciesInCheckBox" default-binding="true">
<constraints>
<grid row="19" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="org/jetbrains/idea/svn17/SvnBundle" key="svn.option.ignore.whitespace.in.annotate"/>
</properties>
</component>
<component id="cd5ae" class="javax.swing.JCheckBox" binding="myShowMergeSourceInAnnotate" default-binding="true">
<constraints>
<grid row="18" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="org/jetbrains/idea/svn17/SvnBundle" key="annotation.show.merge.sources.default.text"/>
</properties>
</component>
<grid id="fa93d" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="17" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<hspacer id="44294">
<constraints>
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
</hspacer>
<component id="aa58e" class="javax.swing.JSpinner" binding="myNumRevsInAnnotations" custom-create="true">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="a1351" class="javax.swing.JCheckBox" binding="myMaximumNumberOfRevisionsCheckBox" default-binding="true">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Maximum number of revisions to look back in annotations"/>
</properties>
</component>
</children>
</grid>
<component id="41718" class="javax.swing.JCheckBox" binding="myCheckNestedInQuickMerge">
<constraints>
<grid row="16" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Check svn:mergeinfo in target subtree when preparing for merge"/>
</properties>
</component>
<grid id="dace9" layout-manager="GridLayoutManager" row-count="2" column-count="4" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="20" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="234" class="javax.swing.JLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="SSH connection timeout:"/>
</properties>
</component>
<hspacer id="ca199">
<constraints>
<grid row="0" column="3" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
</hspacer>
<component id="21604" class="javax.swing.JSpinner" binding="mySSHConnectionTimeout" custom-create="true">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="44801" class="javax.swing.JLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="SSH read timeout:"/>
</properties>
</component>
<component id="8e59f" class="javax.swing.JSpinner" binding="mySSHReadTimeout" custom-create="true">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<hspacer id="8e62f">
<constraints>
<grid row="1" column="3" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
</hspacer>
<component id="b9617" class="javax.swing.JLabel">
<constraints>
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="seconds"/>
</properties>
</component>
<component id="732b" class="javax.swing.JLabel">
<constraints>
<grid row="1" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="seconds"/>
</properties>
</component>
</children>
</grid>
<hspacer id="72c3d">
<constraints>
<grid row="20" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
</hspacer>
<component id="33d81" class="com.intellij.ui.components.JBLabel" binding="myWarningLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<foreground color="-65536"/>
<text resource-bundle="org/jetbrains/idea/svn17/SvnBundle" key="svn17.plugin.not.ready"/>
</properties>
</component>
<component id="f7ade" class="com.intellij.ui.HyperlinkLabel" binding="myLinkLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="9" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
</children>
</grid>
</form>
@@ -0,0 +1,294 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.application.ApplicationNamesInfo;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.ui.HyperlinkLabel;
import com.intellij.ui.MultiLineTooltipUI;
import com.intellij.ui.components.JBLabel;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.idea.svn17.config.ConfigureProxiesListener;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
public class SvnConfigurable implements Configurable {
private final Project myProject;
private JCheckBox myUseDefaultCheckBox;
private TextFieldWithBrowseButton myConfigurationDirectoryText;
private JButton myClearAuthButton;
private JCheckBox myUseCommonProxy;
private JButton myEditProxiesButton;
private JPanel myComponent;
private JLabel myConfigurationDirectoryLabel;
private JLabel myClearCacheLabel;
private JLabel myUseCommonProxyLabel;
private JLabel myEditProxyLabel;
private JCheckBox myLockOnDemand;
private JCheckBox myCheckNestedInQuickMerge;
private JCheckBox myDetectNestedWorkingCopiesCheckBox;
private JCheckBox myIgnoreWhitespaceDifferenciesInCheckBox;
private JCheckBox myShowMergeSourceInAnnotate;
private JSpinner myNumRevsInAnnotations;
private JCheckBox myMaximumNumberOfRevisionsCheckBox;
private JSpinner mySSHConnectionTimeout;
private JSpinner mySSHReadTimeout;
private JBLabel myWarningLabel;
private HyperlinkLabel myLinkLabel;
@NonNls private static final String HELP_ID = "project.propSubversion";
public SvnConfigurable(Project project) {
myProject = project;
myLinkLabel.setHyperlinkTarget("http://youtrack.jetbrains.net/issue/IDEA-72400");
myLinkLabel.setHyperlinkText("More information");
myUseDefaultCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
boolean enabled = !myUseDefaultCheckBox.isSelected();
myConfigurationDirectoryText.setEnabled(enabled);
myConfigurationDirectoryLabel.setEnabled(enabled);
SvnConfiguration17 configuration = SvnConfiguration17.getInstance(myProject);
String path = configuration.getConfigurationDirectory();
if (!enabled || path == null) {
myConfigurationDirectoryText.setText(IdeaSubversionConfigurationDirectory.getPath());
}
else {
myConfigurationDirectoryText.setText(path);
}
}
});
myClearAuthButton.addActionListener(new ActionListener(){
public void actionPerformed(final ActionEvent e) {
String path = myConfigurationDirectoryText.getText();
if (path != null) {
int result = Messages.showYesNoDialog(myComponent, SvnBundle.message("confirmation.text.delete.stored.authentication.information"),
SvnBundle.message("confirmation.title.clear.authentication.cache"),
Messages.getWarningIcon());
if (result == 0) {
SvnConfiguration17.RUNTIME_AUTH_CACHE.clear();
SvnConfiguration17.getInstance(myProject).clearAuthenticationDirectory();
}
}
}
});
final FileChooserDescriptor descriptor = createFileDescriptor();
myConfigurationDirectoryText.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
@NonNls String path = myConfigurationDirectoryText.getText().trim();
path = "file://" + path.replace(File.separatorChar, '/');
VirtualFile root = VirtualFileManager.getInstance().findFileByUrl(path);
String oldValue = PropertiesComponent.getInstance().getValue("FileChooser.showHiddens");
PropertiesComponent.getInstance().setValue("FileChooser.showHiddens", Boolean.TRUE.toString());
VirtualFile file = FileChooser.chooseFile(myComponent, descriptor, root);
PropertiesComponent.getInstance().setValue("FileChooser.showHiddens", oldValue);
if (file == null) {
return;
}
myConfigurationDirectoryText.setText(file.getPath().replace('/', File.separatorChar));
}
});
myConfigurationDirectoryText.setEditable(false);
myConfigurationDirectoryLabel.setLabelFor(myConfigurationDirectoryText);
myUseCommonProxy.setText(SvnBundle.message("use.idea.proxy.as.default", ApplicationNamesInfo.getInstance().getProductName()));
myEditProxiesButton.addActionListener(new ConfigureProxiesListener(myProject));
myMaximumNumberOfRevisionsCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
myNumRevsInAnnotations.setEnabled(myMaximumNumberOfRevisionsCheckBox.isSelected());
}
});
myNumRevsInAnnotations.setEnabled(myMaximumNumberOfRevisionsCheckBox.isSelected());
}
private static FileChooserDescriptor createFileDescriptor() {
final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
descriptor.setShowFileSystemRoots(true);
descriptor.setTitle(SvnBundle.message("dialog.title.select.configuration.directory"));
descriptor.setDescription(SvnBundle.message("dialog.description.select.configuration.directory"));
descriptor.setHideIgnored(false);
return descriptor;
}
public JComponent createComponent() {
return myComponent;
}
public String getDisplayName() {
return null;
}
public Icon getIcon() {
return null;
}
public String getHelpTopic() {
return HELP_ID;
}
public boolean isModified() {
if (myComponent == null) {
return false;
}
SvnConfiguration17 configuration = SvnConfiguration17.getInstance(myProject);
if (configuration.isUseDefaultConfiguation() != myUseDefaultCheckBox.isSelected()) {
return true;
}
if (configuration.isIsUseDefaultProxy() != myUseCommonProxy.isSelected()) {
return true;
}
if (configuration.UPDATE_LOCK_ON_DEMAND != myLockOnDemand.isSelected()) {
return true;
}
if (configuration.DETECT_NESTED_COPIES != myDetectNestedWorkingCopiesCheckBox.isSelected()) {
return true;
}
if (configuration.CHECK_NESTED_FOR_QUICK_MERGE != myCheckNestedInQuickMerge.isSelected()) {
return true;
}
if (configuration.IGNORE_SPACES_IN_ANNOTATE != myIgnoreWhitespaceDifferenciesInCheckBox.isSelected()) {
return true;
}
if (configuration.SHOW_MERGE_SOURCES_IN_ANNOTATE != myShowMergeSourceInAnnotate.isSelected()) {
return true;
}
final int annotateRevisions = configuration.getMaxAnnotateRevisions();
final boolean useMaxInAnnot = annotateRevisions != -1;
if (useMaxInAnnot != myMaximumNumberOfRevisionsCheckBox.isSelected()) {
return true;
}
if (myMaximumNumberOfRevisionsCheckBox.isSelected()) {
if (annotateRevisions != ((SpinnerNumberModel) myNumRevsInAnnotations.getModel()).getNumber().intValue()) {
return true;
}
}
if (configuration.mySSHConnectionTimeout/1000 != ((SpinnerNumberModel) mySSHConnectionTimeout.getModel()).getNumber().longValue()) {
return true;
}
if (configuration.mySSHReadTimeout/1000 != ((SpinnerNumberModel) mySSHReadTimeout.getModel()).getNumber().longValue()) {
return true;
}
return !configuration.getConfigurationDirectory().equals(myConfigurationDirectoryText.getText().trim());
}
public void apply() throws ConfigurationException {
SvnConfiguration17 configuration = SvnConfiguration17.getInstance(myProject);
configuration.setConfigurationDirectory(myConfigurationDirectoryText.getText());
configuration.setUseDefaultConfiguation(myUseDefaultCheckBox.isSelected());
configuration.setIsUseDefaultProxy(myUseCommonProxy.isSelected());
if ((! configuration.DETECT_NESTED_COPIES) && (configuration.DETECT_NESTED_COPIES != myDetectNestedWorkingCopiesCheckBox.isSelected())) {
SvnVcs17.getInstance(myProject).invokeRefreshSvnRoots(true);
}
configuration.DETECT_NESTED_COPIES = myDetectNestedWorkingCopiesCheckBox.isSelected();
configuration.CHECK_NESTED_FOR_QUICK_MERGE = myCheckNestedInQuickMerge.isSelected();
configuration.UPDATE_LOCK_ON_DEMAND = myLockOnDemand.isSelected();
configuration.setIgnoreSpacesInAnnotate(myIgnoreWhitespaceDifferenciesInCheckBox.isSelected());
configuration.SHOW_MERGE_SOURCES_IN_ANNOTATE = myShowMergeSourceInAnnotate.isSelected();
if (! myMaximumNumberOfRevisionsCheckBox.isSelected()) {
configuration.setMaxAnnotateRevisions(-1);
} else {
configuration.setMaxAnnotateRevisions(((SpinnerNumberModel) myNumRevsInAnnotations.getModel()).getNumber().intValue());
}
configuration.mySSHConnectionTimeout = ((SpinnerNumberModel) mySSHConnectionTimeout.getModel()).getNumber().longValue() * 1000;
configuration.mySSHReadTimeout = ((SpinnerNumberModel) mySSHReadTimeout.getModel()).getNumber().longValue() * 1000;
}
public void reset() {
SvnConfiguration17 configuration = SvnConfiguration17.getInstance(myProject);
String path = configuration.getConfigurationDirectory();
if (configuration.isUseDefaultConfiguation() || path == null) {
path = IdeaSubversionConfigurationDirectory.getPath();
}
myConfigurationDirectoryText.setText(path);
myUseDefaultCheckBox.setSelected(configuration.isUseDefaultConfiguation());
myUseCommonProxy.setSelected(configuration.isIsUseDefaultProxy());
myDetectNestedWorkingCopiesCheckBox.setSelected(configuration.DETECT_NESTED_COPIES);
myCheckNestedInQuickMerge.setSelected(configuration.CHECK_NESTED_FOR_QUICK_MERGE);
boolean enabled = !myUseDefaultCheckBox.isSelected();
myConfigurationDirectoryText.setEnabled(enabled);
myConfigurationDirectoryLabel.setEnabled(enabled);
myLockOnDemand.setSelected(configuration.UPDATE_LOCK_ON_DEMAND);
myIgnoreWhitespaceDifferenciesInCheckBox.setSelected(configuration.IGNORE_SPACES_IN_ANNOTATE);
myShowMergeSourceInAnnotate.setSelected(configuration.SHOW_MERGE_SOURCES_IN_ANNOTATE);
final int annotateRevisions = configuration.getMaxAnnotateRevisions();
if (annotateRevisions == -1) {
myMaximumNumberOfRevisionsCheckBox.setSelected(false);
myNumRevsInAnnotations.setValue(SvnConfiguration17.ourMaxAnnotateRevisionsDefault);
} else {
myMaximumNumberOfRevisionsCheckBox.setSelected(true);
myNumRevsInAnnotations.setValue(annotateRevisions);
}
myNumRevsInAnnotations.setEnabled(myMaximumNumberOfRevisionsCheckBox.isSelected());
mySSHConnectionTimeout.setValue(Long.valueOf(configuration.mySSHConnectionTimeout / 1000));
mySSHReadTimeout.setValue(Long.valueOf(configuration.mySSHReadTimeout / 1000));
}
public void disposeUIResources() {
}
private void createUIComponents() {
myLockOnDemand = new JCheckBox() {
@Override
public JToolTip createToolTip() {
JToolTip toolTip = new JToolTip(){{
setUI(new MultiLineTooltipUI());
}};
toolTip.setComponent(this);
return toolTip;
}
};
final SvnConfiguration17 configuration = SvnConfiguration17.getInstance(myProject);
int value = configuration.getMaxAnnotateRevisions();
value = (value == -1) ? SvnConfiguration17.ourMaxAnnotateRevisionsDefault : value;
myNumRevsInAnnotations = new JSpinner(new SpinnerNumberModel(value, 10, 100000, 100));
final int maximum = 30 * 60 * 1000;
final long connection = configuration.mySSHConnectionTimeout <= maximum ? configuration.mySSHConnectionTimeout : maximum;
final long read = configuration.mySSHReadTimeout <= maximum ? configuration.mySSHReadTimeout : maximum;
mySSHConnectionTimeout = new JSpinner(new SpinnerNumberModel(Long.valueOf(connection / 1000).longValue(), 0, maximum, 10));
mySSHReadTimeout = new JSpinner(new SpinnerNumberModel(Long.valueOf(read / 1000).longValue(), 0, maximum, 10));
}
}
@@ -0,0 +1,527 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.DefaultJDOMExternalizer;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.annotate.AnnotationListener;
import org.jdom.Attribute;
import org.jdom.DataConversionException;
import org.jdom.Element;
import org.jetbrains.idea.svn17.dialogs.SvnAuthenticationProvider;
import org.jetbrains.idea.svn17.dialogs.SvnInteractiveAuthenticationProvider;
import org.jetbrains.idea.svn17.update.MergeRootInfo;
import org.jetbrains.idea.svn17.update.UpdateRootInfo;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationProvider;
import org.tmatesoft.svn.core.auth.SVNAuthentication;
import org.tmatesoft.svn.core.internal.wc.ISVNAuthenticationStorage;
import org.tmatesoft.svn.core.internal.wc.SVNConfigFile;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.wc.ISVNOptions;
import org.tmatesoft.svn.core.wc.SVNWCUtil;
import java.io.File;
import java.io.FilenameFilter;
import java.util.*;
@State(
name = "SvnConfiguration17",
storages = {
@Storage(
file = "$WORKSPACE_FILE$"
)
}
)
public class SvnConfiguration17 implements PersistentStateComponent<Element> {
private static final Logger LOG = Logger.getInstance("org.jetbrains.idea.svn.SvnConfiguration");
public final static int ourMaxAnnotateRevisionsDefault = 500;
private final static String SERVERS_FILE_NAME = "servers";
public static final String UPGRADE_AUTO = "auto";
public static final String UPGRADE_AUTO_15 = "auto1.5";
public static final String UPGRADE_AUTO_16 = "auto1.6";
public static final String UPGRADE_AUTO_17 = "auto1.7";
public static final String UPGRADE_NONE = "none";
public String USER = "";
public String PASSWORD = "";
public String[] ADD_PATHS = null;
private String myConfigurationDirectory;
private boolean myIsUseDefaultConfiguration;
private boolean myIsUseDefaultProxy;
private ISVNOptions myOptions;
private boolean myIsKeepLocks;
private boolean myRemoteStatus;
private final Project myProject;
private SvnAuthenticationManager myAuthManager;
private SvnAuthenticationManager myPassiveAuthManager;
private SvnAuthenticationManager myInteractiveManager;
private String myUpgradeMode;
private SvnSupportOptions mySupportOptions;
private int myMaxAnnotateRevisions = ourMaxAnnotateRevisionsDefault;
private final static long DEFAULT_SSH_TIMEOUT = 30 * 1000;
public long mySSHConnectionTimeout = DEFAULT_SSH_TIMEOUT;
public long mySSHReadTimeout = DEFAULT_SSH_TIMEOUT;
public static final AuthStorage RUNTIME_AUTH_CACHE = new AuthStorage();
public String LAST_MERGED_REVISION = null;
public SVNDepth UPDATE_DEPTH = SVNDepth.INFINITY;
public boolean MERGE_DRY_RUN = false;
public boolean MERGE_DIFF_USE_ANCESTRY = true;
public boolean UPDATE_LOCK_ON_DEMAND = false;
public boolean IGNORE_SPACES_IN_MERGE = false;
public boolean DETECT_NESTED_COPIES = true;
public boolean CHECK_NESTED_FOR_QUICK_MERGE = false;
public boolean IGNORE_SPACES_IN_ANNOTATE = true;
public boolean SHOW_MERGE_SOURCES_IN_ANNOTATE = true;
public boolean FORCE_UPDATE = false;
private final Map<File, MergeRootInfo> myMergeRootInfos = new HashMap<File, MergeRootInfo>();
private final Map<File, UpdateRootInfo> myUpdateRootInfos = new HashMap<File, UpdateRootInfo>();
private final List<AnnotationListener> myAnnotationListeners;
private SvnInteractiveAuthenticationProvider myInteractiveProvider;
@Override
public Element getState() {
Element element = new Element("state");
try {
writeExternal(element);
}
catch (WriteExternalException e) {
LOG.error(e);
}
return element;
}
@Override
public void loadState(Element state) {
try {
readExternal(state);
}
catch (InvalidDataException e) {
LOG.error(e);
}
}
public static SvnConfiguration17 getInstance(final Project project) {
return ServiceManager.getService(project, SvnConfiguration17.class);
}
public SvnConfiguration17(final Project project) {
myProject = project;
myAnnotationListeners = new ArrayList<AnnotationListener>();
}
// accessed on AWT
public void addAnnotationListener(final AnnotationListener listener) {
myAnnotationListeners.add(listener);
}
public void removeAnnotationListener(final AnnotationListener listener) {
myAnnotationListeners.remove(listener);
}
public void setIgnoreSpacesInAnnotate(final boolean value) {
final boolean changed = IGNORE_SPACES_IN_ANNOTATE != value;
IGNORE_SPACES_IN_ANNOTATE = value;
if (changed) {
fireForAnnotationListeners();
}
}
private void fireForAnnotationListeners() {
final AnnotationListener[] listeners = myAnnotationListeners.toArray(new AnnotationListener[myAnnotationListeners.size()]);
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
for (int i = 0; i < listeners.length; i++) {
final AnnotationListener listener = listeners[i];
listener.onAnnotationChanged();
}
}
}, ModalityState.NON_MODAL);
}
public class SvnSupportOptions {
/**
* version of "support SVN in IDEA". for features tracking. should grow
*/
private Long myVersion;
public SvnSupportOptions(final Long version) {
myVersion = version;
// will be set to SvnSupportOptions.CHANGELIST_SUPPORT after sync
if (myVersion == null || myVersion.longValue() < SvnSupportOptions.CHANGELIST_SUPPORT) {
myVersion = SvnSupportOptions.UPGRADE_TO_15_VERSION_ASKED;
}
}
private final static long UPGRADE_TO_15_VERSION_ASKED = 123;
private final static long CHANGELIST_SUPPORT = 124;
private final static long UPGRADE_TO_16_VERSION_ASKED = 125;
public boolean upgradeTo16Asked() {
return (myVersion != null) && (UPGRADE_TO_16_VERSION_ASKED <= myVersion);
}
public boolean changeListsSynchronized() {
return (myVersion != null) && (CHANGELIST_SUPPORT <= myVersion);
}
public void upgrade() {
myVersion = UPGRADE_TO_16_VERSION_ASKED;
}
}
public SvnSupportOptions getSupportOptions() {
if (mySupportOptions == null) {
// used to be kept in SvnBranchConfigurationManager
mySupportOptions = new SvnSupportOptions(SvnBranchConfigurationManager17.getInstance(myProject).getSupportValue());
}
return mySupportOptions;
}
public String getConfigurationDirectory() {
if (myConfigurationDirectory == null || isUseDefaultConfiguation()) {
myConfigurationDirectory = IdeaSubversionConfigurationDirectory.getPath();
}
return myConfigurationDirectory;
}
public boolean isUseDefaultConfiguation() {
return myIsUseDefaultConfiguration;
}
public void setConfigurationDirectory(String path) {
myConfigurationDirectory = path;
File dir = path == null ? new File(IdeaSubversionConfigurationDirectory.getPath()) : new File(path);
SVNConfigFile.createDefaultConfiguration(dir);
myOptions = null;
myAuthManager = null;
myPassiveAuthManager = null;
myInteractiveManager = null;
myInteractiveProvider = null;
RUNTIME_AUTH_CACHE.clear();
}
public void setUseDefaultConfiguation(boolean useDefault) {
myIsUseDefaultConfiguration = useDefault;
myOptions = null;
myAuthManager = null;
myPassiveAuthManager = null;
myInteractiveManager = null;
myInteractiveProvider = null;
RUNTIME_AUTH_CACHE.clear();
}
public ISVNOptions getOptions(Project project) {
if (myOptions == null) {
File path = new File(getConfigurationDirectory());
myOptions = SVNWCUtil.createDefaultOptions(path.getAbsoluteFile(), true);
}
return myOptions;
}
public SvnAuthenticationManager getAuthenticationManager(final SvnVcs17 svnVcs) {
if (myAuthManager == null) {
// reloaded when configuration directory changes
myAuthManager = new SvnAuthenticationManager(myProject, new File(getConfigurationDirectory()));
getInteractiveManager(svnVcs);
// to init
myAuthManager.setAuthenticationProvider(new SvnAuthenticationProvider(svnVcs, myInteractiveProvider));
myAuthManager.setRuntimeStorage(RUNTIME_AUTH_CACHE);
}
return myAuthManager;
}
public SvnAuthenticationManager getPassiveAuthenticationManager() {
if (myPassiveAuthManager == null) {
myPassiveAuthManager = new SvnAuthenticationManager(myProject, new File(getConfigurationDirectory()));
myPassiveAuthManager.setAuthenticationProvider(new ISVNAuthenticationProvider() {
@Override
public SVNAuthentication requestClientAuthentication(String kind,
SVNURL url,
String realm,
SVNErrorMessage errorMessage,
SVNAuthentication previousAuth,
boolean authMayBeStored) {
return null;
}
@Override
public int acceptServerAuthentication(SVNURL url, String realm, Object certificate, boolean resultMayBeStored) {
return REJECTED;
}
});
myPassiveAuthManager.setRuntimeStorage(RUNTIME_AUTH_CACHE);
}
return myPassiveAuthManager;
}
public SvnAuthenticationManager getInteractiveManager(final SvnVcs17 svnVcs) {
if (myInteractiveManager == null) {
myInteractiveManager = new SvnAuthenticationManager(myProject, new File(getConfigurationDirectory()));
myInteractiveManager.setRuntimeStorage(RUNTIME_AUTH_CACHE);
myInteractiveProvider = new SvnInteractiveAuthenticationProvider(svnVcs, myInteractiveManager);
myInteractiveManager.setAuthenticationProvider(myInteractiveProvider);
}
return myInteractiveManager;
}
public void getServerFilesManagers(final Ref<SvnServerFileManager> systemManager, final Ref<SvnServerFileManager> userManager) {
// created only if does not exist
SVNConfigFile.createDefaultConfiguration(new File(getConfigurationDirectory()));
systemManager.set(new SvnServerFileManagerImpl(new IdeaSVNConfigFile(new File(SVNFileUtil.getSystemConfigurationDirectory(), SERVERS_FILE_NAME))));
userManager.set(new SvnServerFileManagerImpl(new IdeaSVNConfigFile(new File(getConfigurationDirectory(), SERVERS_FILE_NAME))));
}
public String getUpgradeMode() {
return myUpgradeMode;
}
public void setUpgradeMode(String upgradeMode) {
myUpgradeMode = upgradeMode;
}
@SuppressWarnings({"HardCodedStringLiteral"})
public void readExternal(Element element) throws InvalidDataException {
DefaultJDOMExternalizer.readExternal(this, element);
List elems = element.getChildren("addpath");
LOG.debug(elems.toString());
ADD_PATHS = new String[elems.size()];
for (int i = 0; i < elems.size(); i++) {
Element elem = (Element)elems.get(i);
ADD_PATHS[i] = elem.getAttributeValue("path");
}
Element configurationDirectory = element.getChild("configuration");
if (configurationDirectory != null) {
myConfigurationDirectory = configurationDirectory.getText();
Attribute defaultAttr = configurationDirectory.getAttribute("useDefault");
try {
myIsUseDefaultConfiguration = defaultAttr != null && defaultAttr.getBooleanValue();
}
catch (DataConversionException e) {
myIsUseDefaultConfiguration = false;
}
}
else {
myIsUseDefaultConfiguration = true;
}
// compatibility: this setting was moved from .iws to global settings
List urls = element.getChildren("checkoutURL");
for (Object url1 : urls) {
Element child = (Element)url1;
String url = child.getText();
if (url != null) {
SvnApplicationSettings17.getInstance().addCheckoutURL(url);
}
}
myIsKeepLocks = element.getChild("keepLocks") != null;
myRemoteStatus = element.getChild("remoteStatus") != null;
myUpgradeMode = element.getChild("upgradeMode") != null ? element.getChild("upgradeMode").getText() : null;
final Element useProxy = element.getChild("myIsUseDefaultProxy");
if (useProxy == null) {
myIsUseDefaultProxy = false;
} else {
myIsUseDefaultProxy = Boolean.parseBoolean(useProxy.getText());
}
final Element supportedVersion = element.getChild("supportedVersion");
if (supportedVersion != null) {
try {
mySupportOptions = new SvnSupportOptions(Long.parseLong(supportedVersion.getText().trim()));
} catch (NumberFormatException e) {
mySupportOptions = new SvnSupportOptions(null);
}
}
final Attribute maxAnnotateRevisions = element.getAttribute("maxAnnotateRevisions");
if (maxAnnotateRevisions != null) {
try {
myMaxAnnotateRevisions = maxAnnotateRevisions.getIntValue();
}
catch (DataConversionException e) {
//
}
}
}
@SuppressWarnings({"HardCodedStringLiteral"})
public void writeExternal(Element element) throws WriteExternalException {
DefaultJDOMExternalizer.writeExternal(this, element);
if (ADD_PATHS != null) {
for (String aADD_PATHS : ADD_PATHS) {
Element elem = new Element("addpath");
elem.setAttribute("path", aADD_PATHS);
element.addContent(elem);
}
}
if (myConfigurationDirectory != null) {
Element configurationDirectory = new Element("configuration");
configurationDirectory.setText(myConfigurationDirectory);
configurationDirectory.setAttribute("useDefault", myIsUseDefaultConfiguration ? "true" : "false");
element.addContent(configurationDirectory);
}
if (myIsKeepLocks) {
element.addContent(new Element("keepLocks"));
}
if (myRemoteStatus) {
element.addContent(new Element("remoteStatus"));
}
if (myUpgradeMode != null) {
element.addContent(new Element("upgradeMode").setText(myUpgradeMode));
}
element.addContent(new Element("myIsUseDefaultProxy").setText(myIsUseDefaultProxy ? "true" : "false"));
if (mySupportOptions != null) {
element.addContent(new Element("supportedVersion").setText("" + mySupportOptions.myVersion));
}
element.setAttribute("maxAnnotateRevisions", "" + myMaxAnnotateRevisions);
}
public boolean isKeepLocks() {
return myIsKeepLocks;
}
public void setKeepLocks(boolean keepLocks) {
myIsKeepLocks = keepLocks;
}
public boolean isRemoteStatus() {
return myRemoteStatus;
}
public void setRemoteStatus(boolean remote) {
myRemoteStatus = remote;
}
public boolean isIsUseDefaultProxy() {
return myIsUseDefaultProxy;
}
public void setIsUseDefaultProxy(final boolean isUseDefaultProxy) {
myIsUseDefaultProxy = isUseDefaultProxy;
}
public static class AuthStorage implements ISVNAuthenticationStorage {
private final Map<String, Object> myStorage = new Hashtable<String, Object>();
public void clear() {
myStorage.clear();
}
public void putData(String kind, String realm, Object data) {
if (data == null) {
myStorage.remove(kind + "$" + realm);
} else {
myStorage.put(kind + "$" + realm, data);
}
}
public Object getData(String kind, String realm) {
return myStorage.get(kind + "$" + realm);
}
}
public MergeRootInfo getMergeRootInfo(final File file, final SvnVcs17 svnVcs) {
if (!myMergeRootInfos.containsKey(file)) {
myMergeRootInfos.put(file, new MergeRootInfo(file, svnVcs));
}
return myMergeRootInfos.get(file);
}
public UpdateRootInfo getUpdateRootInfo(File file, final SvnVcs17 svnVcs) {
if (!myUpdateRootInfos.containsKey(file)) {
myUpdateRootInfos.put(file, new UpdateRootInfo(file, svnVcs));
}
return myUpdateRootInfos.get(file);
}
public Map<File, UpdateRootInfo> getUpdateInfosMap() {
return Collections.unmodifiableMap(myUpdateRootInfos);
}
private static final List<String> ourAuthKinds = Arrays.asList(ISVNAuthenticationManager.PASSWORD, ISVNAuthenticationManager.SSH,
ISVNAuthenticationManager.SSL, ISVNAuthenticationManager.USERNAME, "svn.ssl.server", "svn.ssh.server");
public void clearAuthenticationDirectory() {
final File authDir = new File(getConfigurationDirectory(), "auth");
if (authDir.exists()) {
ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
public void run() {
final ProgressIndicator ind = ProgressManager.getInstance().getProgressIndicator();
if (ind != null) {
ind.setIndeterminate(true);
ind.setText("Clearing stored credentials in " + authDir.getAbsolutePath());
}
final File[] files = authDir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return ourAuthKinds.contains(name);
}
});
for (File dir : files) {
if (ind != null) {
ind.setText("Deleting " + dir.getAbsolutePath());
}
FileUtil.delete(dir);
}
}
}, "button.text.clear.authentication.cache", false, myProject);
}
}
public boolean haveCredentialsFor(final String kind, final String realm) {
return RUNTIME_AUTH_CACHE.getData(kind, realm) != null;
}
public void acknowledge(final String kind, final String realm, final Object object) {
RUNTIME_AUTH_CACHE.putData(kind, realm, object);
}
public void clearCredentials(final String kind, final String realm) {
RUNTIME_AUTH_CACHE.putData(kind, realm, null);
}
public int getMaxAnnotateRevisions() {
return myMaxAnnotateRevisions;
}
public void setMaxAnnotateRevisions(int maxAnnotateRevisions) {
myMaxAnnotateRevisions = maxAnnotateRevisions;
}
}
@@ -0,0 +1,140 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Throwable2Computable;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.ContentRevision;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import com.intellij.openapi.vcs.impl.ContentRevisionCache;
import com.intellij.openapi.vcs.impl.CurrentRevisionProvider;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNWCClient;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
/**
* @author yole
*/
class SvnContentRevision implements ContentRevision {
private final SvnVcs17 myVcs;
protected final FilePath myFile;
private final SVNRevision myRevision;
/**
* this flag is necessary since SVN would not do remote request only if constant SVNRevision.BASE
* -> usual current revision content class can't be used
*/
private final boolean myUseBaseRevision;
protected SvnContentRevision(SvnVcs17 vcs, @NotNull final FilePath file, final SVNRevision revision, final boolean useBaseRevision) {
myVcs = vcs;
myRevision = revision;
myUseBaseRevision = useBaseRevision;
myFile = file;
}
public static SvnContentRevision createBaseRevision(@NotNull SvnVcs17 vcs, @NotNull final FilePath file, final SVNRevision revision) {
if (file.getFileType().isBinary()) {
return new SvnBinaryContentRevision(vcs, file, revision, true);
}
return new SvnContentRevision(vcs, file, revision, true);
}
public static SvnContentRevision createRemote(@NotNull SvnVcs17 vcs, @NotNull final FilePath file, final SVNRevision revision) {
if (file.getFileType().isBinary()) {
return new SvnBinaryContentRevision(vcs, file, revision, false);
}
return new SvnContentRevision(vcs, file, revision, false);
}
@Nullable
public String getContent() throws VcsException {
try {
if (myUseBaseRevision) {
return ContentRevisionCache.getOrLoadCurrentAsString(myVcs.getProject(), myFile, myVcs.getKeyInstanceMethod(),
new CurrentRevisionProvider() {
@Override
public VcsRevisionNumber getCurrentRevision() throws VcsException {
return getRevisionNumber();
}
@Override
public Pair<VcsRevisionNumber, byte[]> get()
throws VcsException, IOException {
return new Pair<VcsRevisionNumber, byte[]>(getRevisionNumber(), getUpToDateBinaryContent());
}
}).getSecond();
} else {
return ContentRevisionCache.getOrLoadAsString(myVcs.getProject(), myFile, getRevisionNumber(), myVcs.getKeyInstanceMethod(),
ContentRevisionCache.UniqueType.REPOSITORY_CONTENT,
new Throwable2Computable<byte[], VcsException, IOException>() {
@Override
public byte[] compute() throws VcsException, IOException {
return getUpToDateBinaryContent();
}
});
}
}
catch (IOException e) {
throw new VcsException(e);
}
}
protected byte[] getUpToDateBinaryContent() throws VcsException {
File file = myFile.getIOFile();
File lock = new File(file.getParentFile(), SvnUtil.PATH_TO_LOCK_FILE);
if (lock.exists()) {
throw new VcsException("Can not access file base revision contents: administrative area is locked");
}
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
SVNWCClient wcClient = myVcs.createWCClient();
try {
wcClient.doGetFileContents(file, SVNRevision.UNDEFINED, myUseBaseRevision ? SVNRevision.BASE : myRevision, true, buffer);
buffer.close();
}
catch (SVNException e) {
throw new VcsException(e);
}
catch (IOException e) {
throw new VcsException(e);
}
return buffer.toByteArray();
}
@NotNull
public FilePath getFile() {
return myFile;
}
@NotNull
public VcsRevisionNumber getRevisionNumber() {
return new SvnRevisionNumber(myRevision);
}
@NonNls
public String toString() {
return myFile.getPath();
}
}
@@ -0,0 +1,110 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.application.ApplicationManager;
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.vcs.RequestsMerger;
import com.intellij.util.Consumer;
import com.intellij.util.concurrency.Semaphore;
public class SvnCopiesRefreshManager {
private final CopiesRefresh myCopiesRefresh;
public SvnCopiesRefreshManager(final Project project, final SvnFileUrlMappingImpl17 mapping) {
myCopiesRefresh = new MyVeryRefresh();
final Runnable refresher = new MyRefresher(mapping);
final RequestsMerger requestsMerger = new RequestsMerger(refresher, new Consumer<Runnable>() {
public void consume(final Runnable runnable) {
ApplicationManager.getApplication().executeOnPooledThread(runnable);
}
});
((MyVeryRefresh) myCopiesRefresh).setRequestMerger(requestsMerger);
}
public CopiesRefresh getCopiesRefresh() {
return myCopiesRefresh;
}
private class MyVeryRefresh implements CopiesRefresh {
private static final long ourQueryInterval = 1000;
private RequestsMerger myRequestMerger;
private final ProgressManager myPm;
private MyVeryRefresh() {
myPm = ProgressManager.getInstance();
}
public void setRequestMerger(RequestsMerger requestMerger) {
myRequestMerger = requestMerger;
}
public void ensureInit() {
synchRequest(myPm.getProgressIndicator(), true);
}
public void asynchRequest() {
myRequestMerger.request();
}
public void synchRequest() {
synchRequest(myPm.getProgressIndicator(), false);
}
private void synchRequest(final ProgressIndicator pi, final boolean isOnlyInit) {
final Semaphore semaphore = new Semaphore();
final Runnable waiter = new Runnable() {
public void run() {
semaphore.up();
}
};
semaphore.down();
if (isOnlyInit) {
myRequestMerger.ensureInitialization(waiter);
} else {
myRequestMerger.waitRefresh(waiter);
}
while (true) {
if (semaphore.waitFor(ourQueryInterval)) break;
if (pi != null) {
pi.checkCanceled();
}
}
}
}
private static class MyRefresher implements Runnable {
private final SvnFileUrlMappingImpl17 myMapping;
private MyRefresher(final SvnFileUrlMappingImpl17 mapping) {
myMapping = mapping;
}
public void run() {
try {
myMapping.realRefresh();
}
catch (ProcessCanceledException e) {
//
}
}
}
}
@@ -0,0 +1,21 @@
/*
* Copyright 2000-2009 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.svn17;
public class SvnCopyRootSimple {
public String myVcsRoot;
public String myCopyRoot;
}
@@ -0,0 +1,189 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.actions.VcsContextFactory;
import com.intellij.openapi.vcs.changes.ContentRevision;
import com.intellij.openapi.vcs.diff.DiffMixin;
import com.intellij.openapi.vcs.diff.DiffProvider;
import com.intellij.openapi.vcs.diff.ItemLatestState;
import com.intellij.openapi.vcs.history.VcsRevisionDescription;
import com.intellij.openapi.vcs.history.VcsRevisionDescriptionImpl;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.idea.svn17.history.LatestExistentSearcher;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.wc.*;
import java.io.File;
public class SvnDiffProvider implements DiffProvider, DiffMixin {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnDiffProvider");
public static final String COMMIT_MESSAGE = "svn:log";
private final SvnVcs17 myVcs;
public SvnDiffProvider(final SvnVcs17 vcs) {
myVcs = vcs;
}
public VcsRevisionNumber getCurrentRevision(VirtualFile file) {
final SVNStatusClient client = myVcs.createStatusClient();
try {
final SVNStatus svnStatus = client.doStatus(new File(file.getPresentableUrl()), false, false);
if (svnStatus.getCommittedRevision().equals(SVNRevision.UNDEFINED) && svnStatus.isCopied()) {
return new SvnRevisionNumber(svnStatus.getCopyFromRevision());
}
return new SvnRevisionNumber(svnStatus.getRevision());
}
catch (SVNException e) {
LOG.debug(e); // most likely the file is unversioned
return null;
}
}
@Override
public VcsRevisionDescription getCurrentRevisionDescription(VirtualFile file) {
File path = new File(file.getPresentableUrl());
return getCurrentRevisionDescription(path);
}
private VcsRevisionDescription getCurrentRevisionDescription(File path) {
final SVNWCClient client = myVcs.createWCClient();
try {
final SVNInfo svnInfo = client.doInfo(path, SVNRevision.UNDEFINED);
if (svnInfo.getCommittedRevision().equals(SVNRevision.UNDEFINED) && ! svnInfo.getCopyFromRevision().equals(SVNRevision.UNDEFINED) &&
svnInfo.getCopyFromURL() != null) {
SVNURL copyUrl = svnInfo.getCopyFromURL();
String localPath = myVcs.getSvnFileUrlMapping().getLocalPath(copyUrl.toString());
if (localPath != null) {
return getCurrentRevisionDescription(new File(localPath));
}
}
final String message = getProperties(client, path);
return new VcsRevisionDescriptionImpl(new SvnRevisionNumber(svnInfo.getCommittedRevision()), svnInfo.getCommittedDate(),
svnInfo.getAuthor(), message);
}
catch (SVNException e) {
LOG.debug(e); // most likely the file is unversioned
return null;
}
}
private String getProperties(SVNWCClient client, File path) throws SVNException {
final String[] message = new String[1];
client.doGetRevisionProperty(path, null, SVNRevision.BASE, new ISVNPropertyHandler() {
@Override
public void handleProperty(File path, SVNPropertyData property) throws SVNException {
handle(property);
}
@Override
public void handleProperty(SVNURL url, SVNPropertyData property) throws SVNException {
handle(property);
}
@Override
public void handleProperty(long revision, SVNPropertyData property) throws SVNException {
handle(property);
}
private void handle(SVNPropertyData data) {
if (COMMIT_MESSAGE.equals(data.getName())) {
message[0] = data.getValue().getString();
}
}
});
return message[0];
}
private static ItemLatestState defaultResult() {
return createResult(SVNRevision.HEAD, true, true);
}
private static ItemLatestState createResult(final SVNRevision revision, final boolean exists, boolean defaultHead) {
return new ItemLatestState(new SvnRevisionNumber(revision), exists, defaultHead);
}
public ItemLatestState getLastRevision(VirtualFile file) {
return getLastRevision(new File(file.getPath()));
}
public ContentRevision createFileContent(final VcsRevisionNumber revisionNumber, final VirtualFile selectedFile) {
FilePath filePath = VcsContextFactory.SERVICE.getInstance().createFilePathOn(selectedFile);
final SVNRevision svnRevision = ((SvnRevisionNumber)revisionNumber).getRevision();
if (! SVNRevision.HEAD.equals(svnRevision)) {
if (revisionNumber.equals(getCurrentRevision(selectedFile))) {
return SvnContentRevision.createBaseRevision(myVcs, filePath, svnRevision);
}
}
// not clear why we need it, with remote check..
final SVNStatusClient client = myVcs.createStatusClient();
try {
final SVNStatus svnStatus = client.doStatus(new File(selectedFile.getPresentableUrl()), false, false);
if (svnRevision.equals(svnStatus.getRevision())) {
return SvnContentRevision.createBaseRevision(myVcs, filePath, svnRevision);
}
}
catch (SVNException e) {
LOG.debug(e); // most likely the file is unversioned
}
return SvnContentRevision.createRemote(myVcs, filePath, svnRevision);
}
public ItemLatestState getLastRevision(FilePath filePath) {
return getLastRevision(filePath.getIOFile());
}
public VcsRevisionNumber getLatestCommittedRevision(VirtualFile vcsRoot) {
// todo
return null;
}
private ItemLatestState getLastRevision(final File file) {
final SVNStatusClient client = myVcs.createStatusClient();
try {
final SVNStatus svnStatus = client.doStatus(file, true, false);
if (svnStatus == null) {
// IDEADEV-21785 (no idea why this can happen)
LOG.info("No SVN status returned for " + file.getPath());
return defaultResult();
}
final boolean exists = ! SVNStatusType.STATUS_DELETED.equals(svnStatus.getRemoteContentsStatus()) &&
! SVNStatusType.STATUS_DELETED.equals(svnStatus.getRemoteNodeStatus());
if (! exists) {
// get really latest revision
final LatestExistentSearcher searcher = new LatestExistentSearcher(myVcs, svnStatus.getURL());
final long revision = searcher.getDeletionRevision();
return createResult(SVNRevision.create(revision), exists, false);
}
final SVNRevision remoteRevision = svnStatus.getRemoteRevision();
if (remoteRevision != null) {
return createResult(remoteRevision, exists, false);
}
return createResult(svnStatus.getRevision(), exists, false);
}
catch (SVNException e) {
LOG.debug(e); // most likely the file is unversioned
return defaultResult();
}
}
}
@@ -0,0 +1,61 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.vcs.EditFileProvider;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNProperty;
import org.tmatesoft.svn.core.wc.SVNPropertyData;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNWCClient;
import java.io.File;
/**
* @author alex
*/
public class SvnEditFileProvider implements EditFileProvider {
private final SvnVcs17 myVCS;
public SvnEditFileProvider(SvnVcs17 vcs) {
myVCS = vcs;
}
public void editFiles(VirtualFile[] files) throws VcsException {
File[] ioFiles = new File[files.length];
SVNWCClient client = myVCS.createWCClient();
for (int i = 0; i < files.length; i++) {
ioFiles[i] = new File(files[i].getPath());
try {
SVNPropertyData property = client
.doGetProperty(ioFiles[i], SVNProperty.NEEDS_LOCK, SVNRevision.WORKING, SVNRevision.WORKING);
if (property == null || property.getValue() == null) {
throw new VcsException(SvnBundle.message("exception.text.file.miss.svn", ioFiles[i].getName()));
}
}
catch (SVNException e) {
throw new VcsException(e);
}
}
SvnUtil.doLockFiles(myVCS.getProject(), myVCS, ioFiles);
}
public String getRequestText() {
return SvnBundle.message("confirmation.text.edit.file");
}
}
@@ -0,0 +1,180 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.AbstractVcs;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.FilePathImpl;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileAdapter;
import com.intellij.openapi.vfs.VirtualFileEvent;
import javax.swing.*;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class SvnEntriesFileListener extends VirtualFileAdapter {
private final Project myProject;
private final Collection<SvnEntriesListener> myListeners = new ArrayList<SvnEntriesListener>();
private final ProjectLevelVcsManager myVcsManager;
public SvnEntriesFileListener(final Project project) {
myProject = project;
myVcsManager = ProjectLevelVcsManager.getInstance(myProject);
}
public void fileCreated(VirtualFileEvent event) {
if (! event.isFromRefresh()) {
return;
}
final VirtualFile file = event.getFile();
if (SvnUtil.SVN_ADMIN_DIR_NAME.equals(file.getName())) {
if (event.getParent() != null) {
VirtualFile parent = event.getParent();
fireFileStatusesChanged(parent);
fileEntriesChanged(parent);
}
return;
}
final AbstractVcs vcsFor = myVcsManager.getVcsFor(file);
if (vcsFor == null) return;
if (SvnVcs17.VCS_NAME.equals(vcsFor.getName())) {
final RootUrlInfo path = ((SvnVcs17)vcsFor).getSvnFileUrlMapping().getWcRootForFilePath(new File(file.getPath()));
if (path != null && WorkingCopyFormat.ONE_DOT_SEVEN.equals(path.getFormat())) {
VcsDirtyScopeManager.getInstance(myProject).filesDirty(Collections.singletonList(file), null);
}
}
}
public void contentsChanged(VirtualFileEvent event) {
if (! event.isFromRefresh()) {
return;
}
final VirtualFile file = event.getFile();
if (isEntriesFile(file) && file.getParent() != null) {
VirtualFile parent = file.getParent();
if (parent != null) {
VirtualFile grandParent = parent.getParent();
if (grandParent != null) {
fireFileStatusesChanged(grandParent);
fileEntriesChanged(grandParent);
}
}
return;
}
final AbstractVcs vcsFor = myVcsManager.getVcsFor(file);
if (vcsFor == null) return;
if (SvnVcs17.VCS_NAME.equals(vcsFor.getName())) {
final RootUrlInfo path = ((SvnVcs17)vcsFor).getSvnFileUrlMapping().getWcRootForFilePath(new File(file.getPath()));
if (path != null && WorkingCopyFormat.ONE_DOT_SEVEN.equals(path.getFormat())) {
VcsDirtyScopeManager.getInstance(myProject).filesDirty(Collections.singletonList(file), null);
fileRevisionProbablyChanged(file);
}
}
}
public void fileDeleted(VirtualFileEvent event) {
if (!event.isFromRefresh()) {
return;
}
final VirtualFile file = event.getFile();
if (SvnUtil.SVN_ADMIN_DIR_NAME.equals(file.getName())) {
if (event.getParent() != null) {
VirtualFile parent = event.getParent();
fireFileStatusesChanged(parent);
fileEntriesChanged(parent);
}
return;
}
if (event.getParent() != null) {
final VirtualFile parent = event.getParent();
final AbstractVcs vcsFor = myVcsManager.getVcsFor(parent);
if (vcsFor == null) return;
if (SvnVcs17.VCS_NAME.equals(vcsFor.getName())) {
final RootUrlInfo path = ((SvnVcs17)vcsFor).getSvnFileUrlMapping().getWcRootForFilePath(new File(parent.getPath()));
if (path != null && WorkingCopyFormat.ONE_DOT_SEVEN.equals(path.getFormat())) {
VcsDirtyScopeManager.getInstance(myProject).filePathsDirty(
Collections.<FilePath>singletonList(new FilePathImpl(new File(file.getPath()), file.isDirectory())), null);
}
}
}
}
private void fileRevisionProbablyChanged(final VirtualFile file) {
final SvnEntriesListener[] listeners = myListeners.toArray(new SvnEntriesListener[myListeners.size()]);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for (SvnEntriesListener listener : listeners) {
listener.fileVersionProbablyChanged(file);
}
}
});
}
private void fileEntriesChanged(final VirtualFile parent) {
final SvnEntriesListener[] listeners = myListeners.toArray(new SvnEntriesListener[myListeners.size()]);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for (SvnEntriesListener listener : listeners) {
listener.onEntriesChanged(parent);
}
}
});
}
public void addListener(SvnEntriesListener listener) {
myListeners.add(listener);
}
public void removeListener(SvnEntriesListener listener) {
myListeners.remove(listener);
}
private void fireFileStatusesChanged(VirtualFile parent) {
final VirtualFile[] children = parent.getChildren();
final List<VirtualFile> files = new ArrayList<VirtualFile>(children.length + 1);
files.add(parent);
Collections.addAll(files, children);
VcsDirtyScopeManager.getInstance(myProject).filesDirty(files, null);
/*
final FileStatusManager fileStatusManager = FileStatusManager.getInstance(myProject);
final VirtualFile[] children = parent.getChildren();
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
for (int i = 0; i < children.length; i++) {
VirtualFile child = children[i];
fileStatusManager.fileStatusChanged(child);
}
}
}, ModalityState.NON_MMODAL);
*/
}
private static boolean isEntriesFile(final VirtualFile file) {
VirtualFile parent = file.getParent();
return !file.isDirectory() && SvnUtil.ENTRIES_FILE_NAME.equals(file.getName()) && parent != null && SvnUtil.SVN_ADMIN_DIR_NAME.equals(parent.getName());
}
}
@@ -0,0 +1,23 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.vfs.VirtualFile;
public interface SvnEntriesListener {
void onEntriesChanged(VirtualFile directory);
void fileVersionProbablyChanged(final VirtualFile file);
}
@@ -0,0 +1,41 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.vcs.FileStatus;
import com.intellij.openapi.vcs.FileStatusFactory;
import java.awt.*;
public class SvnFileStatus {
private SvnFileStatus() {
}
public static final FileStatus EXTERNAL =
FileStatusFactory.SERVICE.getInstance()
.createFileStatus("IDEA_SVN_FILESTATUS_EXTERNAL", SvnBundle.message("file.status.external"), new Color(0x72A038));
public static final FileStatus OBSTRUCTED =
FileStatusFactory.SERVICE.getInstance()
.createFileStatus("IDEA_SVN_FILESTATUS_OBSTRUCTED", SvnBundle.message("file.status.obstructed"), new Color(0x727238));
public static final FileStatus REPLACED =
FileStatusFactory.SERVICE.getInstance().createFileStatus("IDEA_SVN_REPLACED",
SvnBundle.message("file.status.replaced"),
FileStatus.COLOR_ADDED);
}
@@ -0,0 +1,874 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandAdapter;
import com.intellij.openapi.command.CommandEvent;
import com.intellij.openapi.command.undo.UndoManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.actions.VcsContextFactory;
import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
import com.intellij.openapi.vfs.LocalFileOperationsHandler;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.RefreshQueue;
import com.intellij.openapi.vfs.newvfs.RefreshSession;
import com.intellij.util.ThrowableConsumer;
import com.intellij.util.containers.MultiMap;
import com.intellij.vcsUtil.ActionWithTempFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.wc.*;
import java.io.File;
import java.io.IOException;
import java.util.*;
public class SvnFileSystemListener extends CommandAdapter implements LocalFileOperationsHandler {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnFileSystemListener");
private final LocalFileSystem myLfs;
private static class AddedFileInfo {
private final VirtualFile myDir;
private final String myName;
@Nullable private final File myCopyFrom;
private final boolean myRecursive;
public AddedFileInfo(final VirtualFile dir, final String name, @Nullable final File copyFrom, boolean recursive) {
myDir = dir;
myName = name;
myCopyFrom = copyFrom;
myRecursive = recursive;
}
}
private static class MovedFileInfo {
private final Project myProject;
private final File mySrc;
private final File myDst;
private MovedFileInfo(final Project project, final File src, final File dst) {
myProject = project;
mySrc = src;
myDst = dst;
}
}
private final MultiMap<Project, AddedFileInfo> myAddedFiles = new MultiMap<Project, AddedFileInfo>();
private final MultiMap<Project, File> myDeletedFiles = new MultiMap<Project, File>();
private final List<MovedFileInfo> myMovedFiles = new ArrayList<MovedFileInfo>();
private final Map<Project, List<VcsException>> myMoveExceptions = new HashMap<Project, List<VcsException>>();
private final List<VirtualFile> myFilesToRefresh = new ArrayList<VirtualFile>();
@Nullable private File myStorageForUndo;
private final List<Pair<File, File>> myUndoStorageContents = new ArrayList<Pair<File, File>>();
private boolean myUndoingMove = false;
public SvnFileSystemListener() {
myLfs = LocalFileSystem.getInstance();
}
private void addToMoveExceptions(final Project project, final SVNException e) {
List<VcsException> exceptionList = myMoveExceptions.get(project);
if (exceptionList == null) {
exceptionList = new ArrayList<VcsException>();
myMoveExceptions.put(project, exceptionList);
}
VcsException vcsException;
if (SVNErrorCode.ENTRY_EXISTS.equals(e.getErrorMessage().getErrorCode())) {
vcsException = new VcsException(Arrays.asList("Target of move operation is already under version control.",
"Subversion move had not been performed. ", e.getMessage()));
} else {
vcsException = new VcsException(e);
}
exceptionList.add(vcsException);
}
@Nullable
public File copy(final VirtualFile file, final VirtualFile toDir, final String copyName) throws IOException {
SvnVcs17 vcs = getVCS(toDir);
if (vcs == null) {
vcs = getVCS(file);
}
if (vcs == null) {
return null;
}
File srcFile = new File(file.getPath());
File destFile = new File(new File(toDir.getPath()), copyName);
final boolean dstDirUnderControl = SvnUtil.isSvnVersioned(destFile.getParentFile());
if (! dstDirUnderControl && !isPendingAdd(vcs.getProject(), toDir)) {
return null;
}
if (! SvnUtil.isSvnVersioned(srcFile.getParentFile())) {
myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(toDir, copyName, null, false));
return null;
}
final SVNStatus fileStatus = getFileStatus(vcs, srcFile);
if (fileStatus != null && SvnVcs17.svnStatusIs(fileStatus, SVNStatusType.STATUS_ADDED)) {
myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(toDir, copyName, null, false));
return null;
}
if (sameRoot(vcs, file.getParent(), toDir)) {
myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(toDir, copyName, srcFile, false));
return null;
}
myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(toDir, copyName, null, false));
return null;
}
private boolean sameRoot(final SvnVcs17 vcs, final VirtualFile srcDir, final VirtualFile dstDir) {
final UUIDHelper helper = new UUIDHelper(vcs);
final String srcUUID = helper.getRepositoryUUID(vcs.getProject(), srcDir);
final String dstUUID = helper.getRepositoryUUID(vcs.getProject(), dstDir);
return srcUUID != null && dstUUID != null && srcUUID.equals(dstUUID);
}
private class UUIDHelper {
private final SVNWCClient myWcClient;
private UUIDHelper(final SvnVcs17 vcs) {
myWcClient = vcs.createWCClient();
}
/**
* passed dir must be under VC control (it is assumed)
*/
@Nullable
public String getRepositoryUUID(final Project project, final VirtualFile dir) {
try {
final SVNInfo info1 = myWcClient.doInfo(new File(dir.getPath()), SVNRevision.UNDEFINED);
if (info1 == null || info1.getRepositoryUUID() == null) {
// go deeper if current parent was added (if parent was added, it theoretically could NOT know its repo UUID)
final VirtualFile parent = dir.getParent();
if (parent == null) {
return null;
}
if (isPendingAdd(project, parent)) {
return getRepositoryUUID(project, parent);
}
} else {
return info1.getRepositoryUUID();
}
} catch (SVNException e) {
// go to return default
}
return null;
}
}
public boolean move(VirtualFile file, VirtualFile toDir) throws IOException {
File srcFile = getIOFile(file);
File dstFile = new File(getIOFile(toDir), file.getName());
final SvnVcs17 vcs = getVCS(toDir);
final SvnVcs17 sourceVcs = getVCS(file);
if (vcs == null && sourceVcs == null) return false;
if (vcs == null) {
return false;
}
if (sourceVcs == null) {
return createItem(toDir, file.getName(), file.isDirectory(), true);
}
if (isPendingAdd(vcs.getProject(), toDir)) {
myMovedFiles.add(new MovedFileInfo(sourceVcs.getProject(), srcFile, dstFile));
return true;
}
else {
final VirtualFile oldParent = file.getParent();
myFilesToRefresh.add(oldParent);
myFilesToRefresh.add(toDir);
return doMove(sourceVcs, srcFile, dstFile);
}
}
public boolean rename(VirtualFile file, String newName) throws IOException {
File srcFile = getIOFile(file);
File dstFile = new File(srcFile.getParentFile(), newName);
SvnVcs17 vcs = getVCS(file);
if (vcs != null) {
myFilesToRefresh.add(file.getParent());
return doMove(vcs, srcFile, dstFile);
}
return false;
}
private boolean doMove(@NotNull SvnVcs17 vcs, final File src, final File dst) {
long srcTime = src.lastModified();
try {
final boolean isUndo = isUndo(vcs);
final String list = isUndo ? null : SvnChangelistListener.getCurrentMapping(vcs.getProject(), src);
final boolean is17 = SvnUtil.is17CopyPart(src);
if (is17) {
if (for17move(vcs, src, dst, isUndo)) return false;
} else {
if (for16move(vcs, src, dst, isUndo)) return false;
}
if (! isUndo && list != null) {
SvnChangelistListener.putUnderList(vcs.getProject(), list, dst);
}
dst.setLastModified(srcTime);
}
catch (SVNException e) {
addToMoveExceptions(vcs.getProject(), e);
return false;
}
return true;
}
private boolean for17move(SvnVcs17 vcs, File src, File dst, boolean undo) throws SVNException {
if (undo) {
final SVNWCClient wcClient = vcs.createWCClient();
myUndoingMove = true;
wcClient.doRevert(dst, true);
wcClient.doRevert(src, true);
restoreFromUndoStorage(dst);
} else {
if (doUsualMove(vcs, src)) return true;
final SVNCopyClient copyClient = vcs.createCopyClient();
final SVNCopySource svnCopySource = new SVNCopySource(SVNRevision.UNDEFINED, SVNRevision.BASE, src);
copyClient.doCopy(new SVNCopySource[]{svnCopySource}, dst, true, false, true);
}
return false;
}
private boolean doUsualMove(SvnVcs17 vcs, File src) {
// if src is not under version control, do usual move.
SVNStatus srcStatus = getFileStatus(vcs, src);
if (srcStatus == null || SvnVcs17.svnStatusIsUnversioned(srcStatus) ||
SvnVcs17.svnStatusIs(srcStatus, SVNStatusType.STATUS_OBSTRUCTED) ||
SvnVcs17.svnStatusIs(srcStatus, SVNStatusType.STATUS_MISSING) ||
SvnVcs17.svnStatusIs(srcStatus, SVNStatusType.STATUS_EXTERNAL)) {
return true;
}
return false;
}
private boolean for16move(SvnVcs17 vcs, File src, File dst, boolean undo) throws SVNException {
SVNMoveClient mover = vcs.createMoveClient();
if (undo) {
myUndoingMove = true;
restoreFromUndoStorage(dst);
mover.undoMove(src, dst);
}
else {
// if src is not under version control, do usual move.
if (doUsualMove(vcs, src)) return true;
mover.doMove(src, dst);
}
return false;
}
private void restoreFromUndoStorage(final File dst) {
String normPath = FileUtil.toSystemIndependentName(dst.getPath());
for (Iterator<Pair<File, File>> it = myUndoStorageContents.iterator(); it.hasNext();) {
Pair<File, File> e = it.next();
final String p = FileUtil.toSystemIndependentName(e.first.getPath());
if (p.startsWith(normPath)) {
try {
FileUtil.rename(e.second, e.first);
}
catch (IOException ex) {
LOG.error(ex);
FileUtil.asyncDelete(e.second);
}
it.remove();
}
}
if (myStorageForUndo != null) {
final File[] files = myStorageForUndo.listFiles();
if (files == null || files.length == 0) {
FileUtil.asyncDelete(myStorageForUndo);
myStorageForUndo = null;
}
}
}
public boolean createFile(VirtualFile dir, String name) throws IOException {
return createItem(dir, name, false, false);
}
public boolean createDirectory(VirtualFile dir, String name) throws IOException {
return createItem(dir, name, true, false);
}
/**
* delete file or directory (both 'undo' and 'do' modes)
* unversioned: do nothing, return false
* obstructed: do nothing, return false
* external or wc root: do nothing, return false
* missing: do nothing, return false
* <p/>
* versioned: schedule for deletion, return true
* added: schedule for deletion (make unversioned), return true
* copied, but not scheduled: schedule for deletion, return true
* replaced: schedule for deletion, return true
* <p/>
* deleted: do nothing, return true (strange)
*/
public boolean delete(VirtualFile file) throws IOException {
SvnVcs17 vcs = getVCS(file);
if (vcs != null && SvnUtil.isAdminDirectory(file)) {
return true;
}
File ioFile = getIOFile(file);
if (! SvnUtil.isSvnVersioned(ioFile.getParentFile())) {
return false;
}
try {
if (SVNWCUtil.isWorkingCopyRoot(ioFile)) {
return false;
}
} catch (SVNException e) {
//
}
SVNStatus status = getFileStatus(ioFile);
if (status == null ||
SvnVcs17.svnStatusIsUnversioned(status) ||
SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_OBSTRUCTED) ||
SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_MISSING) ||
SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_EXTERNAL) ||
SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_IGNORED)) {
return false;
} else if (SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_DELETED)) {
if (isUndo(vcs)) {
moveToUndoStorage(file);
}
return true;
}
else {
if (vcs != null) {
if (SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_ADDED)) {
try {
final SVNWCClient wcClient = vcs.createWCClient();
wcClient.doRevert(ioFile, false);
}
catch (SVNException e) {
// ignore
}
}
else {
myDeletedFiles.putValue(vcs.getProject(), ioFile);
// packages deleted from disk should not be deleted from svn (IDEADEV-16066)
if (file.isDirectory() || isUndo(vcs)) return true;
}
}
return false;
}
}
private void moveToUndoStorage(final VirtualFile file) {
if (myStorageForUndo == null) {
try {
myStorageForUndo = FileUtil.createTempDirectory("svnUndoStorage", "");
}
catch (IOException e) {
LOG.error(e);
return;
}
}
final File tmpFile = FileUtil.findSequentNonexistentFile(myStorageForUndo, "tmp", "");
myUndoStorageContents.add(0, new Pair<File, File>(new File(file.getPath()), tmpFile));
new File(file.getPath()).renameTo(tmpFile);
}
/**
* add file or directory:
* <p/>
* parent directory is:
* unversioned: do nothing, return false
* versioned:
* entry is:
* null: create entry, schedule for addition
* missing: do nothing, return false
* deleted, 'do' mode: try to create entry and it schedule for addition if kind is the same, otherwise do nothing, return false.
* deleted: 'undo' mode: try to revert non-recursively, if kind is the same, otherwise do nothing, return false.
* anything else: return false.
*/
private boolean createItem(VirtualFile dir, String name, boolean directory, final boolean recursive) {
SvnVcs17 vcs = getVCS(dir);
if (vcs == null) {
return false;
}
if (isUndo(vcs) && SvnUtil.isAdminDirectory(dir, name)) {
return false;
}
File ioDir = getIOFile(dir);
boolean pendingAdd = isPendingAdd(vcs.getProject(), dir);
if (! SvnUtil.isSvnVersioned(ioDir) && ! pendingAdd) {
return false;
}
SVNWCClient wcClient = vcs.createWCClient();
File targetFile = new File(ioDir, name);
SVNStatus status = getFileStatus(vcs, targetFile);
if (status == null || status.getContentsStatus() == SVNStatusType.STATUS_NONE ||
status.getContentsStatus() == SVNStatusType.STATUS_UNVERSIONED) {
myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(dir, name, null, recursive));
return false;
}
else if (SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_MISSING)) {
return false;
}
else if (SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_DELETED)) {
SVNNodeKind kind = status.getKind();
// kind differs.
if (directory && kind != SVNNodeKind.DIR || !directory && kind != SVNNodeKind.FILE) {
return false;
}
try {
if (isUndo(vcs)) {
wcClient.doRevert(targetFile, false);
return true;
}
myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(dir, name, null, recursive));
return false;
}
catch (SVNException e) {
SVNFileUtil.deleteAll(targetFile, true);
return false;
}
}
return false;
}
private boolean isPendingAdd(final Project project, final VirtualFile dir) {
final Collection<AddedFileInfo> addedFileInfos = myAddedFiles.get(project);
for(AddedFileInfo i: addedFileInfos) {
if (i.myDir == dir.getParent() && i.myName.equals(dir.getName())) {
return true;
}
}
return false;
}
public void commandStarted(CommandEvent event) {
myUndoingMove = false;
final Project project = event.getProject();
if (project == null) return;
commandStarted(project);
}
void commandStarted(final Project project) {
myUndoingMove = false;
myMoveExceptions.remove(project);
}
public void commandFinished(CommandEvent event) {
final Project project = event.getProject();
if (project == null) return;
commandFinished(project);
}
void commandFinished(final Project project) {
checkOverwrites(project);
if (myAddedFiles.containsKey(project)) {
processAddedFiles(project);
}
processMovedFiles(project);
if (myDeletedFiles.containsKey(project)) {
processDeletedFiles(project);
}
final List<VcsException> exceptionList = myMoveExceptions.get(project);
if (exceptionList != null && ! exceptionList.isEmpty()) {
AbstractVcsHelper.getInstance(project).showErrors(exceptionList, SvnBundle.message("move.files.errors.title"));
}
if (!myFilesToRefresh.isEmpty()) {
refreshFiles(project);
}
}
private void checkOverwrites(final Project project) {
final Collection<AddedFileInfo> addedFileInfos = myAddedFiles.get(project);
final Collection<File> deletedFiles = myDeletedFiles.get(project);
if (addedFileInfos.isEmpty() || deletedFiles.isEmpty()) return;
final Iterator<AddedFileInfo> iterator = addedFileInfos.iterator();
while (iterator.hasNext()) {
AddedFileInfo addedFileInfo = iterator.next();
final File ioFile = new File(addedFileInfo.myDir.getPath(), addedFileInfo.myName);
if (deletedFiles.remove(ioFile)) {
iterator.remove();
}
}
}
private void refreshFiles(final Project project) {
final List<VirtualFile> toRefreshFiles = new ArrayList<VirtualFile>();
final List<VirtualFile> toRefreshDirs = new ArrayList<VirtualFile>();
for (VirtualFile file : myFilesToRefresh) {
if (file == null) continue;
if (file.isDirectory()) {
toRefreshDirs.add(file);
} else {
toRefreshFiles.add(file);
}
}
// if refresh asynchronously, local changes would also be notified that they are dirty asynchronously,
// and commit could be executed while not all changes are visible
final RefreshSession session = RefreshQueue.getInstance().createSession(true, true, new Runnable() {
public void run() {
if (project.isDisposed()) return;
filterOutInvalid(toRefreshFiles);
filterOutInvalid(toRefreshDirs);
final VcsDirtyScopeManager vcsDirtyScopeManager = VcsDirtyScopeManager.getInstance(project);
vcsDirtyScopeManager.filesDirty(toRefreshFiles, toRefreshDirs);
}
});
filterOutInvalid(myFilesToRefresh);
session.addAllFiles(myFilesToRefresh);
session.launch();
myFilesToRefresh.clear();
}
private static void filterOutInvalid(final Collection<VirtualFile> files) {
for (Iterator<VirtualFile> iterator = files.iterator(); iterator.hasNext();) {
final VirtualFile file = iterator.next();
if (! file.isValid() || ! file.exists()) {
LOG.info("Refresh root is not valid: " + file.getPath());
iterator.remove();
}
}
}
private void processAddedFiles(Project project) {
SvnVcs17 vcs = SvnVcs17.getInstance(project);
List<VirtualFile> addedVFiles = new ArrayList<VirtualFile>();
Map<VirtualFile, File> copyFromMap = new HashMap<VirtualFile, File>();
final Set<VirtualFile> recursiveItems = new HashSet<VirtualFile>();
fillAddedFiles(project, vcs, addedVFiles, copyFromMap, recursiveItems);
if (addedVFiles.isEmpty()) return;
final VcsShowConfirmationOption.Value value = vcs.getAddConfirmation().getValue();
if (value != VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY) {
final AbstractVcsHelper vcsHelper = AbstractVcsHelper.getInstance(project);
final Collection<VirtualFile> filesToProcess = promptAboutAddition(vcs, addedVFiles, value, vcsHelper);
if (filesToProcess != null && !filesToProcess.isEmpty()) {
final List<VcsException> exceptions = new ArrayList<VcsException>();
runInBackground(project, "Adding files to Subversion",
createAdditionRunnable(project, vcs, copyFromMap, filesToProcess, exceptions));
if (!exceptions.isEmpty()) {
vcsHelper.showErrors(exceptions, SvnBundle.message("add.files.errors.title"));
}
}
}
}
private void runInBackground(final Project project, final String name, final Runnable runnable) {
if (ApplicationManager.getApplication().isDispatchThread()) {
ProgressManager.getInstance().runProcessWithProgressSynchronously(runnable, name, false, project);
} else {
runnable.run();
}
}
private Runnable createAdditionRunnable(final Project project,
final SvnVcs17 vcs,
final Map<VirtualFile, File> copyFromMap,
final Collection<VirtualFile> filesToProcess,
final List<VcsException> exceptions) {
return new Runnable() {
@Override
public void run() {
SVNWCClient wcClient = vcs.createWCClient();
final SVNCopyClient copyClient = vcs.createCopyClient();
for(VirtualFile file: filesToProcess) {
final File ioFile = new File(file.getPath());
try {
final File copyFrom = copyFromMap.get(file);
if (copyFrom != null) {
try {
new ActionWithTempFile(ioFile) {
protected void executeInternal() throws VcsException {
try {
// not recursive
final SVNCopySource[] copySource = {new SVNCopySource(SVNRevision.WORKING, SVNRevision.WORKING, copyFrom)};
copyClient.doCopy(copySource, ioFile, false, true, true);
}
catch (SVNException e) {
throw new VcsException(e);
}
}
}.execute();
}
catch (VcsException e) {
exceptions.add(e);
}
}
else {
wcClient.doAdd(ioFile, true, false, false, true);
}
VcsDirtyScopeManager.getInstance(project).fileDirty(file);
}
catch (SVNException e) {
exceptions.add(new VcsException(e));
}
}
}
};
}
private Collection<VirtualFile> promptAboutAddition(SvnVcs17 vcs,
List<VirtualFile> addedVFiles,
VcsShowConfirmationOption.Value value,
AbstractVcsHelper vcsHelper) {
Collection<VirtualFile> filesToProcess;
if (value == VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY) {
filesToProcess = addedVFiles;
}
else {
final String singleFilePrompt;
if (addedVFiles.size() == 1 && addedVFiles.get(0).isDirectory()) {
singleFilePrompt = SvnBundle.getString("confirmation.text.add.dir");
}
else {
singleFilePrompt = SvnBundle.getString("confirmation.text.add.file");
}
filesToProcess = vcsHelper.selectFilesToProcess(addedVFiles, SvnBundle.message("confirmation.title.add.multiple.files"),
null,
SvnBundle.message("confirmation.title.add.file"), singleFilePrompt,
vcs.getAddConfirmation());
}
return filesToProcess;
}
private void fillAddedFiles(Project project,
SvnVcs17 vcs,
List<VirtualFile> addedVFiles,
Map<VirtualFile, File> copyFromMap,
Set<VirtualFile> recursiveItems) {
final Collection<AddedFileInfo> addedFileInfos = myAddedFiles.remove(project);
final ChangeListManager changeListManager = ChangeListManager.getInstance(project);
for (AddedFileInfo addedFileInfo : addedFileInfos) {
final File ioFile = new File(getIOFile(addedFileInfo.myDir), addedFileInfo.myName);
VirtualFile addedFile = addedFileInfo.myDir.findChild(addedFileInfo.myName);
if (addedFile == null) {
addedFile = myLfs.refreshAndFindFileByIoFile(ioFile);
}
if (addedFile != null) {
final SVNStatus fileStatus = getFileStatus(vcs, ioFile);
if (fileStatus == null || ! SvnVcs17.svnStatusIs(fileStatus, SVNStatusType.STATUS_IGNORED)) {
boolean isIgnored = changeListManager.isIgnoredFile(addedFile);
if (!isIgnored) {
addedVFiles.add(addedFile);
copyFromMap.put(addedFile, addedFileInfo.myCopyFrom);
if (addedFileInfo.myRecursive) {
recursiveItems.add(addedFile);
}
}
}
}
}
}
private void processDeletedFiles(Project project) {
final List<FilePath> deletedFiles = new ArrayList<FilePath>();
fillDeletedFiles(project, deletedFiles);
if (deletedFiles.isEmpty() || myUndoingMove) return;
SvnVcs17 vcs = SvnVcs17.getInstance(project);
final VcsShowConfirmationOption.Value value = vcs.getDeleteConfirmation().getValue();
if (value != VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY) {
final AbstractVcsHelper vcsHelper = AbstractVcsHelper.getInstance(project);
Collection<FilePath> filesToProcess;
filesToProcess = promptAboutDeletion(deletedFiles, vcs, value, vcsHelper);
if (filesToProcess != null && !filesToProcess.isEmpty()) {
List<VcsException> exceptions = new ArrayList<VcsException>();
runInBackground(project, "Deleting files from Subversion", createDeleteRunnable(project, vcs, filesToProcess, exceptions));
if (!exceptions.isEmpty()) {
vcsHelper.showErrors(exceptions, SvnBundle.message("delete.files.errors.title"));
}
}
for (FilePath file : deletedFiles) {
final FilePath parent = file.getParentPath();
if (parent != null) {
myFilesToRefresh.add(parent.getVirtualFile());
}
}
if (filesToProcess != null) {
deletedFiles.removeAll(filesToProcess);
}
for (FilePath file : deletedFiles) {
FileUtil.delete(file.getIOFile());
}
}
}
private Runnable createDeleteRunnable(final Project project,
final SvnVcs17 vcs,
final Collection<FilePath> filesToProcess,
final List<VcsException> exceptions) {
return new Runnable() {
public void run() {
SVNWCClient wcClient = vcs.createWCClient();
for(FilePath file: filesToProcess) {
VirtualFile vFile = file.getVirtualFile(); // for deleted directories
File ioFile = new File(file.getPath());
try {
wcClient.doDelete(ioFile, true, false);
if (vFile != null && vFile.isValid() && vFile.isDirectory()) {
vFile.refresh(true, true);
VcsDirtyScopeManager.getInstance(project).dirDirtyRecursively(vFile);
}
else {
VcsDirtyScopeManager.getInstance(project).fileDirty(file);
}
}
catch (SVNException e) {
exceptions.add(new VcsException(e));
}
}
}
};
}
private Collection<FilePath> promptAboutDeletion(List<FilePath> deletedFiles,
SvnVcs17 vcs,
VcsShowConfirmationOption.Value value,
AbstractVcsHelper vcsHelper) {
Collection<FilePath> filesToProcess;
if (value == VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY) {
filesToProcess = new ArrayList<FilePath>(deletedFiles);
}
else {
final String singleFilePrompt;
if (deletedFiles.size() == 1 && deletedFiles.get(0).isDirectory()) {
singleFilePrompt = SvnBundle.getString("confirmation.text.delete.dir");
}
else {
singleFilePrompt = SvnBundle.getString("confirmation.text.delete.file");
}
final Collection<FilePath> files = vcsHelper
.selectFilePathsToProcess(deletedFiles, SvnBundle.message("confirmation.title.delete.multiple.files"), null,
SvnBundle.message("confirmation.title.delete.file"), singleFilePrompt, vcs.getDeleteConfirmation());
filesToProcess = files == null ? null : new ArrayList<FilePath>(files);
}
return filesToProcess;
}
private void fillDeletedFiles(Project project, List<FilePath> deletedFiles) {
final Collection<File> files = myDeletedFiles.remove(project);
for (File file : files) {
final FilePath filePath = VcsContextFactory.SERVICE.getInstance().createFilePathOn(file);
deletedFiles.add(filePath);
}
}
private void processMovedFiles(final Project project) {
if (myMovedFiles.isEmpty()) return;
final Runnable runnable = new Runnable() {
public void run() {
for (Iterator<MovedFileInfo> iterator = myMovedFiles.iterator(); iterator.hasNext();) {
MovedFileInfo movedFileInfo = iterator.next();
if (movedFileInfo.myProject == project) {
doMove(SvnVcs17.getInstance(project), movedFileInfo.mySrc, movedFileInfo.myDst);
iterator.remove();
}
}
}
};
runInBackground(project, "Moving files in Subversion", runnable);
}
@Nullable
private static SvnVcs17 getVCS(VirtualFile file) {
Project[] projects = ProjectManager.getInstance().getOpenProjects();
for (Project project : projects) {
AbstractVcs vcs = ProjectLevelVcsManager.getInstance(project).getVcsFor(file);
if (vcs instanceof SvnVcs17) {
return (SvnVcs17)vcs;
}
}
return null;
}
private static File getIOFile(VirtualFile vf) {
return new File(vf.getPath()).getAbsoluteFile();
}
@Nullable
private static SVNStatus getFileStatus(File file) {
final SVNClientManager clientManager = SVNClientManager.newInstance();
try {
SVNStatusClient stClient = clientManager.getStatusClient();
return getFileStatus(file, stClient);
}
finally {
clientManager.dispose();
}
}
@Nullable
private static SVNStatus getFileStatus(SvnVcs17 vcs, File file) {
SVNStatusClient stClient = vcs.createStatusClient();
return getFileStatus(file, stClient);
}
@Nullable
private static SVNStatus getFileStatus(final File file, final SVNStatusClient stClient) {
try {
return stClient.doStatus(file, false);
}
catch (SVNException e) {
return null;
}
}
private static boolean isUndoOrRedo(@NotNull final Project project) {
final UndoManager undoManager = UndoManager.getInstance(project);
return undoManager.isUndoInProgress() || undoManager.isRedoInProgress();
}
private static boolean isUndo(SvnVcs17 vcs) {
if (vcs == null || vcs.getProject() == null) {
return false;
}
Project p = vcs.getProject();
return UndoManager.getInstance(p).isUndoInProgress();
}
public void afterDone(final ThrowableConsumer<LocalFileOperationsHandler, IOException> invoker) {
}
}
@@ -0,0 +1,187 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.command.CommandEvent;
import com.intellij.openapi.command.CommandListener;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectLocator;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.LocalFileOperationsHandler;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ThrowableConsumer;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class SvnFileSystemListenerWrapper {
private final LocalFileOperationsHandler myProxy;
private final CommandListener myListener;
public SvnFileSystemListenerWrapper(final SvnFileSystemListener delegate) {
final MyCommandListener listener = new MyCommandListener(delegate);
myListener = listener;
final MyStorage storage = new MyStorage(listener);
myProxy = (LocalFileOperationsHandler) Proxy.newProxyInstance(LocalFileOperationsHandler.class.getClassLoader(),
new Class<?>[]{LocalFileOperationsHandler.class}, new MyInvoker(storage, delegate));
}
public void registerSelf() {
LocalFileSystem.getInstance().registerAuxiliaryFileOperationsHandler(myProxy);
CommandProcessor.getInstance().addCommandListener(myListener);
}
public void unregisterSelf() {
LocalFileSystem.getInstance().unregisterAuxiliaryFileOperationsHandler(myProxy);
CommandProcessor.getInstance().removeCommandListener(myListener);
}
private static class MyCommandListener implements CommandListener, MyMarker {
private volatile boolean myInCommand;
private final SvnFileSystemListener myDelegate;
public MyCommandListener(final SvnFileSystemListener delegate) {
myDelegate = delegate;
}
public void start(final Project project) {
if (!myInCommand && project != null) {
myDelegate.commandStarted(project);
}
}
public void finish(final Project project) {
if (! myInCommand && project != null) {
myDelegate.commandFinished(project);
}
}
public void commandStarted(CommandEvent event) {
myInCommand = true;
myDelegate.commandStarted(event);
}
public void beforeCommandFinished(CommandEvent event) {
myDelegate.beforeCommandFinished(event);
}
public void commandFinished(CommandEvent event) {
myInCommand = false;
myDelegate.commandFinished(event);
}
public void undoTransparentActionStarted() {
myDelegate.undoTransparentActionStarted();
}
public void undoTransparentActionFinished() {
myDelegate.undoTransparentActionFinished();
}
}
private interface MyMarker {
void start(final Project project);
void finish(final Project project);
}
private static class MyStorage implements InvocationHandler {
private final MyMarker myMarker;
private final Map<Project, Pair<String, Object[]>> myStarted;
private MyStorage(final MyMarker marker) {
myMarker = marker;
myStarted = new HashMap<Project, Pair<String, Object[]>>();
}
@Nullable
private static Project getProject(Object[] args) {
for (Object arg : args) {
if (arg instanceof VirtualFile) {
return ProjectLocator.getInstance().guessProjectForFile((VirtualFile) arg);
}
}
return null;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
final Project project = getProject(args);
if (project != null) {
final Pair<String, Object[]> pair = myStarted.get(project);
if (pair != null && method.getName().equals(pair.getFirst()) && Arrays.equals(args, pair.getSecond())) {
myMarker.finish(project);
}
}
//dont return null for auto unboxing to not face NPE
return "boolean".equals(method.getReturnType().getName()) ? Boolean.TRUE : null;
}
private void register(final Method method, final Object[] args) {
final Object[] newArr = new Object[args.length];
System.arraycopy(args, 0, newArr, 0, args.length);
final Project project = getProject(args);
if (project != null) {
myMarker.start(project);
myStarted.put(project, new Pair<String, Object[]>(method.getName(), newArr));
Disposer.register(project, new Disposable() {
public void dispose() {
myStarted.remove(project);
}
});
}
}
}
private static class MyInvoker implements InvocationHandler {
private final Object myDelegate;
private final MyStorage myParent;
private final LocalFileOperationsHandler myParentProxy;
private MyInvoker(final MyStorage parent, Object delegate) {
myParent = parent;
myDelegate = delegate;
myParentProxy = (LocalFileOperationsHandler) Proxy.newProxyInstance(LocalFileOperationsHandler.class.getClassLoader(),
new Class<?>[]{LocalFileOperationsHandler.class}, myParent);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("afterDone".equals(method.getName()) && args.length == 1) {
((ThrowableConsumer<LocalFileOperationsHandler, IOException>)args[0]).consume(myParentProxy);
return null;
}
if (LocalFileOperationsHandler.class.equals(method.getDeclaringClass())) {
myParent.register(method, args);
}
if ("equals".equals(method.getName())) {
return args[0].equals(this);
}
else if ("hashCode".equals(method.getName())) {
return 1;
}
return method.invoke(myDelegate, args);
}
}
}
@@ -0,0 +1,49 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.vcs.AbstractVcs;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.Nullable;
import org.tmatesoft.svn.core.SVNURL;
import java.io.File;
import java.util.List;
public interface SvnFileUrlMapping17 extends AbstractVcs.RootsConvertor {
@Nullable
SVNURL getUrlForFile(final File file);
@Nullable
String getLocalPath(final String url);
@Nullable
RootUrlInfo getWcRootForUrl(final String url);
List<RootUrlInfo> getAllWcInfos();
@Nullable
RootUrlInfo getWcRootForFilePath(final File file);
/**
* @return true if roots under SVN set by the user differs from real WC roots (are under specified roots)
*/
boolean rootsDiffer();
VirtualFile[] getNotFilteredRoots();
boolean isEmpty();
}
@@ -0,0 +1,578 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.lifecycle.PeriodicalTasksCloser;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.ProjectComponent;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.DumbAwareRunnable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Getter;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.ObjectsConvertor;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.ThreadLocalDefendedInvoker;
import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vcs.changes.InvokeAfterUpdateMode;
import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl;
import com.intellij.openapi.vcs.impl.VcsInitObject;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import com.intellij.util.containers.Convertor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.tmatesoft.svn.core.SVNDepth;
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 java.io.File;
import java.util.*;
@State(
name = "SvnFileUrlMappingImpl17",
storages = {
@Storage(
file = "$WORKSPACE_FILE$"
)}
)
class SvnFileUrlMappingImpl17 implements SvnFileUrlMapping17, PersistentStateComponent<SvnMappingSavedPart>, ProjectComponent {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnFileUrlMappingImpl17");
private final SvnCompatibilityChecker myChecker;
private final Object myMonitor = new Object();
// strictly: what real roots are under what vcs mappings
private final SvnMapping myMapping;
// grouped; if there are several mappings one under another, will return the upmost
private final SvnMapping myMoreRealMapping;
private final MyRootsHelper myHelper;
private final Project myProject;
private final NestedCopiesSink myTempSink;
private boolean myInitialized;
private static class MyRootsHelper extends ThreadLocalDefendedInvoker<VirtualFile[]> {
private final ProjectLevelVcsManager myPlVcsManager;
private MyRootsHelper(final ProjectLevelVcsManager vcsManager) {
myPlVcsManager = vcsManager;
}
protected VirtualFile[] execute(Project project) {
return myPlVcsManager.getRootsUnderVcs(SvnVcs17.getInstance(project));
}
}
public static SvnFileUrlMappingImpl17 getInstance(final Project project) {
return PeriodicalTasksCloser.getInstance().safeGetComponent(project, SvnFileUrlMappingImpl17.class);
}
private SvnFileUrlMappingImpl17(final Project project, final ProjectLevelVcsManager vcsManager) {
myProject = project;
myMapping = new SvnMapping();
myMoreRealMapping = new SvnMapping();
myHelper = new MyRootsHelper(vcsManager);
myChecker = new SvnCompatibilityChecker(project);
myTempSink = new NestedCopiesSink();
}
@Nullable
public SVNURL getUrlForFile(final File file) {
final RootUrlInfo rootUrlInfo = getWcRootForFilePath(file);
if (rootUrlInfo == null) {
return null;
}
final String absolutePath = file.getAbsolutePath();
final String rootAbsPath = rootUrlInfo.getIoFile().getAbsolutePath();
if (absolutePath.length() < rootAbsPath.length()) {
// remove last separator from etalon name
if (absolutePath.equals(rootAbsPath.substring(0, rootAbsPath.length() - 1))) {
return rootUrlInfo.getAbsoluteUrlAsUrl();
}
return null;
}
final String relativePath = absolutePath.substring(rootAbsPath.length());
try {
return rootUrlInfo.getAbsoluteUrlAsUrl().appendPath(FileUtil.toSystemIndependentName(relativePath), true);
}
catch (SVNException e) {
LOG.info(e);
return null;
}
}
@Nullable
public String getLocalPath(final String url) {
synchronized (myMonitor) {
final String rootUrl = getUrlRootForUrl(url);
if (rootUrl == null) {
return null;
}
final RootUrlInfo parentInfo = myMapping.byUrl(rootUrl);
if (parentInfo == null) {
return null;
}
return fileByUrl(parentInfo.getIoFile().getAbsolutePath(), rootUrl, url).getAbsolutePath();
}
}
public static File fileByUrl(final String parentPath, final String parentUrl, final String childUrl) {
return new File(parentPath, childUrl.substring(parentUrl.length()));
}
@Nullable
public RootUrlInfo getWcRootForFilePath(final File file) {
synchronized (myMonitor) {
final String root = getRootForPath(file);
if (root == null) {
return null;
}
return myMapping.byFile(root);
}
}
public boolean rootsDiffer() {
synchronized (myMonitor) {
return myMapping.isRootsDifferFromSettings();
}
}
@Nullable
public RootUrlInfo getWcRootForUrl(final String url) {
synchronized (myMonitor) {
final String rootUrl = getUrlRootForUrl(url);
if (rootUrl == null) {
return null;
}
final RootUrlInfo result = myMapping.byUrl(rootUrl);
if (result == null) {
LOG.info("Inconsistent maps for url:" + url + " found root url: " + rootUrl);
return null;
}
return result;
}
}
/**
* Returns real working copies roots - if there is <Project Root> -> Subversion setting,
* and there is one working copy, will return one root
*/
public List<RootUrlInfo> getAllWcInfos() {
synchronized (myMonitor) {
// a copy is created inside
return myMoreRealMapping.getAllCopies();
}
}
public List<VirtualFile> convertRoots(final List<VirtualFile> result) {
if (ThreadLocalDefendedInvoker.isInside()) return result;
synchronized (myMonitor) {
final List<VirtualFile> cachedRoots = myMapping.getUnderVcsRoots();
final List<VirtualFile> lonelyRoots = myMapping.getLonelyRoots();
if (! lonelyRoots.isEmpty()) {
myChecker.reportNoRoots(lonelyRoots);
}
if (cachedRoots.isEmpty()) {
// todo +-
return result;
}
return cachedRoots;
}
}
public void acceptNestedData(final Set<NestedCopiesBuilder.MyPointInfo> set) {
myTempSink.add(set);
}
private boolean init() {
synchronized (myMonitor) {
final boolean result = myInitialized;
myInitialized = true;
return result;
}
}
public void realRefresh() {
final SvnVcs17 vcs = SvnVcs17.getInstance(myProject);
final VirtualFile[] roots = myHelper.executeDefended(myProject);
final CopiesApplier copiesApplier = new CopiesApplier();
final CopiesDetector copiesDetector = new CopiesDetector(vcs, copiesApplier, new Getter<NestedCopiesData>() {
public NestedCopiesData get() {
return myTempSink.receive();
}
});
// do not send additional request for nested copies when in init state
copiesDetector.detectCopyRoots(roots, init());
}
private class CopiesApplier {
public void apply(final SvnVcs17 vcs, final List<RootUrlInfo> roots, final List<VirtualFile> lonelyRoots) {
final SvnMapping mapping = new SvnMapping();
mapping.addAll(roots);
mapping.reportLonelyRoots(lonelyRoots);
final SvnMapping groupedMapping = new SvnMapping();
final List<RootUrlInfo> filtered = new ArrayList<RootUrlInfo>();
ForNestedRootChecker.filterOutSuperfluousChildren(vcs, roots, filtered);
groupedMapping.addAll(filtered);
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
synchronized (myMonitor) {
myMapping.copyFrom(mapping);
myMoreRealMapping.copyFrom(groupedMapping);
}
}
});
myProject.getMessageBus().syncPublisher(SvnVcs17.ROOTS_RELOADED).run();
}
}
private static class CopiesDetector {
private final SvnVcs17 myVcs;
private final CopiesApplier myApplier;
private final List<VirtualFile> myLonelyRoots;
private final List<RootUrlInfo> myTopRoots;
private final RepositoryRoots myRepositoryRoots;
private final Getter<NestedCopiesData> myGate;
private CopiesDetector(final SvnVcs17 vcs, final CopiesApplier applier, final Getter<NestedCopiesData> gate) {
myVcs = vcs;
myApplier = applier;
myGate = gate;
myTopRoots = new ArrayList<RootUrlInfo>();
myLonelyRoots = new ArrayList<VirtualFile>();
myRepositoryRoots = new RepositoryRoots(myVcs);
}
public void detectCopyRoots(final VirtualFile[] roots, final boolean clearState) {
final Getter<Boolean> cancelGetter = new Getter<Boolean>() {
public Boolean get() {
return myVcs.getProject().isDisposed();
}
};
for (final VirtualFile vcsRoot : roots) {
final List<Real> foundRoots = ForNestedRootChecker.getAllNestedWorkingCopies(vcsRoot, myVcs, false, cancelGetter);
if (foundRoots.isEmpty()) {
myLonelyRoots.add(vcsRoot);
}
// filter out bad(?) items
for (Real foundRoot : foundRoots) {
final SVNURL repoRoot = foundRoot.getInfo().getRepositoryRootURL();
if (repoRoot == null) {
LOG.info("Error: cannot find repository URL for versioned folder: " + foundRoot.getFile().getPath());
} else {
myRepositoryRoots.register(repoRoot);
myTopRoots.add(new RootUrlInfo(repoRoot, foundRoot.getInfo().getURL(),
SvnFormatSelector.getWorkingCopyFormat(new File(foundRoot.getFile().getPath())), foundRoot.getFile(), vcsRoot));
}
}
}
if (! SvnConfiguration17.getInstance(myVcs.getProject()).DETECT_NESTED_COPIES) {
myApplier.apply(myVcs, myTopRoots, myLonelyRoots);
} else {
addNestedRoots(clearState);
}
}
private void addNestedRoots(final boolean clearState) {
final List<VirtualFile> basicVfRoots = ObjectsConvertor.convert(myTopRoots, new Convertor<RootUrlInfo, VirtualFile>() {
public VirtualFile convert(final RootUrlInfo real) {
return real.getVirtualFile();
}
});
final ChangeListManager clManager = ChangeListManager.getInstance(myVcs.getProject());
if (clearState) {
// clear what was reported before (could be for currently-not-existing roots)
myGate.get();
}
clManager.invokeAfterUpdate(new Runnable() {
public void run() {
final List<RootUrlInfo> nestedRoots = new ArrayList<RootUrlInfo>();
final NestedCopiesData data = myGate.get();
for (NestedCopiesBuilder.MyPointInfo info : data.getSet()) {
if (NestedCopyType.external.equals(info.getType()) || NestedCopyType.switched.equals(info.getType())) {
final File infoFile = new File(info.getFile().getPath());
boolean copyFound = false;
for (RootUrlInfo topRoot : myTopRoots) {
if (topRoot.getIoFile().equals(infoFile)) {
topRoot.setType(info.getType());
copyFound = true;
break;
}
}
if (copyFound) {
continue;
}
try {
final SVNStatus svnStatus = SvnUtil.getStatus(myVcs, infoFile);
if (svnStatus.getURL() == null) continue;
info.setUrl(svnStatus.getURL());
info.setFormat(WorkingCopyFormat.getInstance(svnStatus.getWorkingCopyFormat()));
}
catch (Exception e) {
continue;
}
}
for (RootUrlInfo topRoot : myTopRoots) {
if (VfsUtil.isAncestor(topRoot.getVirtualFile(), info.getFile(), true)) {
final SVNURL repoRoot = myRepositoryRoots.ask(info.getUrl());
if (repoRoot != null) {
final RootUrlInfo rootInfo = new RootUrlInfo(repoRoot, info.getUrl(), info.getFormat(), info.getFile(), topRoot.getRoot());
rootInfo.setType(info.getType());
nestedRoots.add(rootInfo);
}
break;
}
}
}
// check those top roots which ARE externals, but that was not detected due to they itself were the status request target
//new SvnNestedTypeRechecker(myVcs.getProject(), myTopRoots).run();
myTopRoots.addAll(nestedRoots);
myApplier.apply(myVcs, myTopRoots, myLonelyRoots);
}
}, InvokeAfterUpdateMode.SILENT_CALLBACK_POOLED, null, new Consumer<VcsDirtyScopeManager>() {
public void consume(VcsDirtyScopeManager vcsDirtyScopeManager) {
if (clearState) {
vcsDirtyScopeManager.filesDirty(null, basicVfRoots);
}
}
}, null);
}
}
private static SVNStatus getExternalItemStatus(final SvnVcs17 vcs, final File file) {
final SVNStatusClient statusClient = vcs.createStatusClient();
try {
if (file.isDirectory()) {
return statusClient.doStatus(file, false);
} else {
final File parent = file.getParentFile();
if (parent != null) {
statusClient.setFilesProvider(new ISVNStatusFileProvider() {
public Map getChildrenFiles(File parent) {
return Collections.singletonMap(file.getAbsolutePath(), file);
}
});
final Ref<SVNStatus> refStatus = new Ref<SVNStatus>();
statusClient.doStatus(parent, SVNRevision.WORKING, SVNDepth.FILES, false, true, false, false, new ISVNStatusHandler() {
public void handleStatus(final SVNStatus status) throws SVNException {
if (file.equals(status.getFile())) {
refStatus.set(status);
}
}
}, null);
return refStatus.get();
}
}
}
catch (SVNException e) {
//
}
return null;
}
private static class RepositoryRoots {
private final SvnVcs17 myVcs;
private final Set<SVNURL> myRoots;
private RepositoryRoots(final SvnVcs17 vcs) {
myVcs = vcs;
myRoots = new HashSet<SVNURL>();
}
public void register(final SVNURL url) {
myRoots.add(url);
}
public SVNURL ask(final SVNURL url) {
for (SVNURL root : myRoots) {
if (root.equals(SVNURLUtil.getCommonURLAncestor(root, url))) {
return root;
}
}
final SVNURL newUrl = SvnUtil.getRepositoryRoot(myVcs, url);
if (newUrl != null) {
myRoots.add(newUrl);
return newUrl;
}
return null;
}
}
private static class RepoInfo {
private final SVNURL myUrl;
private final boolean myRepoSupportsMergeinfo;
private RepoInfo(final SVNURL url, final boolean repoSupportsMergeinfo) {
myRepoSupportsMergeinfo = repoSupportsMergeinfo;
myUrl = url;
}
public boolean isRepoSupportsMergeinfo() {
return myRepoSupportsMergeinfo;
}
public SVNURL getUrl() {
return myUrl;
}
}
@Nullable
public String getUrlRootForUrl(final String currentUrl) {
for (String url : myMapping.getUrls()) {
if (SVNPathUtil.isAncestor(url, currentUrl)) {
return url;
}
}
return null;
}
@Nullable
public String getRootForPath(final File currentPath) {
String convertedPath = currentPath.getAbsolutePath();
convertedPath = (currentPath.isDirectory() && (! convertedPath.endsWith(File.separator))) ? convertedPath + File.separator :
convertedPath;
synchronized (myMonitor) {
return myMapping.getRootForPath(convertedPath);
}
}
public VirtualFile[] getNotFilteredRoots() {
return myHelper.executeDefended(myProject);
}
public boolean isEmpty() {
synchronized (myMonitor) {
return myMapping.isEmpty();
}
}
public SvnMappingSavedPart getState() {
final SvnMappingSavedPart result = new SvnMappingSavedPart();
final SvnMapping mapping = new SvnMapping();
final SvnMapping realMapping = new SvnMapping();
synchronized (myMonitor) {
mapping.copyFrom(myMapping);
realMapping.copyFrom(myMoreRealMapping);
}
for (RootUrlInfo info : mapping.getAllCopies()) {
result.add(convert(info));
}
for (RootUrlInfo info : realMapping.getAllCopies()) {
result.addReal(convert(info));
}
return result;
}
private SvnCopyRootSimple convert(final RootUrlInfo info) {
final SvnCopyRootSimple copy = new SvnCopyRootSimple();
copy.myVcsRoot = FileUtil.toSystemDependentName(info.getRoot().getPath());
copy.myCopyRoot = info.getIoFile().getAbsolutePath();
return copy;
}
public void loadState(final SvnMappingSavedPart state) {
((ProjectLevelVcsManagerImpl) ProjectLevelVcsManager.getInstance(myProject)).addInitializationRequest(
VcsInitObject.AFTER_COMMON, new DumbAwareRunnable() {
public void run() {
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
public void run() {
final SvnMapping mapping = new SvnMapping();
final SvnMapping realMapping = new SvnMapping();
try {
fillMapping(mapping, state.getMappingRoots());
fillMapping(realMapping, state.getMoreRealMappingRoots());
} catch (ProcessCanceledException e) {
throw e;
} catch (Throwable t) {
LOG.info(t);
return;
}
synchronized (myMonitor) {
myMapping.copyFrom(mapping);
myMoreRealMapping.copyFrom(realMapping);
}
}
});
}
});
}
private void fillMapping(final SvnMapping mapping, final List<SvnCopyRootSimple> list) {
final LocalFileSystem lfs = LocalFileSystem.getInstance();
for (SvnCopyRootSimple simple : list) {
final VirtualFile copyRoot = lfs.findFileByIoFile(new File(simple.myCopyRoot));
final VirtualFile vcsRoot = lfs.findFileByIoFile(new File(simple.myVcsRoot));
if (copyRoot == null || vcsRoot == null) continue;
final SvnVcs17 vcs = SvnVcs17.getInstance(myProject);
final SVNInfo svnInfo = vcs.getInfo(copyRoot);
if ((svnInfo == null) || (svnInfo.getRepositoryRootURL() == null)) continue;
final RootUrlInfo info = new RootUrlInfo(svnInfo.getRepositoryRootURL(), svnInfo.getURL(),
SvnFormatSelector.getWorkingCopyFormat(svnInfo.getFile()), copyRoot, vcsRoot);
mapping.add(info);
}
}
public void projectOpened() {
}
public void projectClosed() {
}
@NotNull
public String getComponentName() {
return "SvnFileUrlMappingImpl17";
}
public void initComponent() {
}
public void disposeComponent() {
}
}
@@ -0,0 +1,165 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.util.WaitForProgressToShow;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn17.dialogs.UpgradeFormatDialog;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.internal.wc.admin.ISVNAdminAreaFactorySelector;
import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminAreaFactory;
import org.tmatesoft.svn.core.internal.wc2.SvnWcGeneration;
import org.tmatesoft.svn.core.wc2.SvnOperationFactory;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
public class SvnFormatSelector implements ISVNAdminAreaFactorySelector {
public Collection getEnabledFactories(File path, Collection factories, boolean writeAccess) throws SVNException {
if (ApplicationManager.getApplication().isUnitTestMode()) {
return factories;
}
if (! writeAccess) {
return factories;
}
final WorkingCopyFormat format = getWorkingCopyFormat(path);
Collection result = format2Factories(format, factories);
if (result == null) {
final WorkingCopyFormat presetFormat = SvnWorkingCopyFormatHolder.getPresetFormat();
if (presetFormat != null) {
result = format2Factories(presetFormat, factories);
}
}
if (result == null) {
throw new SVNException(SVNErrorMessage.create(SVNErrorCode.WC_NOT_DIRECTORY));
}
return result;
}
@Nullable
static Collection format2Factories(final WorkingCopyFormat format, final Collection factories) {
if (WorkingCopyFormat.ONE_DOT_SIX.equals(format)) {
return factories;
} else if (WorkingCopyFormat.ONE_DOT_FIVE.equals(format)) {
return factoriesFor15(factories);
} else if (WorkingCopyFormat.ONE_DOT_FOUR.equals(format)) {
return factoriesFor14(factories);
} else if (WorkingCopyFormat.ONE_DOT_THREE.equals(format)) {
return factoriesFor13(factories);
}
return null;
}
private static Collection<SVNAdminAreaFactory> factoriesFor13(final Collection factories) {
for (Iterator iterator = factories.iterator(); iterator.hasNext();) {
final SVNAdminAreaFactory factory = (SVNAdminAreaFactory) iterator.next();
final int supportedVersion = factory.getSupportedVersion();
if (WorkingCopyFormat.ONE_DOT_THREE.getFormat() == supportedVersion) {
return Collections.singletonList(factory);
}
}
return Collections.emptyList();
}
private static Collection<SVNAdminAreaFactory> factoriesFor14(final Collection factories) {
final Collection<SVNAdminAreaFactory> result = new ArrayList<SVNAdminAreaFactory>(2);
for (Iterator iterator = factories.iterator(); iterator.hasNext();) {
final SVNAdminAreaFactory factory = (SVNAdminAreaFactory) iterator.next();
final int supportedVersion = factory.getSupportedVersion();
if ((WorkingCopyFormat.ONE_DOT_FOUR.getFormat() == supportedVersion) ||
(WorkingCopyFormat.ONE_DOT_THREE.getFormat() == supportedVersion)) {
result.add(factory);
}
}
return result;
}
private static Collection<SVNAdminAreaFactory> factoriesFor15(final Collection factories) {
final Collection<SVNAdminAreaFactory> result = new ArrayList<SVNAdminAreaFactory>(2);
for (Iterator iterator = factories.iterator(); iterator.hasNext();) {
final SVNAdminAreaFactory factory = (SVNAdminAreaFactory) iterator.next();
final int supportedVersion = factory.getSupportedVersion();
if ((WorkingCopyFormat.ONE_DOT_FOUR.getFormat() == supportedVersion) ||
(WorkingCopyFormat.ONE_DOT_THREE.getFormat() == supportedVersion) ||
(WorkingCopyFormat.ONE_DOT_FIVE.getFormat() == supportedVersion)) {
result.add(factory);
}
}
return result;
}
public static String showUpgradeDialog(final File path, final Project project, final boolean display13format, final String mode,
@NotNull final Ref<Boolean> wasOk) {
assert ! ApplicationManager.getApplication().isUnitTestMode();
final String[] newMode = new String[] {mode};
WaitForProgressToShow.runOrInvokeAndWaitAboveProgress(new Runnable() {
public void run() {
wasOk.set(displayUpgradeDialog(project, path, display13format, newMode));
}
});
ApplicationManager.getApplication().getMessageBus().syncPublisher(SvnVcs17.WC_CONVERTED).run();
return newMode[0];
}
public static WorkingCopyFormat getWorkingCopyFormat(final File path) {
try {
final SvnWcGeneration svnWcGeneration = SvnOperationFactory.detectWcGeneration(path, true);
if (SvnWcGeneration.V17.equals(svnWcGeneration)) return WorkingCopyFormat.ONE_DOT_SEVEN;
}
catch (SVNException e) {
//
}
int format = 0;
// it is enough to check parent and this.
try {
format = SVNAdminAreaFactory.checkWC(path, false);
} catch (SVNException e) {
//
}
try {
if (format == 0 && path.getParentFile() != null) {
format = SVNAdminAreaFactory.checkWC(path.getParentFile(), false);
}
} catch (SVNException e) {
//
}
return WorkingCopyFormat.getInstance(format);
}
private static boolean displayUpgradeDialog(Project project, File path, final boolean dispay13format, String[] newMode) {
UpgradeFormatDialog dialog = new UpgradeFormatDialog(project, path, false);
dialog.setData(dispay13format, newMode[0]);
dialog.show();
if (dialog.isOK()) {
newMode[0] = dialog.getUpgradeMode();
}
return dialog.isOK();
}
}
@@ -0,0 +1,28 @@
/*
* Copyright 2000-2009 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.svn17;
public class SvnHttpAuthMethodsDefaultChecker {
private static final String AUTH_METHODS_PROPERTY = "svnkit.http.methods";
private static final String OLD_AUTH_METHODS_PROPERTY = "javasvn.http.methods";
public static void check() {
final String priorities = System.getProperty(AUTH_METHODS_PROPERTY, System.getProperty(OLD_AUTH_METHODS_PROPERTY));
if (priorities == null) {
System.setProperty(AUTH_METHODS_PROPERTY, "Basic,Digest,NTLM");
}
}
}
@@ -0,0 +1,152 @@
/*
* Copyright 2000-2011 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.svn17;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.persistent.SmallMapSerializer;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn17.branchConfig.InfoStorage;
import org.jetbrains.idea.svn17.branchConfig.SvnBranchConfigurationNew;
import org.jetbrains.idea.svn17.integrate.SvnBranchItem;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.File;
import java.io.IOException;
import java.util.*;
/**
* Created by IntelliJ IDEA.
* User: Irina.Chernushina
* Date: 8/24/11
* Time: 1:21 PM
*/
public class SvnLoadedBrachesStorage17 {
private final Object myLock;
private SmallMapSerializer<String, Map<String, Collection<SvnBranchItem>>> myState;
private final File myFile;
private final Project myProject;
public SvnLoadedBrachesStorage17(final Project project) {
myProject = project;
final File vcsFile = new File(PathManager.getSystemPath(), "vcs");
File file = new File(vcsFile, "svn_branches17");
file.mkdirs();
myFile = new File(file, project.getLocationHash());
myLock = new Object();
}
@Nullable
public Collection<SvnBranchItem> get(final String url) {
synchronized (myLock) {
if (myState == null) return null;
Map<String, Collection<SvnBranchItem>> map = myState.get("");
return map == null ? null : map.get(SvnBranchConfigurationNew.ensureEndSlash(url));
}
}
public void put(final String url, final Collection<SvnBranchItem> items) {
synchronized (myLock) {
if (myState == null) return;
Map<String, Collection<SvnBranchItem>> map = myState.get("");
if (map == null) {
map = new HashMap<String, Collection<SvnBranchItem>>();
myState.put("", map);
}
map.put(url, items);
}
}
public void activate() {
synchronized (myLock) {
myState = new SmallMapSerializer<String, Map<String, Collection<SvnBranchItem>>>(myFile, new EnumeratorStringDescriptor(), createExternalizer());
}
}
public void deactivate() {
SvnBranchConfigurationManager17 manager = SvnBranchConfigurationManager17.getInstance(myProject);
Map<VirtualFile,SvnBranchConfigurationNew> mapCopy = manager.getSvnBranchConfigManager().getMapCopy();
for (Map.Entry<VirtualFile, SvnBranchConfigurationNew> entry : mapCopy.entrySet()) {
Map<String,InfoStorage<List<SvnBranchItem>>> branchMap = entry.getValue().getBranchMap();
for (Map.Entry<String, InfoStorage<List<SvnBranchItem>>> storageEntry : branchMap.entrySet()) {
put(storageEntry.getKey(), storageEntry.getValue().getValue());
}
}
synchronized (myLock) {
myState.force();
myState = null;
}
}
private DataExternalizer<Map<String, Collection<SvnBranchItem>>> createExternalizer() {
return new DataExternalizer<Map<String, Collection<SvnBranchItem>>>() {
@Override
public void save(DataOutput out, Map<String, Collection<SvnBranchItem>> value) throws IOException {
out.writeInt(value.size());
ArrayList<String> keys = new ArrayList<String>(value.keySet());
Collections.sort(keys);
for (String key : keys) {
out.writeUTF(key);
List<SvnBranchItem> list = new ArrayList<SvnBranchItem>(value.get(key));
Collections.sort(list, SerializationComparator.getInstance());
out.writeInt(list.size());
for (SvnBranchItem item : list) {
out.writeUTF(item.getUrl());
out.writeLong(item.getCreationDateMillis());
out.writeLong(item.getRevision());
}
}
}
@Override
public Map<String, Collection<SvnBranchItem>> read(DataInput in) throws IOException {
final HashMap<String, Collection<SvnBranchItem>> map = new HashMap<String, Collection<SvnBranchItem>>();
int mapSize = in.readInt();
for (int i = 0; i < mapSize; i++) {
final String key = in.readUTF();
final int size = in.readInt();
final ArrayList<SvnBranchItem> list = new ArrayList<SvnBranchItem>(size);
for (int j = 0; j < size; j++) {
String url = in.readUTF();
long creation = in.readLong();
long revision = in.readLong();
list.add(new SvnBranchItem(url, new Date(creation), revision));
}
map.put(key, list);
}
return map;
}
};
}
private static class SerializationComparator implements Comparator<SvnBranchItem> {
private final static SerializationComparator ourInstance = new SerializationComparator();
public static SerializationComparator getInstance() {
return ourInstance;
}
@Override
public int compare(SvnBranchItem o1, SvnBranchItem o2) {
return o1.getUrl().compareToIgnoreCase(o2.getUrl());
}
}
}
@@ -0,0 +1,26 @@
/*
* Copyright 2000-2011 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.svn17;
/**
* Created by IntelliJ IDEA.
* User: Irina.Chernushina
* Date: 8/24/11
* Time: 1:07 PM
* To change this template use File | Settings | File Templates.
*/
public class SvnLoadedBranches {
}
@@ -0,0 +1,49 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.changes.LocallyDeletedChange;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
public class SvnLocallyDeletedChange extends LocallyDeletedChange {
private final ConflictState myConflictState;
public SvnLocallyDeletedChange(@NotNull final FilePath path, final ConflictState state) {
super(path);
myConflictState = state;
}
@Override
public Icon getAddIcon() {
return myConflictState.getIcon();
}
@Override
public String getDescription() {
final String description = myConflictState.getDescription();
if (description != null) {
return SvnBundle.message("svn.changeview.locally.deleted.item.in.conflict.text", description);
}
return description;
}
public ConflictState getConflictState() {
return myConflictState;
}
}
@@ -0,0 +1,148 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.*;
public class SvnMapping {
private final List<VirtualFile> myLonelyRoots;
private final TreeMap<String, RootUrlInfo> myFile2UrlMap;
private final Map<String, RootUrlInfo> myUrl2FileMap;
private boolean myRootsDifferFromSettings;
// no additional info. for caching only (convert roots)
private List<VirtualFile> myPreCalculatedUnderVcsRoots;
public SvnMapping() {
myFile2UrlMap = new TreeMap<String, RootUrlInfo>(FilePathsComparator.getInstance());
myUrl2FileMap = new HashMap<String, RootUrlInfo>();
myLonelyRoots = new ArrayList<VirtualFile>();
myPreCalculatedUnderVcsRoots = null;
myRootsDifferFromSettings = false;
}
public void copyFrom(final SvnMapping other) {
myFile2UrlMap.clear();
myUrl2FileMap.clear();
myLonelyRoots.clear();
myFile2UrlMap.putAll(other.myFile2UrlMap);
myUrl2FileMap.putAll(other.myUrl2FileMap);
myLonelyRoots.addAll(other.myLonelyRoots);
myRootsDifferFromSettings = other.myRootsDifferFromSettings;
myPreCalculatedUnderVcsRoots = null;
}
public void addAll(final Collection<RootUrlInfo> roots) {
for (RootUrlInfo rootInfo : roots) {
final VirtualFile file = rootInfo.getVirtualFile();
final File ioFile = new File(file.getPath());
myRootsDifferFromSettings |= ! rootInfo.getRoot().getPath().equals(file.getPath());
myFile2UrlMap.put(ioFile.getAbsolutePath(), rootInfo);
myUrl2FileMap.put(rootInfo.getAbsoluteUrl(), rootInfo);
}
}
public void add(final RootUrlInfo rootInfo) {
final VirtualFile file = rootInfo.getVirtualFile();
final File ioFile = new File(file.getPath());
myRootsDifferFromSettings |= ! rootInfo.getRoot().getPath().equals(file.getPath());
myFile2UrlMap.put(ioFile.getAbsolutePath(), rootInfo);
myUrl2FileMap.put(rootInfo.getAbsoluteUrl(), rootInfo);
}
public List<VirtualFile> getUnderVcsRoots() {
if (myPreCalculatedUnderVcsRoots == null) {
myPreCalculatedUnderVcsRoots = new ArrayList<VirtualFile>();
for (RootUrlInfo info : myFile2UrlMap.values()) {
myPreCalculatedUnderVcsRoots.add(info.getVirtualFile());
}
}
return myPreCalculatedUnderVcsRoots;
}
public List<RootUrlInfo> getAllCopies() {
return new ArrayList<RootUrlInfo>(myFile2UrlMap.values());
}
@Nullable
public String getRootForPath(final String path) {
String floor = myFile2UrlMap.floorKey(path);
if (floor == null) return null;
NavigableMap<String, RootUrlInfo> head = myFile2UrlMap.headMap(floor, true);
for (String root : head.descendingKeySet()) {
if (startsWithForPaths(root, path)) return root;
}
return null;
}
public Collection<String> getUrls() {
return myUrl2FileMap.keySet();
}
public RootUrlInfo byFile(final String path) {
return myFile2UrlMap.get(path);
}
public RootUrlInfo byUrl(final String url) {
return myUrl2FileMap.get(url);
}
public boolean isEmpty() {
return myUrl2FileMap.isEmpty();
}
public boolean isRootsDifferFromSettings() {
return myRootsDifferFromSettings;
}
public void reportLonelyRoots(final Collection<VirtualFile> roots) {
myLonelyRoots.addAll(roots);
}
public List<VirtualFile> getLonelyRoots() {
return myLonelyRoots;
}
private static class FilePathsComparator implements Comparator<String> {
private final static FilePathsComparator ourInstance = new FilePathsComparator();
public static FilePathsComparator getInstance() {
return ourInstance;
}
@Override
public int compare(String o1, String o2) {
return FileUtil.comparePaths(o1, o2);
}
}
private boolean startsWithForPaths(final String parent, final String child) {
return SystemInfo.isFileSystemCaseSensitive ? child.startsWith(parent) : (StringUtil.startsWithIgnoreCase(child, parent));
}
}
@@ -0,0 +1,45 @@
/*
* Copyright 2000-2009 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.svn17;
import java.util.ArrayList;
import java.util.List;
public class SvnMappingSavedPart {
public List<SvnCopyRootSimple> myMappingRoots;
public List<SvnCopyRootSimple> myMoreRealMappingRoots;
public SvnMappingSavedPart() {
myMappingRoots = new ArrayList<SvnCopyRootSimple>();
myMoreRealMappingRoots = new ArrayList<SvnCopyRootSimple>();
}
public void add(final SvnCopyRootSimple copy) {
myMappingRoots.add(copy);
}
public void addReal(final SvnCopyRootSimple copy) {
myMoreRealMappingRoots.add(copy);
}
public List<SvnCopyRootSimple> getMappingRoots() {
return myMappingRoots;
}
public List<SvnCopyRootSimple> getMoreRealMappingRoots() {
return myMoreRealMappingRoots;
}
}
@@ -0,0 +1,183 @@
/*
* Copyright 2000-2010 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.svn17;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.AbstractFilterChildren;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.MultiMap;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.wc.*;
import java.io.File;
import java.util.*;
// referencies of RootUrlInfo will be kept; so type flag will be raised right in original object
public class SvnNestedTypeRechecker {
private final static Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnNestedTypeRechecker");
private final MultiMap<VirtualFile, RootUrlInfo> myRoots;
private final Map<String, RootUrlInfo> mySourceItems;
private Map<String, Map<String, File>> myTreeTravellingMap;
private final Project myProject;
public SvnNestedTypeRechecker(final Project project, final List<RootUrlInfo> roots) {
myProject = project;
final NestedGatherer nestedGatherer = new NestedGatherer();
nestedGatherer.doFilter(new ArrayList<RootUrlInfo>(roots));
myRoots = nestedGatherer.getNested();
mySourceItems = new HashMap<String, RootUrlInfo>();
for (VirtualFile root : myRoots.keySet()) {
final Collection<RootUrlInfo> items = myRoots.get(root);
for (RootUrlInfo item : items) {
mySourceItems.put(FileUtil.nameToCompare(item.getVirtualFile().getPath()), item);
}
}
}
private void initTravellingMap() {
myTreeTravellingMap = new HashMap<String, Map<String, File>>();
final MultiMap<VirtualFile, File> tmpMap = new MultiMap<VirtualFile, File>();
for (VirtualFile parent : myRoots.keySet()) {
final Collection<RootUrlInfo> children = myRoots.get(parent);
for (RootUrlInfo child : children) {
VirtualFile current = child.getVirtualFile();
while (! current.equals(parent)) {
tmpMap.putValue(current.getParent(), new File(current.getPath()));
current = current.getParent();
if (current.getParent() == null) break; //+-
}
}
}
for (VirtualFile dir : tmpMap.keySet()) {
final Map<String, File> currMap = new HashMap<String, File>();
final Collection<File> coll = tmpMap.get(dir);
for (File file : coll) {
currMap.put(file.getAbsolutePath(), file);
}
myTreeTravellingMap.put(FileUtil.nameToCompare(dir.getPath()), currMap);
}
}
public void run() {
if (! myRoots.isEmpty()) {
initTravellingMap();
final ISVNStatusFileProvider fileProvider = new ISVNStatusFileProvider() {
public Map getChildrenFiles(File parent) {
final Map<String, File> children = myTreeTravellingMap.get(FileUtil.nameToCompare(parent.getAbsolutePath()));
return children == null ? Collections.emptyMap() : children;
}
};
final SvnVcs17 vcs = SvnVcs17.getInstance(myProject);
final SVNStatusClient client = vcs.createStatusClient();
client.setFilesProvider(fileProvider);
for (VirtualFile parent : myRoots.keySet()) {
try {
client.doStatus(new File(parent.getPath()), SVNRevision.WORKING, SVNDepth.INFINITY, false, true, true, false, new ISVNStatusHandler() {
public void handleStatus(final SVNStatus status) throws SVNException {
final boolean switched = status.isSwitched();
final boolean externals = SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_EXTERNAL);
if (switched || externals) {
final RootUrlInfo urlInfo = mySourceItems.get(FileUtil.nameToCompare(status.getFile().getAbsolutePath()));
if (urlInfo != null) {
if (switched) {
urlInfo.setType(NestedCopyType.switched);
} else {
urlInfo.setType(NestedCopyType.external);
}
}
}
}
}, null);
}
catch (SVNException e) {
LOG.debug(e);
}
}
}
}
/*private static SVNStatus getExternalItemStatus(final SvnVcs vcs, final File file) {
final SVNStatusClient statusClient = vcs.createStatusClient();
try {
if (file.isDirectory()) {
return statusClient.doStatus(file, false);
} else {
final File parent = file.getParentFile();
if (parent != null) {
statusClient.setFilesProvider(new ISVNStatusFileProvider() {
public Map getChildrenFiles(File parent) {
return Collections.singletonMap(file.getAbsolutePath(), file);
}
});
final Ref<SVNStatus> refStatus = new Ref<SVNStatus>();
statusClient.doStatus(parent, SVNRevision.WORKING, SVNDepth.FILES, false, true, false, false, new ISVNStatusHandler() {
public void handleStatus(final SVNStatus status) throws SVNException {
if (file.equals(status.getFile())) {
refStatus.set(status);
}
}
}, null);
return refStatus.get();
}
}
}
catch (SVNException e) {
//
}
return null;
}*/
private static class NestedGatherer extends AbstractFilterChildren<RootUrlInfo> {
private final MultiMap<VirtualFile, RootUrlInfo> myNested;
private NestedGatherer() {
myNested = new MultiMap<VirtualFile, RootUrlInfo>();
}
@Override
protected void sortAscending(List<RootUrlInfo> rootUrlInfos) {
Collections.sort(rootUrlInfos, new Comparator<RootUrlInfo>() {
public int compare(final RootUrlInfo r1, final RootUrlInfo r2) {
return new Integer(r1.getVirtualFile().getPath().length()).compareTo(r2.getVirtualFile().getPath().length());
}
});
}
@Override
protected boolean isAncestor(RootUrlInfo parent, RootUrlInfo child) {
final boolean result = VfsUtil.isAncestor(parent.getVirtualFile(), child.getVirtualFile(), true);
if (result) {
myNested.putValue(parent.getVirtualFile(), child);
}
return result;
}
public MultiMap<VirtualFile, RootUrlInfo> getNested() {
return myNested;
}
}
}
@@ -0,0 +1,38 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import org.tmatesoft.svn.core.SVNCancelException;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.SVNEvent;
/**
* @author yole
*/
public class SvnProgressCanceller implements ISVNEventHandler {
public void checkCancelled() throws SVNCancelException {
final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
if (indicator != null && indicator.isCanceled()) {
throw new SVNCancelException();
}
}
public void handleEvent(final SVNEvent event, final double progress) throws SVNException {
}
}
@@ -0,0 +1,27 @@
/*
* Copyright 2000-2009 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.svn17;
@SuppressWarnings({"HardCodedStringLiteral"})
public interface SvnPropertyKeys {
String SVN_EOL_STYLE = "svn:eol-style";
String SVN_KEYWORDS = "svn:keywords";
String SVN_NEEDS_LOCK = "svn:needs-lock";
String SVN_MIME_TYPE = "svn:mime-type";
String SVN_EXECUTABLE = "svn:executable";
String SVN_IGNORE = "svn:ignore";
String SVN_EXTERNALS = "svn:externals";
}
@@ -0,0 +1,52 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.impl.GenericNotifierImpl;
import org.jetbrains.annotations.NotNull;
// todo be done
// todo : two kinds. positive: either into common proxy, or into system properties
public class SvnProxyAuthenticationNotifier extends GenericNotifierImpl<String, String> {
public SvnProxyAuthenticationNotifier(Project project, @NotNull String groupId, @NotNull String title, @NotNull NotificationType type) {
super(project, groupId, title, type);
}
@Override
protected boolean ask(String obj, String description) {
return false; //To change body of implemented methods use File | Settings | File Templates.
}
@NotNull
@Override
protected String getKey(String obj) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@NotNull
@Override
protected String getNotificationContent(String obj) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@NotNull
@Override
protected String getToString(String obj) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
}
@@ -0,0 +1,164 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.vcsUtil.VcsUtil;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.wc.ISVNStatusHandler;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNStatus;
import org.tmatesoft.svn.core.wc.SVNStatusClient;
import java.io.File;
import java.util.LinkedList;
public class SvnRecursiveStatusWalker {
private final StatusWalkerPartner myPartner;
private final StatusReceiver myReceiver;
private final LinkedList<MyItem> myQueue;
private final MyHandler myHandler;
public SvnRecursiveStatusWalker(final StatusReceiver receiver, final StatusWalkerPartner partner) {
myReceiver = receiver;
myPartner = partner;
myQueue = new LinkedList<MyItem>();
myHandler = new MyHandler();
}
public void go(final FilePath rootPath, final SVNDepth depth) throws SVNException {
final MyItem root = new MyItem(rootPath, depth, myPartner.createStatusClient(), false);
myQueue.add(root);
while (! myQueue.isEmpty()) {
myPartner.checkCanceled();
final MyItem item = myQueue.removeFirst();
final FilePath path = item.getPath();
final File ioFile = path.getIOFile();
if (path.isDirectory()) {
myHandler.setCurrentItem(item);
try {
item.getClient().doStatus(ioFile, SVNRevision.WORKING, item.getDepth(), false, false, true, false, myHandler, null);
}
catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_NOT_DIRECTORY) {
final VirtualFile virtualFile = path.getVirtualFile();
if (virtualFile != null) {
if (myPartner.isExcluded(virtualFile)) return;
// self is unversioned
myReceiver.processUnversioned(virtualFile);
processRecursively(virtualFile, item.getDepth());
}
} else {
throw e;
}
}
} else {
if (item.isIsInnerCopyRoot()) {
// this means that the status of parent directory had already been checked and is unversioned;
// to avoid SVN exception on status query to unversioned directory; and since we already know then that the file
// status is "unversioned" -> just register the unversioned file
if (path.getVirtualFile() != null) {
myReceiver.processUnversioned(path.getVirtualFile());
}
} else {
final SVNStatus status = item.getClient().doStatus(ioFile, false, false);
myReceiver.process(path, status, false);
}
}
}
}
private static class MyItem {
private final FilePath myPath;
private final SVNDepth myDepth;
private final SVNStatusClient myClient;
private final boolean myIsInnerCopyRoot;
private MyItem(FilePath path, SVNDepth depth, SVNStatusClient client, boolean isInnerCopyRoot) {
myPath = path;
myDepth = depth;
myClient = client;
myIsInnerCopyRoot = isInnerCopyRoot;
}
public FilePath getPath() {
return myPath;
}
public SVNDepth getDepth() {
return myDepth;
}
public SVNStatusClient getClient() {
return myClient;
}
public boolean isIsInnerCopyRoot() {
return myIsInnerCopyRoot;
}
}
private void processRecursively(final VirtualFile vFile, final SVNDepth prevDepth) {
if (SVNDepth.EMPTY.equals(prevDepth)) return;
if (myPartner.isIgnoredIdeaLevel(vFile)) {
myReceiver.processIgnored(vFile);
return;
}
final SVNDepth newDepth = SVNDepth.INFINITY.equals(prevDepth) ? SVNDepth.INFINITY : SVNDepth.EMPTY;
final SVNStatusClient childClient = myPartner.createStatusClient();
final VirtualFile[] children = vFile.getChildren();
for (VirtualFile child : children) {
final FilePath filePath = VcsUtil.getFilePath(child.getPath(), child.isDirectory());
// recursiveness is used ONLY for search of working copies that have unversioned files above
final MyItem childItem = new MyItem(filePath, newDepth, childClient, true);
myQueue.add(childItem);
}
}
private class MyHandler implements ISVNStatusHandler {
private MyItem myCurrentItem;
public void setCurrentItem(MyItem currentItem) {
myCurrentItem = currentItem;
}
public void handleStatus(final SVNStatus status) throws SVNException {
myPartner.checkCanceled();
final FilePath path = VcsUtil.getFilePath(status.getFile(), status.getKind().equals(SVNNodeKind.DIR));
final VirtualFile vFile = path.getVirtualFile();
if ((vFile != null) && myPartner.isExcluded(vFile)) return;
if ((vFile != null) && (SvnVcs17.svnStatusIsUnversioned(status))) {
myReceiver.processUnversioned(vFile);
if (vFile.isDirectory()) {
processRecursively(vFile, myCurrentItem.getDepth());
}
} else {
myReceiver.process(path, status, myCurrentItem.isIsInnerCopyRoot());
}
}
}
}
@@ -0,0 +1,79 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import org.jetbrains.annotations.NotNull;
import org.tmatesoft.svn.core.wc.SVNRevision;
/**
* @author alex
*/
public class SvnRevisionNumber implements VcsRevisionNumber {
@NotNull
private final SVNRevision myRevision;
public SvnRevisionNumber(@NotNull SVNRevision revision) {
myRevision = revision;
}
public String asString() {
return myRevision.toString();
}
public int compareTo(VcsRevisionNumber vcsRevisionNumber) {
if (vcsRevisionNumber == null || vcsRevisionNumber.getClass() != SvnRevisionNumber.class) {
return -1;
}
SVNRevision rev = ((SvnRevisionNumber)vcsRevisionNumber).myRevision;
if (!myRevision.isValid()) {
return !rev.isValid() ? 0 : -1;
}
if (myRevision.getNumber() >= 0 && rev.getNumber() >= 0) {
return myRevision.getNumber() == rev.getNumber() ? 0 : myRevision.getNumber() > rev.getNumber() ? 1 : -1;
}
else if (myRevision.getDate() != null && rev.getDate() != null) {
return myRevision.getDate().compareTo(rev.getDate());
}
if (myRevision.equals(SVNRevision.HEAD)) {
return rev.equals(SVNRevision.HEAD) ? 0 : 1; // HEAD is greater than a specific rev
}
return myRevision.getID() == rev.getID() ? 0 : myRevision.getID() > rev.getID() ? 1 : -1;
}
@NotNull
public SVNRevision getRevision() {
return myRevision;
}
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final SvnRevisionNumber that = (SvnRevisionNumber)o;
return myRevision.equals(that.myRevision);
}
public int hashCode() {
return myRevision.hashCode();
}
public String toString() {
return myRevision.toString();
}
}
@@ -0,0 +1,131 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.FilePathImpl;
import com.intellij.openapi.vcs.changes.VcsDirtyScope;
import com.intellij.openapi.vfs.VirtualFile;
import java.util.*;
public class SvnScopeZipper implements Runnable {
private final VcsDirtyScope myIn;
private final List<FilePath> myRecursiveDirs;
// instead of set and heavy equals of file path
private final Map<String, MyDirNonRecursive> myNonRecursiveDirs;
// those alone in their immediate parent
private final List<FilePath> mySingleFiles;
public SvnScopeZipper(final VcsDirtyScope in) {
myIn = in;
myRecursiveDirs = new ArrayList<FilePath>(in.getRecursivelyDirtyDirectories());
myNonRecursiveDirs = new HashMap<String, MyDirNonRecursive>();
mySingleFiles = new ArrayList<FilePath>();
}
public void run() {
// if put directly into dirty scope, to access a copy will be created every time
final Set<FilePath> files = myIn.getDirtyFilesNoExpand();
for (FilePath file : files) {
if (file.isDirectory()) {
final VirtualFile vFile = file.getVirtualFile();
// todo take care about this 'not valid' - right now keeping things as they used to be
if (vFile != null && vFile.isValid()) {
final MyDirNonRecursive me = createOrGet(file);
me.setInterestedInParent(true);
for (VirtualFile child : vFile.getChildren()) {
me.add(new FilePathImpl(child));
}
}
} else {
final FilePath parent = file.getParentPath();
if (parent != null) {
final MyDirNonRecursive item = createOrGet(parent);
item.add(file);
}
}
}
// move alone files into a separate list
/*for (Iterator<Map.Entry<String, MyDirNonRecursive>> iterator = myNonRecursiveDirs.entrySet().iterator(); iterator.hasNext();) {
final Map.Entry<String, MyDirNonRecursive> entry = iterator.next();
final MyDirNonRecursive item = entry.getValue();
if ((! item.isInterestedInParent()) && (item.getChildrenList().size() == 1)) {
iterator.remove();
mySingleFiles.add(item.getChildrenList().iterator().next());
}
}*/
}
private MyDirNonRecursive createOrGet(final FilePath parent) {
final String key = getKey(parent);
final MyDirNonRecursive result = myNonRecursiveDirs.get(key);
if (result != null) return result;
final MyDirNonRecursive newItem = new MyDirNonRecursive(parent);
myNonRecursiveDirs.put(key, newItem);
return newItem;
}
public List<FilePath> getRecursiveDirs() {
return myRecursiveDirs;
}
public Map<String, MyDirNonRecursive> getNonRecursiveDirs() {
return myNonRecursiveDirs;
}
public List<FilePath> getSingleFiles() {
return mySingleFiles;
}
static class MyDirNonRecursive {
private boolean myInterestedInParent;
private final FilePath myDir;
// instead of set and heavy equals of file path
private final Map<String, FilePath> myChildren;
private MyDirNonRecursive(final FilePath dir) {
myDir = dir;
myChildren = new HashMap<String, FilePath>();
}
public boolean isInterestedInParent() {
return myInterestedInParent;
}
public void setInterestedInParent(boolean interestedInParent) {
myInterestedInParent = interestedInParent;
}
public void add(final FilePath path) {
myChildren.put(getKey(path), path);
}
public Collection<FilePath> getChildrenList() {
return myChildren.values();
}
public FilePath getDir() {
return myDir;
}
}
public static String getKey(final FilePath path) {
return path.getPresentableUrl();
}
}
@@ -0,0 +1,29 @@
/*
* Copyright 2000-2009 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.svn17;
import org.jetbrains.idea.svn17.config.DefaultProxyGroup;
import org.jetbrains.idea.svn17.config.ProxyGroup;
import java.util.Collection;
import java.util.Map;
public interface SvnServerFileManager {
DefaultProxyGroup getDefaultGroup();
Map<String, ProxyGroup> getGroups();
void updateUserServerFile(final Collection<ProxyGroup> newUserGroups);
}
@@ -0,0 +1,105 @@
/*
* Copyright 2000-2009 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.svn17;
import org.jetbrains.idea.svn17.config.DefaultProxyGroup;
import org.jetbrains.idea.svn17.config.ProxyGroup;
import java.util.*;
public class SvnServerFileManagerImpl implements SvnServerFileManager {
private final DefaultProxyGroup myDefaultGroup;
private final Map<String, ProxyGroup> myGroups;
private final IdeaSVNConfigFile myFile;
public SvnServerFileManagerImpl(final IdeaSVNConfigFile file) {
myFile = file;
myFile.updateGroups();
myGroups = new HashMap<String, ProxyGroup>();
myGroups.putAll(file.getAllGroups());
myDefaultGroup = file.getDefaultGroup();
}
public DefaultProxyGroup getDefaultGroup() {
return (DefaultProxyGroup) myDefaultGroup.copy();
}
public Map<String, ProxyGroup> getGroups() {
// return deep copy
final Map<String, ProxyGroup> result = new HashMap<String, ProxyGroup>(myGroups);
for (Map.Entry<String, ProxyGroup> entry : myGroups.entrySet()) {
result.put(entry.getKey(), entry.getValue().copy());
}
return result;
}
public void updateUserServerFile(final Collection<ProxyGroup> newUserGroups) {
final Map<String, ProxyGroup> oldGroups = getGroups();
for (ProxyGroup proxyGroup : newUserGroups) {
if (proxyGroup.isDefault()) {
processGroup(proxyGroup, getDefaultGroup(), false);
} else {
findAndProcessGroup(proxyGroup, oldGroups);
}
}
for (String groupName : oldGroups.keySet()) {
myFile.deleteGroup(groupName);
}
myFile.save();
}
private void processGroup(final ProxyGroup newGroup, final ProxyGroup oldGroup, final boolean groupWasAdded) {
final String newGroupName = newGroup.getName();
if (groupWasAdded) {
myFile.addGroup(newGroupName, newGroup.getPatterns(), newGroup.getProperties());
} else {
final Map<String, String> oldProperties = oldGroup.getProperties();
final Map<String, String> newProperties = newGroup.getProperties();
final Set<String> deletedProperties = new HashSet<String>();
for (String oldKey : oldProperties.keySet()) {
if (! newProperties.containsKey(oldKey)) {
deletedProperties.add(oldKey);
}
}
final Map<String, String> newOrModifiedProperties = new HashMap<String, String>();
for (Map.Entry<String, String> entry : newProperties.entrySet()) {
final String oldValue = oldProperties.get(entry.getKey());
if ((oldValue == null) || (! oldValue.equals(entry.getValue()))) {
newOrModifiedProperties.put(entry.getKey(), entry.getValue());
}
}
myFile.modifyGroup(newGroupName, newGroup.getPatterns(), deletedProperties, newOrModifiedProperties, newGroup.isDefault());
}
}
private void findAndProcessGroup(final ProxyGroup newGroup, final Map<String, ProxyGroup> oldGroups) {
final String newGroupName = newGroup.getName();
final ProxyGroup oldGroup = oldGroups.get(newGroupName);
final boolean groupWasAdded = (oldGroup == null) && (! newGroup.isDefault());
if (! groupWasAdded) {
// to track deleted
oldGroups.remove(newGroupName);
}
processGroup(newGroup, oldGroup, groupWasAdded);
}
}
@@ -0,0 +1,77 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.vcs.FileStatus;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.wc.SVNStatus;
import org.tmatesoft.svn.core.wc.SVNStatusType;
public class SvnStatusConvertor {
private SvnStatusConvertor() {
}
public static FileStatus convertStatus(final SVNStatus status) throws SVNException {
if (status == null) {
return FileStatus.UNKNOWN;
}
if (SvnVcs17.svnStatusIsUnversioned(status)) {
return FileStatus.UNKNOWN;
}
else if (SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_MISSING)) {
return FileStatus.DELETED_FROM_FS;
}
else if (SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_EXTERNAL)) {
return SvnFileStatus.EXTERNAL;
}
else if (SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_OBSTRUCTED)) {
return SvnFileStatus.OBSTRUCTED;
}
else if (SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_IGNORED)) {
return FileStatus.IGNORED;
}
else if (SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_ADDED)) {
return FileStatus.ADDED;
}
else if (SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_DELETED)) {
return FileStatus.DELETED;
}
else if (SvnVcs17.svnStatusIs(status, SVNStatusType.STATUS_REPLACED)) {
return SvnFileStatus.REPLACED;
}
else if (status.getContentsStatus() == SVNStatusType.STATUS_CONFLICTED ||
status.getPropertiesStatus() == SVNStatusType.STATUS_CONFLICTED) {
if (status.getContentsStatus() == SVNStatusType.STATUS_CONFLICTED &&
status.getPropertiesStatus() == SVNStatusType.STATUS_CONFLICTED) {
return FileStatus.MERGED_WITH_BOTH_CONFLICTS;
} else if (status.getContentsStatus() == SVNStatusType.STATUS_CONFLICTED) {
return FileStatus.MERGED_WITH_CONFLICTS;
}
return FileStatus.MERGED_WITH_PROPERTY_CONFLICTS;
}
else if (status.getContentsStatus() == SVNStatusType.STATUS_MODIFIED ||
status.getPropertiesStatus() == SVNStatusType.STATUS_MODIFIED) {
return FileStatus.MODIFIED;
}
else if (status.isSwitched()) {
return FileStatus.SWITCHED;
}
else if (status.isCopied()) {
return FileStatus.ADDED;
}
return FileStatus.NOT_CHANGED;
}
}
@@ -0,0 +1,52 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.FileStatus;
import com.intellij.openapi.vcs.FileStatusManager;
import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vcs.changes.ChangeListManagerImpl;
import com.intellij.openapi.vfs.VirtualFile;
public class SvnStatusUtil {
private SvnStatusUtil() {
}
public static boolean isUnderControl(final Project project, final VirtualFile file) {
final ChangeListManager clManager = ChangeListManager.getInstance(project);
return (! isIgnoredInAnySense(clManager, file)) && (! clManager.isUnversioned(file));
}
public static boolean isAdded(final Project project, final VirtualFile file) {
final FileStatus status = FileStatusManager.getInstance(project).getStatus(file);
return FileStatus.ADDED.equals(status);
}
public static boolean isExplicitlyLocked(final Project project, final VirtualFile file) {
final ChangeListManager clManager = ChangeListManager.getInstance(project);
return ((ChangeListManagerImpl) clManager).isLogicallyLocked(file);
}
public static boolean isIgnoredInAnySense(final ChangeListManager clManager, final VirtualFile file) {
return clManager.isIgnoredFile(file) || FileStatus.IGNORED.equals(clManager.getStatus(file));
}
public static boolean fileCanBeAdded(final Project project, final VirtualFile file) {
final ChangeListManager clManager = ChangeListManager.getInstance(project);
return isIgnoredInAnySense(clManager, file) || clManager.isUnversioned(file);
}
}
@@ -0,0 +1,691 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.application.ApplicationManager;
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.Ref;
import com.intellij.openapi.vcs.AbstractVcsHelper;
import com.intellij.openapi.vcs.RepositoryLocation;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.*;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.util.ArrayUtil;
import com.intellij.util.NotNullFunction;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn17.branchConfig.SvnBranchConfigurationNew;
import org.jetbrains.idea.svn17.dialogs.LockDialog;
import org.jetbrains.idea.svn17.dialogs.WCInfo;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.internal.wc.admin.SVNEntry;
import org.tmatesoft.svn.core.internal.wc.admin.SVNWCAccess;
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 java.io.File;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.*;
public class SvnUtil {
@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 DIR_PROPS_FILE_NAME = "dir-props";
@NonNls public static final String PATH_TO_LOCK_FILE = SVN_ADMIN_DIR_NAME + "/lock";
@NonNls public static final String LOCK_FILE_NAME = "lock";
private SvnUtil() {
}
public static void crawlWCRoots(VirtualFile path, NotNullFunction<VirtualFile, Collection<VirtualFile>> callback) {
if (path == null) {
return;
}
final boolean isDirectory = path.isDirectory();
final VirtualFile parentVFile = (!isDirectory) || (!path.exists()) ? path.getParent() : path;
if (parentVFile == null) {
return;
}
final File parent = new File(parentVFile.getPath());
if (isSvnVersioned(parent)) {
checkCanceled();
final Collection<VirtualFile> pending = callback.fun(path);
checkCanceled();
for (VirtualFile virtualFile : pending) {
crawlWCRoots(virtualFile, callback);
}
} else if (isDirectory) {
checkCanceled();
VirtualFile[] children = path.getChildren();
for (int i = 0; children != null && i < children.length; i++) {
checkCanceled();
VirtualFile child = children[i];
if (child.isDirectory()) {
crawlWCRoots(child, callback);
}
}
}
}
public static boolean isSvnVersioned(File parent) {
return SVNWCUtil.isVersionedDirectory(parent) || is17CopyPart(parent);
}
public static Collection<VirtualFile> crawlWCRoots(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(vf, callback, progress);
}
private static Collection<VirtualFile> crawlWCRoots(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(parentIo)) {
checkCanceled(progress);
callback.handleWorkingCopyRoot(parentIo, 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(file, callback, progress));
}
}
}
return result;
}
private static void checkCanceled() {
final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
checkCanceled(indicator);
}
private static void checkCanceled(final ProgressIndicator progress) {
if (progress != null && progress.isCanceled()) {
throw new ProcessCanceledException();
}
}
public static String[] getLocationsForModule(final SvnVcs17 vcs, File path, ProgressIndicator progress) {
LocationsCrawler crawler = new LocationsCrawler(vcs);
crawlWCRoots(path, crawler, progress);
return crawler.getLocations();
}
public static Map<String, File> getLocationInfoForModule(final SvnVcs17 vcs, File path, ProgressIndicator progress) {
final LocationsCrawler crawler = new LocationsCrawler(vcs);
crawlWCRoots(path, crawler, progress);
return crawler.getLocationInfos();
}
public static void doLockFiles(Project project, final SvnVcs17 activeVcs, 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 != null && ioFiles.length > 1);
dialog.show();
if (!dialog.isOK()) {
return;
}
lockMessage = dialog.getComment();
force = dialog.isForce();
}
else {
lockMessage = "";
force = false;
}
final SVNException[] exception = new SVNException[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();
SVNWCClient wcClient;
try {
wcClient = activeVcs.createWCClient();
wcClient.setEventHandler(eventHandler);
if (progress != null) {
progress.setText(SvnBundle.message("progress.text.locking.files"));
}
for (File ioFile : ioFiles) {
if (progress != null) {
progress.checkCanceled();
}
File file = ioFile;
if (progress != null) {
progress.setText2(SvnBundle.message("progress.text2.processing.file", file.getName()));
}
wcClient.doLock(new File[]{file}, force, lockMessage);
}
}
catch (SVNException 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)));
}
AbstractVcsHelper.getInstance(project).showErrors(exceptions, SvnBundle.message("message.title.lock.failures"));
}
WindowManager.getInstance().getStatusBar(project).setInfo(SvnBundle.message("message.text.files.locked", count[0]));
if (exception[0] != null) {
throw new VcsException(exception[0]);
}
}
public static void doUnlockFiles(Project project, final SvnVcs17 activeVcs, final File[] ioFiles) throws VcsException {
final boolean force = true;
final SVNException[] exception = new SVNException[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();
SVNWCClient wcClient;
try {
wcClient = activeVcs.createWCClient();
wcClient.setEventHandler(eventHandler);
if (progress != null) {
progress.setText(SvnBundle.message("progress.text.unlocking.files"));
}
for (File ioFile : ioFiles) {
if (progress != null) {
progress.checkCanceled();
}
File file = ioFile;
if (progress != null) {
progress.setText2(SvnBundle.message("progress.text2.processing.file", file.getName()));
}
wcClient.doUnlock(new File[]{file}, force);
}
}
catch (SVNException 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"));
}
WindowManager.getInstance().getStatusBar(project).setInfo(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_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 "";
}
private static class LocationsCrawler implements SvnWCRootCrawler {
private final SvnVcs17 myVcs;
private final Map<String, File> myLocations;
public LocationsCrawler(SvnVcs17 vcs) {
myVcs = vcs;
myLocations = new HashMap<String, File>();
}
public String[] getLocations() {
final Set<String> set = myLocations.keySet();
return ArrayUtil.toStringArray(set);
}
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()));
}
try {
SVNWCClient wcClient = myVcs.createWCClient();
SVNInfo info = wcClient.doInfo(root, SVNRevision.UNDEFINED);
if (info != null && info.getURL() != null) {
myLocations.put(info.getURL().toString(), info.getFile());
}
}
catch (SVNException e) {
//
}
if (progress != null) {
progress.setText(oldText);
}
}
}
@Nullable
public static String getRepositoryUUID(final SvnVcs17 vcs, final File file) {
final SVNWCClient client = vcs.createWCClient();
try {
final SVNInfo info = client.doInfo(file, SVNRevision.UNDEFINED);
return (info == null) ? null : info.getRepositoryUUID();
} catch (SVNException e) {
return null;
}
}
@Nullable
public static String getRepositoryUUID(final SvnVcs17 vcs, final SVNURL url) {
final SVNWCClient client = vcs.createWCClient();
try {
final SVNInfo info = client.doInfo(url, SVNRevision.UNDEFINED, SVNRevision.UNDEFINED);
return (info == null) ? null : info.getRepositoryUUID();
} catch (SVNException e) {
return null;
}
}
@Nullable
public static SVNURL getRepositoryRoot(final SvnVcs17 vcs, final File file) {
final SVNWCClient client = vcs.createWCClient();
try {
final SVNInfo info = client.doInfo(file, SVNRevision.UNDEFINED);
return (info == null) ? null : info.getRepositoryRootURL();
} catch (SVNException e) {
return null;
}
}
@Nullable
public static SVNURL getRepositoryRoot(final SvnVcs17 vcs, final String url) {
try {
return getRepositoryRoot(vcs, SVNURL.parseURIEncoded(url));
}
catch (SVNException e) {
return null;
}
}
@Nullable
public static SVNURL getRepositoryRoot(final SvnVcs17 vcs, final SVNURL url) {
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;
}
}
public static boolean isWorkingCopyRoot(final File file) {
try {
return SVNWCUtil.isWorkingCopyRoot(file);
} catch (SVNException e) {
return false;
}
}
@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 = VfsUtil.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 SvnVcs17 vcs, final VirtualFile vcsRoot, final String urlPath) {
final SvnBranchConfigurationNew configuration;
try {
final SVNURL url = SVNURL.parseURIEncoded(urlPath);
configuration = SvnBranchConfigurationManager17.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 String path) {
if (path.length() == 0) {
// project root
return project.getBaseDir();
}
return LocalFileSystem.getInstance().findFileByPath(path);
}
@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 isOneDotFiveAvailable(final Project project, final RepositoryLocation location) {
final SvnVcs17 vcs = SvnVcs17.getInstance(project);
final List<WCInfo> infos = vcs.getAllWcInfos();
for (WCInfo info : infos) {
if (! info.getFormat().supportsMergeInfo()) {
continue;
}
final String url = info.getUrl().toString();
if ((location != null) && (! location.toPresentableString().startsWith(url)) &&
(! url.startsWith(location.toPresentableString()))) {
continue;
}
if (! checkRepositoryVersion15(vcs, url)) {
continue;
}
return true;
}
return false;
}
public static boolean checkRepositoryVersion15(final SvnVcs17 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();
}
}
}
public static SVNStatus getStatus(final SvnVcs17 vcs, final File file) {
final SVNStatusClient statusClient = vcs.createStatusClient();
try {
return statusClient.doStatus(file, false);
}
catch (SVNException e) {
return null;
}
}
public static SVNDepth getDepth(final SvnVcs17 vcs, final File file) {
final SVNWCClient client = vcs.createWCClient();
try {
final SVNInfo svnInfo = client.doInfo(file, SVNRevision.UNDEFINED);
if (svnInfo != null) {
return svnInfo.getDepth();
}
}
catch (SVNException e) {
//
}
return 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(File parent, final String name) {
if (name.equals(SVN_ADMIN_DIR_NAME)) {
return true;
}
if (parent != null) {
if (parent.getName().equals(SVN_ADMIN_DIR_NAME)) {
return true;
}
parent = parent.getParentFile();
if (parent != null && parent.getName().equals(SVN_ADMIN_DIR_NAME)) {
return true;
}
}
return false;
}
public static boolean isAdminDirectory(VirtualFile parent, String name) {
// never allow to delete admin directories by themselves (this can happen during LVCS 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;
}
@Nullable
public static SVNURL getUrl(final File file) {
SVNWCAccess wcAccess = SVNWCAccess.newInstance(null);
try {
wcAccess.probeOpen(file, false, 0);
SVNEntry entry = wcAccess.getVersionedEntry(file, false);
return entry.getSVNURL();
} catch (SVNException e) {
//
} finally {
try {
wcAccess.close();
}
catch (SVNException e) {
//
}
}
return null;
}
public static boolean doesRepositorySupportMergeinfo(final SvnVcs17 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 SvnVcs17 vcs, final String url) throws SVNException {
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 byte[] decode(final Charset charset, final byte[] buffer) {
if (charset != null && ! CharsetToolkit.UTF8_CHARSET.equals(charset)) {
return CharsetToolkit.UTF8_CHARSET.encode(charset.decode(ByteBuffer.wrap(buffer))).array();
}
return buffer;
}
@Nullable
public static File getWcCopyRootIf17(final File file, @Nullable final File upperBound) {
File current = file;
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 && upperBound.equals(current)) return null;
current = current.getParentFile();
}
catch (SVNException e) {
return null;
}
}
return null;
}
public static boolean is17CopyPart(final File file) {
try {
return SvnWcGeneration.V17.equals(SvnOperationFactory.detectWcGeneration(file, true));
}
catch (SVNException e) {
return false;
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,27 @@
/*
* Copyright 2000-2009 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.svn17;
import com.intellij.openapi.progress.ProgressIndicator;
import java.io.File;
/**
* @author alex
*/
public interface SvnWCRootCrawler {
void handleWorkingCopyRoot(File root, ProgressIndicator progress);
}
@@ -0,0 +1,31 @@
/*
* Copyright 2000-2009 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.svn17;
public class SvnWorkingCopyFormatHolder {
private static final ThreadLocal<WorkingCopyFormat> myPresetFormat = new ThreadLocal<WorkingCopyFormat>();
private SvnWorkingCopyFormatHolder() {
}
public static WorkingCopyFormat getPresetFormat() {
return myPresetFormat.get();
}
public static void setPresetFormat(final WorkingCopyFormat value) {
myPresetFormat.set(value);
}
}
@@ -0,0 +1,50 @@
/*
* Copyright 2000-2010 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.svn17;
import com.intellij.openapi.vcs.changes.ui.ChangesViewContentI;
import com.intellij.openapi.vcs.changes.ui.ChangesViewContentManager;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentFactory;
import org.jetbrains.idea.svn17.dialogs.CopiesPanel;
public class WorkingCopiesContent {
private final SvnVcs17 myVcs;
private Content myShownContent;
public WorkingCopiesContent(SvnVcs17 vcs) {
myVcs = vcs;
}
public void activate() {
final ChangesViewContentI cvcm = ChangesViewContentManager.getInstance(myVcs.getProject());
final ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();
final CopiesPanel copiesPanel = new CopiesPanel(myVcs.getProject());
myShownContent = contentFactory.createContent(copiesPanel.getComponent(), SvnBundle.message("dialog.show.svn.map.title"), true);
myShownContent.setCloseable(false);
cvcm.addContent(myShownContent);
myShownContent.setPreferredFocusableComponent(copiesPanel.getPreferredFocusedComponent());
}
public void deactivate() {
if (myShownContent != null) {
final ChangesViewContentI cvcm = ChangesViewContentManager.getInstance(myVcs.getProject());
cvcm.removeContent(myShownContent);
myShownContent = null;
}
}
}
@@ -0,0 +1,55 @@
/*
* Copyright 2000-2009 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.svn17;
import org.tmatesoft.svn.core.SVNURL;
import java.io.File;
public class WorkingCopy {
private final File myFile;
private final SVNURL myUrl;
public WorkingCopy(File file, SVNURL url) {
myFile = file;
myUrl = url;
}
public File getFile() {
return myFile;
}
public SVNURL getUrl() {
return myUrl;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
WorkingCopy that = (WorkingCopy)o;
if (!myFile.equals(that.myFile)) return false;
return true;
}
@Override
public int hashCode() {
return myFile.hashCode();
}
}
@@ -0,0 +1,103 @@
/*
* Copyright 2000-2009 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.svn17;
/**
* since not all constants are available from svnkit & constants are fixed
*/
public enum WorkingCopyFormat {
ONE_DOT_THREE(4, false, false, false, SvnBundle.message("dialog.show.svn.map.table.version13.text")),
ONE_DOT_FOUR(8, false, false, false, SvnBundle.message("dialog.show.svn.map.table.version14.text")),
ONE_DOT_FIVE(9, true, true, false, SvnBundle.message("dialog.show.svn.map.table.version15.text")),
ONE_DOT_SIX(10, true, true, true, SvnBundle.message("dialog.show.svn.map.table.version16.text")),
ONE_DOT_SEVEN(12, true, true, true, SvnBundle.message("dialog.show.svn.map.table.version17.text")),
UNKNOWN(0, false, false, false, "unknown");
private final int myFormat;
private final boolean myChangelistSupport;
private final boolean myMergeInfoSupport;
private final boolean myTreeConflictSupport;
private final String myName;
private WorkingCopyFormat(final int format, boolean changelistSupport, boolean mergeInfoSupport, boolean treeConflictSupport, String name) {
myFormat = format;
myChangelistSupport = changelistSupport;
myMergeInfoSupport = mergeInfoSupport;
myTreeConflictSupport = treeConflictSupport;
myName = name;
}
public boolean supportsChangelists() {
return myChangelistSupport;
}
public boolean supportsMergeInfo() {
return myMergeInfoSupport;
}
public boolean supportsTreeConflicts() {
return myTreeConflictSupport;
}
public String getName() {
return myName;
}
public static WorkingCopyFormat getInstance(final int value) {
if (ONE_DOT_FIVE.getFormat() == value) {
return ONE_DOT_FIVE;
} else if (ONE_DOT_FOUR.getFormat() == value) {
return ONE_DOT_FOUR;
} else if (ONE_DOT_THREE.getFormat() == value) {
return ONE_DOT_THREE;
} else if (ONE_DOT_SIX.getFormat() == value) {
return ONE_DOT_SIX;
} else if (ONE_DOT_SEVEN.getFormat() == value) {
return ONE_DOT_SEVEN;
}
return UNKNOWN;
}
public static WorkingCopyFormat getInstance(final String updateOption) {
if (SvnConfiguration17.UPGRADE_AUTO_17.equals(updateOption)) {
return ONE_DOT_SEVEN;
} else if (SvnConfiguration17.UPGRADE_AUTO_16.equals(updateOption)) {
return ONE_DOT_SIX;
} else if (SvnConfiguration17.UPGRADE_AUTO_15.equals(updateOption)) {
return ONE_DOT_FIVE;
} else if (SvnConfiguration17.UPGRADE_AUTO.equals(updateOption)) {
return ONE_DOT_FOUR;
}
return ONE_DOT_THREE;
}
public int getFormat() {
return myFormat;
}
public String getOption() {
if (ONE_DOT_SEVEN.equals(this)) {
return SvnConfiguration17.UPGRADE_AUTO_17;
} else if (ONE_DOT_SIX.equals(this)) {
return SvnConfiguration17.UPGRADE_AUTO_16;
} else if (ONE_DOT_FIVE.equals(this)) {
return SvnConfiguration17.UPGRADE_AUTO_15;
} else if (ONE_DOT_FOUR.equals(this)) {
return SvnConfiguration17.UPGRADE_AUTO;
}
return SvnConfiguration17.UPGRADE_NONE;
}
}

Some files were not shown because too many files have changed in this diff Show More