diff options
author | Tor Norbye <tnorbye@google.com> | 2013-08-29 10:40:28 -0700 |
---|---|---|
committer | Tor Norbye <tnorbye@google.com> | 2013-08-29 10:40:35 -0700 |
commit | 932259520ebaedeb2ccf4b7594bad50c700963d7 (patch) | |
tree | 2b52334b8a019d9652625b1432deac11a7c4b31e /plugins/svn4idea | |
parent | e47d04f1f804b9e725b768725da436af6788f19b (diff) | |
download | idea-932259520ebaedeb2ccf4b7594bad50c700963d7.tar.gz |
Snapshot 1c7917945d4706cdbb32b599f81abd05c0251e32 from idea/132.27 branch of git://git.jetbrains.org/idea/community.git
1c79179: 2013-08-29 Yann Cébron - DevKit: smart EP "implementation" highlighting/completion
f3a83bc: 2013-08-29 Roman Shevchenko - IDEA-112827 (NPE in error reporter)
464a45b: 2013-08-29 Anna Kozlova - move pin button to the right side of popup (IDEA-112435)
1635be8: 2013-08-29 Yann Cébron - Dom EPs: add some <with> tags
6bcac42: 2013-08-29 Roman Shevchenko - IDEA-112824 (suppress SVN logging for normal use)
16d0ba4: 2013-08-29 Anna Kozlova - NPE
285509c4c: 2013-08-28 Max Medvedev - IDEA-111110 Groovy: Introduce Field Refactoring doesn't suggest to choose destination class
5415af1: 2013-08-28 Yann Cébron - Merge remote-tracking branch 'origin/master'
8462f3b: 2013-08-28 Dmitry Jemerov - cleanup
2ad6100: 2013-08-28 Yann Cébron - Merge remote-tracking branch 'origin/master'
9de8bfc: 2013-08-28 Dmitry Jemerov - Merge branch 'master' of git://github.com/niktrop/intellij-community into pull92
6315415: 2013-08-28 Dmitry Jemerov - cleanup
7281b73: 2013-08-28 Dmitry Jemerov - Merge branch 'cjfm3' of git://github.com/max-kammerer/intellij-community into pull87
6e31350: 2013-08-28 Dmitry Jemerov - fix couple of issues with https://github.com/JetBrains/intellij-community/pull/94
3695aa0: 2013-08-28 Dmitry Jemerov - Merge branch 'master' of git://github.com/asedunov/intellij-community into pull94
ce2c15f: 2013-08-28 Yann Cébron - fix javadoc @see link
6794ead: 2013-08-28 Anna Kozlova - skip adverts when server doesn't accept provided info
851da00: 2013-08-28 Anna Kozlova - skip advs in tests and headless mode
cddbc28: 2013-08-28 Anna Kozlova - EA-48802 - assert: JavaFileManagerBase.findClass
1eafaae: 2013-08-28 Anna Kozlova - EA-49147 - NPE: UnusedDeclarationInspection.isReadObjectMethod
f1a2040: 2013-08-28 Anna Kozlova - EA-49155 - NPE: JavaChangeSignatureDialog.doCalculateSignature
2684709: 2013-08-28 Dmitry Jemerov - MalformedFormatStringInspectionTest fixed
ce8088e: 2013-08-28 Evgeny Gerashchenko - Removed extra checking for duplicate annotations in one file. It is performed when reading file anyway.
9d4c7ca: 2013-08-28 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
2fd8641: 2013-08-28 Eugene Kudelevsky - add possibility to setup lookup element for XML attribute values; IDEA-102167 layout_* attributes should go first
3b065b1: 2013-08-28 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
d8d9168: 2013-08-28 Dmitry Trofimov - Libs update.
28a95a6: 2013-08-28 Alexey Kudravtsev - compilation
d4a0d69: 2013-08-28 Alexey Kudravtsev - notnull
f9f8006: 2013-08-28 Alexey Kudravtsev - moved to appropriate package
4d058f8: 2013-08-28 Alexey Kudravtsev - cleanup
111301a: 2013-08-28 Alexey Kudravtsev - extra method
01e59b0: 2013-08-28 Alexey Kudravtsev - doc
da966ae: 2013-08-28 Alexey Kudravtsev - cleanup
7503616: 2013-08-28 Alexey Kudravtsev - add method to javaversionservice
7d6de4f: 2013-08-28 Alexey Kudravtsev - notnull
dee081f: 2013-08-27 Alexey Kudravtsev - statistics
dd327c0: 2013-08-28 Dmitry Trofimov - Focus fixes.
7231931: 2013-08-28 Vladimir Krivosheev - WEB-8988 Return "open in browser" in context menu
2102255: 2013-08-28 Roman Shevchenko - app: install-over range extended for next EAPs
ddad3bb: 2013-08-28 Dmitry Avdeev - IDEA-112728 Can't create new task from tasks menu in toolbar: no need to fix lost typing for Go To Task
a8160c2: 2013-08-28 Dmitry Avdeev - typo
7328cbd: 2013-08-28 Dmitry Avdeev - cleanup
abbdb5a: 2013-08-28 Dmitry Avdeev - cleanup
a5c8503: 2013-08-28 Dmitry Avdeev - do not allow empty task name
e8daf37: 2013-08-28 Dmitry Avdeev - simplified
dff28d3: 2013-08-28 Dmitry Avdeev - simplified
38513a8: 2013-08-28 Dmitry Avdeev - cleanup
c09dd55: 2013-08-28 Vladimir Krivosheev - VariablesGroup — avoid array copy
8f3c91f: 2013-08-28 Roman Shevchenko - java: correct character escaping in decompiler
9a19e30: 2013-08-28 Dmitry Trofimov - Fixed hiding and activating of the terminal (PY-10669).
df672ba: 2013-08-28 Dmitry Trofimov - Terminal system settings refactored.
4653b54: 2013-08-28 Anna Kozlova - unknown features equality fixed
2896270: 2013-08-28 Anna Kozlova - ensure read access
ccff3af: 2013-08-28 Anna Kozlova - suggest to download plugins by unknown run configurations
9c8a3d2: 2013-08-28 Konstantin Bulenkov - include os.arch and jdk build number in about
861984c: 2013-08-28 Denis Fokin - IDEA-108265. We should not do anything if an empty array is passed.
a6b3441: 2013-08-28 Sergey Simonchik - EA-49063 - AIOOBE: ScriptRunnerUtil$ScriptOutput.onTextAvailable
845ee5c: 2013-08-28 Vladimir Krivosheev - value nullability
36121a2: 2013-08-28 Konstantin Kolosovsky - Merge branch 'svn1_8_new'
1515b70: 2013-08-28 Mikhail Golubev - Merge remote-tracking branch 'origin/master'
33d684e0: 2013-08-28 Mikhail Golubev - IDEA-110012 Not all Redmine Issues Available on "Open Task" (Limited to 100?)
39899b1: 2013-08-28 Bas Leijdekkers - IDEA-112782 (Change signature dialog shows unexpected 'cannot resolve symbol' message)
e469928: 2013-08-28 Anna Kozlova - accept test config methods in non-test classes (IDEA-112537)
b53abed: 2013-08-27 Anna Kozlova - fix typo
3074f49: 2013-08-28 Dmitry Avdeev - IDEA-112781 Open YouTrack task: Create changelist doesn't work with SVN ?
43c8897: 2013-08-27 Dmitry Avdeev - cleanup
ff6217a: 2013-08-28 Kirill Likhodedov - Annotate overriding methods
8e2d0e1: 2013-08-28 Roman Shevchenko - terminal: platform's Guava should be good enough for the plugin
2055780: 2013-08-27 Roman Shevchenko - EA-49123 (do not load extensions from static initializer)
116dc30: 2013-08-27 Roman Shevchenko - EA-49235 (check proxy port)
2b4f96f: 2013-08-27 Roman Shevchenko - EA-49235 (code readability)
ed03bca: 2013-08-27 Roman Shevchenko - EA-49142 (NPE, cleanup)
e6053d0: 2013-08-27 Roman Shevchenko - EA-49102 (face user with printing errors)
a748474: 2013-08-28 Vladimir Krivosheev - 4.1.0. update netty (now it is not patched build, https://github.com/netty/netty/pull/1762)
3c60901: 2013-08-28 Konstantin Bulenkov - better selection for mixed languages
20decc3: 2013-08-28 Konstantin Bulenkov - fix selection for files with multiple languages
878ad26: 2013-08-27 Max Medvedev - IDEA-111100 Groovy: Introduce Variable/Parameter Refactorings don't suggest to replace occurrences if applied to expressions inside code blocks
8a60662: 2013-08-27 Max Medvedev - IDEA-110981 Groovy: "Split into declaration and assignment" intention leaves unnecessary "=" if applied to closures
77912c1: 2013-08-27 Max Medvedev - IDEA-111101 Groovy: In-Place Introduce Field: Alt+I mnemonic doesn't work in the refactoring preview
088f68e: 2013-08-27 Max Medvedev - IDEA-111027 Groovy: In-Place Introduce Variable: PIEAE at GrInplaceIntroducer.<init> on introducing a variable within one-line method/closure
a2210a4: 2013-08-27 Aleksei Sedunov - Extract inheritor candidate check into separate InheritanceChecker interface
f40be5e: 2013-08-27 Alexander Zolotov - Filter moduleAwareConfigurables by module
cf7704f: 2013-08-26 Alexander Zolotov - WEB-6452 SASS suggests functions at the top, instead of property values
165ccf7: 2013-08-27 Konstantin Kolosovsky - IDEA-94942 Fixed diff, annotate errors in history view after rename/move
7b6396a: 2013-08-27 Dmitry Jemerov - branch number 132
e176d25: 2013-08-27 Sergey Evdokimov - IDEA-112754 Maven import: NCDFE for org/jetbrains/plugins/groovy/util/ClassInstanceCache
43c49f6: 2013-08-27 Sergey Evdokimov - IDEA-112754 Maven import: NCDFE for org/jetbrains/plugins/groovy/util/ClassInstanceCache
377dd45: 2013-08-27 Konstantin Bulenkov - fix memory leak
203fb69: 2013-08-27 Sergey Simonchik - WEB-9011 Karma plugin ignored tests
3b896f2: 2013-08-27 Sergey Evdokimov - Make project unignored when new module is created by ignored project
9ae29c6: 2013-08-27 Sergey Evdokimov - Remove maven project from project tree when user deletes module. +review CR-IC-2084
0f0f39e: 2013-08-27 Vladimir Krivosheev - cleanup, Overrides
3dddec4: 2013-08-27 Vladimir Krivosheev - cleanup
a2fff55: 2013-08-27 Aleksey Pivovarov - Github: Add API function for loading Commit Comments
9bd1660: 2013-08-27 Aleksey Pivovarov - fix IndexOutOfBoundsException on inserting empty collection to empty model
46b8998: 2013-08-27 Nadya Zabrodina - exception during annotate copied file fixed
66640b2: 2013-08-27 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
c597bcc: 2013-08-27 Sergey Evdokimov - Remove maven project from project tree when user deletes module. +review CR-IC-2084
33e932a: 2013-08-27 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
7cc9cca: 2013-08-27 Vladislav.Soroka - IDEA-79466 gradle support should generate web module configuration
42e649d: 2013-08-27 Dmitry Jemerov - better names for couple of new classes added to API
5c70eaf: 2013-08-27 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
2e0f1fa: 2013-08-27 Dmitry Trofimov - Update lib.
dcc2c4b: 2013-08-27 Dmitry Trofimov - Override isRetina.
05b716b: 2013-08-27 nik - processing dependencies in JPS: @NotNull annotations and javadoc added
6314b6e: 2013-08-27 Dmitry Trofimov - Fixed for Retina.
9d1886c: 2013-08-27 Dmitry Avdeev - navigatable xsd documentation
0f389c5: 2013-08-27 Vladimir Krivosheev - hide internal class XValuePresenterAdapter
a0386eb: 2013-08-27 Mikhail Golubev - Merge remote-tracking branch 'origin/master'
53248dc: 2013-08-27 Vladimir Krivosheev - fix createPresenter
d0355b3: 2013-08-27 Sergey Evdokimov - Remove maven project from project tree when user deletes module. +review CR-IC @Anton.Makeev
fb48f62: 2013-08-27 Sergey Evdokimov - Optimization of MavenProjectsTree.isManagedFile()
6b123aa: 2013-08-27 nik - JPS dependencies enumerator: convenient method added
d4579e1: 2013-08-27 Dmitry Avdeev - UrlPsiReference promoted
3b1feea: 2013-08-27 Dmitry Avdeev - cleanup
ee18443: 2013-08-27 Dmitry Avdeev - cleanup
ca2484e: 2013-08-27 Dmitry Avdeev - cleanup
c9e045b: 2013-08-27 Vladimir Krivosheev - 1) methods "void setPresentation(@NonNls String name, @Nullable Icon icon, @NonNls @Nullable String type, @NonNls @NotNull String separator, @NonNls @NotNull String value, boolean hasChildren);" and "void setPresentation(@NonNls String name, @Nullable Icon icon, @NonNls @Nullable String type, @NonNls @NotNull String value, boolean hasChildren);"
b38d58a: 2013-08-27 Konstantin Kolosovsky - IDEA-94942 Fixed treating svn client warnings as errors
8cab12c: 2013-08-27 Dmitry Jemerov - separate UI and non-UI parts of MalformedFormatStringInspection
93e3fa6: 2013-08-20 Jason Holmes - Custom "Malformed format string" inspection
b9f6fde: 2013-08-27 nik - actions to mark/unmark roots in Project View refactored to support custom root types
0455e46: 2013-08-27 Aleksey Pivovarov - Github: remove useless listener
7f40613: 2013-08-27 Aleksey Pivovarov - Github: reset User on token change
c8a5402: 2013-08-27 Aleksey Pivovarov - Github: change layout
89769be: 2013-08-27 Aleksey Pivovarov - Github: add test
0d8ab04: 2013-08-27 Aleksey Pivovarov - Github: comment
cba103c: 2013-08-27 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
d6ca049: 2013-08-27 Anna Kozlova - allow autoPopup after custom symbols (IDEA-112571)
a70c338: 2013-08-27 Konstantin Bulenkov - IDEA-110846 File Structure pop-up doesn't work properly for template languages
4896775: 2013-08-27 Konstantin Bulenkov - recent files duplicates
05835fa: 2013-08-27 Fedor Korotkov - WEB-6328 Add support for HTML5 <main> element
2407d7b: 2013-08-27 Anna Kozlova - allow to call getValue without readAction as it was before
29cb25d: 2013-08-27 Dmitry Avdeev - fixing the leak
7d3932a: 2013-08-27 Dmitry Avdeev - IDEA-112708 Static classes in JSP class block are considered an error
f6dbce2: 2013-08-26 Mikhail Golubev - IDEA-112605 Task management: can't add Generic server: NoClassDefFoundError: XPathFileType
82c1dc1: 2013-08-27 Konstantin Kolosovsky - Merge branch 'svn1_8_new'
08d46f1: 2013-08-27 Alexey Kudravtsev - highlightVisitor moves and cleanup
e0fec9d: 2013-08-27 Alexey Kudravtsev - Merge remote-tracking branch 'origin/master'
e9b1dfc: 2013-08-27 Anna Kozlova - dumb smart lambda completion (IDEA-112553)
6da30a4: 2013-08-26 Alexey Kudravtsev - calculate column/offset: optimisation of the no-tabs case
c0990c4: 2013-08-26 Alexey Kudravtsev - race conditions?
135e250: 2013-08-26 Alexey Kudravtsev - notnull, cleanup
75b28ba: 2013-08-26 Alexey Kudravtsev - file was not rehighlighted on some changes
5647d35: 2013-08-26 Sergey Evdokimov - Make myManagedFilesPaths a Set to avoid duplication.
680dd76: 2013-08-27 Aleksey Pivovarov - Github: fix memory leak on Exception in setUp();
e3092b7: 2013-08-27 Dmitry Avdeev - IDEA-112611 Task management: DVCS: closing a task fails: "Cannot delete the branch master"
d12d4fe: 2013-08-27 Anna Kozlova - logging for EA-49099 - PIEAE: PsiAnchor$StubIndexReference.getStartOffset
3cc53fe: 2013-08-27 Anna Kozlova - revert changes in api
10fbef9: 2013-08-27 Roman Shevchenko - Test data updated
a5455ba: 2013-08-27 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
8ed3ae8: 2013-08-27 Dmitry Trofimov - Updated lib.
e6df583: 2013-08-27 Dmitry Trofimov - Added guava lib to terminal.
1853090: 2013-08-27 Dmitry Trofimov - Draw image Retina support.
a7ebcaf: 2013-08-27 Dmitry Trofimov - Open session action moved to constructor.
5176bfa: 2013-08-26 Sergey Evdokimov - Fix maven tests
291f740: 2013-08-26 Vladimir Krivosheev - we must check if any port free too
18d4be6: 2013-08-26 Vassiliy Kudryashov - IDEA-70769 Settings panel: increase speed of scrollbars
0c76aca: 2013-08-26 Vassiliy Kudryashov - IDEA-112524 Working directory for default rake tasks is changed to $MODULE_DIR$ after project's reopening
e2b7f6b: 2013-08-26 Konstantin Bulenkov - pattern dependent delay
1916142: 2013-08-26 Vladimir Krivosheev - cleanup
3da9dcd: 2013-08-26 Vladimir Krivosheev - done: custom binary request handler
e02eba9: 2013-08-26 Aleksey Pivovarov - Github: do not produce dozens of notifications
8e41d59: 2013-08-26 Aleksey Pivovarov - Simplify
78c800d: 2013-08-26 Aleksey Pivovarov - Github: respect 'max' parameter
3afb19f: 2013-08-26 Maxim.Mossienko - proper versioning of stub index when persistent enumerator version changes
c853e9e: 2013-08-26 Eugene Kudelevsky - IDEA-112376 add "importFilter" extension point to force using FQN when importing class
31281a9: 2013-08-26 Kirill Likhodedov - [git] Don't write empty lines to LOG.debug.
493b9fb: 2013-08-26 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
60384ba: 2013-08-26 Dmitry Trofimov - Enable Run local terminal action for Windows.
bfa8e61: 2013-08-26 Dmitry Jemerov - stupid typo fixed
04a76c3: 2013-08-26 Anna Kozlova - IDEA-112555 Bad code is green with method references on instance
8f5139d: 2013-08-26 Denis Fokin - IDEA-108265. Now user is asked whether the project should be opened in a new frame.
da958ab: 2013-08-26 Natalia Ukhorskaya - Decompile chars, bytes and shorts correctly
8008709: 2013-08-26 Roman Shevchenko - java: incorrect annotation decoding fixed
9f07ea5: 2013-08-26 Roman Shevchenko - logging
e07c905: 2013-08-26 Dmitry Trofimov - Merge remote-tracking branch 'origin/master'
e81832d: 2013-08-26 Dmitry Trofimov - This update fixes pty on windows.
e36607b: 2013-08-26 Konstantin Bulenkov - new renderer
9ea47a5: 2013-08-26 nik - source roots editors: obtain icons for content tree from extension
d52dfb8: 2013-08-26 nik - constants moved
710d3ba: 2013-08-26 niktrop - Code style fixed
aa8832d: 2013-08-26 Sergey Simonchik - code style: two subsequent ifs merged to reduce inner indent
ed88487: 2013-08-26 Konstantin Kolosovsky - IDEA-94942 Provide detailed error messages to user instead of general ones
e0df7a3: 2013-08-26 Vassiliy Kudryashov - IDEA-112524 Working directory for default rake tasks is changed to $MODULE_DIR$ after project's reopening
e60a55b: 2013-08-26 nik - source roots editors refactored: root type specific UI moved to extension
928ea20: 2013-08-26 Mikhail Golubev - Merge remote-tracking branch 'origin/master'
70d526d: 2013-08-26 Anna Kozlova - guardedBy itself support (IDEA-112565)
2e23cb8: 2013-08-26 Anna Kozlova - restore bytecode viewer for java classes
89dffc9: 2013-08-26 Anna Kozlova - cal property name: accept without any other checks non-letter prefixes (IDEA-112585)
bab044f: 2013-08-26 Vladimir Krivosheev - restore Alarm.cancelRequest
dd817dc: 2013-08-26 Sergey Simonchik - simplification
7046dc4: 2013-08-26 niktrop - Some refactoring for reusing GenerateEquals UI in scala plugin
44e4219: 2013-08-26 Sergey Evdokimov - IDEA-112529 Maven: code completion could work in file path value with property references
867be29: 2013-08-26 Denis Fokin - Jumplist libraries changes. This is a release version of dll with eliminated MSVCRT dependencies.
7b0029d: 2013-08-26 Anton Makeev - CIDR: language tests in windows +review CR-OC @micha
9bef3a2: 2013-08-26 Konstantin Kolosovsky - IDEA-94942 Updated cleanup behavior after previous command failed
8447776: 2013-08-26 Mikhail Golubev - Remove SelectorBasedResponseHandler#getSelectorFileType, delegate to #getResponseType instead. Add missing @NotNull annotation, update doc comments.
d7ecb44: 2013-08-26 Anna Kozlova - treat classes with before/after methods as test classes for bad declared exceptions (IDEA-112537)
4be2bcf: 2013-08-26 Anna Kozlova - restore suggestion to remove 'abstract' when method has body
c57c308: 2013-08-26 Anna Kozlova - local can be final inside lambda body (IDEA-112630)
b8170a6: 2013-08-26 Vladimir Krivosheev - isValuesCustomSorted, add or not SortValuesAction (alphabetically sort) TODO: this action should be moved to "Variables" as gear action
b56b744: 2013-08-26 Mikhail Golubev - Merge remote-tracking branch 'origin/master'
ba7e194: 2013-08-23 Mikhail Golubev - Migrate Assembla to new GenericRepository
dd0885d: 2013-08-23 Mikhail Golubev - Refactor TemplateVariable
aa94a3c: 2013-08-23 Mikhail Golubev - Add several tests of date parsing
0e7e61e: 2013-08-23 Mikhail Golubev - Reflective PsiElements creation in JqlParserDefinition
a5f3011: 2013-08-26 Konstantin Kolosovsky - IDEA-94942 Short refactoring (removed duplication)
01cd38b: 2013-08-26 Sergey Simonchik - unnecessary line removed
7c34a2a: 2013-08-26 Dmitry Avdeev - IDEA-60895 No completion for enumerated and boolean values of xml tags
8b83a30: 2013-08-26 Vladislav.Soroka - gradle: cosmetic UI fix
8ee6a76: 2013-08-26 Roman Shevchenko - Convenient debug logging
cde5373: 2013-08-26 Dmitry Avdeev - IDEA-60895 No completion for enumerated and boolean values of xml tags
75d9b47: 2013-08-24 Max Medvedev - IDEA-112621 Groovy: Remove explicit type declaration intention
d1c29fb: 2013-08-23 Max Medvedev - separate visit methods for all types of classes, enums, interfaces, annotation types, and anonymous classes
5eca3d0: 2013-08-23 Max Medvedev - Byte code viewer shows byte code for groovy scripts
c96d27f: 2013-08-23 Max Medvedev - Convert anonymous class to closure: don't insert 'as Runnable' if groovy version is at least 2.2
7e993a0: 2013-08-23 Max Medvedev - IDEA-112560 process only applicable mixins to a ref
c33dc4a: 2013-08-26 Roman Shevchenko - platform: unified loading of system libraries (done right)
d492a6f: 2013-08-25 Roman Shevchenko - logging
2f2b546: 2013-08-25 Roman Shevchenko - IDEA-112462 (allow plugins to extend lib search path)
2a39bf2: 2013-08-25 Maxim.Mossienko - restart lexer from 0 offset when searching from start
a0858dbf: 2013-08-25 Maxim.Mossienko - 20% more compact compiler caches (-50M for IDEA project)
5ce3373: 2013-08-25 Maxim.Mossienko - IDEA-111918 Find: comments / string literals only: just 1 entry is found in each comment or literal
68ffc65: 2013-08-25 Maxim.Mossienko - faster contol + shift + N / control + N by default
eb67af1: 2013-08-24 Vassiliy Kudryashov - IDEA-107413 Cannot drag'n'drop more than one item in Changes View
d5ed7b5: 2013-08-24 nik - store properties of source folder in JPS element
b3dd357: 2013-08-24 nik - typo
5b8b1ff: 2013-08-24 Kirill Likhodedov - Merge remote-tracking branch 'origin/master'
0dd284a: 2013-08-24 Kirill Likhodedov - Better assertion error in the DefaultLogger
3146c0b: 2013-08-23 Bas Leijdekkers - foreach can also initialize field
6356a11: 2013-08-23 Konstantin Bulenkov - completely refacrored
6ad3452: 2013-08-23 Anna Kozlova - show conflict on invert boolean and method references (IDEA-112572)
4726276: 2013-08-23 Anna Kozlova - extract method from lambda body: accept parameters of parent method (IDEA-112570)
16a5e32: 2013-08-23 Gregory.Shrago - EditorEx: permanent header API
edc3497: 2013-08-23 Konstantin Kolosovsky - IDEA-94942 Content retrieval from svn refactored to ClientFactory model
0f40312: 2013-08-23 Mikhail Golubev - Change JqlQuery methods, JqlTerminalClause should extend JqlClause
c335f2b: 2013-08-23 Mikhail Golubev - IDEA-111811 Task management: JIRA: JQL: code completion suggests nothing after closing parenthesis
6e6972a: 2013-08-23 Konstantin Kolosovsky - IDEA-94942 Annotate action
e9d5412: 2013-08-23 Mikhail Golubev - Update description of NATIVE_SEARCH feature in TaskRepository
3c948a8: 2013-08-22 Konstantin Kolosovsky - IDEA-94942 Code cleanup - unused parameters removed
64706df: 2013-08-22 Konstantin Kolosovsky - IDEA-112184
0e41cad: 2013-08-22 Konstantin Kolosovsky - IDEA-94942 Simple property client to fix SvnMergeProvider.isBinary implementation
8091bb0: 2013-08-22 Konstantin Kolosovsky - IDEA-94942 Implemented file conflicts resolving Updated "svn info" result parsing
040f405: 2013-08-21 Konstantin Kolosovsky - IDEA-94942 SvnBindClient - unsupported methods removed
cef0440: 2013-08-21 Konstantin Kolosovsky - IDEA-94942 "bindSvn" module classes moved to "svn4idea" SvnBindClient unsupported methods will be removed in next commit (to track change like "rename" instead of "delete" + "add" to preserve history)
6c36a93: 2013-08-20 Konstantin Kolosovsky - IDEA-112184 Added logging to detect authentication issues
928c01e: 2013-08-07 Konstantin Kolosovsky - IDEA-94942 - Basic svn 1.8 test support
d880599: 2013-08-20 Konstantin Kolosovsky - IDEA-94942 Small text fixes Ignoring tests for old/not used functionality
71384c1: 2013-08-19 Konstantin Kolosovsky - IDEA-94942 Copy and move actions
a5f7e7c: 2013-08-19 Konstantin Kolosovsky - IDEA-94942 Delete action
e197c08: 2013-08-19 Konstantin Kolosovsky - IDEA-94942 Fixed authentication for svn protocol
c28c127: 2013-08-19 Konstantin Kolosovsky - IDEA-94942 Short add, revert commands refactoring
e06a346: 2013-08-19 Konstantin Kolosovsky - IDEA-94942 Revert action Fixed status command for single file Fixed status result parsing for normal (non-modified versioned) file
0f66f9d: 2013-08-19 Konstantin Kolosovsky - IDEA-94942 Logging and comments for some commands
c928fe5: 2013-08-16 Konstantin Kolosovsky - IDEA-94942 Force command line client usage if working copy of svn 1.8 format detected
d6e4e38: 2013-08-16 Konstantin Kolosovsky - IDEA-94942 Fixed "add" action output parsing for binary files
1236be5: 2013-08-15 Konstantin Kolosovsky - IDEA-94942 Small refactoring and fixes after review
e05b576: 2013-08-15 Konstantin Kolosovsky - IDEA-94942 "Add" action for files and directories
ccf6085: 2013-08-15 Konstantin Kolosovsky - IDEA-94942 "Subversion" -> "Show History" for files and folders
aba7390: 2013-08-14 Konstantin Kolosovsky - IDEA-94942 Add command refactored
555d597: 2013-08-14 Konstantin Kolosovsky - IDEA-94942 Fixing status, info commands to use correct arguments
b083f31: 2013-08-14 Konstantin Kolosovsky - IDEA-94942 Diff provider (without revision properties) Small command refactoring
f657dc8: 2013-08-13 Konstantin Kolosovsky - IDEA-94942 Authentication updates Authentication for remote status command Result parsing for remote info command 2 way SSL support (could be issues with ordinary password entering after 2 way SSL)
2785122: 2013-07-29 Konstantin Kolosovsky - IDEA-94942 initial svn 1.8 support with already existing command line functionality
857bfd5: 2013-08-12 max-kammerer - Update CoreJavaFileManagerTest.java
13539bc: 2013-08-09 Mikhael Bogdanov - CoreJavaFileManager.findClass: properly resolve $ in inner class names
Change-Id: Ica3d3d647183983bcd88ce2fb3450deb86343cdb
Diffstat (limited to 'plugins/svn4idea')
120 files changed, 3384 insertions, 2183 deletions
diff --git a/plugins/svn4idea/bindSvn/bindSvn.iml b/plugins/svn4idea/bindSvn/bindSvn.iml deleted file mode 100644 index 846587cef36f..000000000000 --- a/plugins/svn4idea/bindSvn/bindSvn.iml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<module type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" inherit-compiler-output="true"> - <exclude-output /> - <content url="file://$MODULE_DIR$"> - <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> - </content> - <orderEntry type="inheritedJdk" /> - <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="module" module-name="platform-api" /> - <orderEntry type="module" module-name="vcs-api" /> - <orderEntry type="module-library"> - <library> - <CLASSES> - <root url="jar://$MODULE_DIR$/lib/javahl.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$MODULE_DIR$/lib/javahlsrc.zip!/src" /> - </SOURCES> - </library> - </orderEntry> - </component> -</module> - diff --git a/plugins/svn4idea/bindSvn/lib/javahl.jar b/plugins/svn4idea/bindSvn/lib/javahl.jar Binary files differdeleted file mode 100644 index a0c5370065af..000000000000 --- a/plugins/svn4idea/bindSvn/lib/javahl.jar +++ /dev/null diff --git a/plugins/svn4idea/bindSvn/lib/javahlsrc.zip b/plugins/svn4idea/bindSvn/lib/javahlsrc.zip Binary files differdeleted file mode 100644 index 9c50118203b7..000000000000 --- a/plugins/svn4idea/bindSvn/lib/javahlsrc.zip +++ /dev/null diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindClient.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindClient.java deleted file mode 100644 index c155b78ddc53..000000000000 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindClient.java +++ /dev/null @@ -1,925 +0,0 @@ -/* - * Copyright 2000-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.idea.svn; - -import org.tigris.subversion.javahl.*; - -import java.io.OutputStream; -import java.util.Map; - -/** - * Created with IntelliJ IDEA. - * User: Irina.Chernushina - * Date: 2/5/13 - * Time: 3:08 PM - */ -public class SvnBindClient implements SVNClientInterface { - private final String myExecutablePath; - private CommitEventHandler myHandler; - private AuthenticationCallback myAuthenticationCallback; - - public SvnBindClient(String path) { - myExecutablePath = path; - } - - @Override - public void dispose() { - } - - @Override - public Version getVersion() { - // todo real version - throw new UnsupportedOperationException(); - } - - @Override - public String getAdminDirectoryName() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isAdminDirectory(String name) { - throw new UnsupportedOperationException(); - } - - @Override - public String getLastPath() { - throw new UnsupportedOperationException(); - } - - @Override - public Status singleStatus(String path, boolean onServer) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public Status[] status(String path, boolean descend, boolean onServer, boolean getAll) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public Status[] status(String path, boolean descend, boolean onServer, boolean getAll, boolean noIgnore) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public Status[] status(String path, boolean descend, boolean onServer, boolean getAll, boolean noIgnore, boolean ignoreExternals) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void status(String path, - int depth, - boolean onServer, - boolean getAll, - boolean noIgnore, - boolean ignoreExternals, - String[] changelists, - StatusCallback callback) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public DirEntry[] list(String url, Revision revision, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public DirEntry[] list(String url, Revision revision, Revision pegRevision, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void list(String url, - Revision revision, - Revision pegRevision, - int depth, - int direntFields, - boolean fetchLocks, - ListCallback callback) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void username(String username) { - throw new UnsupportedOperationException(); - } - - @Override - public void password(String password) { - throw new UnsupportedOperationException(); - } - - @Override - public void setPrompt(PromptUserPassword prompt) { - throw new UnsupportedOperationException(); - } - - @Override - public LogMessage[] logMessages(String path, Revision revisionStart, Revision revisionEnd) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public LogMessage[] logMessages(String path, Revision revisionStart, Revision revisionEnd, boolean stopOnCopy) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public LogMessage[] logMessages(String path, Revision revisionStart, Revision revisionEnd, boolean stopOnCopy, boolean discoverPath) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public LogMessage[] logMessages(String path, - Revision revisionStart, - Revision revisionEnd, - boolean stopOnCopy, - boolean discoverPath, - long limit) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void logMessages(String path, - Revision pegRevision, - Revision revisionStart, - Revision revisionEnd, - boolean stopOnCopy, - boolean discoverPath, - boolean includeMergedRevisions, - String[] revProps, - long limit, - LogMessageCallback callback) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void logMessages(String path, - Revision pegRevision, - RevisionRange[] ranges, - boolean stopOnCopy, - boolean discoverPath, - boolean includeMergedRevisions, - String[] revProps, - long limit, - LogMessageCallback callback) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long checkout(String moduleName, String destPath, Revision revision, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long checkout(String moduleName, - String destPath, - Revision revision, - Revision pegRevision, - boolean recurse, - boolean ignoreExternals) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long checkout(String moduleName, - String destPath, - Revision revision, - Revision pegRevision, - int depth, - boolean ignoreExternals, - boolean allowUnverObstructions) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void notification(Notify notify) { - throw new UnsupportedOperationException(); - } - - @Override - public void notification2(Notify2 notify) { - throw new UnsupportedOperationException(); - } - - @Override - public void setConflictResolver(ConflictResolverCallback listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void setProgressListener(ProgressListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void commitMessageHandler(CommitMessage messageHandler) { - throw new UnsupportedOperationException(); - } - - @Override - public void remove(String[] path, String message, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void remove(String[] path, String message, boolean force, boolean keepLocal, Map revpropTable) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void revert(String path, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void revert(String path, int depth, String[] changelists) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void add(String path, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void add(String path, boolean recurse, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void add(String path, int depth, boolean force, boolean noIgnores, boolean addParents) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long update(String path, Revision revision, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long[] update(String[] path, Revision revision, boolean recurse, boolean ignoreExternals) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long update(String path, - Revision revision, - int depth, - boolean depthIsSticky, - boolean ignoreExternals, - boolean allowUnverObstructions) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long[] update(String[] path, - Revision revision, - int depth, - boolean depthIsSticky, - boolean ignoreExternals, - boolean allowUnverObstructions) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long commit(String[] path, String message, boolean recurse) throws ClientException { - return commit(path, message, recurse? 3 : 0, false, false, null, null); - } - - @Override - public long commit(String[] path, String message, boolean recurse, boolean noUnlock) throws ClientException { - return commit(path, message, recurse? 3 : 0, noUnlock, false, null, null); - } - - @Override - public long commit(String[] path, - String message, - int depth, - boolean noUnlock, - boolean keepChangelist, - String[] changelists, - Map revpropTable) throws ClientException { - final long commit = new SvnCommitRunner(myExecutablePath, myHandler, myAuthenticationCallback). - commit(path, message, depth, noUnlock, keepChangelist, changelists, revpropTable); - if (commit < 0) { - throw new BindClientException("Wrong committed revision number: " + commit, null, -1); - } - return commit; - } - - @Override - public void copy(CopySource[] sources, - String destPath, - String message, - boolean copyAsChild, - boolean makeParents, - boolean ignoreExternals, - Map revpropTable) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void copy(CopySource[] sources, String destPath, String message, boolean copyAsChild, boolean makeParents, Map revpropTable) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void copy(String srcPath, String destPath, String message, Revision revision) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void move(String[] srcPaths, - String destPath, - String message, - boolean force, - boolean moveAsChild, - boolean makeParents, - Map revpropTable) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void move(String srcPath, String destPath, String message, Revision ignored, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void move(String srcPath, String destPath, String message, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void mkdir(String[] path, String message, boolean makeParents, Map revpropTable) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void mkdir(String[] path, String message) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void cleanup(String path) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void resolve(String path, int depth, int conflictResult) throws SubversionException { - throw new UnsupportedOperationException(); - } - - @Override - public void resolved(String path, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long doExport(String srcPath, String destPath, Revision revision, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long doExport(String srcPath, - String destPath, - Revision revision, - Revision pegRevision, - boolean force, - boolean ignoreExternals, - boolean recurse, - String nativeEOL) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long doExport(String srcPath, - String destPath, - Revision revision, - Revision pegRevision, - boolean force, - boolean ignoreExternals, - int depth, - String nativeEOL) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long doSwitch(String path, String url, Revision revision, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public long doSwitch(String path, - String url, - Revision revision, - Revision pegRevision, - int depth, - boolean depthIsSticky, - boolean ignoreExternals, - boolean allowUnverObstructions) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void doImport(String path, String url, String message, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void doImport(String path, - String url, - String message, - int depth, - boolean noIgnore, - boolean ignoreUnknownNodeTypes, - Map revpropTable) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public String[] suggestMergeSources(String path, Revision pegRevision) throws SubversionException { - throw new UnsupportedOperationException(); - } - - @Override - public void merge(String path1, Revision revision1, String path2, Revision revision2, String localPath, boolean force, boolean recurse) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void merge(String path1, - Revision revision1, - String path2, - Revision revision2, - String localPath, - boolean force, - boolean recurse, - boolean ignoreAncestry, - boolean dryRun) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void merge(String path1, - Revision revision1, - String path2, - Revision revision2, - String localPath, - boolean force, - int depth, - boolean ignoreAncestry, - boolean dryRun, - boolean recordOnly) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void merge(String path, - Revision pegRevision, - Revision revision1, - Revision revision2, - String localPath, - boolean force, - boolean recurse, - boolean ignoreAncestry, - boolean dryRun) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void merge(String path, - Revision pegRevision, - RevisionRange[] revisions, - String localPath, - boolean force, - int depth, - boolean ignoreAncestry, - boolean dryRun, - boolean recordOnly) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void mergeReintegrate(String path, Revision pegRevision, String localPath, boolean dryRun) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public Mergeinfo getMergeinfo(String path, Revision pegRevision) throws SubversionException { - throw new UnsupportedOperationException(); - } - - @Override - public void getMergeinfoLog(int kind, - String pathOrUrl, - Revision pegRevision, - String mergeSourceUrl, - Revision srcPegRevision, - boolean discoverChangedPaths, - int depth, - String[] revProps, - LogMessageCallback callback) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void getMergeinfoLog(int kind, - String pathOrUrl, - Revision pegRevision, - String mergeSourceUrl, - Revision srcPegRevision, - boolean discoverChangedPaths, - String[] revProps, - LogMessageCallback callback) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void diff(String target1, Revision revision1, String target2, Revision revision2, String outFileName, boolean recurse) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void diff(String target1, - Revision revision1, - String target2, - Revision revision2, - String outFileName, - boolean recurse, - boolean ignoreAncestry, - boolean noDiffDeleted, - boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void diff(String target1, - Revision revision1, - String target2, - Revision revision2, - String relativeToDir, - String outFileName, - int depth, - String[] changelists, - boolean ignoreAncestry, - boolean noDiffDeleted, - boolean force, - boolean copiesAsAdds) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void diff(String target1, - Revision revision1, - String target2, - Revision revision2, - String relativeToDir, - String outFileName, - int depth, - String[] changelists, - boolean ignoreAncestry, - boolean noDiffDeleted, - boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void diff(String target, - Revision pegRevision, - Revision startRevision, - Revision endRevision, - String outFileName, - boolean recurse, - boolean ignoreAncestry, - boolean noDiffDeleted, - boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void diff(String target, - Revision pegRevision, - Revision startRevision, - Revision endRevision, - String relativeToDir, - String outFileName, - int depth, - String[] changelists, - boolean ignoreAncestry, - boolean noDiffDeleted, - boolean force, - boolean copiesAsAdds) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void diff(String target, - Revision pegRevision, - Revision startRevision, - Revision endRevision, - String relativeToDir, - String outFileName, - int depth, - String[] changelists, - boolean ignoreAncestry, - boolean noDiffDeleted, - boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void diffSummarize(String target1, - Revision revision1, - String target2, - Revision revision2, - int depth, - String[] changelists, - boolean ignoreAncestry, - DiffSummaryReceiver receiver) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void diffSummarize(String target, - Revision pegRevision, - Revision startRevision, - Revision endRevision, - int depth, - String[] changelists, - boolean ignoreAncestry, - DiffSummaryReceiver receiver) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public PropertyData[] properties(String path) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public PropertyData[] properties(String path, Revision revision) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public PropertyData[] properties(String path, Revision revision, Revision pegRevision) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void properties(String path, Revision revision, Revision pegRevision, int depth, String[] changelists, ProplistCallback callback) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertySet(String path, String name, String value, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertySet(String path, String name, String value, boolean recurse, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertySet(String path, String name, byte[] value, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertySet(String path, String name, byte[] value, boolean recurse, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertySet(String path, String name, String value, int depth, String[] changelists, boolean force, Map revpropTable) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertyRemove(String path, String name, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertyRemove(String path, String name, int depth, String[] changelists) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertyCreate(String path, String name, String value, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertyCreate(String path, String name, String value, boolean recurse, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertyCreate(String path, String name, byte[] value, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertyCreate(String path, String name, byte[] value, boolean recurse, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void propertyCreate(String path, String name, String value, int depth, String[] changelists, boolean force) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public PropertyData revProperty(String path, String name, Revision rev) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public PropertyData[] revProperties(String path, Revision rev) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void setRevProperty(String path, String name, Revision rev, String value, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void setRevProperty(String path, String name, Revision rev, String value, String originalValue, boolean force) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public PropertyData propertyGet(String path, String name) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public PropertyData propertyGet(String path, String name, Revision revision) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public PropertyData propertyGet(String path, String name, Revision revision, Revision pegRevision) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public byte[] fileContent(String path, Revision revision) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public byte[] fileContent(String path, Revision revision, Revision pegRevision) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void streamFileContent(String path, Revision revision, Revision pegRevision, int bufferSize, OutputStream stream) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void relocate(String from, String to, String path, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public byte[] blame(String path, Revision revisionStart, Revision revisionEnd) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void blame(String path, Revision revisionStart, Revision revisionEnd, BlameCallback callback) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void blame(String path, Revision pegRevision, Revision revisionStart, Revision revisionEnd, BlameCallback callback) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void blame(String path, - Revision pegRevision, - Revision revisionStart, - Revision revisionEnd, - boolean ignoreMimeType, - boolean includeMergedRevisions, - BlameCallback2 callback) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void blame(String path, - Revision pegRevision, - Revision revisionStart, - Revision revisionEnd, - boolean ignoreMimeType, - boolean includeMergedRevisions, - BlameCallback3 callback) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void setConfigDirectory(String configDir) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public String getConfigDirectory() throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void cancelOperation() throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public Info info(String path) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void addToChangelist(String[] paths, String changelist, int depth, String[] changelists) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void removeFromChangelists(String[] paths, int depth, String[] changelists) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void getChangelists(String rootPath, String[] changelists, int depth, ChangelistCallback callback) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void lock(String[] path, String comment, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void unlock(String[] path, boolean force) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public Info2[] info2(String pathOrUrl, Revision revision, Revision pegRevision, boolean recurse) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void info2(String pathOrUrl, Revision revision, Revision pegRevision, int depth, String[] changelists, InfoCallback callback) - throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public String getVersionInfo(String path, String trailUrl, boolean lastChanged) throws ClientException { - throw new UnsupportedOperationException(); - } - - @Override - public void upgrade(String path) throws ClientException { - throw new UnsupportedOperationException(); - } - - public void setHandler(CommitEventHandler handler) { - myHandler = handler; - } - - public void setAuthenticationCallback(AuthenticationCallback authenticationCallback) { - myAuthenticationCallback = authenticationCallback; - } -} diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/status/LockWrapper.java b/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/status/LockWrapper.java deleted file mode 100644 index f4e0f4cf2269..000000000000 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/status/LockWrapper.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2000-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.idea.svn.status; - -import org.apache.subversion.javahl.types.Lock; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.util.Date; - -/** - * Created with IntelliJ IDEA. - * User: Irina.Chernushina - * Date: 2/21/12 - * Time: 2:33 PM - */ -public class LockWrapper { - private String myPath; - private String myID; - private String myOwner; - private String myComment; - private Date myCreationDate; - private Date myExpirationDate; - - public LockWrapper(String path, String ID, String owner, String comment, Date creationDate, Date expirationDate) { - myPath = path; - myID = ID; - myOwner = owner; - myComment = comment; - myCreationDate = creationDate; - myExpirationDate = expirationDate; - } - - public LockWrapper() { - } - - public String getPath() { - return myPath; - } - - public void setPath(String path) { - myPath = path; - } - - public String getID() { - return myID; - } - - public void setID(String ID) { - myID = ID; - } - - public String getOwner() { - return myOwner; - } - - public void setOwner(String owner) { - myOwner = owner; - } - - public String getComment() { - return myComment; - } - - public void setComment(String comment) { - myComment = comment; - } - - public Date getCreationDate() { - return myCreationDate; - } - - public void setCreationDate(Date creationDate) { - myCreationDate = creationDate; - } - - public Date getExpirationDate() { - return myExpirationDate; - } - - public void setExpirationDate(Date expirationDate) { - myExpirationDate = expirationDate; - } - - public org.tigris.subversion.javahl.Lock create() { - final Date creation = getCreationDate(); - final Date expiration = getExpirationDate(); - final Lock newLock = new Lock(getOwner(), getPath(), getID(), getComment(), creation == null ? 0 : creation.getTime(), - expiration == null ? 0 : expiration.getTime()); - try { - final Constructor<org.tigris.subversion.javahl.Lock> constructor = org.tigris.subversion.javahl.Lock.class.getConstructor(Lock.class); - constructor.setAccessible(true); - return constructor.newInstance(newLock); - } - catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } - catch (InvocationTargetException e) { - throw new RuntimeException(e); - } - catch (InstantiationException e) { - throw new RuntimeException(e); - } - catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } -} diff --git a/plugins/svn4idea/bindSvn/src/org/tigris/subversion/javahl/BindClientException.java b/plugins/svn4idea/bindSvn/src/org/tigris/subversion/javahl/BindClientException.java deleted file mode 100644 index f8acc17d0558..000000000000 --- a/plugins/svn4idea/bindSvn/src/org/tigris/subversion/javahl/BindClientException.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2000-2013 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.tigris.subversion.javahl; - -import org.jetbrains.annotations.NotNull; - -/** - * Created with IntelliJ IDEA. - * User: Irina.Chernushina - * Date: 2/25/13 - * Time: 6:29 PM - */ -public class BindClientException extends ClientException { - private Throwable myCause; - - public BindClientException(String message, String source, int aprError) { - super(message, source, aprError); - } - - public BindClientException(org.apache.subversion.javahl.ClientException ex) { - super(ex); - } - - public static BindClientException create(@NotNull final Throwable t, final int code) { - final BindClientException exception = new BindClientException(t.getMessage(), null, code); - exception.myCause = t; - return exception; - } - - @Override - public Throwable getCause() { - return myCause; - } -} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/ForNestedRootChecker.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/ForNestedRootChecker.java index 4559a3e603e2..6a62276b3a1a 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/ForNestedRootChecker.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/ForNestedRootChecker.java @@ -49,11 +49,9 @@ public class ForNestedRootChecker { private static class UrlConstructor { final SvnVcs myVcs; - final SVNWCClient myClient; private UrlConstructor(final SvnVcs vcs) { myVcs = vcs; - myClient = myVcs.createWCClient(); } @Nullable diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/RootsToWorkingCopies.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/RootsToWorkingCopies.java index 6cd0df2744d1..5ca7f9eaa39b 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/RootsToWorkingCopies.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/RootsToWorkingCopies.java @@ -123,52 +123,31 @@ public class RootsToWorkingCopies implements VcsListener { @Nullable private WorkingCopy calculateRoot(final VirtualFile root) { + File workingCopyRoot = SvnUtil.getWorkingCopyRootNew(new File(root.getPath())); WorkingCopy workingCopy = null; - final File ioFile = new File(root.getPath()); - File workingCopyRoot = null; - try { - workingCopyRoot = SVNWCUtil.getWorkingCopyRoot(ioFile, true); - if (workingCopyRoot != null) { - // ok to use low-level 1.6 API, 1.7 is checked below - 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, false); - } - } finally { - wcAccess.close(); - } - } - } - catch (SVNException e) { - // - } - if (workingCopy == null) { - workingCopyRoot = SvnUtil.getWcCopyRootIf17(ioFile, null); - if (workingCopyRoot != null) { - final SVNInfo svnInfo; - try { - svnInfo = SvnVcs.getInstance(myProject).createWCClient().doInfo(workingCopyRoot, SVNRevision.UNDEFINED); - workingCopy = new WorkingCopy(workingCopyRoot, svnInfo.getURL(), true); - } - catch (SVNException e) { - // - } + + if (workingCopyRoot != null) { + final SVNInfo svnInfo = myVcs.getInfo(workingCopyRoot); + + if (svnInfo != null && svnInfo.getURL() != null) { + workingCopy = new WorkingCopy(workingCopyRoot, svnInfo.getURL(), true); } } + + return registerWorkingCopy(root, workingCopy); + } + + private WorkingCopy registerWorkingCopy(@NotNull VirtualFile root, @Nullable WorkingCopy resolvedWorkingCopy) { synchronized (myLock) { - if (workingCopy == null) { + if (resolvedWorkingCopy == null) { myRootMapping.remove(root); myUnversioned.add(root); } else { myUnversioned.remove(root); - myRootMapping.put(root, workingCopy); + myRootMapping.put(root, resolvedWorkingCopy); } } - return workingCopy; + return resolvedWorkingCopy; } public void clear() { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAbstractWriteOperationLocks.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAbstractWriteOperationLocks.java index b4fd10492dfb..29e87422bb53 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAbstractWriteOperationLocks.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAbstractWriteOperationLocks.java @@ -36,6 +36,7 @@ import java.util.concurrent.locks.ReentrantLock; * Date: 10/19/12 * Time: 12:09 PM */ +// TODO: Such locking functionality is not required anymore. Likely to be removed (together with SvnProxies). public abstract class SvnAbstractWriteOperationLocks { private final long myTimeout; private final static Map<String, Lock> myLockMap = new HashMap<String, Lock>(); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationNotifier.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationNotifier.java index 8a94f0076acf..4efcce6f0db6 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationNotifier.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationNotifier.java @@ -350,7 +350,9 @@ public class SvnAuthenticationNotifier extends GenericNotifierImpl<SvnAuthentica } SvnInteractiveAuthenticationProvider.clearCallState(); try { + // start svnkit authentication cycle SvnVcs.getInstance(project).createWCClient(manager).doInfo(url, SVNRevision.UNDEFINED, SVNRevision.HEAD); + //SvnVcs.getInstance(project).getInfo(url, SVNRevision.HEAD, manager); } catch (SVNAuthenticationException e) { log(e); return false; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties index c306386eb0f8..61a5bb26f663 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties @@ -537,6 +537,7 @@ 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 +dialog.show.svn.map.table.version18.text=1.8 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}).\ diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProvider.java index dc8cbb1cce8b..b28ebd53dde2 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProvider.java @@ -85,7 +85,7 @@ public class SvnChangeProvider implements ChangeProvider { statusReceiver.addListener(context); statusReceiver.addListener(nestedCopiesBuilder); - final SvnRecursiveStatusWalker walker = new SvnRecursiveStatusWalker(myVcs.getProject(), statusReceiver.getMulticaster(), partner); + final SvnRecursiveStatusWalker walker = new SvnRecursiveStatusWalker(myVcs, statusReceiver.getMulticaster(), partner); for (FilePath path : zipper.getRecursiveDirs()) { walker.go(path, SVNDepth.INFINITY); @@ -180,7 +180,7 @@ public class SvnChangeProvider implements ChangeProvider { 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(myVcs.getProject(), context, partner); + final SvnRecursiveStatusWalker walker = new SvnRecursiveStatusWalker(myVcs, context, partner); walker.go(path, recursive ? SVNDepth.INFINITY : SVNDepth.IMMEDIATES); processCopiedAndDeleted(context, null); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java index 40c34d394590..9689dcee4a71 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java @@ -259,23 +259,15 @@ class SvnChangeProviderContext implements StatusReceiver { 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); + final SVNInfo svnInfo = myVcs.getInfo(file); + + if (svnInfo != null) { + 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, SvnVcs.getKey()); } - catch (SVNException e) { - final SVNErrorCode errorCode = e.getErrorMessage().getErrorCode(); - if (SVNErrorCode.WC_PATH_NOT_FOUND.equals(errorCode) || - SVNErrorCode.UNVERSIONED_RESOURCE.equals(errorCode) || SVNErrorCode.WC_NOT_WORKING_COPY.equals(errorCode)) { - 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, - SvnVcs.getKey()); } private void checkSwitched(final FilePath filePath, final ChangelistBuilder builder, final SVNStatus status, diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangelistListener.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangelistListener.java index ee326a42afb2..51d5dc486c51 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangelistListener.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangelistListener.java @@ -176,11 +176,9 @@ public class SvnChangelistListener implements ChangeListListener { } @Nullable - public static String getCurrentMapping(final Project project, final File file) { - final SvnVcs vcs = SvnVcs.getInstance(project); - final SVNStatusClient statusClient = vcs.createStatusClient(); + public static String getCurrentMapping(final SvnVcs vcs, final File file) { try { - final SVNStatus status = statusClient.doStatus(file, false); + final SVNStatus status = vcs.getFactory(file).createStatusClient().doStatus(file, false); return status == null ? null : status.getChangelistName(); } catch (SVNException e) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfigurable.form b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfigurable.form index e4f0e7c8ae7e..55f7512fbb81 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfigurable.form +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfigurable.form @@ -18,7 +18,7 @@ <properties/> <border type="none"/> <children> - <grid id="2ae3a" layout-manager="GridLayoutManager" row-count="6" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> + <grid id="2ae3a" layout-manager="GridLayoutManager" row-count="5" 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> <tabbedpane title="General"/> @@ -29,7 +29,7 @@ <grid id="a4729" layout-manager="GridLayoutManager" row-count="3" 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> - <grid row="3" column="0" row-span="1" col-span="3" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> + <grid row="2" column="0" row-span="1" col-span="3" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> </constraints> <properties/> <border type="none"/> @@ -87,7 +87,7 @@ </grid> <component id="df054" class="javax.swing.JCheckBox" binding="myUseDefaultCheckBox"> <constraints> - <grid row="2" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/> + <grid row="1" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/> </constraints> <properties> <text resource-bundle="org/jetbrains/idea/svn/SvnBundle" key="checkbox.configure.use.system.default.configuration.directory"/> @@ -96,7 +96,7 @@ <grid id="137aa" layout-manager="GridLayoutManager" row-count="1" 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> - <grid row="5" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> + <grid row="4" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> </constraints> <properties/> <border type="none"/> @@ -123,36 +123,29 @@ </grid> <hspacer id="e8f24"> <constraints> - <grid row="5" column="2" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/> + <grid row="4" 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> <vspacer id="aa6b2"> <constraints> - <grid row="4" column="0" row-span="1" col-span="2" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/> + <grid row="3" column="0" row-span="1" col-span="2" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/> </constraints> </vspacer> - <component id="300b5" 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="Subversion 1.7 Acceleration"/> - </properties> - </component> <grid id="12735" layout-manager="GridLayoutManager" row-count="1" 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> - <grid row="1" column="0" row-span="1" col-span="3" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> + <grid row="0" column="0" row-span="1" col-span="3" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> </constraints> <properties/> <border type="none"/> <children> <component id="3ecd1" class="com.intellij.ui.components.JBCheckBox" binding="myWithCommandLineClient"> <constraints> - <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="2" use-parent-layout="false"/> + <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="with command line client:"/> + <margin top="2" left="3" bottom="2" right="3"/> + <text value="Use command line client:"/> </properties> </component> <component id="44a8" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myCommandLineClient"> diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnContentRevision.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnContentRevision.java index 38c099ba4136..cff64beea8b2 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnContentRevision.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnContentRevision.java @@ -31,6 +31,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc.SVNStatus; +import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.File; import java.io.IOException; @@ -113,7 +114,8 @@ public class SvnContentRevision implements ContentRevision, MarkerVcsContentRevi if (lock.exists()) { throw new VcsException("Can not access file base revision contents: administrative area is locked"); } - return SvnUtil.getFileContents(myVcs, file.getPath(), false, myUseBaseRevision ? SVNRevision.BASE : myRevision, SVNRevision.UNDEFINED); + return SvnUtil.getFileContents(myVcs, SvnTarget.fromFile(file), myUseBaseRevision ? SVNRevision.BASE : myRevision, + SVNRevision.UNDEFINED); } @NotNull diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnDiffProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnDiffProvider.java index 2a8a00aa1ae6..813d41c48e24 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnDiffProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnDiffProvider.java @@ -17,6 +17,7 @@ package org.jetbrains.idea.svn; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.actions.VcsContextFactory; import com.intellij.openapi.vcs.changes.ContentRevision; import com.intellij.openapi.vcs.diff.DiffMixin; @@ -26,8 +27,10 @@ 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.svn.commandLine.SvnCommandLineStatusClient; import org.jetbrains.idea.svn.history.LatestExistentSearcher; import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.wc.*; @@ -43,19 +46,12 @@ public class SvnDiffProvider implements DiffProvider, DiffMixin { } public VcsRevisionNumber getCurrentRevision(VirtualFile file) { - final SVNWCClient client = myVcs.createWCClient(); - try { - final SVNInfo svnInfo = client.doInfo(new File(file.getPresentableUrl()), SVNRevision.UNDEFINED); - if (svnInfo == null) return null; - if (SVNRevision.UNDEFINED.equals(svnInfo.getCommittedRevision()) && svnInfo.getCopyFromRevision() != null) { - return new SvnRevisionNumber(svnInfo.getCopyFromRevision()); - } - return new SvnRevisionNumber(svnInfo.getRevision()); - } - catch (SVNException e) { - LOG.debug(e); // most likely the file is unversioned - return null; + final SVNInfo svnInfo = myVcs.getInfo(new File(file.getPresentableUrl())); + if (svnInfo == null) return null; + if (SVNRevision.UNDEFINED.equals(svnInfo.getCommittedRevision()) && svnInfo.getCopyFromRevision() != null) { + return new SvnRevisionNumber(svnInfo.getCopyFromRevision()); } + return new SvnRevisionNumber(svnInfo.getRevision()); } @Override @@ -65,53 +61,36 @@ public class SvnDiffProvider implements DiffProvider, DiffMixin { } 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) && + final SVNInfo svnInfo = myVcs.getInfo(path); + if (svnInfo == null) { + return null; + } + + 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)); - } + 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); + } + + try { + final String message = getCommitMessage(path); return new VcsRevisionDescriptionImpl(new SvnRevisionNumber(svnInfo.getCommittedRevision()), svnInfo.getCommittedDate(), svnInfo.getAuthor(), message); } - catch (SVNException e) { + catch (VcsException 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 String getCommitMessage(File path) throws VcsException { + SVNPropertyData property = + myVcs.getFactory(path).createPropertyClient().getProperty(path, COMMIT_MESSAGE, true, null, SVNRevision.BASE); - private void handle(SVNPropertyData data) { - if (COMMIT_MESSAGE.equals(data.getName())) { - message[0] = data.getValue().getString(); - } - } - }); - return message[0]; + return property != null ? SVNPropertyValue.getPropertyAsString(property.getValue()) : null; } private static ItemLatestState defaultResult() { @@ -135,18 +114,45 @@ public class SvnDiffProvider implements DiffProvider, DiffMixin { 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())) { + SVNStatus svnStatus = getFileStatus(new File(selectedFile.getPresentableUrl()), false); + if (svnStatus != null && svnRevision.equals(svnStatus.getRevision())) { return SvnContentRevision.createBaseRevision(myVcs, filePath, svnRevision); - } + } + return SvnContentRevision.createRemote(myVcs, filePath, svnRevision); + } + + private SVNStatus getFileStatus(File file, boolean remote) { + WorkingCopyFormat format = myVcs.getWorkingCopyFormat(file); + + return WorkingCopyFormat.ONE_DOT_EIGHT.equals(format) ? getStatusCommandLine(file, remote) : getStatusWithSvnKit(file, remote); + } + + private SVNStatus getStatusWithSvnKit(File file, boolean remote) { + SVNStatus result = null; + + try { + result = myVcs.createStatusClient().doStatus(file, remote, false); } catch (SVNException e) { LOG.debug(e); // most likely the file is unversioned } - return SvnContentRevision.createRemote(myVcs, filePath, svnRevision); + + return result; + } + + private SVNStatus getStatusCommandLine(File file, boolean remote) { + SVNStatus result = null; + + try { + result = new SvnCommandLineStatusClient(myVcs.getProject()).doStatus(file, remote, false); + } + catch (SVNException e) { + LOG.debug(e); + } + + return result; } public ItemLatestState getLastRevision(FilePath filePath) { @@ -159,36 +165,29 @@ public class SvnDiffProvider implements DiffProvider, DiffMixin { } private ItemLatestState getLastRevision(final File file) { - final SVNStatusClient client = myVcs.createStatusClient(); - try { - final SVNStatus svnStatus = client.doStatus(file, true); - if (svnStatus == null || itemExists(svnStatus) && SVNRevision.UNDEFINED.equals(svnStatus.getRemoteRevision())) { - // IDEADEV-21785 (no idea why this can happen) - final SVNInfo info = myVcs.createWCClient().doInfo(file, SVNRevision.HEAD); - if (info == null || info.getURL() == null) { - LOG.info("No SVN status returned for " + file.getPath()); - return defaultResult(); - } - return createResult(info.getCommittedRevision(), true, false); + final SVNStatus svnStatus = getFileStatus(file, true); + if (svnStatus == null || itemExists(svnStatus) && SVNRevision.UNDEFINED.equals(svnStatus.getRemoteRevision())) { + // IDEADEV-21785 (no idea why this can happen) + final SVNInfo info = myVcs.getInfo(file, SVNRevision.HEAD); + if (info == null || info.getURL() == null) { + LOG.info("No SVN status returned for " + file.getPath()); + return defaultResult(); } - final boolean exists = itemExists(svnStatus); - if (! exists) { - // get really latest revision - final LatestExistentSearcher searcher = new LatestExistentSearcher(myVcs, svnStatus.getURL()); - final long revision = searcher.getDeletionRevision(); + return createResult(info.getCommittedRevision(), true, false); + } + final boolean exists = itemExists(svnStatus); + 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); + return createResult(SVNRevision.create(revision), exists, false); } - catch (SVNException e) { - LOG.debug(e); // most likely the file is unversioned - return defaultResult(); + final SVNRevision remoteRevision = svnStatus.getRemoteRevision(); + if (remoteRevision != null) { + return createResult(remoteRevision, exists, false); } + return createResult(svnStatus.getRevision(), exists, false); } private boolean itemExists(SVNStatus svnStatus) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java index 062c412f2336..da1adc60327c 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java @@ -44,10 +44,7 @@ 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.SVNErrorMessage; -import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.SVNNodeKind; +import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.wc.SVNFileUtil; import org.tmatesoft.svn.core.wc.*; @@ -160,10 +157,10 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp } private class UUIDHelper { - private final SVNWCClient myWcClient; + private final SvnVcs myVcs; private UUIDHelper(final SvnVcs vcs) { - myWcClient = vcs.createWCClient(); + myVcs = vcs; } /** @@ -175,7 +172,7 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp final SVNInfo info1 = new RepeatSvnActionThroughBusy() { @Override protected void executeImpl() throws SVNException { - myT = myWcClient.doInfo(new File(dir.getPath()), SVNRevision.UNDEFINED); + myT = myVcs.getInfo(new File(dir.getPath())); } }.compute(); if (info1 == null || info1.getRepositoryUUID() == null) { @@ -244,10 +241,11 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp long srcTime = src.lastModified(); try { final boolean isUndo = isUndo(vcs); - final String list = isUndo ? null : SvnChangelistListener.getCurrentMapping(vcs.getProject(), src); + final String list = isUndo ? null : SvnChangelistListener.getCurrentMapping(vcs, src); - final boolean is17 = SvnUtil.is17CopyPart(src); - if (is17) { + WorkingCopyFormat format = vcs.getWorkingCopyFormat(src); + final boolean is17OrLater = WorkingCopyFormat.ONE_DOT_EIGHT.equals(format) || WorkingCopyFormat.ONE_DOT_SEVEN.equals(format); + if (is17OrLater) { SVNStatus srcStatus = getFileStatus(vcs, src); final File toDir = dst.getParentFile(); SVNStatus dstStatus = getFileStatus(vcs, toDir); @@ -283,29 +281,18 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp ourStatusesForUndoMove.add(SVNStatusType.STATUS_ADDED); } - private boolean for17move(SvnVcs vcs, final File src, final File dst, boolean undo, SVNStatus srcStatus) throws SVNException { + private boolean for17move(final SvnVcs vcs, final File src, final File dst, boolean undo, SVNStatus srcStatus) throws SVNException { if (srcStatus != null && srcStatus.getCopyFromURL() == null) { undo = false; } if (undo) { myUndoingMove = true; - final SVNWCClient wcClient = vcs.createWCClient(); - new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - wcClient.doRevert(dst, true); - } - }.execute(); + createRevertAction(vcs, dst, true).execute(); copyUnversionedMembersOfDirectory(src, dst); if (srcStatus == null || SvnVcs.svnStatusIsUnversioned(srcStatus)) { FileUtil.delete(src); } else { - new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - wcClient.doRevert(src, true); - } - }.execute(); + createRevertAction(vcs, src, true).execute(); } restoreFromUndoStorage(dst); } else { @@ -317,15 +304,9 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp copyFileOrDir(src, dst); } catch (IOException e) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), e); + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); } - final SVNWCClient wcClient = vcs.createWCClient(); - new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - wcClient.doDelete(src, true, false); - } - }.execute(); + createDeleteAction(vcs, src, true).execute(); return false; } moveFileWithSvn(vcs, src, dst); @@ -333,13 +314,16 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp return false; } - public static void moveFileWithSvn(SvnVcs vcs, File src, final File dst) throws SVNException { - final SVNCopyClient copyClient = vcs.createCopyClient(); - final SVNCopySource svnCopySource = new SVNCopySource(SVNRevision.UNDEFINED, SVNRevision.WORKING, src); + public static void moveFileWithSvn(final SvnVcs vcs, final File src, final File dst) throws SVNException { new RepeatSvnActionThroughBusy() { @Override protected void executeImpl() throws SVNException { - copyClient.doCopy(new SVNCopySource[]{svnCopySource}, dst, true, false, true); + try { + vcs.getFactory(src).createCopyMoveClient().copy(src, dst, false, true); + } + catch (VcsException e) { + wrapAndThrow(e); + } } }.execute(); } @@ -357,7 +341,7 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp copyFileOrDir(src, dst); } catch (IOException e) { - exc[0] = new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), e); + exc[0] = new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); return false; } } @@ -464,7 +448,7 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp * deleted: do nothing, return true (strange) */ public boolean delete(VirtualFile file) throws IOException { - SvnVcs vcs = getVCS(file); + final SvnVcs vcs = getVCS(file); if (vcs != null && SvnUtil.isAdminDirectory(file)) { return true; } @@ -476,12 +460,8 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp if (! SvnUtil.isSvnVersioned(vcs.getProject(), ioFile.getParentFile())) { return false; } - try { - if (SVNWCUtil.isWorkingCopyRoot(ioFile)) { - return false; - } - } catch (SVNException e) { - // + if (SvnUtil.isWorkingCopyRoot(ioFile)) { + return false; } SVNStatus status = getFileStatus(vcs, ioFile); @@ -507,13 +487,7 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp } if (SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_ADDED)) { try { - final SVNWCClient wcClient = vcs.createWCClient(); - new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - wcClient.doRevert(ioFile, false); - } - }.execute(); + createRevertAction(vcs, ioFile, false).execute(); } catch (SVNException e) { // ignore @@ -529,6 +503,41 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp } } + @NotNull + private RepeatSvnActionThroughBusy createRevertAction(@NotNull final SvnVcs vcs, @NotNull final File file, final boolean recursive) { + return new RepeatSvnActionThroughBusy() { + @Override + protected void executeImpl() throws SVNException { + try { + vcs.getFactory(file).createRevertClient().revert(new File[]{file}, SVNDepth.fromRecurse(recursive), null); + } + catch (VcsException e) { + wrapAndThrow(e); + } + } + }; + } + + @NotNull + private RepeatSvnActionThroughBusy createDeleteAction(@NotNull final SvnVcs vcs, @NotNull final File file, final boolean force) { + return new RepeatSvnActionThroughBusy() { + @Override + protected void executeImpl() throws SVNException { + try { + vcs.getFactory(file).createDeleteClient().delete(file, force); + } + catch (VcsException e) { + wrapAndThrow(e); + } + } + }; + } + + private static void wrapAndThrow(VcsException e) throws SVNException { + // TODO: probably we should wrap into new exception only if e.getCause is not SVNException + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.FS_GENERAL, e), e); + } + private boolean isAboveSourceOfCopyOrMove(final Project p, File ioFile) { for (MovedFileInfo file : myMovedFiles) { if (FileUtil.isAncestor(ioFile, file.mySrc, false)) return true; @@ -583,7 +592,6 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp if (! SvnUtil.isSvnVersioned(vcs.getProject(), ioDir) && ! pendingAdd) { return false; } - final SVNWCClient wcClient = vcs.createWCClient(); final File targetFile = new File(ioDir, name); SVNStatus status = getFileStatus(vcs, targetFile); @@ -603,12 +611,7 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp } try { if (isUndo(vcs)) { - new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - wcClient.doRevert(targetFile, false); - } - }.execute(); + createRevertAction(vcs, targetFile, false).execute(); return true; } myAddedFiles.putValue(vcs.getProject(), new AddedFileInfo(dir, name, null, recursive)); @@ -759,8 +762,6 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp return new Runnable() { @Override public void run() { - final SVNWCClient wcClient = vcs.createWCClient(); - final SVNCopyClient copyClient = vcs.createCopyClient(); for(VirtualFile file: filesToProcess) { final File ioFile = new File(file.getPath()); try { @@ -771,11 +772,15 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp protected void executeInternal() throws VcsException { try { // not recursive - final SVNCopySource[] copySource = {new SVNCopySource(SVNRevision.WORKING, SVNRevision.WORKING, copyFrom)}; new RepeatSvnActionThroughBusy() { @Override protected void executeImpl() throws SVNException { - copyClient.doCopy(copySource, ioFile, false, true, true); + try { + vcs.getFactory(copyFrom).createCopyMoveClient().copy(copyFrom, ioFile, true, false); + } + catch (VcsException e) { + wrapAndThrow(e); + } } }.execute(); } @@ -793,7 +798,12 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp new RepeatSvnActionThroughBusy() { @Override protected void executeImpl() throws SVNException { - wcClient.doAdd(ioFile, true, false, false, true); + try { + vcs.getFactory(ioFile).createAddClient().add(ioFile, null, false, false, true, null); + } + catch (VcsException e) { + wrapAndThrow(e); + } } }.execute(); } @@ -915,17 +925,11 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp final List<VcsException> exceptions) { return new Runnable() { public void run() { - final SVNWCClient wcClient = vcs.createWCClient(); for(FilePath file: filesToProcess) { VirtualFile vFile = file.getVirtualFile(); // for deleted directories final File ioFile = new File(file.getPath()); try { - new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - wcClient.doDelete(ioFile, true, false); - } - }.execute(); + createDeleteAction(vcs, ioFile, true).execute(); if (vFile != null && vFile.isValid() && vFile.isDirectory()) { vFile.refresh(true, true); VcsDirtyScopeManager.getInstance(project).dirDirtyRecursively(vFile); @@ -978,18 +982,15 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp private void fillDeletedFiles(Project project, List<Pair<FilePath, WorkingCopyFormat>> deletedFiles, Collection<FilePath> deleteAnyway) throws SVNException { final SvnVcs vcs = SvnVcs.getInstance(project); - final SVNStatusClient sc = vcs.createStatusClient(); final Collection<File> files = myDeletedFiles.remove(project); for (final File file : files) { - boolean isAdded = false; - final SVNStatus status; - status = new RepeatSvnActionThroughBusy() { - @Override - protected void executeImpl() throws SVNException { - myT = sc.doStatus(file, false); - } - }.compute(); - isAdded = SVNStatusType.STATUS_ADDED.equals(status.getNodeStatus()); + final SVNStatus status = new RepeatSvnActionThroughBusy() { + @Override + protected void executeImpl() throws SVNException { + myT = vcs.getFactory(file).createStatusClient().doStatus(file, false); + } + }.compute(); + boolean isAdded = SVNStatusType.STATUS_ADDED.equals(status.getNodeStatus()); final FilePath filePath = VcsContextFactory.SERVICE.getInstance().createFilePathOn(file); if (isAdded) { deleteAnyway.add(filePath); @@ -1034,18 +1035,12 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp } @Nullable - private static SVNStatus getFileStatus(SvnVcs vcs, File file) { - SVNStatusClient stClient = vcs.createStatusClient(); - return getFileStatus(file, stClient); - } - - @Nullable - private static SVNStatus getFileStatus(final File file, final SVNStatusClient stClient) { + private static SVNStatus getFileStatus(@NotNull final SvnVcs vcs, @NotNull final File file) { try { return new RepeatSvnActionThroughBusy() { @Override protected void executeImpl() throws SVNException { - myT = stClient.doStatus(file, false); + myT = vcs.getFactory(file).createStatusClient().doStatus(file, false); } }.compute(); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileUrlMappingImpl.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileUrlMappingImpl.java index 46545c43a4cc..68e7c07848c2 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileUrlMappingImpl.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileUrlMappingImpl.java @@ -312,8 +312,8 @@ public class SvnFileUrlMappingImpl implements SvnFileUrlMapping, PersistentState 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)); + myTopRoots.add(new RootUrlInfo(repoRoot, foundRoot.getInfo().getURL(), SvnFormatSelector.findRootAndGetFormat( + new File(foundRoot.getFile().getPath())), foundRoot.getFile(), vcsRoot)); } } } @@ -541,8 +541,9 @@ public class SvnFileUrlMappingImpl implements SvnFileUrlMapping, PersistentState 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); + final RootUrlInfo info = + new RootUrlInfo(svnInfo.getRepositoryRootURL(), svnInfo.getURL(), SvnFormatSelector.findRootAndGetFormat(svnInfo.getFile()), + copyRoot, vcsRoot); mapping.add(info); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFormatSelector.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFormatSelector.java index 94d21dbbbd9d..77783badc2f8 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFormatSelector.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFormatSelector.java @@ -16,6 +16,7 @@ package org.jetbrains.idea.svn; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Ref; import com.intellij.util.WaitForProgressToShow; @@ -37,6 +38,9 @@ import java.util.Collections; import java.util.Iterator; public class SvnFormatSelector implements ISVNAdminAreaFactorySelector { + + private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnFormatSelector"); + public Collection getEnabledFactories(File path, Collection factories, boolean writeAccess) throws SVNException { if (ApplicationManager.getApplication().isUnitTestMode()) { return factories; @@ -145,7 +149,19 @@ public class SvnFormatSelector implements ISVNAdminAreaFactorySelector { return newMode[0]; } + public static WorkingCopyFormat findRootAndGetFormat(final File path) { + File root = SvnUtil.getWorkingCopyRootNew(path); + + return root != null ? getWorkingCopyFormat(root) : WorkingCopyFormat.UNKNOWN; + } + public static WorkingCopyFormat getWorkingCopyFormat(final File path) { + WorkingCopyFormat format = SvnUtil.getFormat(path); + + return WorkingCopyFormat.UNKNOWN.equals(format) ? detectWithSvnKit(path) : format; + } + + private static WorkingCopyFormat detectWithSvnKit(File path) { try { final SvnWcGeneration svnWcGeneration = SvnOperationFactory.detectWcGeneration(path, true); if (SvnWcGeneration.V17.equals(svnWcGeneration)) return WorkingCopyFormat.ONE_DOT_SEVEN; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnProxies.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnProxies.java index 97b87efaa7f9..fcac3966c2d6 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnProxies.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnProxies.java @@ -29,6 +29,7 @@ import java.util.concurrent.atomic.AtomicReference; * Date: 10/19/12 * Time: 4:22 PM */ +// TODO: Likely to be removed (together with SvnAbstractWriteOperationLocks). public class SvnProxies { private final SvnAbstractWriteOperationLocks myLocks; private final AtomicReference<LearningProxy<SVNChangelistClientI, SVNException>> myChangelist; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java index 4a49dc16a153..8b518199f5be 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java @@ -47,13 +47,15 @@ import java.util.LinkedList; public class SvnRecursiveStatusWalker { private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnRecursiveStatusWalker"); private final StatusWalkerPartner myPartner; + private final SvnVcs myVcs; private final Project myProject; private final StatusReceiver myReceiver; private final LinkedList<MyItem> myQueue; private final MyHandler myHandler; - public SvnRecursiveStatusWalker(final Project project, final StatusReceiver receiver, final StatusWalkerPartner partner) { - myProject = project; + public SvnRecursiveStatusWalker(final SvnVcs vcs, final StatusReceiver receiver, final StatusWalkerPartner partner) { + myVcs = vcs; + myProject = vcs.getProject(); myReceiver = receiver; myPartner = partner; myQueue = new LinkedList<MyItem>(); @@ -61,7 +63,7 @@ public class SvnRecursiveStatusWalker { } public void go(final FilePath rootPath, final SVNDepth depth) throws SVNException { - final MyItem root = new MyItem(myProject, rootPath, depth, myPartner.createStatusClient(), false); + final MyItem root = new MyItem(myVcs, rootPath, depth, myPartner.createStatusClient(), false); myQueue.add(root); while (! myQueue.isEmpty()) { @@ -83,7 +85,7 @@ public class SvnRecursiveStatusWalker { } } else { try { - final SVNStatus status = item.getClient().doStatus(ioFile, false, false); + final SVNStatus status = item.getClient(ioFile).doStatus(ioFile, false, false); myReceiver.process(path, status); } catch (SVNException e) { handleStatusException(item, path, e); @@ -115,18 +117,20 @@ public class SvnRecursiveStatusWalker { private final Project myProject; private final FilePath myPath; private final SVNDepth myDepth; - private final SVNStatusClient myClient; private final SvnStatusClientI mySvnClient; + private final SvnStatusClientI myCommandLineClient; private final boolean myIsInnerCopyRoot; private final SvnConfiguration myConfiguration17; + private final SvnVcs myVcs; - private MyItem(Project project, FilePath path, SVNDepth depth, SVNStatusClient client, boolean isInnerCopyRoot) { - myProject = project; + private MyItem(SvnVcs vcs, FilePath path, SVNDepth depth, SVNStatusClient client, boolean isInnerCopyRoot) { + myVcs = vcs; + myProject = vcs.getProject(); myConfiguration17 = SvnConfiguration.getInstance(myProject); myPath = path; myDepth = depth; - myClient = client; mySvnClient = new SvnkitSvnStatusClient(client); + myCommandLineClient = new SvnCommandLineStatusClient(myProject); myIsInnerCopyRoot = isInnerCopyRoot; } @@ -138,20 +142,20 @@ public class SvnRecursiveStatusWalker { return myDepth; } - public SvnStatusClientI getClient() { - return mySvnClient; - } - public SvnStatusClientI getClient(final File file) { - if (! SVNDepth.INFINITY.equals(myDepth)) { - return mySvnClient; + WorkingCopyFormat format = myVcs.getWorkingCopyFormat(file); + + if (format == WorkingCopyFormat.ONE_DOT_EIGHT) { + return myCommandLineClient; } + // check format if (CheckJavaHL.isPresent() && SvnConfiguration.UseAcceleration.javaHL.equals(myConfiguration17.myUseAcceleration) && Svn17Detector.is17(myProject, file)) { return new JavaHLSvnStatusClient(myProject); - } else if (SvnConfiguration.UseAcceleration.commandLine.equals(myConfiguration17.myUseAcceleration) && Svn17Detector.is17(myProject, file)) { - return new SvnCommandLineStatusClient(myProject); + } else if (SvnConfiguration.UseAcceleration.commandLine.equals(myConfiguration17.myUseAcceleration)) { + // apply command line disregarding working copy format + return myCommandLineClient; } return mySvnClient; } @@ -186,7 +190,7 @@ public class SvnRecursiveStatusWalker { return true; } if (file.isDirectory() && new File(file, SVNFileUtil.getAdminDirectoryName()).exists()) { - final MyItem childItem = new MyItem(myProject, path, newDepth, myPartner.createStatusClient(), true); + final MyItem childItem = new MyItem(myVcs, path, newDepth, myPartner.createStatusClient(), true); myQueue.add(childItem); } else if (vf != null) { myReceiver.processUnversioned(vf); @@ -228,12 +232,13 @@ public class SvnRecursiveStatusWalker { } public void checkIfCopyRootWasReported(@Nullable final SVNStatus ioFileStatus, final File ioFile) { - if (! myMetCurrentItem && FileUtil.filesEqual(ioFile, myCurrentItem.getPath().getIOFile())) { + File itemFile = myCurrentItem.getPath().getIOFile(); + if (! myMetCurrentItem && FileUtil.filesEqual(ioFile, itemFile)) { myMetCurrentItem = true; SVNStatus statusInner; try { statusInner = ioFileStatus != null ? ioFileStatus : - myCurrentItem.getClient().doStatus(myCurrentItem.getPath().getIOFile(), false); + myCurrentItem.getClient(itemFile).doStatus(itemFile, false); } catch (SVNException e) { LOG.info(e); @@ -294,7 +299,7 @@ public class SvnRecursiveStatusWalker { //myReceiver.processUnversioned(vFile); //processRecursively(vFile, myCurrentItem.getDepth()); } else { - final MyItem childItem = new MyItem(myProject, new FilePathImpl(vFile), SVNDepth.INFINITY, + final MyItem childItem = new MyItem(myVcs, new FilePathImpl(vFile), SVNDepth.INFINITY, myPartner.createStatusClient(), true); myQueue.add(childItem); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnTestWriteOperationLocks.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnTestWriteOperationLocks.java index 853e995ca219..efea2f93de08 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnTestWriteOperationLocks.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnTestWriteOperationLocks.java @@ -25,6 +25,7 @@ import java.io.File; * Date: 10/23/12 * Time: 2:31 PM */ +// TODO: Used only in SvnLockingTest which is not required anymore. Likely to be removed. public class SvnTestWriteOperationLocks extends SvnAbstractWriteOperationLocks { private final WorkingCopy myWorkingCopy; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java index 0c049ac4671d..7cd6bdd91d3f 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java @@ -30,21 +30,19 @@ import com.intellij.openapi.vcs.AbstractVcsHelper; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ChangesUtil; -import com.intellij.openapi.vcs.impl.ContentRevisionCache; -import com.intellij.openapi.vfs.LocalFileSystem; -import com.intellij.openapi.vfs.VfsUtilCore; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.openapi.vfs.*; import com.intellij.openapi.wm.impl.status.StatusBarUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.Convertor; import com.intellij.util.containers.MultiMap; -import com.intellij.vcsUtil.VcsUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.ClientFactory; import org.jetbrains.idea.svn.branchConfig.SvnBranchConfigurationNew; import org.jetbrains.idea.svn.dialogs.LockDialog; +import org.tmatesoft.sqljet.core.SqlJetException; +import org.tmatesoft.sqljet.core.table.SqlJetDb; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; import org.tmatesoft.svn.core.internal.wc.SVNFileUtil; @@ -53,14 +51,14 @@ import org.tmatesoft.svn.core.io.SVNCapability; import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.wc.*; import org.tmatesoft.svn.core.wc2.SvnOperationFactory; +import org.tmatesoft.svn.core.wc2.SvnTarget; -import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.IOException; -import java.io.OutputStream; import java.util.*; public class SvnUtil { + // TODO: ASP.NET hack behavior should be supported - http://svn.apache.org/repos/asf/subversion/trunk/notes/asp-dot-net-hack.txt + // TODO: Remember this when moving out SVNKit classes. @NonNls public static final String SVN_ADMIN_DIR_NAME = SVNFileUtil.getAdminDirectoryName(); @NonNls public static final String ENTRIES_FILE_NAME = "entries"; @NonNls public static final String WC_DB_FILE_NAME = "wc.db"; @@ -71,13 +69,9 @@ public class SvnUtil { private SvnUtil() { } public static boolean isSvnVersioned(final Project project, File parent) { - try { - final SVNInfo info = SvnVcs.getInstance(project).createWCClient().doInfo(parent, SVNRevision.UNDEFINED); - return info != null; - } - catch (SVNException e) { - return false; - } + final SVNInfo info = SvnVcs.getInstance(project).getInfo(parent); + + return info != null; } public static Collection<VirtualFile> crawlWCRoots(final Project project, File path, SvnWCRootCrawler callback, ProgressIndicator progress) { @@ -123,15 +117,8 @@ public class SvnUtil { @Nullable public static String getExactLocation(final SvnVcs vcs, File path) { - try { - SVNWCClient wcClient = vcs.createWCClient(); - SVNInfo info = wcClient.doInfo(path, SVNRevision.UNDEFINED); - if (info != null && info.getURL() != null) { - return info.getURL().toString(); - } - } - catch (SVNException ignored) { } - return null; + SVNInfo info = vcs.getInfo(path); + return info != null && info.getURL() != null ? info.getURL().toString() : null; } public static Map<String, File> getLocationInfoForModule(final SvnVcs vcs, File path, ProgressIndicator progress) { @@ -288,7 +275,9 @@ public class SvnUtil { } public static String formatRepresentation(final WorkingCopyFormat format) { - if (WorkingCopyFormat.ONE_DOT_SEVEN.equals(format)) { + if (WorkingCopyFormat.ONE_DOT_EIGHT.equals(format)) { + return SvnBundle.message("dialog.show.svn.map.table.version18.text"); + } else if (WorkingCopyFormat.ONE_DOT_SEVEN.equals(format)) { return SvnBundle.message("dialog.show.svn.map.table.version17.text"); } else if (WorkingCopyFormat.ONE_DOT_SIX.equals(format)) { return SvnBundle.message("dialog.show.svn.map.table.version16.text"); @@ -352,6 +341,54 @@ public class SvnUtil { return result; } + /** + * Gets working copy internal format. Works for 1.7 and 1.8. + * + * @param path + * @return + */ + public static WorkingCopyFormat getFormat(final File path) { + int format = 0; + File dbFile = resolveDatabase(path); + + if (dbFile != null) { + SqlJetDb db = null; + try { + db = SqlJetDb.open(dbFile, false); + format = db.getOptions().getUserVersion(); + } + catch (SqlJetException e) { + LOG.error(e); + } finally { + if (db != null) { + try { + db.close(); + } + catch (SqlJetException e) { + LOG.error(e); + } + } + } + } + + return WorkingCopyFormat.getInstance(format); + } + + private static File resolveDatabase(final File path) { + File dbFile = getWcDb(path); + File result = null; + + try { + if (dbFile.exists() && dbFile.isFile()) { + result = dbFile; + } + } catch (SecurityException e) { + LOG.error("Failed to access working copy database", e); + } + + return result; + } + private static class LocationsCrawler implements SvnWCRootCrawler { private final SvnVcs myVcs; private final Map<String, File> myLocations; @@ -371,15 +408,9 @@ public class SvnUtil { 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) { - // + SVNInfo info = myVcs.getInfo(root); + if (info != null && info.getURL() != null) { + myLocations.put(info.getURL().toString(), info.getFile()); } if (progress != null) { progress.setText(oldText); @@ -389,20 +420,15 @@ public class SvnUtil { @Nullable public static String getRepositoryUUID(final SvnVcs 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; - } + final SVNInfo info = vcs.getInfo(file); + return info != null ? info.getRepositoryUUID() : null; } @Nullable public static String getRepositoryUUID(final SvnVcs vcs, final SVNURL url) { - final SVNWCClient client = vcs.createWCClient(); try { - final SVNInfo info = client.doInfo(url, SVNRevision.UNDEFINED, SVNRevision.UNDEFINED); + final SVNInfo info = vcs.getInfo(url, SVNRevision.UNDEFINED); + return (info == null) ? null : info.getRepositoryUUID(); } catch (SVNException e) { return null; @@ -411,19 +437,14 @@ public class SvnUtil { @Nullable public static SVNURL getRepositoryRoot(final SvnVcs 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; - } + final SVNInfo info = vcs.getInfo(file); + return info != null ? info.getRepositoryRootURL() : null; } @Nullable public static SVNURL getRepositoryRoot(final SvnVcs vcs, final String url) { try { - return getRepositoryRoot(vcs, SVNURL.parseURIEncoded(url), true); + return getRepositoryRoot(vcs, SVNURL.parseURIEncoded(url)); } catch (SVNException e) { return null; @@ -431,18 +452,14 @@ public class SvnUtil { } @Nullable - public static SVNURL getRepositoryRoot(final SvnVcs vcs, final SVNURL url, boolean allowRemote) throws SVNException { - final SVNWCClient client = vcs.createWCClient(); - SVNInfo info = client.doInfo(url, SVNRevision.UNDEFINED, SVNRevision.HEAD); + public static SVNURL getRepositoryRoot(final SvnVcs vcs, final SVNURL url) throws SVNException { + SVNInfo info = vcs.getInfo(url, SVNRevision.HEAD); + return (info == null) ? null : info.getRepositoryRootURL(); } public static boolean isWorkingCopyRoot(final File file) { - try { - return SVNWCUtil.isWorkingCopyRoot(file); - } catch (SVNException e) { - return false; - } + return FileUtil.filesEqual(file, getWorkingCopyRootNew(file)); } @Nullable @@ -548,17 +565,9 @@ public class SvnUtil { } public static SVNDepth getDepth(final SvnVcs 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; + SVNInfo info = vcs.getInfo(file); + + return info != null && info.getDepth() != null ? info.getDepth() : SVNDepth.UNKNOWN; } public static boolean seemsLikeVersionedDir(final VirtualFile file) { @@ -590,21 +599,17 @@ public class SvnUtil { } public static SVNURL getCommittedURL(final SvnVcs vcs, final File file) { - final File root = getWorkingCopyRoot(file); - if (root == null) return null; - return getUrl(vcs, root); + final File root = getWorkingCopyRootNew(file); + + return root == null ? null : getUrl(vcs, root); } @Nullable public static SVNURL getUrl(final SvnVcs vcs, final File file) { - try { - final SVNInfo info = vcs.createWCClient().doInfo(file, SVNRevision.UNDEFINED); - return info == null ? null : info.getURL(); // todo for moved items? - } - catch (SVNException e) { - LOG.debug(e); - return null; - } + // todo for moved items? + final SVNInfo info = vcs.getInfo(file); + + return info == null ? null : info.getURL(); } public static boolean doesRepositorySupportMergeInfo(final SvnVcs vcs, final SVNURL url) { @@ -648,17 +653,9 @@ public class SvnUtil { @Nullable public static File getWcCopyRootIf17(final File file, @Nullable final File upperBound) { - File current = file; - boolean wcDbFound = false; - while (current != null) { - File wcDb; - if ((wcDb = getWcDb(current)).exists() && ! wcDb.isDirectory()) { - wcDbFound = true; - break; - } - current = current.getParentFile(); - } - if (! wcDbFound) return null; + File current = getParentWithDb(file); + if (current == null) return null; + while (current != null) { try { final SvnWcGeneration svnWcGeneration = SvnOperationFactory.detectWcGeneration(current, false); @@ -674,6 +671,40 @@ public class SvnUtil { return null; } + /** + * Utility method that deals also with 1.8 working copies. + * TODO: Should be renamed when all parts updated for 1.8. + * + * @param file + * @return + */ + @Nullable + public static File getWorkingCopyRootNew(final File file) { + File current = getParentWithDb(file); + if (current == null) return getWorkingCopyRoot(file); + + WorkingCopyFormat format = getFormat(current); + + return WorkingCopyFormat.ONE_DOT_EIGHT.equals(format) || WorkingCopyFormat.ONE_DOT_SEVEN.equals(format) + ? current + : getWorkingCopyRoot(file); + } + + private static File getParentWithDb(File file) { + File current = file; + boolean wcDbFound = false; + while (current != null) { + File wcDb; + if ((wcDb = getWcDb(current)).exists() && ! wcDb.isDirectory()) { + wcDbFound = true; + break; + } + current = current.getParentFile(); + } + if (! wcDbFound) return null; + return current; + } + public static boolean is17CopyPart(final File file) { try { return SvnWcGeneration.V17.equals(SvnOperationFactory.detectWcGeneration(file, true)); @@ -703,44 +734,24 @@ public class SvnUtil { return result; } - public static byte[] getFileContents(final SvnVcs vcs, final String path, final boolean isUrl, final SVNRevision revision, - final SVNRevision pegRevision) + public static byte[] getFileContents(@NotNull final SvnVcs vcs, + @NotNull final SvnTarget target, + @Nullable final SVNRevision revision, + @Nullable final SVNRevision pegRevision) throws VcsException { - final int maxSize = VcsUtil.getMaxVcsLoadedFileSize(); - ByteArrayOutputStream buffer = new ByteArrayOutputStream() { - @Override - public synchronized void write(int b) { - if (size() > maxSize) throw new FileTooBigRuntimeException(); - super.write(b); - } + ClientFactory factory = target.isFile() ? vcs.getFactory(target.getFile()) : vcs.getFactory(); - @Override - public synchronized void write(byte[] b, int off, int len) { - if (size() > maxSize) throw new FileTooBigRuntimeException(); - super.write(b, off, len); - } + return factory.createContentClient().getContent(target, revision, pegRevision); + } - @Override - public synchronized void writeTo(OutputStream out) throws IOException { - if (size() > maxSize) throw new FileTooBigRuntimeException(); - super.writeTo(out); - } - }; - SVNWCClient wcClient = vcs.createWCClient(); + public static SVNURL parseUrl(@NotNull String url) { try { - if (isUrl) { - wcClient.doGetFileContents(SVNURL.parseURIEncoded(path), pegRevision, revision, true, buffer); - } else { - wcClient.doGetFileContents(new File(path), pegRevision, revision, true, buffer); - } - ContentRevisionCache.checkContentsSize(path, buffer.size()); - } catch (FileTooBigRuntimeException e) { - ContentRevisionCache.checkContentsSize(path, buffer.size()); - } catch (SVNException e) { - throw new VcsException(e); + return SVNURL.parseURIEncoded(url); + } + catch (SVNException e) { + IllegalArgumentException runtimeException = new IllegalArgumentException(); + runtimeException.initCause(e); + throw runtimeException; } - return buffer.toByteArray(); } - - private static class FileTooBigRuntimeException extends RuntimeException {} } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java index 748fc86c694c..1a5a0f8b8af7 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java @@ -13,8 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - - package org.jetbrains.idea.svn; import com.intellij.ide.FrameStateListener; @@ -68,8 +66,12 @@ import org.jetbrains.idea.svn.actions.CleanupWorker; import org.jetbrains.idea.svn.actions.ShowPropertiesDiffWithLocalAction; import org.jetbrains.idea.svn.actions.SvnMergeProvider; import org.jetbrains.idea.svn.annotate.SvnAnnotationProvider; +import org.jetbrains.idea.svn.api.ClientFactory; +import org.jetbrains.idea.svn.api.CmdClientFactory; +import org.jetbrains.idea.svn.api.SvnKitClientFactory; import org.jetbrains.idea.svn.checkin.SvnCheckinEnvironment; import org.jetbrains.idea.svn.checkout.SvnCheckoutProvider; +import org.jetbrains.idea.svn.commandLine.SvnCommandLineInfoClient; import org.jetbrains.idea.svn.commandLine.SvnExecutableChecker; import org.jetbrains.idea.svn.dialogs.SvnBranchPointsCalculator; import org.jetbrains.idea.svn.dialogs.WCInfo; @@ -79,6 +81,7 @@ import org.jetbrains.idea.svn.history.SvnCommittedChangesProvider; import org.jetbrains.idea.svn.history.SvnHistoryProvider; import org.jetbrains.idea.svn.lowLevel.PrimitivePool; import org.jetbrains.idea.svn.networking.SSLProtocolExceptionParser; +import org.jetbrains.idea.svn.portable.SvnWcClientI; import org.jetbrains.idea.svn.rollback.SvnRollbackEnvironment; import org.jetbrains.idea.svn.update.SvnIntegrateEnvironment; import org.jetbrains.idea.svn.update.SvnUpdateEnvironment; @@ -195,9 +198,10 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> { }; private SvnCheckoutProvider myCheckoutProvider; - public void checkCommandLineVersion() { - myChecker.checkExecutableAndNotifyIfNeeded(); - } + private ClientFactory cmdClientFactory; + private ClientFactory svnKitClientFactory; + + private final boolean myLogExceptions; static { System.setProperty("svnkit.log.native.calls", "true"); @@ -235,8 +239,8 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> { public SvnVcs(final Project project, MessageBus bus, SvnConfiguration svnConfiguration, final SvnLoadedBrachesStorage storage) { super(project, VCS_NAME); + myLoadedBranchesStorage = storage; - LOG.debug("ct"); myRootsToWorkingCopies = new RootsToWorkingCopies(this); myConfiguration = svnConfiguration; myAuthNotifier = new SvnAuthenticationNotifier(this); @@ -284,6 +288,9 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> { // remove used some time before old notification group ids correctNotificationIds(); myChecker = new SvnExecutableChecker(myProject); + + Application app = ApplicationManager.getApplication(); + myLogExceptions = app != null && (app.isInternal() || app.isUnitTestMode()); } private void correctNotificationIds() { @@ -348,6 +355,10 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> { }); } + public void checkCommandLineVersion() { + myChecker.checkExecutableAndNotifyIfNeeded(); + } + public void invokeRefreshSvnRoots() { if (REFRESH_LOG.isDebugEnabled()) { REFRESH_LOG.debug("refresh: ", new Throwable()); @@ -481,6 +492,9 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> { myChecker.checkExecutableAndNotifyIfNeeded(); } + cmdClientFactory = new CmdClientFactory(this); + svnKitClientFactory = new SvnKitClientFactory(this); + // do one time after project loaded StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new DumbAwareRunnable() { @Override @@ -911,24 +925,127 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> { return new File(file, pathToDirProps); } + /** + * Provides info either with command line or SvnKit based on project settings. + * Call this method only if failed to detect working copy format by any other means. + * + * @param file + * @return + */ + private SVNInfo runInfoCommand(@NotNull final File file) { + SVNInfo result = null; + + try { + result = SvnConfiguration.UseAcceleration.commandLine.equals(myConfiguration.myUseAcceleration) + ? getInfoCommandLine(file, SVNRevision.UNDEFINED) + : getInfoSvnKit(file); + } + catch (SVNException e) { + handleInfoException(e); + } + + return result; + } + + public SVNInfo getInfo(@NotNull SVNURL url, + SVNRevision pegRevision, + SVNRevision revision, + ISVNAuthenticationManager manager) throws SVNException { + if (SvnConfiguration.UseAcceleration.commandLine.equals(myConfiguration.myUseAcceleration)) { + return createInfoClient().doInfo(url, pegRevision, revision); + } else { + return (manager != null ? createWCClient(manager) : createWCClient()).doInfo(url, pegRevision, revision); + } + } + + public SVNInfo getInfo(@NotNull SVNURL url, SVNRevision revision) throws SVNException { + return getInfo(url, SVNRevision.UNDEFINED, revision, null); + } + @Nullable - public SVNInfo getInfo(final VirtualFile file) { + public SVNInfo getInfo(@NotNull final VirtualFile file) { final File ioFile = new File(file.getPath()); return getInfo(ioFile); } - public SVNInfo getInfo(File ioFile) { + @Nullable + public SVNInfo getInfo(@NotNull String path) { + return getInfo(new File(path)); + } + + @Nullable + public SVNInfo getInfo(@NotNull File ioFile) { + WorkingCopyFormat format = getWorkingCopyFormat(ioFile); + SVNInfo result = null; + try { - SVNWCClient wcClient = createWCClient(); - SVNInfo info = wcClient.doInfo(ioFile, SVNRevision.UNDEFINED); - if (info == null || info.getRepositoryRootURL() == null) { - info = wcClient.doInfo(ioFile, SVNRevision.HEAD); - } - return info; + result = format == WorkingCopyFormat.ONE_DOT_EIGHT ? getInfoCommandLine(ioFile, SVNRevision.UNDEFINED) : runInfoCommand(ioFile); } catch (SVNException e) { - return null; + handleInfoException(e); } + + return result; + } + + @Nullable + public SVNInfo getInfo(@NotNull File ioFile, @NotNull SVNRevision revision) { + WorkingCopyFormat format = getWorkingCopyFormat(ioFile); + SVNInfo result = null; + + try { + result = format == WorkingCopyFormat.ONE_DOT_EIGHT ? getInfoCommandLine(ioFile, revision) : getInfoSvnKit(ioFile, revision); + } + catch (SVNException e) { + handleInfoException(e); + } + + return result; + } + + private void handleInfoException(SVNException e) { + final SVNErrorCode errorCode = e.getErrorMessage().getErrorCode(); + if (!myLogExceptions || + SVNErrorCode.WC_PATH_NOT_FOUND.equals(errorCode) || + SVNErrorCode.UNVERSIONED_RESOURCE.equals(errorCode) || + SVNErrorCode.WC_NOT_WORKING_COPY.equals(errorCode)) { + LOG.debug(e); + } + else { + LOG.error(e); + } + } + + private SVNInfo getInfoSvnKit(@NotNull File ioFile) throws SVNException { + SVNInfo info = getInfoSvnKit(ioFile, SVNRevision.UNDEFINED); + if (info == null || info.getRepositoryRootURL() == null) { + info = getInfoSvnKit(ioFile, SVNRevision.HEAD); + } + return info; + } + + private SVNInfo getInfoSvnKit(@NotNull File ioFile, SVNRevision revision) throws SVNException { + return createWCClient().doInfo(ioFile, revision); + } + + private SVNInfo getInfoCommandLine(@NotNull File ioFile, SVNRevision revision) throws SVNException { + SvnCommandLineInfoClient client = new SvnCommandLineInfoClient(myProject); + return client.doInfo(ioFile, revision); + } + + private SvnWcClientI createInfoClient() { + return new SvnCommandLineInfoClient(myProject); + } + + public WorkingCopyFormat getWorkingCopyFormat(@NotNull File ioFile) { + RootUrlInfo rootInfo = getSvnFileUrlMapping().getWcRootForFilePath(ioFile); + WorkingCopyFormat format = rootInfo != null ? rootInfo.getFormat() : WorkingCopyFormat.UNKNOWN; + + if (WorkingCopyFormat.UNKNOWN.equals(format)) { + format = SvnFormatSelector.findRootAndGetFormat(ioFile); + } + + return format; } public void refreshSSLProperty() { @@ -1245,4 +1362,27 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> { } return myCheckoutProvider; } + + /** + * Try to avoid usages of this method (for now) as it could not correctly for all cases + * detect svn 1.8 working copy format to guarantee command line client. + * + * For instance, when working copies of several formats are presented in project + * (though it seems to be rather unlikely case). + * + * @return + */ + public ClientFactory getFactory() { + // check working copy format of project directory + WorkingCopyFormat format = getWorkingCopyFormat(new File(getProject().getBaseDir().getPath())); + + return WorkingCopyFormat.ONE_DOT_EIGHT.equals(format) || + myConfiguration.myUseAcceleration.equals(SvnConfiguration.UseAcceleration.commandLine) ? cmdClientFactory : svnKitClientFactory; + } + + public ClientFactory getFactory(@NotNull File file) { + WorkingCopyFormat format = getWorkingCopyFormat(file); + + return WorkingCopyFormat.ONE_DOT_EIGHT.equals(format) ? cmdClientFactory : getFactory(); + } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnWriteOperationLocks.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnWriteOperationLocks.java index 36e0b08c5a86..ca97edb1c228 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnWriteOperationLocks.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnWriteOperationLocks.java @@ -30,6 +30,7 @@ import java.io.File; * Date: 10/23/12 * Time: 2:29 PM */ +// TODO: Such locking functionality is not required anymore. Likely to be removed. public class SvnWriteOperationLocks extends SvnAbstractWriteOperationLocks { private final RootsToWorkingCopies myRootsToWorkingCopies; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/WorkingCopyFormat.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/WorkingCopyFormat.java index d40f947a71ee..c007541816c5 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/WorkingCopyFormat.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/WorkingCopyFormat.java @@ -15,19 +15,22 @@ */ package org.jetbrains.idea.svn; -import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb; - /** * 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")), + ONE_DOT_EIGHT(12, true, true, true, SvnBundle.message("dialog.show.svn.map.table.version18.text")), UNKNOWN(0, false, false, false, "unknown"); + public static final int INTERNAL_FORMAT_17 = 29; + public static final int INTERNAL_FORMAT_18 = 31; + private final int myFormat; private final boolean myChangelistSupport; private final boolean myMergeInfoSupport; @@ -60,10 +63,11 @@ public enum WorkingCopyFormat { public static WorkingCopyFormat getInstance(final int value) { // somewhy 1.7 wc format can also be 29 - if (ISVNWCDb.WC_FORMAT_17 == value) { + if (INTERNAL_FORMAT_17 == value) { return ONE_DOT_SEVEN; - } - if (ONE_DOT_FIVE.getFormat() == value) { + } else if (INTERNAL_FORMAT_18 == value) { + return ONE_DOT_EIGHT; + } else if (ONE_DOT_FIVE.getFormat() == value) { return ONE_DOT_FIVE; } else if (ONE_DOT_FOUR.getFormat() == value) { return ONE_DOT_FOUR; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AddAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AddAction.java index 06c255b6ed38..7558a483fcd7 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AddAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AddAction.java @@ -30,8 +30,6 @@ import org.jetbrains.idea.svn.SvnBundle; import org.jetbrains.idea.svn.SvnStatusUtil; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.checkin.SvnCheckinEnvironment; -import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.wc.SVNWCClient; import java.util.*; @@ -68,8 +66,6 @@ public class AddAction extends BasicAction { ProjectLevelVcsManager manager = ProjectLevelVcsManager.getInstance(project); manager.startBackgroundVcsOperation(); try { - - SVNWCClient wcClient = activeVcs.createWCClient(); final Set<VirtualFile> additionallyDirty = new HashSet<VirtualFile>(); final FileStatusManager fileStatusManager = FileStatusManager.getInstance(project); for (VirtualFile item : items) { @@ -84,13 +80,13 @@ public class AddAction extends BasicAction { } } } - Collection<SVNException> exceptions = - SvnCheckinEnvironment.scheduleUnversionedFilesForAddition(wcClient, Arrays.asList(items), true); + Collection<VcsException> exceptions = + SvnCheckinEnvironment.scheduleUnversionedFilesForAddition(activeVcs, Arrays.asList(items), true); additionallyDirty.addAll(Arrays.asList(items)); markDirty(project, additionallyDirty); if (!exceptions.isEmpty()) { final Collection<String> messages = new ArrayList<String>(exceptions.size()); - for (SVNException exception : exceptions) { + for (VcsException exception : exceptions) { messages.add(exception.getMessage()); } throw new VcsException(messages); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/MarkResolvedAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/MarkResolvedAction.java index 87b36970228b..d65c6d3c9c20 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/MarkResolvedAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/MarkResolvedAction.java @@ -19,6 +19,7 @@ package org.jetbrains.idea.svn.actions; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.vcs.AbstractVcs; @@ -32,8 +33,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.svn.SvnBundle; import org.jetbrains.idea.svn.SvnStatusUtil; import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.conflict.ConflictClient; import org.jetbrains.idea.svn.dialogs.SelectFilesDialog; -import org.tmatesoft.svn.core.SVNDepth; +import org.jetbrains.idea.svn.portable.SvnStatusClientI; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.wc.*; @@ -42,6 +44,8 @@ import java.util.Collection; import java.util.TreeSet; public class MarkResolvedAction extends BasicAction { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.actions.MarkResolvedAction"); + protected String getActionName(AbstractVcs vcs) { return SvnBundle.message("action.name.mark.resolved"); } @@ -88,15 +92,13 @@ public class MarkResolvedAction extends BasicAction { } pathsArray = dialog.getSelectedPaths(); try { - SVNWCClient wcClient = vcs.createWCClient(); for (String path : pathsArray) { File ioFile = new File(path); - wcClient.doResolve(ioFile, SVNDepth.EMPTY, SVNConflictChoice.MERGED); + ConflictClient client = vcs.getFactory(ioFile).createConflictClient(); + + client.resolve(ioFile, true); } } - catch (SVNException e) { - throw new VcsException(e); - } finally { for (VirtualFile file : files) { VcsDirtyScopeManager.getInstance(project).fileDirty(file); @@ -115,10 +117,12 @@ public class MarkResolvedAction extends BasicAction { private static Collection<String> collectResolvablePaths(final SvnVcs vcs, VirtualFile[] files) { final Collection<String> target = new TreeSet<String>(); - SVNStatusClient stClient = vcs.createStatusClient(); for (VirtualFile file : files) { try { - stClient.doStatus(new File(file.getPath()), true, false, false, false, new ISVNStatusHandler() { + File path = new File(file.getPath()); + SvnStatusClientI client = vcs.getFactory(path).createStatusClient(); + + client.doStatus(path, true, false, false, false, new ISVNStatusHandler() { public void handleStatus(SVNStatus status) { if (status.getContentsStatus() == SVNStatusType.STATUS_CONFLICTED || status.getPropertiesStatus() == SVNStatusType.STATUS_CONFLICTED) { @@ -128,7 +132,7 @@ public class MarkResolvedAction extends BasicAction { }); } catch (SVNException e) { - // + LOG.warn(e); } } return target; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnMergeProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnMergeProvider.java index 4c582d01557d..3130dc948c13 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnMergeProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnMergeProvider.java @@ -27,12 +27,13 @@ import com.intellij.vcsUtil.VcsRunnable; import com.intellij.vcsUtil.VcsUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.svn.SvnRevisionNumber; +import org.jetbrains.idea.svn.SvnUtil; import org.jetbrains.idea.svn.SvnVcs; -import org.tmatesoft.svn.core.SVNDepth; -import org.tmatesoft.svn.core.SVNException; +import org.jetbrains.idea.svn.properties.PropertyClient; import org.tmatesoft.svn.core.SVNProperty; import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.wc.*; +import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.ByteArrayOutputStream; import java.io.File; @@ -57,38 +58,34 @@ public class SvnMergeProvider implements MergeProvider { final MergeData data = new MergeData(); VcsRunnable runnable = new VcsRunnable() { public void run() throws VcsException { - SvnVcs vcs = SvnVcs.getInstance(myProject); File oldFile = null; File newFile = null; File workingFile = null; - SVNWCClient client; boolean mergeCase = false; - try { - client = vcs.createWCClient(); - SVNInfo info = client.doInfo(new File(file.getPath()), SVNRevision.UNDEFINED); - if (info != null) { - oldFile = info.getConflictOldFile(); - newFile = info.getConflictNewFile(); + SvnVcs vcs = SvnVcs.getInstance(myProject); + SVNInfo info = vcs.getInfo(file); + + if (info != null) { + oldFile = info.getConflictOldFile(); + newFile = info.getConflictNewFile(); + workingFile = info.getConflictWrkFile(); + mergeCase = workingFile == null || workingFile.getName().contains("working"); + // for debug + if (workingFile == null) { + LOG.info("Null working file when merging text conflict for " + file.getPath() + " old file: " + oldFile + " new file: " + newFile); + } + if (mergeCase) { + // this is merge case + oldFile = info.getConflictNewFile(); + newFile = info.getConflictOldFile(); workingFile = info.getConflictWrkFile(); - mergeCase = workingFile == null || workingFile.getName().contains("working"); - // for debug - if (workingFile == null) { - LOG.info("Null working file when merging text conflict for " + file.getPath() + " old file: " + oldFile + " new file: " + newFile); - } - if (mergeCase) { - // this is merge case - oldFile = info.getConflictNewFile(); - newFile = info.getConflictOldFile(); - workingFile = info.getConflictWrkFile(); - } - data.LAST_REVISION_NUMBER = new SvnRevisionNumber(info.getRevision()); } - } - catch (SVNException e) { - throw new VcsException(e); + data.LAST_REVISION_NUMBER = new SvnRevisionNumber(info.getRevision()); + } else { + throw new VcsException("Could not get info for " + file.getPath()); } if (oldFile == null || newFile == null || workingFile == null) { - ByteArrayOutputStream bos = getBaseRevisionContents(client, file); + ByteArrayOutputStream bos = getBaseRevisionContents(vcs, file); data.ORIGINAL = bos.toByteArray(); data.LAST = bos.toByteArray(); data.CURRENT = readFile(new File(file.getPath())); @@ -99,7 +96,7 @@ public class SvnMergeProvider implements MergeProvider { data.CURRENT = readFile(workingFile); } if (mergeCase) { - final ByteArrayOutputStream contents = getBaseRevisionContents(vcs.createWCClient(), file); + final ByteArrayOutputStream contents = getBaseRevisionContents(vcs, file); if (! Arrays.equals(contents.toByteArray(), data.ORIGINAL)) { // swap base and server: another order of merge arguments byte[] original = data.ORIGINAL; @@ -114,13 +111,17 @@ public class SvnMergeProvider implements MergeProvider { return data; } - private ByteArrayOutputStream getBaseRevisionContents(SVNWCClient client, VirtualFile file) { + private ByteArrayOutputStream getBaseRevisionContents(@NotNull SvnVcs vcs, @NotNull VirtualFile file) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { - client.doGetFileContents(new File(file.getPath()), SVNRevision.UNDEFINED, SVNRevision.BASE, true, bos); + byte[] contents = SvnUtil.getFileContents(vcs, SvnTarget.fromFile(new File(file.getPath())), SVNRevision.BASE, SVNRevision.UNDEFINED); + bos.write(contents); } - catch (SVNException e) { - // + catch (VcsException e) { + LOG.warn(e); + } + catch (IOException e) { + LOG.warn(e); } return bos; } @@ -135,13 +136,14 @@ public class SvnMergeProvider implements MergeProvider { } public void conflictResolvedForFile(VirtualFile file) { + // TODO: Add possibility to resolve content conflicts separately from property conflicts. SvnVcs vcs = SvnVcs.getInstance(myProject); + File path = new File(file.getPath()); try { - SVNWCClient client = vcs.createWCClient(); - client.doResolve(new File(file.getPath()), SVNDepth.EMPTY, true, false, SVNConflictChoice.MERGED); + vcs.getFactory(path).createConflictClient().resolve(path, false); } - catch (SVNException e) { - // + catch (VcsException e) { + LOG.warn(e); } // the .mine/.r## files have been deleted final VirtualFile parent = file.getParent(); @@ -152,17 +154,20 @@ public class SvnMergeProvider implements MergeProvider { public boolean isBinary(@NotNull final VirtualFile file) { SvnVcs vcs = SvnVcs.getInstance(myProject); + try { - SVNWCClient client = vcs.createWCClient(); File ioFile = new File(file.getPath()); - SVNPropertyData svnPropertyData = client.doGetProperty(ioFile, SVNProperty.MIME_TYPE, SVNRevision.UNDEFINED, SVNRevision.WORKING); + PropertyClient client = vcs.getFactory(ioFile).createPropertyClient(); + + SVNPropertyData svnPropertyData = client.getProperty(ioFile, SVNProperty.MIME_TYPE, false, SVNRevision.UNDEFINED, SVNRevision.WORKING); if (svnPropertyData != null && SVNProperty.isBinaryMimeType(SVNPropertyValue.getPropertyAsString(svnPropertyData.getValue()))) { return true; } } - catch (SVNException e) { - // + catch (VcsException e) { + LOG.warn(e); } + return false; } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/add/AddClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/add/AddClient.java new file mode 100644 index 000000000000..718a56dea9af --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/add/AddClient.java @@ -0,0 +1,23 @@ +package org.jetbrains.idea.svn.add; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.SvnClient; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public interface AddClient extends SvnClient { + + void add(@NotNull File file, + @Nullable SVNDepth depth, + boolean makeParents, + boolean includeIgnored, + boolean force, + @Nullable ISVNEventHandler handler) throws VcsException; +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/add/CmdAddClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/add/CmdAddClient.java new file mode 100644 index 000000000000..c7866713c9c1 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/add/CmdAddClient.java @@ -0,0 +1,69 @@ +package org.jetbrains.idea.svn.add; + +import com.intellij.openapi.vcs.VcsException; +import com.intellij.util.containers.Convertor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.api.FileStatusResultParser; +import org.jetbrains.idea.svn.commandLine.CommandUtil; +import org.jetbrains.idea.svn.commandLine.SvnCommandName; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; +import org.tmatesoft.svn.core.wc.SVNEvent; +import org.tmatesoft.svn.core.wc.SVNStatusType; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Konstantin Kolosovsky. + */ +public class CmdAddClient extends BaseSvnClient implements AddClient { + + private static final String STATUS = "\\s*(\\w)\\s*"; + private static final String OPTIONAL_FILE_TYPE = "(\\(.*\\))?"; + private static final String PATH = "\\s*(.*?)\\s*"; + private static final Pattern CHANGED_PATH = Pattern.compile(STATUS + OPTIONAL_FILE_TYPE + PATH); + + @Override + public void add(@NotNull File file, + @Nullable SVNDepth depth, + boolean makeParents, + boolean includeIgnored, + boolean force, + @Nullable ISVNEventHandler handler) throws VcsException { + List<String> parameters = prepareParameters(file, depth, makeParents, includeIgnored, force); + + // TODO: handler should be called in parallel with command execution, but this will be in other thread + // TODO: check if that is ok for current handler implementation + // TODO: add possibility to invoke "handler.checkCancelled" - process should be killed + CommandUtil.execute(myVcs, SvnCommandName.add, parameters, new FileStatusResultParser(CHANGED_PATH, handler, new AddStatusConvertor())); + } + + private static List<String> prepareParameters(File file, SVNDepth depth, boolean makeParents, boolean includeIgnored, boolean force) { + List<String> parameters = new ArrayList<String>(); + + CommandUtil.put(parameters, file); + CommandUtil.put(parameters, depth); + CommandUtil.put(parameters, makeParents, "--parents"); + CommandUtil.put(parameters, includeIgnored, "--no-ignore"); + CommandUtil.put(parameters, force, "--force"); + + return parameters; + } + + private static class AddStatusConvertor implements Convertor<Matcher, SVNEvent> { + @Override + public SVNEvent convert(Matcher o) { + SVNStatusType contentStatus = CommandUtil.getStatusType(o.group(1)); + String path = o.group(3); + + return new SVNEvent(new File(path), null, null, 0, contentStatus, null, null, null, null, null, null, null, + null, null, null); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/add/SvnKitAddClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/add/SvnKitAddClient.java new file mode 100644 index 000000000000..d6250c71f073 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/add/SvnKitAddClient.java @@ -0,0 +1,39 @@ +package org.jetbrains.idea.svn.add; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; +import org.tmatesoft.svn.core.wc.SVNWCClient; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnKitAddClient extends BaseSvnClient implements AddClient { + + @Override + public void add(@NotNull File file, + @Nullable SVNDepth depth, + boolean makeParents, + boolean includeIgnored, + boolean force, + @Nullable ISVNEventHandler handler) throws VcsException { + try { + SVNWCClient client = myVcs.createWCClient(); + + client.setEventHandler(handler); + client.doAdd(file, force, + false, // directory should already be created + makeParents, // not used but will be passed as makeParents value + SVNDepth.recurseFromDepth(depth)); + } + catch (SVNException e) { + throw new VcsException(e); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/AnnotateClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/AnnotateClient.java new file mode 100644 index 000000000000..df30cfd89ae2 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/AnnotateClient.java @@ -0,0 +1,24 @@ +package org.jetbrains.idea.svn.annotate; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.SvnClient; +import org.tmatesoft.svn.core.wc.ISVNAnnotateHandler; +import org.tmatesoft.svn.core.wc.SVNDiffOptions; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc2.SvnTarget; + +/** + * @author Konstantin Kolosovsky. + */ +public interface AnnotateClient extends SvnClient { + + void annotate(@NotNull SvnTarget target, + @NotNull SVNRevision startRevision, + @NotNull SVNRevision endRevision, + @Nullable SVNRevision pegRevision, + boolean includeMergedRevisions, + @Nullable SVNDiffOptions diffOptions, + @Nullable ISVNAnnotateHandler handler) throws VcsException; +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/CmdAnnotateClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/CmdAnnotateClient.java new file mode 100644 index 000000000000..d29d40eca84a --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/CmdAnnotateClient.java @@ -0,0 +1,126 @@ +package org.jetbrains.idea.svn.annotate; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.CommandUtil; +import org.jetbrains.idea.svn.commandLine.SvnCommand; +import org.jetbrains.idea.svn.commandLine.SvnCommandName; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.ISVNAnnotateHandler; +import org.tmatesoft.svn.core.wc.SVNDiffOptions; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc2.SvnTarget; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +public class CmdAnnotateClient extends BaseSvnClient implements AnnotateClient { + + @Override + public void annotate(@NotNull SvnTarget target, + @NotNull SVNRevision startRevision, + @NotNull SVNRevision endRevision, + @Nullable SVNRevision pegRevision, + boolean includeMergedRevisions, + @Nullable SVNDiffOptions diffOptions, + @Nullable final ISVNAnnotateHandler handler) throws VcsException { + // TODO: after merge remove setting includeMergedRevisions to false and update parsing + includeMergedRevisions = false; + + List<String> parameters = new ArrayList<String>(); + CommandUtil.put(parameters, target.getPathOrUrlString(), pegRevision); + parameters.add("--revision"); + parameters.add(startRevision + ":" + endRevision); + CommandUtil.put(parameters, includeMergedRevisions, "--use-merge-history"); + CommandUtil.put(parameters, diffOptions); + parameters.add("--xml"); + + SvnCommand command = CommandUtil.execute(myVcs, SvnCommandName.blame, parameters, null); + + parseOutput(command.getOutput(), handler); + } + + public void parseOutput(@NotNull String output, @Nullable ISVNAnnotateHandler handler) throws VcsException { + try { + JAXBContext context = JAXBContext.newInstance(BlameInfo.class); + Unmarshaller unmarshaller = context.createUnmarshaller(); + BlameInfo info = (BlameInfo)unmarshaller.unmarshal(new StringReader(output)); + + if (handler != null && info != null && info.target != null && info.target.lineEntries != null) { + for (LineEntry entry : info.target.lineEntries) { + invokeHandler(handler, entry); + } + } + } + catch (JAXBException e) { + throw new VcsException(e); + } + catch (SVNException e) { + throw new VcsException(e); + } + } + + private static void invokeHandler(ISVNAnnotateHandler handler, LineEntry entry) throws SVNException { + // line numbers in our api start from 0 - not from 1 like in svn output + handler.handleLine(entry.date(), entry.revision(), entry.author(), null, null, 0, null, null, entry.lineNumber - 1); + } + + @XmlRootElement(name = "blame") + public static class BlameInfo { + + @XmlElement(name = "target") + public TargetEntry target; + } + + public static class TargetEntry { + + @XmlElement(name = "entry") + List<LineEntry> lineEntries; + } + + public static class LineEntry { + + @XmlAttribute(name = "line-number") + public int lineNumber; + + @XmlElement(name = "commit") + public CommitEntry commit; + + public long revision() { + return commit != null ? commit.revision : 0; + } + + public String author() { + return commit != null ? commit.author : null; + } + + public Date date() { + return commit != null ? commit.date : null; + } + } + + public static class CommitEntry { + + @XmlAttribute(name = "revision") + public long revision; + + @XmlElement(name = "author") + public String author; + + @XmlElement(name = "date") + public Date date; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnAnnotationProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnAnnotationProvider.java index d0885c8a293e..88adf25e401c 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnAnnotationProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnAnnotationProvider.java @@ -32,14 +32,16 @@ import com.intellij.openapi.vcs.history.*; import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.*; +import org.jetbrains.idea.svn.history.HistoryClient; import org.jetbrains.idea.svn.history.SvnChangeList; import org.jetbrains.idea.svn.history.SvnFileRevision; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.wc.*; +import org.tmatesoft.svn.core.wc2.SvnTarget; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; @@ -89,9 +91,8 @@ public class SvnAnnotationProvider implements AnnotationProvider, VcsCacheableAn final String contents; if (loadExternally) { - final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - myVcs.createWCClient().doGetFileContents(ioFile, SVNRevision.UNDEFINED, SVNRevision.BASE, true, buffer); - contents = LoadTextUtil.getTextByBinaryPresentation(buffer.toByteArray(), file, false, false).toString(); + byte[] data = SvnUtil.getFileContents(myVcs, SvnTarget.fromFile(ioFile), SVNRevision.BASE, SVNRevision.UNDEFINED); + contents = LoadTextUtil.getTextByBinaryPresentation(data, file, false, false).toString(); } else { final byte[] bytes = VcsHistoryUtil.loadRevisionContent(revision); contents = LoadTextUtil.getTextByBinaryPresentation(bytes, file, false, false).toString(); @@ -99,16 +100,13 @@ public class SvnAnnotationProvider implements AnnotationProvider, VcsCacheableAn final SvnFileAnnotation result = new SvnFileAnnotation(myVcs, file, contents, lastChangedRevision); - SVNWCClient wcClient = myVcs.createWCClient(); - info = wcClient.doInfo(ioFile, SVNRevision.UNDEFINED); + info = myVcs.getInfo(ioFile); if (info == null) { exception[0] = new VcsException(new SVNException(SVNErrorMessage.create(SVNErrorCode.UNKNOWN, "File ''{0}'' is not under version control", ioFile))); return; } final String url = info.getURL() == null ? null : info.getURL().toString(); - SVNLogClient client = myVcs.createLogClient(); - setLogClientOptions(client); SVNRevision endRevision = ((SvnFileRevision) revision).getRevision(); if (SVNRevision.WORKING.equals(endRevision)) { endRevision = info.getRevision(); @@ -122,14 +120,20 @@ public class SvnAnnotationProvider implements AnnotationProvider, VcsCacheableAn final boolean calculateMergeinfo = SvnConfiguration.getInstance(myVcs.getProject()).SHOW_MERGE_SOURCES_IN_ANNOTATE && SvnUtil.checkRepositoryVersion15(myVcs, url); - final SVNRevision svnRevision = ((SvnRevisionNumber)revision.getRevisionNumber()).getRevision(); + final MySteppedLogGetter logGetter = new MySteppedLogGetter( + myVcs, ioFile, progress, + myVcs.getFactory(ioFile).createHistoryClient(), endRevision, result, + url, calculateMergeinfo, file.getCharset()); - final MySteppedLogGetter logGetter = new MySteppedLogGetter(myVcs, ioFile, progress, client, endRevision, result, url, calculateMergeinfo, file.getCharset()); logGetter.go(); final LinkedList<SVNRevision> rp = logGetter.getRevisionPoints(); + // TODO: only 2 elements will be in rp and for loop will be executed only once - probably rewrite with Pair + AnnotateClient annotateClient = myVcs.getFactory(ioFile).createAnnotateClient(); for (int i = 0; i < rp.size() - 1; i++) { - client.doAnnotate(ioFile, svnRevision, rp.get(i + 1), rp.get(i), true, calculateMergeinfo, annotateHandler, null); + annotateClient.annotate(SvnTarget.fromFile(ioFile), rp.get(i + 1), rp.get(i), ((SvnFileRevision)revision).getPegRevision(), + calculateMergeinfo, + getLogClientOptions(myVcs), annotateHandler); } if (rp.get(1).getNumber() > 0) { @@ -137,31 +141,15 @@ public class SvnAnnotationProvider implements AnnotationProvider, VcsCacheableAn } annotation[0] = result; } - catch (SVNException e) { - if (SVNErrorCode.FS_NOT_FOUND.equals(e.getErrorMessage().getErrorCode())) { - final CommittedChangesProvider<SvnChangeList,ChangeBrowserSettings> provider = myVcs.getCommittedChangesProvider(); - try { - final Pair<SvnChangeList, FilePath> pair = provider.getOneList(file, revision.getRevisionNumber()); - if (pair != null && info != null && pair.getSecond() != null && ! Comparing.equal(pair.getSecond().getIOFile(), ioFile)) { - annotation[0] = annotateNonExisting(pair, revision, info, file.getCharset(), file); - return; - } - } - catch (VcsException e1) { - exception[0] = e1; - } - catch (SVNException e1) { - exception[0] = new VcsException(e); - } - catch (IOException e1) { - exception[0] = new VcsException(e); - } - } - exception[0] = new VcsException(e); - } catch (IOException e) { + catch (IOException e) { exception[0] = new VcsException(e); } catch (VcsException e) { - exception[0] = e; + if (e.getCause() instanceof SVNException) { + handleSvnException(ioFile, info, (SVNException)e.getCause(), file, revision, annotation, exception); + } + else { + exception[0] = e; + } } } }; @@ -177,6 +165,35 @@ public class SvnAnnotationProvider implements AnnotationProvider, VcsCacheableAn return annotation[0]; } + private void handleSvnException(File ioFile, + SVNInfo info, + SVNException e, + VirtualFile file, + VcsFileRevision revision, + FileAnnotation[] annotation, VcsException[] exception) { + // TODO: Check how this scenario could be reproduced by user and what changes needs to be done for command line client + if (SVNErrorCode.FS_NOT_FOUND.equals(e.getErrorMessage().getErrorCode())) { + final CommittedChangesProvider<SvnChangeList,ChangeBrowserSettings> provider = myVcs.getCommittedChangesProvider(); + try { + final Pair<SvnChangeList, FilePath> pair = provider.getOneList(file, revision.getRevisionNumber()); + if (pair != null && info != null && pair.getSecond() != null && ! Comparing.equal(pair.getSecond().getIOFile(), ioFile)) { + annotation[0] = annotateNonExisting(pair, revision, info, file.getCharset(), file); + return; + } + } + catch (VcsException e1) { + exception[0] = e1; + } + catch (SVNException e1) { + exception[0] = new VcsException(e); + } + catch (IOException e1) { + exception[0] = new VcsException(e); + } + } + exception[0] = new VcsException(e); + } + public static File getCommonAncestor(final File file1, final File file2) throws IOException { if (FileUtil.filesEqual(file1, file2)) return file1; final File can1 = file1.getCanonicalFile(); @@ -214,8 +231,7 @@ public class SvnAnnotationProvider implements AnnotationProvider, VcsCacheableAn final String relativePath = FileUtil.getRelativePath(root.getPath(), wasFile.getPath(), File.separatorChar); if (relativePath == null) throw new VcsException("Can not find relative path for " + wasFile.getPath() + "@" + revision.getRevisionNumber().asString()); - SVNWCClient wcClient = myVcs.createWCClient(); - SVNInfo wcRootInfo = wcClient.doInfo(root, SVNRevision.UNDEFINED); + SVNInfo wcRootInfo = myVcs.getInfo(root); if (wcRootInfo == null || wcRootInfo.getURL() == null) { throw new VcsException("Can not find relative path for " + wasFile.getPath() + "@" + revision.getRevisionNumber().asString()); } @@ -225,21 +241,18 @@ public class SvnAnnotationProvider implements AnnotationProvider, VcsCacheableAn wasUrl = wasUrl.appendPath(string, true); } - final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); final SVNRevision svnRevision = ((SvnRevisionNumber)revision.getRevisionNumber()).getRevision(); - myVcs.createWCClient().doGetFileContents(wasUrl, svnRevision, svnRevision, true, buffer); - final String contents = LoadTextUtil.getTextByBinaryPresentation(buffer.toByteArray(), - charset == null ? CharsetToolkit.UTF8_CHARSET : charset).toString(); - SVNLogClient client = myVcs.createLogClient(); - setLogClientOptions(client); + byte[] data = SvnUtil.getFileContents(myVcs, SvnTarget.fromURL(wasUrl), svnRevision, svnRevision); + final String contents = LoadTextUtil.getTextByBinaryPresentation(data, charset == null ? CharsetToolkit.UTF8_CHARSET : charset).toString(); final SvnRemoteFileAnnotation result = new SvnRemoteFileAnnotation(myVcs, contents, revision.getRevisionNumber(), pair.getFirst(), pair.getSecond().getPath(), current); final ISVNAnnotateHandler annotateHandler = createAnnotationHandler(ProgressManager.getInstance().getProgressIndicator(), result); final boolean calculateMergeinfo = SvnConfiguration.getInstance(myVcs.getProject()).SHOW_MERGE_SOURCES_IN_ANNOTATE && SvnUtil.checkRepositoryVersion15(myVcs, wasUrl.toString()); - client.doAnnotate(wasUrl, svnRevision, SVNRevision.create(1), svnRevision, true, calculateMergeinfo, annotateHandler, null); - + AnnotateClient client = myVcs.getFactory().createAnnotateClient(); + client.annotate(SvnTarget.fromURL(wasUrl), SVNRevision.create(1), svnRevision, svnRevision, calculateMergeinfo, + getLogClientOptions(myVcs), annotateHandler); return result; } @@ -370,14 +383,14 @@ public class SvnAnnotationProvider implements AnnotationProvider, VcsCacheableAn private final SvnVcs myVcs; private final File myIoFile; private final ProgressIndicator myProgress; - private final SVNLogClient myClient; + private final HistoryClient myClient; private final SVNRevision myEndRevision; private final boolean myCalculateMergeinfo; private final SvnFileAnnotation myResult; private final String myUrl; private final Charset myCharset; - private MySteppedLogGetter(final SvnVcs vcs, final File ioFile, final ProgressIndicator progress, final SVNLogClient client, + private MySteppedLogGetter(final SvnVcs vcs, final File ioFile, final ProgressIndicator progress, final HistoryClient client, final SVNRevision endRevision, final SvnFileAnnotation result, final String url, @@ -395,7 +408,7 @@ public class SvnAnnotationProvider implements AnnotationProvider, VcsCacheableAn myRevisionPoints = new LinkedList<SVNRevision>(); } - public void go() throws SVNException { + public void go() throws VcsException { final int maxAnnotateRevisions = SvnConfiguration.getInstance(myVcs.getProject()).getMaxAnnotateRevisions(); boolean longHistory = true; if (maxAnnotateRevisions == -1) { @@ -437,8 +450,8 @@ public class SvnAnnotationProvider implements AnnotationProvider, VcsCacheableAn myRevisionPoints.add(SVNRevision.create(0)); } - private void doLog(final boolean includeMerged, final SVNRevision truncateTo, final int max) throws SVNException { - myClient.doLog(new File[]{myIoFile}, myEndRevision, truncateTo == null ? SVNRevision.create(1L) : truncateTo, + private void doLog(final boolean includeMerged, final SVNRevision truncateTo, final int max) throws VcsException { + myClient.doLog(myIoFile, myEndRevision, truncateTo == null ? SVNRevision.create(1L) : truncateTo, SVNRevision.UNDEFINED, false, false, includeMerged, max, null, new ISVNLogEntryHandler() { public void handleLogEntry(SVNLogEntry logEntry) { @@ -464,9 +477,7 @@ public class SvnAnnotationProvider implements AnnotationProvider, VcsCacheableAn return true; } - private void setLogClientOptions(final SVNLogClient client) { - if (SvnConfiguration.getInstance(myVcs.getProject()).IGNORE_SPACES_IN_ANNOTATE) { - client.setDiffOptions(new SVNDiffOptions(true, true, true)); - } + private static SVNDiffOptions getLogClientOptions(@NotNull SvnVcs vcs) { + return SvnConfiguration.getInstance(vcs.getProject()).IGNORE_SPACES_IN_ANNOTATE ? new SVNDiffOptions(true, true, true) : null; } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnKitAnnotateClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnKitAnnotateClient.java new file mode 100644 index 000000000000..315178c9079e --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnKitAnnotateClient.java @@ -0,0 +1,42 @@ +package org.jetbrains.idea.svn.annotate; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.ISVNAnnotateHandler; +import org.tmatesoft.svn.core.wc.SVNDiffOptions; +import org.tmatesoft.svn.core.wc.SVNLogClient; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc2.SvnTarget; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnKitAnnotateClient extends BaseSvnClient implements AnnotateClient { + + @Override + public void annotate(@NotNull SvnTarget target, + @NotNull SVNRevision startRevision, + @NotNull SVNRevision endRevision, + @Nullable SVNRevision pegRevision, + boolean includeMergedRevisions, + @Nullable SVNDiffOptions diffOptions, + @Nullable ISVNAnnotateHandler handler) throws VcsException { + try { + SVNLogClient client = myVcs.createLogClient(); + + client.setDiffOptions(diffOptions); + if (target.isFile()) { + client.doAnnotate(target.getFile(), pegRevision, startRevision, endRevision, true, includeMergedRevisions, handler, null); + } + else { + client.doAnnotate(target.getURL(), pegRevision, startRevision, endRevision, true, includeMergedRevisions, handler, null); + } + } + catch (SVNException e) { + throw new VcsException(e); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/BaseSvnClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/BaseSvnClient.java new file mode 100644 index 000000000000..54401cff68b1 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/BaseSvnClient.java @@ -0,0 +1,22 @@ +package org.jetbrains.idea.svn.api; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.SvnVcs; + +/** + * @author Konstantin Kolosovsky. + */ +public abstract class BaseSvnClient implements SvnClient { + protected SvnVcs myVcs; + + @NotNull + @Override + public SvnVcs getVcs() { + return myVcs; + } + + @Override + public void setVcs(@NotNull SvnVcs vcs) { + myVcs = vcs; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/ClientFactory.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/ClientFactory.java new file mode 100644 index 000000000000..b85121637e6a --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/ClientFactory.java @@ -0,0 +1,99 @@ +package org.jetbrains.idea.svn.api; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.add.AddClient; +import org.jetbrains.idea.svn.annotate.AnnotateClient; +import org.jetbrains.idea.svn.conflict.ConflictClient; +import org.jetbrains.idea.svn.content.ContentClient; +import org.jetbrains.idea.svn.copy.CopyMoveClient; +import org.jetbrains.idea.svn.delete.DeleteClient; +import org.jetbrains.idea.svn.history.HistoryClient; +import org.jetbrains.idea.svn.portable.SvnStatusClientI; +import org.jetbrains.idea.svn.properties.PropertyClient; +import org.jetbrains.idea.svn.revert.RevertClient; + +/** + * @author Konstantin Kolosovsky. + */ +public abstract class ClientFactory { + + @NotNull + protected SvnVcs myVcs; + + protected AddClient addClient; + protected AnnotateClient annotateClient; + protected ContentClient contentClient; + protected HistoryClient historyClient; + protected RevertClient revertClient; + protected DeleteClient deleteClient; + protected SvnStatusClientI statusClient; + protected CopyMoveClient copyMoveClient; + protected ConflictClient conflictClient; + protected PropertyClient propertyClient; + + protected ClientFactory(@NotNull SvnVcs vcs) { + myVcs = vcs; + setup(); + } + + protected abstract void setup(); + + @NotNull + public AddClient createAddClient() { + return prepare(addClient); + } + + @NotNull + public AnnotateClient createAnnotateClient() { + return prepare(annotateClient); + } + + @NotNull + public ContentClient createContentClient() { + return prepare(contentClient); + } + + @NotNull + public HistoryClient createHistoryClient() { + return prepare(historyClient); + } + + @NotNull + public RevertClient createRevertClient() { + return prepare(revertClient); + } + + @NotNull + public SvnStatusClientI createStatusClient() { + // TODO: Update this in same like other clients - move to corresponding package, rename clients + return statusClient; + } + + @NotNull + public DeleteClient createDeleteClient() { + return prepare(deleteClient); + } + + @NotNull + public CopyMoveClient createCopyMoveClient() { + return prepare(copyMoveClient); + } + + @NotNull + public ConflictClient createConflictClient() { + return prepare(conflictClient); + } + + @NotNull + public PropertyClient createPropertyClient() { + return prepare(propertyClient); + } + + @NotNull + protected <T extends SvnClient> T prepare(@NotNull T client) { + client.setVcs(myVcs); + + return client; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/CmdClientFactory.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/CmdClientFactory.java new file mode 100644 index 000000000000..ca4088b6a733 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/CmdClientFactory.java @@ -0,0 +1,38 @@ +package org.jetbrains.idea.svn.api; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.add.CmdAddClient; +import org.jetbrains.idea.svn.annotate.CmdAnnotateClient; +import org.jetbrains.idea.svn.commandLine.SvnCommandLineStatusClient; +import org.jetbrains.idea.svn.conflict.CmdConflictClient; +import org.jetbrains.idea.svn.content.CmdContentClient; +import org.jetbrains.idea.svn.copy.CmdCopyMoveClient; +import org.jetbrains.idea.svn.delete.CmdDeleteClient; +import org.jetbrains.idea.svn.history.CmdHistoryClient; +import org.jetbrains.idea.svn.properties.CmdPropertyClient; +import org.jetbrains.idea.svn.revert.CmdRevertClient; + +/** + * @author Konstantin Kolosovsky. + */ +public class CmdClientFactory extends ClientFactory { + + public CmdClientFactory(@NotNull SvnVcs vcs) { + super(vcs); + } + + @Override + protected void setup() { + addClient = new CmdAddClient(); + annotateClient = new CmdAnnotateClient(); + contentClient = new CmdContentClient(); + historyClient = new CmdHistoryClient(); + revertClient = new CmdRevertClient(); + deleteClient = new CmdDeleteClient(); + copyMoveClient = new CmdCopyMoveClient(); + conflictClient = new CmdConflictClient(); + propertyClient = new CmdPropertyClient(); + statusClient = new SvnCommandLineStatusClient(myVcs.getProject()); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/FileStatusResultParser.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/FileStatusResultParser.java new file mode 100644 index 000000000000..fc202c265a6a --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/FileStatusResultParser.java @@ -0,0 +1,68 @@ +package org.jetbrains.idea.svn.api; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.util.containers.Convertor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; +import org.tmatesoft.svn.core.wc.SVNEvent; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Konstantin Kolosovsky. + */ +public class FileStatusResultParser { + + private static final double DEFAULT_PROGRESS = 0.0; + + @NotNull + private Pattern myLinePattern; + + @Nullable + private ISVNEventHandler handler; + + @NotNull + private Convertor<Matcher, SVNEvent> myConvertor; + + public FileStatusResultParser(@NotNull Pattern linePattern, + @Nullable ISVNEventHandler handler, + @NotNull Convertor<Matcher, SVNEvent> convertor) { + myLinePattern = linePattern; + this.handler = handler; + myConvertor = convertor; + } + + public void parse(@NotNull String output) throws VcsException { + if (StringUtil.isEmpty(output)) { + return; + } + + for (String line : StringUtil.splitByLines(output)) { + onLine(line); + } + } + + public void onLine(@NotNull String line) throws VcsException { + Matcher matcher = myLinePattern.matcher(line); + if (matcher.matches()) { + process(matcher); + } + else { + throw new VcsException("unknown state on line " + line); + } + } + + public void process(@NotNull Matcher matcher) throws VcsException { + if (handler != null) { + try { + handler.handleEvent(myConvertor.convert(matcher), DEFAULT_PROGRESS); + } catch (SVNException e) { + throw new VcsException(e); + } + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/SvnClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/SvnClient.java new file mode 100644 index 000000000000..90124f1cccb6 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/SvnClient.java @@ -0,0 +1,15 @@ +package org.jetbrains.idea.svn.api; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.SvnVcs; + +/** + * @author Konstantin Kolosovsky. + */ +public interface SvnClient { + + @NotNull + SvnVcs getVcs(); + + void setVcs(@NotNull SvnVcs vcs); +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/api/SvnKitClientFactory.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/SvnKitClientFactory.java new file mode 100644 index 000000000000..7e1951893969 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/api/SvnKitClientFactory.java @@ -0,0 +1,38 @@ +package org.jetbrains.idea.svn.api; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.add.SvnKitAddClient; +import org.jetbrains.idea.svn.annotate.SvnKitAnnotateClient; +import org.jetbrains.idea.svn.conflict.SvnKitConflictClient; +import org.jetbrains.idea.svn.content.SvnKitContentClient; +import org.jetbrains.idea.svn.copy.SvnKitCopyMoveClient; +import org.jetbrains.idea.svn.delete.SvnKitDeleteClient; +import org.jetbrains.idea.svn.history.SvnKitHistoryClient; +import org.jetbrains.idea.svn.portable.SvnkitSvnStatusClient; +import org.jetbrains.idea.svn.properties.SvnKitPropertyClient; +import org.jetbrains.idea.svn.revert.SvnKitRevertClient; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnKitClientFactory extends ClientFactory { + + public SvnKitClientFactory(@NotNull SvnVcs vcs) { + super(vcs); + } + + @Override + protected void setup() { + addClient = new SvnKitAddClient(); + annotateClient = new SvnKitAnnotateClient(); + contentClient = new SvnKitContentClient(); + historyClient = new SvnKitHistoryClient(); + revertClient = new SvnKitRevertClient(); + deleteClient = new SvnKitDeleteClient(); + copyMoveClient = new SvnKitCopyMoveClient(); + conflictClient = new SvnKitConflictClient(); + propertyClient = new SvnKitPropertyClient(); + statusClient = new SvnkitSvnStatusClient(myVcs.createStatusClient()); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/branchConfig/DefaultConfigLoader.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/branchConfig/DefaultConfigLoader.java index 021568aac806..2b779fd0de70 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/branchConfig/DefaultConfigLoader.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/branchConfig/DefaultConfigLoader.java @@ -49,7 +49,7 @@ public class DefaultConfigLoader { final SvnVcs vcs = SvnVcs.getInstance(project); File rootFile = new File(vcsRoot.getPath()); - final SVNInfo info = vcs.createWCClient().doInfo(rootFile, SVNRevision.UNDEFINED); + final SVNInfo info = vcs.getInfo(rootFile); if (info == null || info.getURL() == null) { LOG.info("Directory is not a working copy: " + vcsRoot.getPresentableUrl()); return null; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/branchConfig/SvnBranchConfigurationNew.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/branchConfig/SvnBranchConfigurationNew.java index 28e74e7c395e..483bfda7cfd3 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/branchConfig/SvnBranchConfigurationNew.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/branchConfig/SvnBranchConfigurationNew.java @@ -223,9 +223,8 @@ public class SvnBranchConfigurationNew { private BranchRootSearcher(final SvnVcs vcs, final VirtualFile root) throws SVNException { myRoot = root; myBranchesUnder = new HashMap<String, String>(); - final SVNWCClient client = vcs.createWCClient(); - final SVNInfo info = client.doInfo(new File(myRoot.getPath()), SVNRevision.UNDEFINED); - myRootUrl = info.getURL(); + final SVNInfo info = vcs.getInfo(myRoot.getPath()); + myRootUrl = info != null ? info.getURL() : null; } public boolean accept(final String url) throws SVNException { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaCommitHandler.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaCommitHandler.java index 3bd8256e7b02..5ac6b967777e 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaCommitHandler.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaCommitHandler.java @@ -16,8 +16,8 @@ package org.jetbrains.idea.svn.checkin; import com.intellij.openapi.progress.ProgressIndicator; -import org.jetbrains.idea.svn.CommitEventHandler; -import org.jetbrains.idea.svn.CommitEventType; +import org.jetbrains.idea.svn.commandLine.CommitEventHandler; +import org.jetbrains.idea.svn.commandLine.CommitEventType; import org.jetbrains.idea.svn.SvnBundle; import java.io.File; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaSvnkitBasedAuthenticationCallback.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaSvnkitBasedAuthenticationCallback.java index 995646f50d45..f765595745b3 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaSvnkitBasedAuthenticationCallback.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaSvnkitBasedAuthenticationCallback.java @@ -28,6 +28,7 @@ import com.intellij.util.net.HttpConfigurable; import com.intellij.util.proxy.CommonProxy; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.*; +import org.jetbrains.idea.svn.commandLine.AuthenticationCallback; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.auth.*; import org.tmatesoft.svn.core.internal.util.SVNBase64; @@ -77,6 +78,20 @@ public class IdeaSvnkitBasedAuthenticationCallback implements AuthenticationCall return new CredentialsAuthenticator(myVcs).tryAuthenticate(realm, url, file, previousFailed, passwordRequest); } + @Nullable + @Override + public SVNAuthentication requestCredentials(@Nullable SVNURL url, String type) { + SVNAuthentication authentication = + url != null ? myVcs.getSvnConfiguration().getInteractiveManager(myVcs).getProvider().requestClientAuthentication( + type, url, url.toDecodedString(), null, null, true) : null; + + if (authentication == null) { + LOG.warn("Could not get authentication. Type - " + type + ", Url - " + url); + } + + return authentication; + } + @Override public boolean acceptSSLServerCertificate(final File file, final String realm) { final File base = getExistingParent(file); @@ -354,7 +369,9 @@ public class IdeaSvnkitBasedAuthenticationCallback implements AuthenticationCall }, new ThrowableRunnable<SVNException>() { @Override public void run() throws SVNException { + // NOTE: DO NOT replace this call - SSL authentication highly tied to SVNKit myVcs.createWCClient(active).doInfo(myUrl, SVNRevision.UNDEFINED, SVNRevision.HEAD); + //myVcs.getInfo(myUrl, SVNRevision.HEAD, active); } } ); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnCheckinEnvironment.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnCheckinEnvironment.java index 433e4063e02c..c7d2d4b41bf2 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnCheckinEnvironment.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnCheckinEnvironment.java @@ -48,8 +48,8 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.*; +import org.jetbrains.idea.svn.commandLine.SvnBindClient; import org.jetbrains.idea.svn.commandLine.SvnCommandLineStatusClient; -import org.tigris.subversion.javahl.ClientException; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.wc.*; @@ -177,7 +177,7 @@ public class SvnCheckinEnvironment implements CheckinEnvironment { if (committables.isEmpty()) { return; } - if (WorkingCopyFormat.ONE_DOT_SEVEN.equals(format) && + if (WorkingCopyFormat.ONE_DOT_EIGHT.equals(format) || WorkingCopyFormat.ONE_DOT_SEVEN.equals(format) && SvnConfiguration.UseAcceleration.commandLine.equals(SvnConfiguration.getInstance(mySvnVcs.getProject()).myUseAcceleration) && (SvnAuthenticationManager.HTTP.equals(url.getProtocol()) || SvnAuthenticationManager.HTTPS.equals(url.getProtocol()))) { doWithCommandLine(committables, comment, exception, feedback); @@ -256,14 +256,25 @@ public class SvnCheckinEnvironment implements CheckinEnvironment { }); final IdeaSvnkitBasedAuthenticationCallback authenticationCallback = new IdeaSvnkitBasedAuthenticationCallback(mySvnVcs); try { - final SvnBindClient client = new SvnBindClient(SvnApplicationSettings.getInstance().getCommandLinePath()); + final SvnBindClient client = new SvnBindClient(SvnApplicationSettings.getInstance().getCommandLinePath(), new Convertor<String[], SVNURL>() { + @Override + public SVNURL convert(String[] o) { + SVNInfo info = o.length > 0 ? mySvnVcs.getInfo(o[0]) : null; + + if (info == null || info.getURL() == null) { + LOG.warn("Could not resolve repository url for commit. Paths - " + Arrays.toString(o)); + } + + return info != null ? info.getURL() : null; + } + }); client.setAuthenticationCallback(authenticationCallback); client.setHandler(new IdeaCommitHandler(ProgressManager.getInstance().getProgressIndicator())); final long revision = client.commit(ArrayUtil.toStringArray(paths), comment, false, false); reportCommittedRevisions(feedback, String.valueOf(revision)); } - catch (ClientException e) { - exception.add(new VcsException(e)); + catch (VcsException e) { + exception.add(e); } finally { authenticationCallback.reset(); } @@ -348,25 +359,19 @@ public class SvnCheckinEnvironment implements CheckinEnvironment { private List<File> getCommitables(List<File> paths) { final Adder adder = new Adder(); - SVNStatusClient statusClient = mySvnVcs.createStatusClient(); for (File path : paths) { File file = path.getAbsoluteFile(); adder.add(file); if (file.getParentFile() != null) { - addParents(statusClient, file.getParentFile(), adder); + addParents(file.getParentFile(), adder); } } return adder.getResult(); } - private static void addParents(SVNStatusClient statusClient, File file, final Adder adder) { - SVNStatus status; - try { - status = statusClient.doStatus(file, false); - } - catch (SVNException e) { - return; - } + private void addParents(File file, final Adder adder) { + SVNStatus status = getStatus(file); + if (status != null && (SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_ADDED) || SvnVcs.svnStatusIs(status, SVNStatusType.STATUS_REPLACED))) { @@ -374,11 +379,33 @@ public class SvnCheckinEnvironment implements CheckinEnvironment { adder.add(file); file = file.getParentFile(); if (file != null) { - addParents(statusClient, file, adder); + addParents(file, adder); } } } + private SVNStatus getStatus(File file) { + SVNStatus result = null; + WorkingCopyFormat format = mySvnVcs.getWorkingCopyFormat(file); + + try { + result = WorkingCopyFormat.ONE_DOT_EIGHT.equals(format) ? getStatusCommandLine(file) : getStatusSvnKit(file); + } + catch (SVNException e) { + // do nothing + } + + return result; + } + + private SVNStatus getStatusSvnKit(File file) throws SVNException { + return mySvnVcs.createStatusClient().doStatus(file, false); + } + + private SVNStatus getStatusCommandLine(File file) throws SVNException { + return new SvnCommandLineStatusClient(mySvnVcs.getProject()).doStatus(file, false); + } + private static List<File> collectPaths(final List<Change> changes) { // case sensitive.. ArrayList<File> result = new ArrayList<File>(); @@ -418,15 +445,14 @@ public class SvnCheckinEnvironment implements CheckinEnvironment { public List<VcsException> scheduleMissingFileForDeletion(List<FilePath> filePaths) { List<VcsException> exceptions = new ArrayList<VcsException>(); - final SVNWCClient wcClient = mySvnVcs.createWCClient(); - List<File> files = ChangesUtil.filePathsToFiles(filePaths); + for (File file : files) { try { - wcClient.doDelete(file, true, false); + mySvnVcs.getFactory(file).createDeleteClient().delete(file, true); } - catch (SVNException e) { - exceptions.add(new VcsException(e)); + catch (VcsException e) { + exceptions.add(e); } } @@ -434,30 +460,22 @@ public class SvnCheckinEnvironment implements CheckinEnvironment { } public List<VcsException> scheduleUnversionedFilesForAddition(List<VirtualFile> files) { - final List<VcsException> result = new ArrayList<VcsException>(); - final SVNWCClient wcClient = mySvnVcs.createWCClient(); - - final List<SVNException> exceptionList = scheduleUnversionedFilesForAddition(wcClient, files); - for (SVNException svnException : exceptionList) { - result.add(new VcsException(svnException)); - } - return result; + return scheduleUnversionedFilesForAddition(mySvnVcs, files); } - public static List<SVNException> scheduleUnversionedFilesForAddition(SVNWCClient wcClient, List<VirtualFile> files) { - return scheduleUnversionedFilesForAddition(wcClient, files, false); + public static List<VcsException> scheduleUnversionedFilesForAddition(@NotNull SvnVcs vcs, List<VirtualFile> files) { + return scheduleUnversionedFilesForAddition(vcs, files, false); } - public static List<SVNException> scheduleUnversionedFilesForAddition(SVNWCClient wcClient, List<VirtualFile> files, final boolean recursive) { - List<SVNException> exceptions = new ArrayList<SVNException>(); - + public static List<VcsException> scheduleUnversionedFilesForAddition(@NotNull SvnVcs vcs, List<VirtualFile> files, final boolean recursive) { Collections.sort(files, FilePathComparator.getInstance()); - wcClient.setEventHandler(new ISVNEventHandler() { + ISVNEventHandler eventHandler = new ISVNEventHandler() { @Override public void handleEvent(SVNEvent event, double progress) throws SVNException { final ProgressManager pm = ProgressManager.getInstance(); final ProgressIndicator pi = pm.getProgressIndicator(); + // TODO: pi is null here when invoking "Add" action if (pi != null && event.getFile() != null) { File file = event.getFile(); pi.setText(SvnBundle.message("progress.text2.adding", file.getName() + " (" + file.getParent() + ")")); @@ -472,12 +490,18 @@ public class SvnCheckinEnvironment implements CheckinEnvironment { if (pi.isCanceled()) throw new SVNCancelException(); } } - }); + }; + + List<VcsException> exceptions = new ArrayList<VcsException>(); + for (VirtualFile file : files) { try { - wcClient.doAdd(new File(FileUtil.toSystemDependentName(file.getPath())), true, false, true, recursive); + File convertedFile = new File(FileUtil.toSystemDependentName(file.getPath())); + SVNDepth depth = recursive ? SVNDepth.INFINITY : SVNDepth.EMPTY; + + vcs.getFactory(convertedFile).createAddClient().add(convertedFile, depth, true, false, true, eventHandler); } - catch (SVNException e) { + catch (VcsException e) { exceptions.add(e); } } diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/AuthenticationCallback.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/AuthenticationCallback.java index bdc9c2deb03c..1944749f3608 100644 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/AuthenticationCallback.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/AuthenticationCallback.java @@ -13,9 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn; +package org.jetbrains.idea.svn.commandLine; import org.jetbrains.annotations.Nullable; +import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.auth.SVNAuthentication; import java.io.File; import java.io.IOException; @@ -51,6 +53,17 @@ public interface AuthenticationCallback { boolean authenticateFor(@Nullable String realm, File base, boolean previousFailed, boolean passwordRequest); /** + * Provides authentication information to access given url by authentication protocol identified by type. + * For instance, username/password for http/svn protocols. SSL client certificate for two way SSL protocol. + * + * @param url url to item in repository + * @param type authentication protocol type with svn specific values, like "svn.simple" for http. + * @return + */ + @Nullable + SVNAuthentication requestCredentials(@Nullable SVNURL url, String type); + + /** * @return config directory if TMP was created */ @Nullable diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandUtil.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandUtil.java new file mode 100644 index 000000000000..737d8a85b60b --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandUtil.java @@ -0,0 +1,218 @@ +package org.jetbrains.idea.svn.commandLine; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.util.ArrayUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.SvnApplicationSettings; +import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.api.FileStatusResultParser; +import org.jetbrains.idea.svn.checkin.IdeaSvnkitBasedAuthenticationCallback; +import org.tmatesoft.svn.core.*; +import org.tmatesoft.svn.core.wc.SVNDiffOptions; +import org.tmatesoft.svn.core.wc.SVNInfo; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc.SVNStatusType; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +public class CommandUtil { + public static SvnLineCommand runSimple(@NotNull SvnCommandName name, + @NotNull SvnVcs vcs, + @Nullable File base, + @Nullable SVNURL url, + List<String> parameters) + throws SVNException { + String exe = resolveExePath(); + base = resolveBaseDirectory(base, exe); + url = resolveRepositoryUrl(vcs, url); + + try { + return SvnLineCommand + .runWithAuthenticationAttempt(exe, base, url, name, new SvnCommitRunner.CommandListener(null), + new IdeaSvnkitBasedAuthenticationCallback(vcs), ArrayUtil.toStringArray(parameters)); + } + catch (SvnBindException e) { + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); + } + } + + @Nullable + private static SVNURL resolveRepositoryUrl(@NotNull SvnVcs vcs, @Nullable SVNURL url) { + if (url == null) { + // TODO: or take it from RootUrlInfo + SVNInfo info = vcs.getInfo(vcs.getProject().getBaseDir()); + + url = info != null ? info.getURL() : null; + } + return url; + } + + @NotNull + private static File resolveBaseDirectory(@Nullable File base, @NotNull String defaultBase) { + return base == null ? new File(defaultBase) : base; + } + + @NotNull + private static String resolveExePath() { + return SvnApplicationSettings.getInstance().getCommandLinePath(); + } + + public static SvnLineCommand runSimple(@NotNull SvnSimpleCommand command, @NotNull SvnVcs vcs, @Nullable File base, @Nullable SVNURL url) + throws SVNException { + // empty command name passed, as command name is already in command.getParameters() + return runSimple(SvnCommandName.empty, vcs, base, url, new ArrayList<String>(Arrays.asList(command.getParameters()))); + } + + /** + * Puts given value to parameters if condition is satisfied + * + * @param parameters + * @param condition + * @param value + */ + public static void put(@NotNull List<String> parameters, boolean condition, @NotNull String value) { + if (condition) { + parameters.add(value); + } + } + + public static void put(@NotNull List<String> parameters, @NotNull File path) { + parameters.add(path.getAbsolutePath()); + } + + public static void put(@NotNull List<String> parameters, @NotNull File path, @Nullable SVNRevision pegRevision) { + put(parameters, path.getAbsolutePath(), pegRevision); + } + + public static void put(@NotNull List<String> parameters, @NotNull String path, @Nullable SVNRevision pegRevision) { + StringBuilder builder = new StringBuilder(path); + + if (pegRevision != null && !SVNRevision.UNDEFINED.equals(pegRevision) && !SVNRevision.WORKING.equals(pegRevision) && + pegRevision.getNumber() > 0) { + builder.append("@"); + builder.append(pegRevision); + } + + parameters.add(builder.toString()); + } + + public static void put(@NotNull List<String> parameters, @NotNull File... paths) { + for (File path : paths) { + put(parameters, path); + } + } + + public static void put(@NotNull List<String> parameters, @Nullable SVNDepth depth) { + if (depth != null && !SVNDepth.UNKNOWN.equals(depth)) { + parameters.add("--depth"); + parameters.add(depth.getName()); + } + } + + public static void put(@NotNull List<String> parameters, @Nullable SVNRevision revision) { + if (revision != null && !SVNRevision.UNDEFINED.equals(revision) && !SVNRevision.WORKING.equals(revision) && revision.getNumber() >= 0) { + parameters.add("--revision"); + parameters.add(revision.toString()); + } + } + + public static void put(@NotNull List<String> parameters, @Nullable SVNDiffOptions diffOptions) { + if (diffOptions != null) { + StringBuilder builder = new StringBuilder(); + + if (diffOptions.isIgnoreAllWhitespace()) { + builder.append(" --ignore-space-change"); + } + if (diffOptions.isIgnoreAmountOfWhitespace()) { + builder.append(" --ignore-all-space"); + } + if (diffOptions.isIgnoreEOLStyle()) { + builder.append(" --ignore-eol-style"); + } + + String value = builder.toString().trim(); + + if (!StringUtil.isEmpty(value)) { + parameters.add("--extensions"); + parameters.add(value); + } + } + } + + /** + * Utility method for running commands changing certain file status information. + * // TODO: Should be replaced with non-static analogue. + * + * @param vcs + * @param name + * @param parameters + * @param parser + * @throws VcsException + */ + public static SvnCommand execute(@NotNull SvnVcs vcs, + @NotNull SvnCommandName name, + @NotNull List<String> parameters, + @Nullable FileStatusResultParser parser) + throws VcsException { + String exe = resolveExePath(); + File base = resolveBaseDirectory(null, exe); + SVNURL url = resolveRepositoryUrl(vcs, null); + + SvnLineCommand command = SvnLineCommand.runWithAuthenticationAttempt( + exe, base, url, name, new SvnCommitRunner.CommandListener(null), + new IdeaSvnkitBasedAuthenticationCallback(vcs), + ArrayUtil.toStringArray(parameters)); + + if (parser != null) { + parser.parse(command.getOutput()); + } + + return command; + } + + /** + * Gets svn status represented by single character. + * + * @param type + * @return + */ + public static char getStatusChar(@Nullable String type) { + return !StringUtil.isEmpty(type) ? type.charAt(0) : ' '; + } + + @NotNull + public static SVNStatusType getStatusType(@Nullable String type) { + return getStatusType(getStatusChar(type)); + } + + @NotNull + public static SVNStatusType getStatusType(char first) { + final SVNStatusType contentsStatus; + if ('A' == first) { + contentsStatus = SVNStatusType.STATUS_ADDED; + } else if ('D' == first) { + contentsStatus = SVNStatusType.STATUS_DELETED; + } else if ('U' == first) { + contentsStatus = SVNStatusType.CHANGED; + } else if ('C' == first) { + contentsStatus = SVNStatusType.CONFLICTED; + } else if ('G' == first) { + contentsStatus = SVNStatusType.MERGED; + } else if ('R' == first) { + contentsStatus = SVNStatusType.STATUS_REPLACED; + } else if ('E' == first) { + contentsStatus = SVNStatusType.STATUS_OBSTRUCTED; + } else { + contentsStatus = SVNStatusType.STATUS_NORMAL; + } + return contentsStatus; + } +} diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/CommitEventHandler.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommitEventHandler.java index 7f3bcf3bf4a5..d74884073863 100644 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/CommitEventHandler.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommitEventHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn; +package org.jetbrains.idea.svn.commandLine; import java.io.File; diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/CommitEventType.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommitEventType.java index 46609e40532c..87dc212d0341 100644 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/CommitEventType.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommitEventType.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn; +package org.jetbrains.idea.svn.commandLine; /** * Created with IntelliJ IDEA. diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/LineCommandListener.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/LineCommandListener.java index ccbc5c37db61..ccbc5c37db61 100644 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/LineCommandListener.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/LineCommandListener.java diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindClient.java new file mode 100644 index 000000000000..8f14cb269179 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindClient.java @@ -0,0 +1,67 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.svn.commandLine; + +import com.intellij.openapi.vcs.VcsException; +import com.intellij.util.containers.Convertor; +import org.tmatesoft.svn.core.SVNURL; + +import java.util.Map; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 2/5/13 + * Time: 3:08 PM + */ +public class SvnBindClient { + private final String myExecutablePath; + private CommitEventHandler myHandler; + private AuthenticationCallback myAuthenticationCallback; + private Convertor<String[], SVNURL> myUrlProvider; + + public SvnBindClient(String path, Convertor<String[], SVNURL> urlProvider) { + myExecutablePath = path; + myUrlProvider = urlProvider; + } + + public long commit(String[] path, String message, boolean recurse, boolean noUnlock) throws VcsException { + return commit(path, message, recurse? 3 : 0, noUnlock, false, null, null); + } + + public long commit(String[] path, + String message, + int depth, + boolean noUnlock, + boolean keepChangelist, + String[] changelists, + Map revpropTable) throws VcsException { + final long commit = new SvnCommitRunner(myExecutablePath, myHandler, myAuthenticationCallback). + commit(path, message, depth, noUnlock, keepChangelist, changelists, revpropTable, myUrlProvider); + if (commit < 0) { + throw new VcsException("Wrong committed revision number: " + commit); + } + return commit; + } + + public void setHandler(CommitEventHandler handler) { + myHandler = handler; + } + + public void setAuthenticationCallback(AuthenticationCallback authenticationCallback) { + myAuthenticationCallback = authenticationCallback; + } +} diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/config/SvnBindException.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindException.java index 8957843e911c..524b928eae92 100644 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/config/SvnBindException.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindException.java @@ -13,12 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn.config; +package org.jetbrains.idea.svn.commandLine; import com.intellij.openapi.vcs.VcsException; -import java.util.Collection; - /** * Created with IntelliJ IDEA. * User: Irina.Chernushina @@ -32,23 +30,7 @@ public class SvnBindException extends VcsException { super(message); } - public SvnBindException(Throwable throwable, boolean isWarning) { - super(throwable, isWarning); - } - public SvnBindException(Throwable throwable) { super(throwable); } - - public SvnBindException(String message, Throwable cause) { - super(message, cause); - } - - public SvnBindException(String message, boolean isWarning) { - super(message, isWarning); - } - - public SvnBindException(Collection<String> messages) { - super(messages); - } } diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindUtil.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindUtil.java index 1fec6b3bf865..7d663bae65b5 100644 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnBindUtil.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindUtil.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn; +package org.jetbrains.idea.svn.commandLine; import java.io.File; import java.text.DateFormat; @@ -31,16 +31,7 @@ import java.util.Locale; * Time: 4:56 PM */ public class SvnBindUtil { - /** - * SVN_ASP_DOT_NET_HACK allows use of an alternate name for Subversion working copy - * administrative directories on Windows (which were formerly always - * named ".svn"), by setting the SVN_ASP_DOT_NET_HACK environment variable. - * When the variable is set (to any value), the administrative directory - * will be "_svn" instead of ".svn". - * - * http://svn.apache.org/repos/asf/subversion/trunk/notes/asp-dot-net-hack.txt - */ - public static final String ADM_NAME = System.getenv("SVN_ASP_DOT_NET_HACK") != null ? "_svn" : ".svn"; + private final static List<DateFormat> ourFormats = new ArrayList<DateFormat>(); static { @@ -88,17 +79,4 @@ public class SvnBindUtil { } return null; } - - public static File getWcRoot(File base) { - File current = base; - while (current != null) { - if (getWcDbUnder(current).exists()) return current; - current = current.getParentFile(); - } - return null; - } - - public static File getWcDbUnder(final File file) { - return new File(file, ADM_NAME + File.separator + "wc.db"); - } } diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java index d6df6bae947e..9e355e56f4d6 100644 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java @@ -16,6 +16,7 @@ package org.jetbrains.idea.svn.commandLine; import com.intellij.execution.configurations.GeneralCommandLine; +import com.intellij.execution.process.CapturingProcessAdapter; import com.intellij.execution.process.OSProcessHandler; import com.intellij.execution.process.ProcessEvent; import com.intellij.execution.process.ProcessListener; @@ -38,6 +39,7 @@ import java.util.List; */ public abstract class SvnCommand { static final Logger LOG = Logger.getInstance(SvnCommand.class.getName()); + private final File myConfigDir; private boolean myIsDestroyed; private int myExitCode; @@ -45,13 +47,13 @@ public abstract class SvnCommand { private final File myWorkingDirectory; private Process myProcess; private OSProcessHandler myHandler; + // TODO: Try to implement commands in a way that they manually indicate if they need full output - to prevent situations + // TODO: when large amount of data needs to be stored instead of just sequential processing. + private CapturingProcessAdapter outputAdapter; private final Object myLock; private final EventDispatcher<ProcessEventListener> myListeners = EventDispatcher.create(ProcessEventListener.class); - - // todo check version - /*c:\Program Files (x86)\CollabNet\Subversion Client17>svn --version --quiet - 1.7.2*/ + private final SvnCommandName myCommandName; public SvnCommand(File workingDirectory, @NotNull SvnCommandName commandName, @NotNull @NonNls String exePath) { this(workingDirectory, commandName, exePath, null); @@ -59,15 +61,34 @@ public abstract class SvnCommand { public SvnCommand(File workingDirectory, @NotNull SvnCommandName commandName, @NotNull @NonNls String exePath, @Nullable File configDir) { + myCommandName = commandName; myLock = new Object(); myCommandLine = new GeneralCommandLine(); myWorkingDirectory = workingDirectory; myCommandLine.setExePath(exePath); myCommandLine.setWorkDirectory(workingDirectory); + myConfigDir = configDir; if (configDir != null) { myCommandLine.addParameters("--config-dir", configDir.getPath()); } - myCommandLine.addParameter(commandName.getName()); + if (!SvnCommandName.empty.equals(commandName)) { + myCommandLine.addParameter(commandName.getName()); + } + } + + public String[] getParameters() { + synchronized (myLock) { + return myCommandLine.getParametersList().getArray(); + } + } + + /** + * Indicates if process was destroyed "manually" by command execution logic. + * + * @return + */ + public boolean isManuallyDestroyed() { + return myIsDestroyed; } public void start() { @@ -112,10 +133,16 @@ public abstract class SvnCommand { } }; + outputAdapter = new CapturingProcessAdapter(); + myHandler.addProcessListener(outputAdapter); myHandler.addProcessListener(processListener); myHandler.startNotify(); } + public String getOutput() { + return outputAdapter.getOutput().getStdout(); + } + /** * Wait for process termination * @param timeout @@ -186,6 +213,18 @@ public abstract class SvnCommand { } } + public String getCommandText() { + synchronized (myLock) { + return myCommandLine.getCommandLineString(); + } + } + + public String getExePath() { + synchronized (myLock) { + return myCommandLine.getExePath(); + } + } + /** * check that process is not started yet * @@ -226,4 +265,8 @@ public abstract class SvnCommand { protected File getWorkingDirectory() { return myWorkingDirectory; } + + public SvnCommandName getCommandName() { + return myCommandName; + } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineInfoClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineInfoClient.java index 0d2f4d00bd39..47b6b5234396 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineInfoClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineInfoClient.java @@ -15,12 +15,13 @@ */ package org.jetbrains.idea.svn.commandLine; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.util.Consumer; -import org.jetbrains.idea.svn.SvnBindUtil; +import org.jetbrains.idea.svn.SvnApplicationSettings; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.portable.SvnExceptionWrapper; import org.jetbrains.idea.svn.portable.SvnkitSvnWcClient; @@ -36,7 +37,9 @@ import javax.xml.parsers.SAXParserFactory; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; /** * Created with IntelliJ IDEA. @@ -45,9 +48,12 @@ import java.util.Collection; * Time: 12:59 PM */ public class SvnCommandLineInfoClient extends SvnkitSvnWcClient { + + private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.commandLine.SvnCommandLineInfoClient"); private final Project myProject; public SvnCommandLineInfoClient(final Project project) { + // TODO: Remove svn kit client instantiation super(SvnVcs.getInstance(project).createWCClient()); myProject = project; } @@ -74,22 +80,56 @@ public class SvnCommandLineInfoClient extends SvnkitSvnWcClient { base = SvnBindUtil.correctUpToExistingParent(base); if (base == null) { // very unrealistic - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), new RuntimeException("Can not find existing parent file")); + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Can not find existing parent file")); } + issueCommand(path.getAbsolutePath(), pegRevision, revision, depth, changeLists, handler, base); + } + + private void issueCommand(String path, SVNRevision pegRevision, + SVNRevision revision, + SVNDepth depth, + Collection changeLists, + final ISVNInfoHandler handler, File base) throws SVNException { final SvnSimpleCommand command = SvnCommandFactory.createSimpleCommand(myProject, base, SvnCommandName.info); + List<String> parameters = new ArrayList<String>(); + + fillParameters(path, pegRevision, revision, depth, parameters); + command.addParameters(parameters); + SvnCommandLineStatusClient.changelistsToCommand(changeLists, command); - if (depth != null) { - command.addParameters("--depth", depth.getName()); + parseResult(handler, base, execute(command)); + } + + private String execute(SvnSimpleCommand command) throws SVNException { + try { + return command.run(); } - if (revision != null && ! SVNRevision.UNDEFINED.equals(revision) && ! SVNRevision.WORKING.equals(revision)) { - command.addParameters("-r", revision.toString()); + catch (VcsException e) { + final String text = e.getMessage(); + final boolean notEmpty = !StringUtil.isEmptyOrSpaces(text); + if (notEmpty && text.contains("W155010")) { + // just null + return null; + } + // not a working copy exception + // "E155007: '' is not a working copy" + if (notEmpty && text.contains("is not a working copy")) { + if (StringUtil.isNotEmpty(command.getOutput())) { + // workaround: as in subversion 1.8 "svn info" on a working copy root outputs such error for parent folder, + // if there are files with conflicts. + // but the requested info is still in the output except root closing tag + return command.getOutput() + "</info>"; + } else { + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.WC_NOT_WORKING_COPY, e), e); + } + } + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); } - command.addParameters("--xml"); - SvnCommandLineStatusClient.changelistsToCommand(changeLists, command); - if (pegRevision != null && ! SVNRevision.UNDEFINED.equals(pegRevision) && ! SVNRevision.WORKING.equals(pegRevision)) { - command.addParameters(path.getPath() + "@" + pegRevision.toString()); - } else { - command.addParameters(path.getPath()); + } + + private void parseResult(final ISVNInfoHandler handler, File base, String result) throws SVNException { + if (StringUtil.isEmpty(result)) { + return; } final SvnInfoHandler[] infoHandler = new SvnInfoHandler[1]; @@ -106,38 +146,34 @@ public class SvnCommandLineInfoClient extends SvnkitSvnWcClient { }); try { - final String result = command.run(); SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); - parser.parse(new ByteArrayInputStream(result.getBytes(CharsetToolkit.UTF8_CHARSET)), infoHandler[0]); + parser.parse(new ByteArrayInputStream(result.getBytes(CharsetToolkit.UTF8_CHARSET)), infoHandler[0]); } catch (SvnExceptionWrapper e) { + LOG.info("info output " + result); throw (SVNException) e.getCause(); } catch (IOException e) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), e); + LOG.info("info output " + result); + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); } catch (ParserConfigurationException e) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), e); + LOG.info("info output " + result); + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); } catch (SAXException e) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), e); - } - catch (VcsException e) { - final String text = e.getMessage(); - final boolean notEmpty = !StringUtil.isEmptyOrSpaces(text); - if (notEmpty && text.contains("W155010")) { - // just null - return; - } - // not a working copy exception - // "E155007: '' is not a working copy" - if (notEmpty && text.contains("is not a working copy")) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.WC_NOT_WORKING_COPY), e); - } - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), e); + LOG.info("info output " + result); + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); } } + private void fillParameters(String path, SVNRevision pegRevision, SVNRevision revision, SVNDepth depth, List<String> parameters) { + CommandUtil.put(parameters, depth); + CommandUtil.put(parameters, revision); + CommandUtil.put(parameters, path, pegRevision); + parameters.add("--xml"); + } + @Override public void doInfo(SVNURL url, SVNRevision pegRevision, SVNRevision revision, boolean recursive, ISVNInfoHandler handler) throws SVNException { @@ -147,7 +183,14 @@ public class SvnCommandLineInfoClient extends SvnkitSvnWcClient { @Override public void doInfo(SVNURL url, SVNRevision pegRevision, SVNRevision revision, SVNDepth depth, ISVNInfoHandler handler) throws SVNException { - throw new UnsupportedOperationException(); + String path = url.toDecodedString(); + List<String> parameters = new ArrayList<String>(); + + fillParameters(path, pegRevision, revision, depth, parameters); + File base = new File(SvnApplicationSettings.getInstance().getCommandLinePath()); + String result = CommandUtil.runSimple(SvnCommandName.info, SvnVcs.getInstance(myProject), base, url, parameters).getOutput(); + + parseResult(handler, base, result); } @Override diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java index a1a31536c8a9..c1429be699af 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java @@ -15,14 +15,16 @@ */ package org.jetbrains.idea.svn.commandLine; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Getter; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.util.containers.Convertor; -import org.jetbrains.idea.svn.SvnBindUtil; +import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.svn.SvnUtil; +import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.portable.PortableStatus; import org.jetbrains.idea.svn.portable.SvnExceptionWrapper; import org.jetbrains.idea.svn.portable.SvnStatusClientI; @@ -48,6 +50,8 @@ import java.util.Map; * Time: 5:21 PM */ public class SvnCommandLineStatusClient implements SvnStatusClientI { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.commandLine.SvnCommandLineStatusClient"); + private final Project myProject; private final SvnCommandLineInfoClient myInfoClient; @@ -102,47 +106,84 @@ public class SvnCommandLineStatusClient implements SvnStatusClientI { final SVNInfo infoBase = myInfoClient.doInfo(base, revision); final SvnSimpleCommand command = SvnCommandFactory.createSimpleCommand(myProject, base, SvnCommandName.st); - putParameters(depth, remote, reportAll, includeIgnored, changeLists, command); + putParameters(path, depth, remote, reportAll, includeIgnored, changeLists, command); + + parseResult(path, revision, handler, base, infoBase, command, execute(command, base)); + return 0; + } + + private String execute(SvnSimpleCommand command, File base) throws SVNException { + String result = CommandUtil.runSimple(command, SvnVcs.getInstance(myProject), base, null).getOutput(); - final SvnStatusHandler[] svnHandl = new SvnStatusHandler[1]; - svnHandl[0] = createStatusHandler(revision, handler, base, infoBase, svnHandl); + if (StringUtil.isEmptyOrSpaces(result)) { + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.FS_GENERAL, "Status request returned nothing for command: " + + command.myCommandLine.getCommandLineString())); + } + + return result; + } + + private void parseResult(final File path, + SVNRevision revision, + ISVNStatusHandler handler, + File base, + SVNInfo infoBase, + SvnSimpleCommand command, String result) throws SVNException { + + if (StringUtil.isEmpty(result)) { + return; + } try { - final String result = command.run(); - if (StringUtil.isEmptyOrSpaces(result)) { - throw new VcsException("Status request returned nothing for command: " + command.myCommandLine.getCommandLineString()); - } + final SvnStatusHandler[] svnHandl = new SvnStatusHandler[1]; + svnHandl[0] = createStatusHandler(revision, handler, base, infoBase, svnHandl); SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); parser.parse(new ByteArrayInputStream(result.getBytes(CharsetToolkit.UTF8_CHARSET)), svnHandl[0]); - if (! svnHandl[0].isAnythingReported()) { - if (! SvnUtil.isSvnVersioned(myProject, path)) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.WC_NOT_DIRECTORY)); + if (!svnHandl[0].isAnythingReported()) { + if (!SvnUtil.isSvnVersioned(myProject, path)) { + throw new SVNException( + SVNErrorMessage.create(SVNErrorCode.WC_NOT_DIRECTORY, "Command - " + command.getCommandText() + ". Result - " + result)); + } else { + // return status indicating "NORMAL" state + // typical output would be like + // <status> + // <target path="1.txt"></target> + // </status> + // so it does not contain any <entry> element and current parsing logic returns null + + PortableStatus status = new PortableStatus(); + status.setPath(path.getAbsolutePath()); + status.setContentsStatus(SVNStatusType.STATUS_NORMAL); + status.setInfoGetter(new Getter<SVNInfo>() { + @Override + public SVNInfo get() { + return createInfoGetter(null).convert(path); + } + }); + handler.handleStatus(status); } } } catch (SvnExceptionWrapper e) { throw (SVNException) e.getCause(); } catch (IOException e) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), e); + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); } catch (ParserConfigurationException e) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), e); + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); } catch (SAXException e) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), e); - } - catch (VcsException e) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR), e); + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e); } - return 0; } - private void putParameters(SVNDepth depth, + private void putParameters(@NotNull File path, SVNDepth depth, boolean remote, boolean reportAll, boolean includeIgnored, Collection changeLists, SvnSimpleCommand command) { + command.addParameters(path.getAbsolutePath()); if (depth != null) { command.addParameters("--depth", depth.getName()); } @@ -171,7 +212,11 @@ public class SvnCommandLineStatusClient implements SvnStatusClientI { final SVNInfo infoBase, final SvnStatusHandler[] svnHandl) { final SvnStatusHandler.ExternalDataCallback callback = createStatusCallback(handler, base, infoBase, svnHandl); - return new SvnStatusHandler(callback, base, new Convertor<File, SVNInfo>() { + return new SvnStatusHandler(callback, base, createInfoGetter(revision)); + } + + private Convertor<File, SVNInfo> createInfoGetter(final SVNRevision revision) { + return new Convertor<File, SVNInfo>() { @Override public SVNInfo convert(File o) { try { @@ -181,7 +226,7 @@ public class SvnCommandLineStatusClient implements SvnStatusClientI { throw new SvnExceptionWrapper(e); } } - }); + }; } public static SvnStatusHandler.ExternalDataCallback createStatusCallback(final ISVNStatusHandler handler, diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineUpdateClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineUpdateClient.java index c0e9c8e79fa2..e0834c2f116f 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineUpdateClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineUpdateClient.java @@ -24,7 +24,6 @@ import com.intellij.util.ArrayUtil; import org.jetbrains.idea.svn.SvnApplicationSettings; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.checkin.IdeaSvnkitBasedAuthenticationCallback; -import org.jetbrains.idea.svn.config.SvnBindException; import org.jetbrains.idea.svn.portable.SvnSvnkitUpdateClient; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.wc.*; @@ -49,9 +48,9 @@ public class SvnCommandLineUpdateClient extends SvnSvnkitUpdateClient { private final VirtualFile myCommonAncestor; private boolean myIgnoreExternals; - public SvnCommandLineUpdateClient(final Project project, VirtualFile commonAncestor) { - super(SvnVcs.getInstance(project).createUpdateClient()); - myProject = project; + public SvnCommandLineUpdateClient(final SvnVcs vcs, VirtualFile commonAncestor) { + super(vcs.createUpdateClient()); + myProject = vcs.getProject(); myCommonAncestor = commonAncestor; } @@ -88,35 +87,7 @@ public class SvnCommandLineUpdateClient extends SvnSvnkitUpdateClient { File base = myCommonAncestor == null ? paths[0] : new File(myCommonAncestor.getPath()); base = base.isDirectory() ? base : base.getParentFile(); - final List<String> parameters = new ArrayList<String>(); - if (revision != null && ! SVNRevision.UNDEFINED.equals(revision) && ! SVNRevision.WORKING.equals(revision)) { - parameters.add("-r"); - parameters.add(revision.toString()); - } - // unknown depth is not used any more for 1.7 -> why? - if (depth != null && ! SVNDepth.UNKNOWN.equals(depth)) { - parameters.add("--depth"); - parameters.add(depth.toString()); - } - if (allowUnversionedObstructions) { - parameters.add("--force"); - } - if (depthIsSticky && depth != null) {// !!! not sure, but not used - parameters.add("--set-depth"); - parameters.add(depth.toString()); - } - if (makeParents) { - parameters.add("--parents"); - } - if (myIgnoreExternals) { - parameters.add("--ignore-externals"); - } - parameters.add("--accept"); - parameters.add("postpone"); - - for (File path : paths) { - parameters.add(path.getPath()); - } + final List<String> parameters = prepareParameters(paths, revision, depth, allowUnversionedObstructions, depthIsSticky, makeParents); final AtomicReference<SVNException> excRef = new AtomicReference<SVNException>(); final ISVNEventHandler handler = getEventHandler(); @@ -166,7 +137,7 @@ public class SvnCommandLineUpdateClient extends SvnSvnkitUpdateClient { } }; SvnLineCommand.runWithAuthenticationAttempt(SvnApplicationSettings.getInstance().getCommandLinePath(), - base, SvnCommandName.up, listener, + base, info.getURL(), SvnCommandName.up, listener, new IdeaSvnkitBasedAuthenticationCallback(SvnVcs.getInstance(myProject)), ArrayUtil.toStringArray(parameters)); } @@ -180,6 +151,29 @@ public class SvnCommandLineUpdateClient extends SvnSvnkitUpdateClient { return updatedToRevision.get(); } + private List<String> prepareParameters(File[] paths, + SVNRevision revision, + SVNDepth depth, + boolean allowUnversionedObstructions, + boolean depthIsSticky, boolean makeParents) { + List<String> parameters = new ArrayList<String>(); + + CommandUtil.put(parameters, revision); + CommandUtil.put(parameters, depth); + CommandUtil.put(parameters, allowUnversionedObstructions, "--force"); + if (depthIsSticky && depth != null) {// !!! not sure, but not used + parameters.add("--set-depth"); + parameters.add(depth.toString()); + } + CommandUtil.put(parameters, makeParents, "--parents"); + CommandUtil.put(parameters, myIgnoreExternals, "--ignore-externals"); + parameters.add("--accept"); + parameters.add("postpone"); + CommandUtil.put(parameters, paths); + + return parameters; + } + private void checkForException(final StringBuffer sbError) throws SVNException { if (sbError.length() == 0) return; diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java index 58c2ff2ab308..c19ea283bbd1 100644 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java @@ -22,13 +22,25 @@ package org.jetbrains.idea.svn.commandLine; * Time: 1:49 PM */ public enum SvnCommandName { + // TODO: temporary command for "more smooth" converting between simple commands and line commands + empty("", false), version("--version", false), info("info", false), st("st", false), up("up", true), ci("commit", true), - cleanup("cleanup", true); - + cleanup("cleanup", true), + cat("cat", false), + add("add", true), + log("log", false), + revert("revert", true), + delete("delete", true), + copy("copy", true), + move("move", true), + resolve("resolve", true), + propget("propget", false), + blame("blame", false); + private final String myName; private final boolean myWriteable; diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnCommitRunner.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommitRunner.java index 525d675303fc..83f6ec156cf1 100644 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/SvnCommitRunner.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommitRunner.java @@ -13,22 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.svn; +package org.jetbrains.idea.svn.commandLine; import com.intellij.execution.process.ProcessOutputTypes; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.VcsException; import com.intellij.util.ArrayUtil; +import com.intellij.util.containers.Convertor; import org.apache.subversion.javahl.types.Revision; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn.commandLine.LineCommandListener; -import org.jetbrains.idea.svn.commandLine.SvnCommandName; -import org.jetbrains.idea.svn.commandLine.SvnLineCommand; -import org.jetbrains.idea.svn.config.SvnBindException; -import org.tigris.subversion.javahl.BindClientException; -import org.tigris.subversion.javahl.ClientException; +import org.tmatesoft.svn.core.SVNURL; import java.io.File; import java.util.*; @@ -42,7 +39,7 @@ import java.util.*; public class SvnCommitRunner { private final String myExePath; @Nullable private final AuthenticationCallback myAuthenticationCallback; - private static final Logger LOG = Logger.getInstance("org.jetbrains.idea.svn.SvnCommitRunner"); + private static final Logger LOG = Logger.getInstance("org.jetbrains.idea.svn.commandLine.SvnCommitRunner"); private SvnCommitRunner.CommandListener myCommandListener; public SvnCommitRunner(@NotNull String path, @Nullable CommitEventHandler handler, @Nullable AuthenticationCallback authenticationCallback) { @@ -57,7 +54,7 @@ public class SvnCommitRunner { boolean noUnlock, boolean keepChangelist, String[] changelists, - Map revpropTable) throws ClientException { + Map revpropTable, Convertor<String[], SVNURL> urlProvider) throws VcsException { if (paths.length == 0) return Revision.SVN_INVALID_REVNUM; final List<String> parameters = new ArrayList<String>(); @@ -84,19 +81,14 @@ public class SvnCommitRunner { Arrays.sort(paths); parameters.addAll(Arrays.asList(paths)); - try { - SvnLineCommand.runWithAuthenticationAttempt(myExePath, new File(paths[0]), SvnCommandName.ci, - myCommandListener, myAuthenticationCallback, ArrayUtil.toStringArray(parameters)); - } - catch (SvnBindException e) { - throw BindClientException.create(e, Revision.SVN_INVALID_REVNUM); - } + SvnLineCommand.runWithAuthenticationAttempt(myExePath, new File(paths[0]), urlProvider.convert(paths), SvnCommandName.ci, + myCommandListener, myAuthenticationCallback, ArrayUtil.toStringArray(parameters)); myCommandListener.throwExceptionIfOccurred(); return myCommandListener.getCommittedRevision(); } - private static class CommandListener extends LineCommandListener { + public static class CommandListener extends LineCommandListener { @Nullable private final CommitEventHandler myHandler; private SvnBindException myException; private long myCommittedRevision = Revision.SVN_INVALID_REVNUM; @@ -106,9 +98,9 @@ public class SvnCommitRunner { myHandler = handler; } - public void throwExceptionIfOccurred() throws BindClientException { + public void throwExceptionIfOccurred() throws VcsException { if (myException != null) { - throw BindClientException.create(myException, Revision.SVN_INVALID_REVNUM); + throw myException; } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoHandler.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoHandler.java index 022130e4a9f6..d8dbb6c19f67 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoHandler.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoHandler.java @@ -18,6 +18,7 @@ package org.jetbrains.idea.svn.commandLine; import com.intellij.openapi.util.Getter; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.Consumer; +import org.jetbrains.annotations.NotNull; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNNodeKind; @@ -49,7 +50,7 @@ public class SvnInfoHandler extends DefaultHandler { public SvnInfoHandler(File base, final Consumer<SVNInfo> infoConsumer) { myBase = base; myInfoConsumer = infoConsumer; - myPending = new SvnInfoStructure(); + myPending = createPending(); myElementsMap = new HashMap<String, Getter<ElementHandlerBase>>(); fillElements(); myParseStack = new ArrayList<ElementHandlerBase>(); @@ -70,7 +71,14 @@ public class SvnInfoHandler extends DefaultHandler { myInfoConsumer.consume(info); } myResultsMap.put(info.getFile(), info); - myPending = new SvnInfoStructure(); + myPending = createPending(); + } + + private SvnInfoStructure createPending() { + SvnInfoStructure pending = new SvnInfoStructure(); + pending.myDepth = SVNDepth.INFINITY; + + return pending; } @Override @@ -90,16 +98,13 @@ public class SvnInfoHandler extends DefaultHandler { public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { assertSAX(! myParseStack.isEmpty()); ElementHandlerBase current = myParseStack.get(myParseStack.size() - 1); - if (mySb.length() > 0) { - current.characters(mySb.toString().trim(), myPending); - mySb.setLength(0); - } while (true) { final boolean createNewChild = current.startElement(uri, localName, qName, attributes); if (createNewChild) { assertSAX(myElementsMap.containsKey(qName)); final ElementHandlerBase newChild = myElementsMap.get(qName).get(); + newChild.setParent(current); newChild.updateInfo(attributes, myPending); myParseStack.add(newChild); return; @@ -116,6 +121,18 @@ public class SvnInfoHandler extends DefaultHandler { } @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + ElementHandlerBase current = myParseStack.get(myParseStack.size() - 1); + String value = mySb.toString().trim(); + + if (!StringUtil.isEmpty(value)) { + current.characters(value, myPending); + } + + mySb.setLength(0); + } + + @Override public void characters(char[] ch, int start, int length) throws SAXException { assertSAX(! myParseStack.isEmpty()); mySb.append(ch, start, length); @@ -252,6 +269,24 @@ public class SvnInfoHandler extends DefaultHandler { return new Url(); } }); + myElementsMap.put("relative-url", new Getter<ElementHandlerBase>() { + @Override + public ElementHandlerBase get() { + return new RelativeUrl(); + } + }); + myElementsMap.put("lock", new Getter<ElementHandlerBase>() { + @Override + public ElementHandlerBase get() { + return new Lock(); + } + }); + myElementsMap.put("created", new Getter<ElementHandlerBase>() { + @Override + public ElementHandlerBase get() { + return new Date(); + } + }); myElementsMap.put("uuid", new Getter<ElementHandlerBase>() { @Override public ElementHandlerBase get() { @@ -270,6 +305,18 @@ public class SvnInfoHandler extends DefaultHandler { return new WcInfo(); } }); + myElementsMap.put("moved-to", new Getter<ElementHandlerBase>() { + @Override + public ElementHandlerBase get() { + return new MovedPath(); + } + }); + myElementsMap.put("moved-from", new Getter<ElementHandlerBase>() { + @Override + public ElementHandlerBase get() { + return new MovedPath(); + } + }); myElementsMap.put("wcroot-abspath", new Getter<ElementHandlerBase>() { @Override public ElementHandlerBase get() { @@ -295,6 +342,12 @@ public class SvnInfoHandler extends DefaultHandler { @Override protected void updateInfo(Attributes attributes, SvnInfoStructure structure) throws SAXException { + // TODO: Currently information for conflict (not tree-conflict) available in svn 1.8 is not used + // TODO: And it also not suite well for SVNKit api + if (getParent() instanceof Conflict) { + return; + } + final String side = attributes.getValue("side"); if ("source-left".equals(side)) { final SvnInfoStructure.ConflictVersion conflictVersion = new SvnInfoStructure.ConflictVersion(); @@ -382,7 +435,7 @@ public class SvnInfoHandler extends DefaultHandler { @Override public void characters(String s, SvnInfoStructure structure) throws SAXException { - structure.myConflictWorking = s; + structure.myConflictNew = new File(s).getName(); } } @@ -397,7 +450,7 @@ public class SvnInfoHandler extends DefaultHandler { @Override public void characters(String s, SvnInfoStructure structure) throws SAXException { - structure.myConflictNew = s; + structure.myConflictWorking = new File(s).getName(); } } @@ -412,14 +465,13 @@ public class SvnInfoHandler extends DefaultHandler { @Override public void characters(String s, SvnInfoStructure structure) throws SAXException { - // todo path? or plus base - structure.myConflictOld = s; + structure.myConflictOld = new File(s).getName(); } } private static class Conflict extends ElementHandlerBase { private Conflict() { - super(new String[]{"prev-base-file","prev-wc-file","cur-base-file","prop-file"}, new String[]{}); + super(new String[]{"prev-base-file","prev-wc-file","cur-base-file","prop-file"}, new String[]{"version"}); } @Override @@ -498,6 +550,25 @@ public class SvnInfoHandler extends DefaultHandler { } } + /** + * "moved-from" and "moved-to" elements are represented by this class. + */ + private static class MovedPath extends ElementHandlerBase { + + private MovedPath() { + super(new String[]{}, new String[]{}); + } + + @Override + protected void updateInfo(Attributes attributes, SvnInfoStructure structure) throws SAXException { + } + + @Override + public void characters(String s, SvnInfoStructure structure) throws SAXException { + // TODO: is there some field to initialize from this value? + } + } + private static class TextUpdated extends ElementHandlerBase { private TextUpdated() { super(new String[]{}, new String[]{}); @@ -617,7 +688,7 @@ public class SvnInfoHandler extends DefaultHandler { private static class WcInfo extends ElementHandlerBase { private WcInfo() { super(new String[]{"wcroot-abspath", "schedule", "depth", "text-updated", "checksum", "changelist", "copy-from-url", - "copy-from-rev"}, new String[]{}); + "copy-from-rev", "moved-to", "moved-from"}, new String[]{}); } @Override @@ -698,11 +769,34 @@ public class SvnInfoHandler extends DefaultHandler { } } + private static class RelativeUrl extends Url{ + @Override + public void characters(String s, SvnInfoStructure structure) throws SAXException { + structure.relativeUrl = s; + } + } + + private static class Lock extends ElementHandlerBase { + private Lock() { + super(new String[]{"created"}, new String[]{}); + } + + @Override + protected void updateInfo(Attributes attributes, SvnInfoStructure structure) throws SAXException { + // TODO: + } + + @Override + public void characters(String s, SvnInfoStructure structure) throws SAXException { + // TODO: + } + } + private static class Entry extends ElementHandlerBase { private final File myBase; private Entry(final File base) { - super(new String[]{"url","repository","wc-info","commit","conflict","tree-conflict"}, new String[]{}); + super(new String[]{"url", "relative-url", "lock", "repository","wc-info","commit","conflict","tree-conflict"}, new String[]{}); myBase = base; } @@ -763,12 +857,22 @@ public class SvnInfoHandler extends DefaultHandler { private abstract static class ElementHandlerBase { private final Set<String> myAwaitedChildren; private final Set<String> myAwaitedChildrenMultiple; + private ElementHandlerBase parent; ElementHandlerBase(String[] awaitedChildren, String[] awaitedChildrenMultiple) { myAwaitedChildren = new HashSet<String>(Arrays.asList(awaitedChildren)); myAwaitedChildrenMultiple = new HashSet<String>(Arrays.asList(awaitedChildrenMultiple)); } + @NotNull + public ElementHandlerBase getParent() { + return parent; + } + + public void setParent(@NotNull ElementHandlerBase parent) { + this.parent = parent; + } + protected abstract void updateInfo(Attributes attributes, SvnInfoStructure structure) throws SAXException; public boolean startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoStructure.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoStructure.java index 0292b7d93614..1aa5248c1939 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoStructure.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoStructure.java @@ -34,6 +34,7 @@ import java.util.Date; */ public class SvnInfoStructure { public File myFile; + public String relativeUrl; public SVNURL myUrl; public SVNURL myRootURL; public long myRevision; diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java index 5821d7a5fb18..c5e3b9e6861c 100644 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java @@ -23,20 +23,30 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.LineHandlerHelper; import com.intellij.openapi.vcs.LineProcessEventListener; import com.intellij.openapi.vcs.VcsException; +import com.intellij.util.ArrayUtil; import com.intellij.util.EventDispatcher; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn.AuthenticationCallback; -import org.jetbrains.idea.svn.SvnBindUtil; -import org.jetbrains.idea.svn.config.SvnBindException; +import org.jetbrains.idea.svn.SvnUtil; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager; +import org.tmatesoft.svn.core.auth.SVNAuthentication; +import org.tmatesoft.svn.core.auth.SVNPasswordAuthentication; +import org.tmatesoft.svn.core.auth.SVNSSLAuthentication; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; +import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Created with IntelliJ IDEA. @@ -47,11 +57,21 @@ import java.util.concurrent.atomic.AtomicReference; * honestly stolen from GitLineHandler */ public class SvnLineCommand extends SvnCommand { + public static final String AUTHENTICATION_REALM = "Authentication realm:"; public static final String CERTIFICATE_ERROR = "Error validating server certificate for"; public static final String PASSPHRASE_FOR = "Passphrase for"; public static final String UNABLE_TO_CONNECT = "svn: E170001:"; public static final String CANNOT_AUTHENTICATE_TO_PROXY = "Could not authenticate to proxy server"; + public static final String AUTHENTICATION_FAILED_MESSAGE = "Authentication failed"; + + private static final String INVALID_CREDENTIALS_FOR_SVN_PROTOCOL = "svn: E170001: Can't get"; + private static final String UNTRUSTED_SERVER_CERTIFICATE = "Server SSL certificate untrusted"; + private static final String ACCESS_TO_PREFIX = "Access to "; + private static final String FORBIDDEN_STATUS = "forbidden"; + private static final String PASSWORD_STRING = "password"; + + private static final Pattern UNABLE_TO_CONNECT_TO_URL_PATTERN = Pattern.compile("Unable to connect to a repository at URL '(.*)'"); // kept for exact text //public static final String CLIENT_CERTIFICATE_FILENAME = "Client certificate filename:"; @@ -66,6 +86,7 @@ public class SvnLineCommand extends SvnCommand { private final EventDispatcher<LineProcessEventListener> myLineListeners; private final AtomicReference<Integer> myExitCode; private final StringBuffer myErr; + private final StringBuffer myStdOut; public SvnLineCommand(File workingDirectory, @NotNull SvnCommandName commandName, @NotNull @NonNls String exePath) { this(workingDirectory, commandName, exePath, null); @@ -76,6 +97,7 @@ public class SvnLineCommand extends SvnCommand { myLineListeners = EventDispatcher.create(LineProcessEventListener.class); myExitCode = new AtomicReference<Integer>(); myErr = new StringBuffer(); + myStdOut = new StringBuffer(); } @Override @@ -89,13 +111,14 @@ public class SvnLineCommand extends SvnCommand { } } - public static void runWithAuthenticationAttempt(final String exePath, - final File firstFile, - SvnCommandName commandName, - final LineCommandListener listener, - @Nullable AuthenticationCallback authenticationCallback, - final String... parameters) throws SvnBindException { - File base = firstFile.isDirectory() ? firstFile : firstFile.getParentFile(); + public static SvnLineCommand runWithAuthenticationAttempt(final String exePath, + final File firstFile, + final SVNURL url, + SvnCommandName commandName, + final LineCommandListener listener, + @Nullable AuthenticationCallback authenticationCallback, + String... parameters) throws SvnBindException { + File base = firstFile != null ? (firstFile.isDirectory() ? firstFile : firstFile.getParentFile()) : null; base = SvnBindUtil.correctUpToExistingParent(base); listener.baseDirectory(base); @@ -111,27 +134,35 @@ public class SvnLineCommand extends SvnCommand { while (true) { final SvnLineCommand command = runCommand(exePath, commandName, listener, base, configDir, parameters); - if (command.myErr.length() > 0) { - final String errText = command.myErr.toString().trim(); - if (authenticationCallback != null) { - final AuthCallbackCase callback = createCallback(errText, authenticationCallback, base); - if (callback != null) { - cleanup(exePath, commandName, base); - if (callback.getCredentials(errText)) { - if (authenticationCallback.getSpecialConfigDir() != null) { - configDir = authenticationCallback.getSpecialConfigDir(); + final Integer exitCode = command.myExitCode.get(); + + // could be situations when exit code = 0, but there is info "warning" in error stream + // for instance, for "svn status" on non-working copy folder + if (exitCode != 0) { + if (command.myErr.length() > 0) { + // handle authentication + final String errText = command.myErr.toString().trim(); + if (authenticationCallback != null) { + final AuthCallbackCase callback = createCallback(errText, authenticationCallback, base, url); + if (callback != null) { + cleanup(exePath, command, base); + if (callback.getCredentials(errText)) { + if (authenticationCallback.getSpecialConfigDir() != null) { + configDir = authenticationCallback.getSpecialConfigDir(); + } + parameters = updateParameters(callback, parameters); + continue; } - continue; } } + throw new SvnBindException(errText); + } else { + throw new SvnBindException("Svn process exited with error code: " + exitCode); } - throw new SvnBindException(errText); + } else if (command.myErr.length() > 0) { + LOG.info("Detected warning - " + command.myErr); } - final Integer exitCode = command.myExitCode.get(); - if (exitCode != 0) { - throw new SvnBindException("Svn process exited with error code: " + exitCode); - } - return; + return command; } } finally { if (authenticationCallback != null) { @@ -140,6 +171,13 @@ public class SvnLineCommand extends SvnCommand { } } + private static String[] updateParameters(AuthCallbackCase callback, String[] parameters) { + List<String> p = new ArrayList<String>(Arrays.asList(parameters)); + + callback.updateParameters(p); + return ArrayUtil.toStringArray(p); + } + private static void writeIdeaConfig2SubversionConfig(@NotNull AuthenticationCallback authenticationCallback, @NotNull File base) throws SvnBindException { if (authenticationCallback.haveDataForTmpConfig()) { try { @@ -158,7 +196,7 @@ public class SvnLineCommand extends SvnCommand { } } - private static AuthCallbackCase createCallback(final String errText, final AuthenticationCallback callback, final File base) { + private static AuthCallbackCase createCallback(final String errText, final AuthenticationCallback callback, final File base, final SVNURL url) { if (errText.startsWith(CERTIFICATE_ERROR)) { return new CertificateCallbackCase(callback, base); } @@ -171,9 +209,85 @@ public class SvnLineCommand extends SvnCommand { if (errText.startsWith(UNABLE_TO_CONNECT) && errText.contains(CANNOT_AUTHENTICATE_TO_PROXY)) { return new ProxyCallback(callback, base); } + // http/https protocol invalid credentials + if (errText.contains(AUTHENTICATION_FAILED_MESSAGE)) { + return new UsernamePasswordCallback(callback, base, url); + } + // messages could be "Can't get password", "Can't get username or password" + if (errText.contains(INVALID_CREDENTIALS_FOR_SVN_PROTOCOL) && errText.contains(PASSWORD_STRING)) { + // svn protocol invalid credentials + return new UsernamePasswordCallback(callback, base, url); + } + // https one-way protocol untrusted server certificate + if (errText.contains(UNTRUSTED_SERVER_CERTIFICATE)) { + return new CertificateCallbackCase(callback, base); + } + // https two-way protocol invalid client certificate + if (errText.contains(ACCESS_TO_PREFIX) && errText.contains(FORBIDDEN_STATUS)) { + return new TwoWaySslCallback(callback, base, url); + } return null; } + // Special callback for svn 1.8 credentials request as --non-interactive does not return + // authentication realm (just url) - so we could not create temp cache + private static class UsernamePasswordCallback extends AuthCallbackCase { + protected SVNAuthentication myAuthentication; + protected SVNURL myUrl; + + protected UsernamePasswordCallback(AuthenticationCallback callback, File base, SVNURL url) { + super(callback, base); + myUrl = url; + } + + @Override + boolean getCredentials(String errText) throws SvnBindException { + myAuthentication = myAuthenticationCallback.requestCredentials(myUrl != null ? myUrl : parseUrlFromError(errText), + getType()); + + return myAuthentication != null; + } + + public String getType() { + return ISVNAuthenticationManager.PASSWORD; + } + + @Override + public void updateParameters(List<String> parameters) { + if (myAuthentication instanceof SVNPasswordAuthentication) { + SVNPasswordAuthentication auth = (SVNPasswordAuthentication)myAuthentication; + + parameters.add("--username"); + parameters.add(auth.getUserName()); + parameters.add("--password"); + parameters.add(auth.getPassword()); + if (!auth.isStorageAllowed()) { + parameters.add("--no-auth-cache"); + } + } + } + + private SVNURL parseUrlFromError(String errorText) { + Matcher matcher = UNABLE_TO_CONNECT_TO_URL_PATTERN.matcher(errorText); + String urlValue = null; + + if (matcher.find()) { + urlValue = matcher.group(1); + } + + return urlValue != null ? parseUrl(urlValue) : null; + } + + private SVNURL parseUrl(String urlValue) { + try { + return SVNURL.parseURIEncoded(urlValue); + } + catch (SVNException e) { + return null; + } + } + } + private static class ProxyCallback extends AuthCallbackCase { protected ProxyCallback(AuthenticationCallback callback, File base) { super(callback, base); @@ -192,7 +306,8 @@ public class SvnLineCommand extends SvnCommand { @Override boolean getCredentials(String errText) throws SvnBindException { - final String realm = cutFirstLine(errText).substring(AUTHENTICATION_REALM.length()).trim(); + final String realm = + errText.startsWith(AUTHENTICATION_REALM) ? cutFirstLine(errText).substring(AUTHENTICATION_REALM.length()).trim() : null; final boolean isPassword = StringUtil.containsIgnoreCase(errText, "password"); if (myTried) { myAuthenticationCallback.clearPassiveCredentials(realm, myBase, isPassword); @@ -212,13 +327,16 @@ public class SvnLineCommand extends SvnCommand { } private static class CertificateCallbackCase extends AuthCallbackCase { + private boolean accepted; + private CertificateCallbackCase(AuthenticationCallback callback, File base) { super(callback, base); } @Override public boolean getCredentials(final String errText) throws SvnBindException { - String realm = cutFirstLine(errText).substring(CERTIFICATE_ERROR.length()); + // parse realm from error text + String realm = errText; final int idx1 = realm.indexOf('\''); if (idx1 == -1) { throw new SvnBindException("Can not detect authentication realm name: " + errText); @@ -229,11 +347,48 @@ public class SvnLineCommand extends SvnCommand { } realm = realm.substring(idx1 + 1, idx2); if (! myTried && myAuthenticationCallback.acceptSSLServerCertificate(myBase, realm)) { + accepted = true; myTried = true; return true; } throw new SvnBindException("Server SSL certificate rejected"); } + + @Override + public void updateParameters(List<String> parameters) { + if (accepted) { + parameters.add("--trust-server-cert"); + } + } + } + + private static class TwoWaySslCallback extends UsernamePasswordCallback { + + protected TwoWaySslCallback(AuthenticationCallback callback, File base, SVNURL url) { + super(callback, base, url); + } + + @Override + public String getType() { + return ISVNAuthenticationManager.SSL; + } + + @Override + public void updateParameters(List<String> parameters) { + if (myAuthentication instanceof SVNSSLAuthentication) { + SVNSSLAuthentication auth = (SVNSSLAuthentication)myAuthentication; + + // TODO: Seems that config option should be specified for concrete server and not for global group. + // as in that case it could be overriden by settings in config file + parameters.add("--config-option"); + parameters.add("servers:global:ssl-client-cert-file=" + auth.getCertificatePath()); + parameters.add("--config-option"); + parameters.add("servers:global:ssl-client-cert-password=" + auth.getPassword()); + if (!auth.isStorageAllowed()) { + parameters.add("--no-auth-cache"); + } + } + } } private static abstract class AuthCallbackCase { @@ -247,17 +402,23 @@ public class SvnLineCommand extends SvnCommand { } abstract boolean getCredentials(final String errText) throws SvnBindException; + + public void updateParameters(List<String> parameters) { + } } - private static void cleanup(String exePath, SvnCommandName commandName, File base) throws SvnBindException { - File wcRoot = SvnBindUtil.getWcRoot(base); - if (wcRoot == null) throw new SvnBindException("Can not find working copy root for: " + base.getPath()); + private static void cleanup(String exePath, SvnCommand command, File base) throws SvnBindException { + // TODO: could be issues with fake "empty" command as it is not writable - but only read commands currently use "empty" command + // TODO: and "empty" command will be removed shortly + if (command.isManuallyDestroyed() && command.getCommandName().isWriteable()) { + File wcRoot = SvnUtil.getWorkingCopyRootNew(base); + if (wcRoot == null) { + throw new SvnBindException("Can not find working copy root for: " + base.getPath()); + } - //cleanup -> check command type - if (commandName.isWriteable()) { - final SvnSimpleCommand command = new SvnSimpleCommand(wcRoot, SvnCommandName.cleanup, exePath); + final SvnSimpleCommand cleanupCommand = new SvnSimpleCommand(wcRoot, SvnCommandName.cleanup, exePath); try { - command.run(); + cleanupCommand.run(); } catch (VcsException e) { throw new SvnBindException(e); @@ -320,6 +481,10 @@ public class SvnLineCommand extends SvnCommand { command.addLineListener(new LineProcessEventListener() { @Override public void onLineAvailable(String line, Key outputType) { + if (ProcessOutputTypes.STDOUT.equals(outputType)) { + command.myStdOut.append(line); + } + if (SvnCommand.LOG.isDebugEnabled()) { SvnCommand.LOG.debug("==> " + line); } diff --git a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java index 75c43befb6cb..a088da0334a3 100644 --- a/plugins/svn4idea/bindSvn/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java @@ -94,7 +94,11 @@ public class SvnSimpleCommand extends SvnCommand { if (myException != null) throw myException; final int code = getExitCode(); if (code == 0) { - return myStdout.toString(); + String result = myStdout.toString(); + + LOG.debug(result); + + return result; } else { final String msg = new StringBuilder("Svn process exited with error code: ").append(code).append("\n") .append("stderr: ").append(myStderr.toString()).append("\nstdout: ").append(getStdout().toString()) diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/UpdateOutputLineConverter.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/UpdateOutputLineConverter.java index 6536646374bd..8700c363f460 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/UpdateOutputLineConverter.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/UpdateOutputLineConverter.java @@ -62,6 +62,8 @@ public class UpdateOutputLineConverter { } public SVNEvent convert(final String line) { + // TODO: Add direct processing of "Summary of conflicts" lines at the end of "svn update" output (if there are conflicts). + // TODO: Now it works ok because parseNormalLine could not determine necessary statuses from that and further lines if (StringUtil.isEmptyOrSpaces(line)) return null; if (line.startsWith(UPDATING)) { @@ -103,9 +105,9 @@ public class UpdateOutputLineConverter { if (line.length() < 5) return null; final char first = line.charAt(0); if (' ' != first && ! ourActions.contains(first)) return null; - final SVNStatusType contentsStatus = getStatusType(first); + final SVNStatusType contentsStatus = CommandUtil.getStatusType(first); final char second = line.charAt(1); - final SVNStatusType propertiesStatus = getStatusType(second); + final SVNStatusType propertiesStatus = CommandUtil.getStatusType(second); final char lock = line.charAt(2); // dont know what to do with stolen lock info if (' ' != lock && 'B' != lock) return null; final char treeConflict = line.charAt(3); @@ -140,28 +142,6 @@ public class UpdateOutputLineConverter { null, action, expectedAction, null, null, null, null, null); } - private SVNStatusType getStatusType(char first) { - final SVNStatusType contentsStatus; - if ('A' == first) { - contentsStatus = SVNStatusType.STATUS_ADDED; - } else if ('D' == first) { - contentsStatus = SVNStatusType.STATUS_DELETED; - } else if ('U' == first) { - contentsStatus = SVNStatusType.CHANGED; - } else if ('C' == first) { - contentsStatus = SVNStatusType.CONFLICTED; - } else if ('G' == first) { - contentsStatus = SVNStatusType.MERGED; - } else if ('R' == first) { - contentsStatus = SVNStatusType.STATUS_REPLACED; - } else if ('E' == first) { - contentsStatus = SVNStatusType.STATUS_OBSTRUCTED; - } else { - contentsStatus = SVNStatusType.STATUS_NORMAL; - } - return contentsStatus; - } - @Nullable private long matchAndGetRevision(final Pattern pattern, final String line) { final Matcher matcher = pattern.matcher(line); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/CmdConflictClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/CmdConflictClient.java new file mode 100644 index 000000000000..79614a24f554 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/CmdConflictClient.java @@ -0,0 +1,33 @@ +package org.jetbrains.idea.svn.conflict; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.CommandUtil; +import org.jetbrains.idea.svn.commandLine.SvnCommandName; +import org.tmatesoft.svn.core.SVNDepth; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +public class CmdConflictClient extends BaseSvnClient implements ConflictClient { + + // TODO: Add possibility to resolve content conflicts separately from property conflicts. + @Override + public void resolve(@NotNull File path, boolean resolvePropertyConflicts) throws VcsException { + List<String> parameters = new ArrayList<String>(); + + CommandUtil.put(parameters, path); + CommandUtil.put(parameters, SVNDepth.EMPTY); + parameters.add("--accept"); + parameters.add("working"); + + // for now parsing of the output is not required as command is executed only for one file + // and will be either successful or exception will be thrown + CommandUtil.execute(myVcs, SvnCommandName.resolve, parameters, null); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/ConflictClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/ConflictClient.java new file mode 100644 index 000000000000..812fdef5621b --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/ConflictClient.java @@ -0,0 +1,15 @@ +package org.jetbrains.idea.svn.conflict; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.api.SvnClient; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public interface ConflictClient extends SvnClient { + + void resolve(@NotNull File path, boolean resolvePropertyConflicts) throws VcsException; +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/SvnKitConflictClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/SvnKitConflictClient.java new file mode 100644 index 000000000000..33ee0f870c95 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/SvnKitConflictClient.java @@ -0,0 +1,25 @@ +package org.jetbrains.idea.svn.conflict; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.SVNConflictChoice; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnKitConflictClient extends BaseSvnClient implements ConflictClient { + @Override + public void resolve(@NotNull File path, boolean resolvePropertyConflicts) throws VcsException { + try { + myVcs.createWCClient().doResolve(path, SVNDepth.EMPTY, true, resolvePropertyConflicts, SVNConflictChoice.MERGED); + } + catch (SVNException e) { + throw new VcsException(e); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/content/CmdContentClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/content/CmdContentClient.java new file mode 100644 index 000000000000..c822fe0a1f48 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/content/CmdContentClient.java @@ -0,0 +1,39 @@ +package org.jetbrains.idea.svn.content; + +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.impl.ContentRevisionCache; +import com.intellij.openapi.vfs.CharsetToolkit; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.*; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc2.SvnTarget; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +public class CmdContentClient extends BaseSvnClient implements ContentClient { + + @Override + public byte[] getContent(@NotNull SvnTarget target, @Nullable SVNRevision revision, @Nullable SVNRevision pegRevision) + throws VcsException, FileTooBigRuntimeException { + // TODO: rewrite this to provide output as Stream + // TODO: rewrite without conversion from String to byte[] + // TODO: Also implement max size constraint like in SvnKitContentClient + List<String> parameters = new ArrayList<String>(); + CommandUtil.put(parameters, target.getPathOrUrlString(), pegRevision); + CommandUtil.put(parameters, revision); + + SvnCommand command = CommandUtil.execute(myVcs, SvnCommandName.cat, parameters, null); + + byte[] bytes = CharsetToolkit.getUtf8Bytes(command.getOutput()); + + ContentRevisionCache.checkContentsSize(target.getPathOrUrlString(), bytes.length); + + return bytes; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/content/ContentClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/content/ContentClient.java new file mode 100644 index 000000000000..3654d09c88bc --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/content/ContentClient.java @@ -0,0 +1,17 @@ +package org.jetbrains.idea.svn.content; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.SvnClient; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc2.SvnTarget; + +/** + * @author Konstantin Kolosovsky. + */ +public interface ContentClient extends SvnClient { + + byte[] getContent(@NotNull SvnTarget target, @Nullable SVNRevision revision, @Nullable SVNRevision pegRevision) + throws VcsException, FileTooBigRuntimeException; +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/content/FileTooBigRuntimeException.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/content/FileTooBigRuntimeException.java new file mode 100644 index 000000000000..0a0e585069a6 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/content/FileTooBigRuntimeException.java @@ -0,0 +1,7 @@ +package org.jetbrains.idea.svn.content; + +/** + * @author Konstantin Kolosovsky. + */ +public class FileTooBigRuntimeException extends RuntimeException { +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/content/SvnKitContentClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/content/SvnKitContentClient.java new file mode 100644 index 000000000000..d5916970e320 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/content/SvnKitContentClient.java @@ -0,0 +1,61 @@ +package org.jetbrains.idea.svn.content; + +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.impl.ContentRevisionCache; +import com.intellij.vcsUtil.VcsUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc.SVNWCClient; +import org.tmatesoft.svn.core.wc2.SvnTarget; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnKitContentClient extends BaseSvnClient implements ContentClient { + + @Override + public byte[] getContent(@NotNull SvnTarget target, @Nullable SVNRevision revision, @Nullable SVNRevision pegRevision) + throws VcsException, FileTooBigRuntimeException { + final int maxSize = VcsUtil.getMaxVcsLoadedFileSize(); + ByteArrayOutputStream buffer = new ByteArrayOutputStream() { + @Override + public synchronized void write(int b) { + if (size() > maxSize) throw new FileTooBigRuntimeException(); + super.write(b); + } + + @Override + public synchronized void write(byte[] b, int off, int len) { + if (size() > maxSize) throw new FileTooBigRuntimeException(); + super.write(b, off, len); + } + + @Override + public synchronized void writeTo(OutputStream out) throws IOException { + if (size() > maxSize) throw new FileTooBigRuntimeException(); + super.writeTo(out); + } + }; + SVNWCClient wcClient = myVcs.createWCClient(); + try { + if (target.isURL()) { + wcClient.doGetFileContents(target.getURL(), pegRevision, revision, true, buffer); + } else { + wcClient.doGetFileContents(target.getFile(), pegRevision, revision, true, buffer); + } + ContentRevisionCache.checkContentsSize(target.getPathOrUrlString(), buffer.size()); + } catch (FileTooBigRuntimeException e) { + ContentRevisionCache.checkContentsSize(target.getPathOrUrlString(), buffer.size()); + } catch (SVNException e) { + throw new VcsException(e); + } + return buffer.toByteArray(); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/CmdCopyMoveClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/CmdCopyMoveClient.java new file mode 100644 index 000000000000..eceef14d994d --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/CmdCopyMoveClient.java @@ -0,0 +1,30 @@ +package org.jetbrains.idea.svn.copy; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.CommandUtil; +import org.jetbrains.idea.svn.commandLine.SvnCommandName; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +public class CmdCopyMoveClient extends BaseSvnClient implements CopyMoveClient { + + @Override + public void copy(@NotNull File src, @NotNull File dst, boolean makeParents, boolean isMove) throws VcsException { + List<String> parameters = new ArrayList<String>(); + + CommandUtil.put(parameters, src); + CommandUtil.put(parameters, dst); + CommandUtil.put(parameters, makeParents, "--parents"); + + // for now parsing of the output is not required as command is executed only for one file + // and will be either successful or exception will be thrown + CommandUtil.execute(myVcs, isMove ? SvnCommandName.move : SvnCommandName.copy, parameters, null); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/CopyMoveClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/CopyMoveClient.java new file mode 100644 index 000000000000..ec48107c8223 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/CopyMoveClient.java @@ -0,0 +1,15 @@ +package org.jetbrains.idea.svn.copy; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.api.SvnClient; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public interface CopyMoveClient extends SvnClient { + + void copy(@NotNull File src, @NotNull File dst, boolean makeParents, boolean isMove) throws VcsException; +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/SvnKitCopyMoveClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/SvnKitCopyMoveClient.java new file mode 100644 index 000000000000..ea84bc6f0816 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/copy/SvnKitCopyMoveClient.java @@ -0,0 +1,28 @@ +package org.jetbrains.idea.svn.copy; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.SVNCopySource; +import org.tmatesoft.svn.core.wc.SVNRevision; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnKitCopyMoveClient extends BaseSvnClient implements CopyMoveClient { + + @Override + public void copy(@NotNull File src, @NotNull File dst, boolean makeParents, boolean isMove) throws VcsException { + final SVNCopySource copySource = new SVNCopySource(isMove ? SVNRevision.UNDEFINED : SVNRevision.WORKING, SVNRevision.WORKING, src); + + try { + myVcs.createCopyClient().doCopy(new SVNCopySource[]{copySource}, dst, isMove, makeParents, true); + } + catch (SVNException e) { + throw new VcsException(e); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/CmdDeleteClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/CmdDeleteClient.java new file mode 100644 index 000000000000..5fc26acfb1d3 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/CmdDeleteClient.java @@ -0,0 +1,29 @@ +package org.jetbrains.idea.svn.delete; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.CommandUtil; +import org.jetbrains.idea.svn.commandLine.SvnCommandName; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +public class CmdDeleteClient extends BaseSvnClient implements DeleteClient { + + @Override + public void delete(@NotNull File path, boolean force) throws VcsException { + List<String> parameters = new ArrayList<String>(); + + CommandUtil.put(parameters, path); + CommandUtil.put(parameters, force, "--force"); + + // for now parsing of the output is not required as command is executed only for one file + // and will be either successful or exception will be thrown + CommandUtil.execute(myVcs, SvnCommandName.delete, parameters, null); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/DeleteClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/DeleteClient.java new file mode 100644 index 000000000000..23a1cf97cb31 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/DeleteClient.java @@ -0,0 +1,15 @@ +package org.jetbrains.idea.svn.delete; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.api.SvnClient; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public interface DeleteClient extends SvnClient { + + void delete(@NotNull File path, boolean force) throws VcsException; +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/SvnKitDeleteClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/SvnKitDeleteClient.java new file mode 100644 index 000000000000..c97cfd2a44bb --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/delete/SvnKitDeleteClient.java @@ -0,0 +1,24 @@ +package org.jetbrains.idea.svn.delete; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.tmatesoft.svn.core.SVNException; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnKitDeleteClient extends BaseSvnClient implements DeleteClient { + + @Override + public void delete(@NotNull File path, boolean force) throws VcsException { + try { + myVcs.createWCClient().doDelete(path, force, false); + } + catch (SVNException e) { + throw new VcsException(e); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/CreateBranchOrTagDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/CreateBranchOrTagDialog.java index 62fc918d8a92..e1ad9157ce7b 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/CreateBranchOrTagDialog.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/CreateBranchOrTagDialog.java @@ -263,17 +263,11 @@ public class CreateBranchOrTagDialog extends DialogWrapper { super.init(); SvnVcs vcs = SvnVcs.getInstance(myProject); String revStr = ""; - try { - SVNWCClient client = vcs.createWCClient(); - SVNInfo info = client.doInfo(mySrcFile, SVNRevision.UNDEFINED); - if (info != null) { - mySrcURL = info.getURL() == null ? null : info.getURL().toString(); - revStr = String.valueOf(info.getRevision()); - myURL = mySrcURL; - } - } - catch (SVNException e) { - // + SVNInfo info = vcs.getInfo(mySrcFile); + if (info != null) { + mySrcURL = info.getURL() == null ? null : info.getURL().toString(); + revStr = String.valueOf(info.getRevision()); + myURL = mySrcURL; } if (myURL == null) { return; @@ -354,15 +348,8 @@ public class CreateBranchOrTagDialog extends DialogWrapper { return true; } else if (myWorkingCopyRadioButton.isSelected()) { - String srcUrl; - try { - SVNWCClient client = SvnVcs.getInstance(myProject).createWCClient(); - SVNInfo info = client.doInfo(mySrcFile, SVNRevision.UNDEFINED); - srcUrl = info != null && info.getURL() != null ? info.getURL().toString() : null; - } - catch (SVNException e) { - srcUrl = null; - } + SVNInfo info = SvnVcs.getInstance(myProject).getInfo(mySrcFile); + String srcUrl = info != null && info.getURL() != null ? info.getURL().toString() : null; if (srcUrl == null) { myErrorLabel.setText(SvnBundle.message("create.branch.no.working.copy.error", myWorkingCopyField.getText())); return false; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/CmdHistoryClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/CmdHistoryClient.java new file mode 100644 index 000000000000..3948ddee57b7 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/CmdHistoryClient.java @@ -0,0 +1,223 @@ +package org.jetbrains.idea.svn.history; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.util.LineSeparator; +import com.intellij.util.containers.hash.HashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.CommandUtil; +import org.jetbrains.idea.svn.commandLine.SvnCommandName; +import org.jetbrains.idea.svn.commandLine.SvnLineCommand; +import org.tmatesoft.svn.core.ISVNLogEntryHandler; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNLogEntry; +import org.tmatesoft.svn.core.SVNLogEntryPath; +import org.tmatesoft.svn.core.wc.SVNRevision; + +import java.io.File; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Konstantin Kolosovsky. + */ +public class CmdHistoryClient extends BaseSvnClient implements HistoryClient { + + private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.history.CmdHistoryClient"); + + @Override + public void doLog(@NotNull File path, + @NotNull SVNRevision startRevision, + @NotNull SVNRevision endRevision, + @Nullable SVNRevision pegRevision, + boolean stopOnCopy, + boolean discoverChangedPaths, + boolean includeMergedRevisions, + long limit, + @Nullable String[] revisionProperties, + @Nullable ISVNLogEntryHandler handler) throws VcsException { + // TODO: add revision properties parameter if necessary + // TODO: svn log command supports --xml option - could update parsing to use xml format + + // TODO: after merge remove setting includeMergedRevisions to false and update parsing + includeMergedRevisions = false; + + List<String> parameters = + prepareCommand(path, startRevision, endRevision, pegRevision, stopOnCopy, discoverChangedPaths, includeMergedRevisions, limit); + + try { + SvnLineCommand command = CommandUtil.runSimple(SvnCommandName.log, myVcs, path, null, parameters); + // TODO: handler should be called in parallel with command execution, but this will be in other thread + // TODO: check if that is ok for current handler implementation + parseOutput(handler, command); + } + catch (SVNException e) { + throw new VcsException(e); + } + } + + private static void parseOutput(@Nullable ISVNLogEntryHandler handler, @NotNull SvnLineCommand command) + throws VcsException, SVNException { + Parser parser = new Parser(handler); + for (String line : StringUtil.splitByLines(command.getOutput(), false)) { + parser.onLine(line); + } + } + + private static List<String> prepareCommand(@NotNull File path, + @NotNull SVNRevision startRevision, + @NotNull SVNRevision endRevision, + @Nullable SVNRevision pegRevision, + boolean stopOnCopy, boolean discoverChangedPaths, boolean includeMergedRevisions, long limit) { + List<String> parameters = new ArrayList<String>(); + + CommandUtil.put(parameters, path, pegRevision); + parameters.add("--revision"); + parameters.add(startRevision + ":" + endRevision); + + CommandUtil.put(parameters, stopOnCopy, "--stop-on-copy"); + CommandUtil.put(parameters, discoverChangedPaths, "--verbose"); + CommandUtil.put(parameters, includeMergedRevisions, "--use-merge-history"); + if (limit > 0) { + parameters.add("--limit"); + parameters.add(String.valueOf(limit)); + } + + return parameters; + } + + private static class Parser { + private static final String REVISION = "\\s*r(\\d+)\\s*"; + private static final String AUTHOR = "\\s*([^|]*)\\s*"; + private static final String DATE = "\\s*([^|]*)\\s*"; + private static final String MESSAGE_LINES = "\\s*(\\d+).*"; + + private static final Pattern ENTRY_START = Pattern.compile("-+"); + private static final Pattern DETAILS = Pattern.compile(REVISION + "\\|" + AUTHOR + "\\|" + DATE + "\\|" + MESSAGE_LINES); + + private static final String STATUS = "\\s*(\\w)"; + private static final String PATH = "\\s*(.*?)\\s*"; + private static final String COPY_FROM_PATH = "(/[^:]*)"; + private static final String COPY_FROM_REVISION = "(\\d+)\\))\\s*"; + private static final String COPY_FROM_INFO = "((\\(from " + COPY_FROM_PATH + ":" + COPY_FROM_REVISION + ")?"; + private static final Pattern CHANGED_PATH = Pattern.compile(STATUS + PATH + COPY_FROM_INFO); + + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); + + ISVNLogEntryHandler handler; + + Entry entry; + boolean waitDetails; + boolean waitChangedPath; + boolean waitMessage; + + public Parser(@Nullable ISVNLogEntryHandler handler) { + this.handler = handler; + } + + public void onLine(@NotNull String line) throws VcsException, SVNException { + if (ENTRY_START.matcher(line).matches()) { + processEntryStart(); + } + else if (waitDetails) { + processDetails(line); + } + else if (waitMessage) { + processMessage(line); + } + else if (StringUtil.isEmpty(line.trim())) { + processChangedPathsFinished(); + } + else if (line.startsWith("Changed paths:")) { + processChangedPathsStarted(); + } + else if (waitChangedPath) { + processChangedPath(line); + } + else { + throw new VcsException("unknown state on line " + line); + } + } + + private void processChangedPath(@NotNull String line) throws VcsException { + Matcher matcher = CHANGED_PATH.matcher(line); + if (!matcher.matches()) { + throw new VcsException("changed path not found in " + line); + } + + String path = matcher.group(2); + char type = CommandUtil.getStatusChar(matcher.group(1)); + String copyPath = matcher.group(5); + long copyRevision = !StringUtil.isEmpty(matcher.group(6)) ? Long.valueOf(matcher.group(6)) : 0; + + entry.changedPaths.put(path, new SVNLogEntryPath(path, type, copyPath, copyRevision)); + } + + private void processChangedPathsStarted() { + waitChangedPath = true; + } + + private void processChangedPathsFinished() { + waitChangedPath = false; + waitMessage = true; + } + + private void processMessage(@NotNull String line) { + entry.message.append(line); + entry.message.append(LineSeparator.LF.getSeparatorString()); + } + + private void processDetails(@NotNull String line) throws VcsException { + Matcher matcher = DETAILS.matcher(line); + if (!matcher.matches()) { + throw new VcsException("details not found in " + line); + } + entry.revision = Long.valueOf(matcher.group(1)); + entry.author = matcher.group(2).trim(); + entry.date = tryGetDate(matcher.group(3)); + + waitDetails = false; + } + + private void processEntryStart() throws SVNException { + if (entry != null) { + handler.handleLogEntry(entry.toLogEntry()); + } + entry = new Entry(); + waitDetails = true; + waitMessage = false; + } + + private static Date tryGetDate(@NotNull String value) { + Date result = null; + try { + result = DATE_FORMAT.parse(value); + } + catch (ParseException e) { + LOG.debug(e); + } + return result; + } + + private static class Entry { + Map<String, SVNLogEntryPath> changedPaths = new HashMap<String, SVNLogEntryPath>(); + long revision; + String author; + Date date; + StringBuilder message = new StringBuilder(); + + private SVNLogEntry toLogEntry() { + return new SVNLogEntry(changedPaths, revision, author, date, message.toString()); + } + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/HistoryClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/HistoryClient.java new file mode 100644 index 000000000000..87e5e76556fa --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/HistoryClient.java @@ -0,0 +1,28 @@ +package org.jetbrains.idea.svn.history; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.SvnClient; +import org.tmatesoft.svn.core.ISVNLogEntryHandler; +import org.tmatesoft.svn.core.wc.SVNRevision; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public interface HistoryClient extends SvnClient { + + // TODO: Url is also supported as parameter in cmd - use Target class + void doLog(@NotNull File path, + @NotNull SVNRevision startRevision, + @NotNull SVNRevision endRevision, + @Nullable SVNRevision pegRevision, + boolean stopOnCopy, + boolean discoverChangedPaths, + boolean includeMergedRevisions, + long limit, + @Nullable String[] revisionProperties, + @Nullable ISVNLogEntryHandler handler) throws VcsException; +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedChangesProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedChangesProvider.java index 7ef0b2f3d0b6..3552853a8479 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedChangesProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedChangesProvider.java @@ -518,9 +518,8 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi revisionBefore = SVNRevision.create(revision); svnurl = SVNURL.parseURIEncoded(url); - final SVNWCClient client = myVcs.createWCClient(); - final SVNInfo info = client.doInfo(svnurl, SVNRevision.UNDEFINED, SVNRevision.HEAD); - targetInfo = client.doInfo(new File(file.getPath()), SVNRevision.UNDEFINED); + final SVNInfo info = myVcs.getInfo(svnurl, SVNRevision.HEAD); + targetInfo = myVcs.getInfo(new File(file.getPath())); if (info == null) { throw new VcsException("Can not get repository URL"); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnEditCommitMessageAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnEditCommitMessageAction.java index 5ca1150869ae..d67e29b598a0 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnEditCommitMessageAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnEditCommitMessageAction.java @@ -137,7 +137,7 @@ public class SvnEditCommitMessageAction extends AnAction { final String url = myLocation.getURL(); final SVNURL root; try { - root = SvnUtil.getRepositoryRoot(myVcs, SVNURL.parseURIEncoded(url), true); + root = SvnUtil.getRepositoryRoot(myVcs, SVNURL.parseURIEncoded(url)); if (root == null) { myException = new VcsException("Can not determine repository root for URL: " + url); return; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnFileRevision.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnFileRevision.java index bcb40a034ac9..046e16c148a4 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnFileRevision.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnFileRevision.java @@ -33,6 +33,7 @@ import org.jetbrains.idea.svn.SvnUtil; import org.jetbrains.idea.svn.SvnVcs; import org.tmatesoft.svn.core.SVNLogEntry; import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.IOException; import java.nio.charset.Charset; @@ -211,7 +212,7 @@ public class SvnFileRevision implements VcsFileRevision { } try { - myContents = SvnUtil.getFileContents(myVCS, myURL, true, myRevision, myPegRevision); + myContents = SvnUtil.getFileContents(myVCS, SvnTarget.fromURL(SvnUtil.parseUrl(myURL)), myRevision, myPegRevision); } catch (VcsException e) { myException = e; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java index 4a2c8725a898..860b646947f5 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java @@ -42,7 +42,6 @@ import org.jetbrains.idea.svn.*; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; import org.tmatesoft.svn.core.internal.wc.SVNErrorManager; -import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.wc.*; import org.tmatesoft.svn.util.SVNLogType; @@ -50,7 +49,6 @@ import javax.swing.*; import javax.swing.table.TableCellRenderer; import java.awt.*; import java.awt.event.MouseEvent; -import java.io.File; import java.nio.charset.Charset; import java.util.Collections; import java.util.Date; @@ -285,18 +283,9 @@ public class SvnHistoryProvider @Override protected void preliminary() throws SVNException { - SVNWCClient wcClient = myVcs.createWCClient(); - myInfo = wcClient.doInfo(new File(myFile.getIOFile().getAbsolutePath()), SVNRevision.UNDEFINED); - wcClient.setEventHandler(new ISVNEventHandler() { - public void handleEvent(SVNEvent event, double progress) throws SVNException { - } - - public void checkCancelled() throws SVNCancelException { - myPI.checkCanceled(); - } - }); + myInfo = myVcs.getInfo(myFile.getIOFile()); if (myInfo == null || myInfo.getRepositoryRootURL() == null) { - myException = new VcsException("File ''{0}'' is not under version control" + myFile.getIOFile()); + myException = new VcsException("File " + myFile.getPath() + " is not under version control"); return; } if (myInfo.getURL() == null) { @@ -319,14 +308,15 @@ public class SvnHistoryProvider myPI.setText2(SvnBundle.message("progress.text2.changes.establishing.connection", myUrl)); } final SVNRevision pegRevision = myInfo.getRevision(); - SVNLogClient client = myVcs.createLogClient(); try { - // a bug noticed when testing: we should pass "limit + 1" to get "limit" rows - client - .doLog(new File[]{new File(myFile.getIOFile().getAbsolutePath())}, - myFrom == null ? SVNRevision.HEAD : myFrom, myTo == null ? SVNRevision.create(1) : myTo, myPeg, - false, true, myShowMergeSources && mySupport15, myLimit + 1, null, - new MyLogEntryHandler(myVcs, myUrl, pegRevision, relativeUrl, createConsumerAdapter(myConsumer), repoRootURL, myFile.getCharset())); + myVcs.getFactory(myFile.getIOFile()).createHistoryClient().doLog( + myFile.getIOFile(), + myFrom == null ? SVNRevision.HEAD : myFrom, + myTo == null ? SVNRevision.create(1) : myTo, myPeg, + false, true, myShowMergeSources && mySupport15, myLimit + 1, null, + new MyLogEntryHandler(myVcs, myUrl, pegRevision, relativeUrl, + createConsumerAdapter(myConsumer), + repoRootURL, myFile.getCharset())); } catch (SVNCancelException e) { // @@ -383,7 +373,6 @@ public class SvnHistoryProvider } } - SVNWCClient wcClient = myVcs.createWCClient(); final SVNURL svnurl = SVNURL.parseURIEncoded(myUrl); SVNRevision operationalFrom = myFrom == null ? SVNRevision.HEAD : myFrom; final SVNURL rootURL = getRepositoryRoot(svnurl, myFrom); @@ -395,6 +384,7 @@ public class SvnHistoryProvider if (myUrl.startsWith(root)) { relativeUrl = myUrl.substring(root.length()); } + // TODO: Update this call to myVcs.getFactory.createHistoryClient SVNLogClient client = myVcs.createLogClient(); // a bug noticed when testing: we should pass "limit + 1" to get "limit" rows client.doLog(svnurl, new String[]{}, myPeg == null ? myFrom : myPeg, @@ -420,6 +410,7 @@ public class SvnHistoryProvider relativeUrl = myUrl.substring(root.length()); } + // TODO: Update this call to myVcs.getFactory.createHistoryClient SVNLogClient client = myVcs.createLogClient(); final RepositoryLogEntryHandler repositoryLogEntryHandler = @@ -437,36 +428,15 @@ public class SvnHistoryProvider } private SVNURL getRepositoryRoot(SVNURL svnurl, SVNRevision operationalFrom) throws SVNException { - final SVNRepository repository = myVcs.createRepository(svnurl); - final SVNURL root = repository.getRepositoryRoot(false); - if (root == null) { - return repository.getRepositoryRoot(true); - } - return root; - /*final SVNWCClient wcClient = myVcs.createWCClient(); - try { - final SVNInfo info; - info = wcClient.doInfo(svnurl, myPeg, operationalFrom); - return info.getRepositoryRootURL(); - } - catch (SVNException e) { - try { - final SVNInfo info; - info = wcClient.doInfo(svnurl, SVNRevision.UNDEFINED, SVNRevision.UNDEFINED); - return info.getRepositoryRootURL(); - } catch (SVNException e1) { - final SVNInfo info; - info = wcClient.doInfo(svnurl, SVNRevision.UNDEFINED, SVNRevision.HEAD); - return info.getRepositoryRootURL(); - } - }*/ + SVNInfo info = myVcs.getInfo(svnurl, SVNRevision.HEAD); + + return info != null ? info.getRepositoryRootURL() : null; } private boolean existsNow(SVNURL svnurl) { - final SVNWCClient wcClient = myVcs.createWCClient(); final SVNInfo info; try { - info = wcClient.doInfo(svnurl, SVNRevision.HEAD, SVNRevision.HEAD); + info = myVcs.getInfo(svnurl, SVNRevision.HEAD, SVNRevision.HEAD, null); } catch (SVNException e) { return false; @@ -493,6 +463,7 @@ public class SvnHistoryProvider protected final SvnPathThroughHistoryCorrection myLastPathCorrector; private final Charset myCharset; protected final ThrowableConsumer<VcsFileRevision, SVNException> myResult; + private final String myLastPath; private VcsFileRevision myPrevious; private final SVNRevision myPegRevision; protected final String myUrl; @@ -512,6 +483,7 @@ public class SvnHistoryProvider throws SVNException, VcsException { myVcs = vcs; myLastPathCorrector = new SvnPathThroughHistoryCorrection(lastPath); + myLastPath = lastPath; myCharset = charset; myIndicator = ProgressManager.getInstance().getProgressIndicator(); myResult = result; @@ -610,9 +582,9 @@ public class SvnHistoryProvider String author = logEntry.getAuthor(); String message = logEntry.getMessage(); SVNRevision rev = SVNRevision.create(logEntry.getRevision()); -// final SVNURL url = myRepositoryRoot.appendPath(myLastPath, true); - final SVNURL url = entryPath != null ? myRepositoryRoot.appendPath(entryPath.getPath(), true) : - myRepositoryRoot.appendPath(myLastPathCorrector.getBefore(), false); + final SVNURL url = myRepositoryRoot.appendPath(myLastPath, true); +// final SVNURL url = entryPath != null ? myRepositoryRoot.appendPath(entryPath.getPath(), true) : +// myRepositoryRoot.appendPath(myLastPathCorrector.getBefore(), false); return new SvnFileRevision(myVcs, myPegRevision, rev, url.toString(), author, date, message, copyPath, myCharset); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistorySession.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistorySession.java index e30f8ca216cb..171b39cad623 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistorySession.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistorySession.java @@ -20,21 +20,19 @@ import com.intellij.openapi.vcs.history.*; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnRevisionNumber; import org.jetbrains.idea.svn.SvnVcs; -import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.wc.SVNInfo; import org.tmatesoft.svn.core.wc.SVNRevision; -import org.tmatesoft.svn.core.wc.SVNWCClient; import java.io.File; import java.util.List; /** -* Created with IntelliJ IDEA. -* User: Irina.Chernushina -* Date: 4/27/12 -* Time: 12:24 PM -* To change this template use File | Settings | File Templates. -*/ + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 4/27/12 + * Time: 12:24 PM + * To change this template use File | Settings | File Templates. + */ public class SvnHistorySession extends VcsAbstractHistorySession { private final SvnVcs myVcs; private final FilePath myCommittedPath; @@ -71,19 +69,8 @@ public class SvnHistorySession extends VcsAbstractHistorySession { } public static VcsRevisionNumber getCurrentCommittedRevision(final SvnVcs vcs, final File file) { - try { - SVNWCClient wcClient = vcs.createWCClient(); - SVNInfo info = wcClient.doInfo(file, SVNRevision.UNDEFINED); - if (info != null) { - return new SvnRevisionNumber(info.getCommittedRevision()); - } - else { - return null; - } - } - catch (SVNException e) { - return null; - } + SVNInfo info = vcs.getInfo(file); + return info != null ? new SvnRevisionNumber(info.getCommittedRevision()) : null; } public FilePath getCommittedPath() { @@ -101,7 +88,8 @@ public class SvnHistorySession extends VcsAbstractHistorySession { @Override public VcsHistorySession copy() { - return new SvnHistorySession(myVcs, getRevisionList(), myCommittedPath, myHaveMergeSources, getCurrentRevisionNumber(), true, myHasLocalSource); + return new SvnHistorySession(myVcs, getRevisionList(), myCommittedPath, myHaveMergeSources, getCurrentRevisionNumber(), true, + myHasLocalSource); } @Override diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnKitHistoryClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnKitHistoryClient.java new file mode 100644 index 000000000000..6d412853400e --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnKitHistoryClient.java @@ -0,0 +1,40 @@ +package org.jetbrains.idea.svn.history; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.tmatesoft.svn.core.ISVNLogEntryHandler; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.SVNLogClient; +import org.tmatesoft.svn.core.wc.SVNRevision; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnKitHistoryClient extends BaseSvnClient implements HistoryClient { + @Override + public void doLog(@NotNull File path, + @NotNull SVNRevision startRevision, + @NotNull SVNRevision endRevision, + @Nullable SVNRevision pegRevision, + boolean stopOnCopy, + boolean discoverChangedPaths, + boolean includeMergedRevisions, + long limit, + @Nullable String[] revisionProperties, + @Nullable ISVNLogEntryHandler handler) throws VcsException { + try { + // TODO: a bug noticed when testing: we should pass "limit + 1" to get "limit" rows + SVNLogClient client = myVcs.createLogClient(); + + client.doLog(new File[]{path}, startRevision, endRevision, pegRevision, stopOnCopy, discoverChangedPaths, includeMergedRevisions, + limit, revisionProperties, handler); + } + catch (SVNException e) { + throw new VcsException(e); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRepositoryContentRevision.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRepositoryContentRevision.java index a668a54f953a..b01308f76c13 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRepositoryContentRevision.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRepositoryContentRevision.java @@ -38,13 +38,10 @@ import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vcs.impl.ContentRevisionCache; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn.SvnBundle; -import org.jetbrains.idea.svn.SvnRevisionNumber; -import org.jetbrains.idea.svn.SvnUtil; -import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.*; import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -108,7 +105,7 @@ public class SvnRepositoryContentRevision implements ContentRevision, MarkerVcsC else { loader.run(); } - final SVNException exception = loader.getException(); + final Exception exception = loader.getException(); if (exception != null) { throw new VcsException(exception); } @@ -148,7 +145,7 @@ public class SvnRepositoryContentRevision implements ContentRevision, MarkerVcsC private final String myPath; private final long myRevision; private final OutputStream myDst; - private SVNException myException; + private Exception myException; public ContentLoader(String path, OutputStream dst, long revision) { myPath = path; @@ -156,7 +153,7 @@ public class SvnRepositoryContentRevision implements ContentRevision, MarkerVcsC myRevision = revision; } - public SVNException getException() { + public Exception getException() { return myException; } @@ -166,16 +163,16 @@ public class SvnRepositoryContentRevision implements ContentRevision, MarkerVcsC progress.setText(SvnBundle.message("progress.text.loading.contents", myPath)); progress.setText2(SvnBundle.message("progress.text2.revision.information", myRevision)); } + try { - SVNRepository repository = myVcs.createRepository(getFullPath()); - try { - repository.getFile("", myRevision, null, myDst); - } - finally { - repository.closeSession(); - } + byte[] contents = SvnUtil.getFileContents(myVcs, SvnTarget.fromURL(SvnUtil.parseUrl(getFullPath())), SVNRevision.create(myRevision), + SVNRevision.UNDEFINED); + myDst.write(contents); } - catch (SVNException e) { + catch (VcsException e) { + myException = e; + } + catch (IOException e) { myException = e; } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/IntegratedSelectedOptionsDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/IntegratedSelectedOptionsDialog.java index 78718a7a0264..bfb9f1ff22c3 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/IntegratedSelectedOptionsDialog.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/IntegratedSelectedOptionsDialog.java @@ -251,19 +251,10 @@ public class IntegratedSelectedOptionsDialog extends DialogWrapper { @Nullable private static SVNURL realTargetUrl(final SvnVcs vcs, final WorkingCopyInfo info, final String targetBranchUrl) { - final SVNWCClient client = vcs.createWCClient(); - try { - final SVNInfo svnInfo = client.doInfo(new File(info.getLocalPath()), SVNRevision.UNDEFINED); - final SVNURL svnurl = svnInfo.getURL(); + final SVNInfo svnInfo = vcs.getInfo(info.getLocalPath()); + final SVNURL svnurl = svnInfo != null ? svnInfo.getURL() : null; - if ((svnurl != null) && (svnurl.toString().startsWith(targetBranchUrl))) { - return svnurl; - } - } - catch (SVNException e) { - // tracked by return value - } - return null; + return (svnurl != null) && (svnurl.toString().startsWith(targetBranchUrl)) ? svnurl : null; } @Nullable diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java index b6c6a898b85a..bc08fff144dc 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java @@ -238,12 +238,7 @@ public class BranchInfo { } private SVNInfo getInfo(final File pathFile) { - try { - return myClient.doInfo(pathFile, SVNRevision.UNDEFINED); - } catch (SVNException e) { - // - } - return null; + return myVcs.getInfo(pathFile); } private SvnMergeInfoCache.MergeCheckResult checkPathGoingUp(final long revisionAsked, final long targetRevision, final String branchRootPath, diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/PortableStatus.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/PortableStatus.java index 184d5598f9fe..9799cf7ae4a4 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/PortableStatus.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/PortableStatus.java @@ -117,6 +117,7 @@ public class PortableStatus extends SVNStatus { return null; } }; + // TODO: Update working copy format detection setWorkingCopyFormat(WorkingCopyFormat.ONE_DOT_SEVEN.getFormat()); } @@ -242,6 +243,30 @@ public class PortableStatus extends SVNStatus { } @Override + public SVNURL getURL() { + SVNURL url = super.getURL(); + + if (url == null) { + SVNInfo info = initInfo(); + url = info != null ? info.getURL() : url; + } + + return url; + } + + @Override + public File getFile() { + File file = super.getFile(); + + if (file == null) { + SVNInfo info = initInfo(); + file = info != null ? info.getFile() : file; + } + + return file; + } + + @Override public SVNRevision getRevision() { final SVNRevision revision = super.getRevision(); if (revision != null && revision.isValid()) return revision; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java new file mode 100644 index 000000000000..4720b33d9d37 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java @@ -0,0 +1,65 @@ +package org.jetbrains.idea.svn.properties; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.CommandUtil; +import org.jetbrains.idea.svn.commandLine.SvnCommand; +import org.jetbrains.idea.svn.commandLine.SvnCommandName; +import org.tmatesoft.svn.core.SVNPropertyValue; +import org.tmatesoft.svn.core.wc.SVNInfo; +import org.tmatesoft.svn.core.wc.SVNPropertyData; +import org.tmatesoft.svn.core.wc.SVNRevision; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { + + @Nullable + @Override + public SVNPropertyData getProperty(@NotNull File path, + @NotNull String property, + boolean revisionProperty, + @Nullable SVNRevision pegRevision, + @Nullable SVNRevision revision) + throws VcsException { + List<String> parameters = new ArrayList<String>(); + + parameters.add(property); + CommandUtil.put(parameters, path, pegRevision); + if (!revisionProperty) { + CommandUtil.put(parameters, revision); + } else { + parameters.add("--revprop"); + CommandUtil.put(parameters, resolveRevisionNumber(path, revision)); + } + + SvnCommand command = CommandUtil.execute(myVcs, SvnCommandName.propget, parameters, null); + + return new SVNPropertyData(property, SVNPropertyValue.create(StringUtil.nullize(command.getOutput())), null); + } + + private SVNRevision resolveRevisionNumber(@NotNull File path, @Nullable SVNRevision revision) throws VcsException { + long result = revision != null ? revision.getNumber() : -1; + + // base should be resolved manually - could not set revision to BASE to get revision property + if (SVNRevision.BASE.equals(revision)) { + SVNInfo info = myVcs.getInfo(path, SVNRevision.BASE); + + result = info != null ? info.getRevision().getNumber() : -1; + } + + if (result == -1) { + throw new VcsException("Could not determine revision number for file " + path + " and revision " + revision); + } + + return SVNRevision.create(result); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java new file mode 100644 index 000000000000..118a397eb23e --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java @@ -0,0 +1,23 @@ +package org.jetbrains.idea.svn.properties; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.SvnClient; +import org.tmatesoft.svn.core.wc.SVNPropertyData; +import org.tmatesoft.svn.core.wc.SVNRevision; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public interface PropertyClient extends SvnClient { + + @Nullable + SVNPropertyData getProperty(@NotNull final File path, + @NotNull final String property, + boolean revisionProperty, + @Nullable SVNRevision pegRevision, + @Nullable SVNRevision revision) throws VcsException; +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java new file mode 100644 index 000000000000..1c3c23619074 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java @@ -0,0 +1,68 @@ +package org.jetbrains.idea.svn.properties; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.wc.ISVNPropertyHandler; +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 Konstantin Kolosovsky. + */ +public class SvnKitPropertyClient extends BaseSvnClient implements PropertyClient { + + @Nullable + @Override + public SVNPropertyData getProperty(@NotNull File path, + @NotNull String property, + boolean revisionProperty, + @Nullable SVNRevision pegRevision, + @Nullable SVNRevision revision) throws VcsException { + try { + if (!revisionProperty) { + return myVcs.createWCClient().doGetProperty(path, property, pegRevision, revision); + } else { + return getRevisionProperty(path, property, revision); + } + } + catch (SVNException e) { + throw new VcsException(e); + } + } + + private SVNPropertyData getRevisionProperty(@NotNull File path, @NotNull final String property, @Nullable SVNRevision revision) throws SVNException{ + final SVNWCClient client = myVcs.createWCClient(); + final SVNPropertyData[] result = new SVNPropertyData[1]; + + client.doGetRevisionProperty(path, null, revision, 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(@NotNull SVNPropertyData data) { + if (property.equals(data.getName())) { + result[0] = data; + } + } + }); + return result[0]; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnPropDetailsProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnPropDetailsProvider.java deleted file mode 100644 index 683024679277..000000000000 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnPropDetailsProvider.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.idea.svn.properties; - -/** - * Created with IntelliJ IDEA. - * User: Irina.Chernushina - * Date: 2/12/12 - * Time: 8:40 PM - */ -public class SvnPropDetailsProvider { -} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/CmdRevertClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/CmdRevertClient.java new file mode 100644 index 000000000000..53c998f001a6 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/CmdRevertClient.java @@ -0,0 +1,79 @@ +package org.jetbrains.idea.svn.revert; + +import com.intellij.openapi.vcs.VcsException; +import com.intellij.util.containers.Convertor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.api.FileStatusResultParser; +import org.jetbrains.idea.svn.commandLine.CommandUtil; +import org.jetbrains.idea.svn.commandLine.SvnCommandName; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; +import org.tmatesoft.svn.core.wc.SVNEvent; +import org.tmatesoft.svn.core.wc.SVNEventAction; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Konstantin Kolosovsky. + */ +public class CmdRevertClient extends BaseSvnClient implements RevertClient { + + private static final String STATUS = "\\s*(.+?)\\s*"; + private static final String PATH = "\\s*\'(.*?)\'\\s*"; + private static final String OPTIONAL_COMMENT = "(.*)"; + private static final Pattern CHANGED_PATH = Pattern.compile(STATUS + PATH + OPTIONAL_COMMENT); + + @Override + public void revert(@NotNull File[] paths, @Nullable SVNDepth depth, @Nullable ISVNEventHandler handler) throws VcsException { + List<String> parameters = prepareParameters(paths, depth); + + // TODO: handler should be called in parallel with command execution, but this will be in other thread + // TODO: check if that is ok for current handler implementation + // TODO: add possibility to invoke "handler.checkCancelled" - process should be killed + CommandUtil + .execute(myVcs, SvnCommandName.revert, parameters, new FileStatusResultParser(CHANGED_PATH, handler, new RevertStatusConvertor())); + } + + private static List<String> prepareParameters(File[] paths, SVNDepth depth) { + ArrayList<String> parameters = new ArrayList<String>(); + + CommandUtil.put(parameters, paths); + CommandUtil.put(parameters, depth); + + return parameters; + } + + private static class RevertStatusConvertor implements Convertor<Matcher, SVNEvent> { + + public SVNEvent convert(@NotNull Matcher matcher) { + String statusMessage = matcher.group(1); + String path = matcher.group(2); + + return new SVNEvent(new File(path), null, null, 0, null, null, null, null, createAction(statusMessage), null, null, null, null, null, + null); + } + + @Nullable + public static SVNEventAction createAction(@NotNull String code) { + SVNEventAction result = null; + + if ("Reverted".equals(code)) { + result = SVNEventAction.REVERT; + } + else if ("Failed to revert".equals(code)) { + result = SVNEventAction.FAILED_REVERT; + } + else if ("Skipped".equals(code)) { + result = SVNEventAction.SKIP; + } + + return result; + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/RevertClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/RevertClient.java new file mode 100644 index 000000000000..053de373bc0a --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/RevertClient.java @@ -0,0 +1,18 @@ +package org.jetbrains.idea.svn.revert; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.SvnClient; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public interface RevertClient extends SvnClient { + + void revert(@NotNull File[] paths, @Nullable SVNDepth depth, @Nullable ISVNEventHandler handler) throws VcsException; +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/SvnKitRevertClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/SvnKitRevertClient.java new file mode 100644 index 000000000000..17c725cf58f6 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/SvnKitRevertClient.java @@ -0,0 +1,31 @@ +package org.jetbrains.idea.svn.revert; + +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.ISVNEventHandler; +import org.tmatesoft.svn.core.wc.SVNWCClient; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnKitRevertClient extends BaseSvnClient implements RevertClient { + + @Override + public void revert(@NotNull File[] paths, @Nullable SVNDepth depth, @Nullable ISVNEventHandler handler) throws VcsException { + SVNWCClient client = myVcs.createWCClient(); + + client.setEventHandler(handler); + try { + client.doRevert(paths, depth, null); + } + catch (SVNException e) { + throw new VcsException(e); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java index 265d4ca5b9e5..4140ca75a717 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java @@ -28,6 +28,7 @@ import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.*; +import org.jetbrains.idea.svn.revert.RevertClient; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.wc.*; @@ -74,8 +75,7 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { checker.gather(changes); exceptions.addAll(checker.getExceptions()); - final SVNWCClient client = mySvnVcs.createWCClient(); - client.setEventHandler(new ISVNEventHandler() { + ISVNEventHandler revertHandler = new ISVNEventHandler() { public void handleEvent(SVNEvent event, double progress) { if (event.getAction() == SVNEventAction.REVERT) { final File file = event.getFile(); @@ -91,7 +91,7 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { public void checkCancelled() { listener.checkCanceled(); } - }); + }; final List<CopiedAsideInfo> fromToModified = new ArrayList<CopiedAsideInfo>(); final MultiMap<File, SVNPropertyData> properties = new MultiMap<File, SVNPropertyData>(); @@ -99,7 +99,7 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { // adds (deletes) // deletes (adds) // modifications - final Reverter reverter = new Reverter(client, exceptions); + final Reverter reverter = new Reverter(mySvnVcs.getFactory().createRevertClient(), revertHandler, exceptions); reverter.revert(checker.getForAdds(), true); reverter.revert(checker.getForDeletes(), true); final List<File> edits = checker.getForEdits(); @@ -256,24 +256,36 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { } private static class Reverter { - private final SVNWCClient myClient; + private final RevertClient myClient; + private ISVNEventHandler myHandler; private final List<VcsException> myExceptions; - private Reverter(SVNWCClient client, List<VcsException> exceptions) { + private Reverter(RevertClient client, ISVNEventHandler handler, List<VcsException> exceptions) { myClient = client; + myHandler = handler; myExceptions = exceptions; } public void revert(final File[] files, final boolean recursive) { if (files.length == 0) return; try { - myClient.doRevert(files, recursive ? SVNDepth.INFINITY : SVNDepth.EMPTY, null); + myClient.revert(files, recursive ? SVNDepth.INFINITY : SVNDepth.EMPTY, myHandler); } - catch (SVNException e) { - if (e.getErrorMessage().getErrorCode() != SVNErrorCode.WC_NOT_DIRECTORY) { - // skip errors on unversioned resources. - myExceptions.add(new VcsException(e)); + catch (VcsException e) { + processRevertError(e); + } + } + + private void processRevertError(@NotNull VcsException e) { + if (e.getCause() instanceof SVNException) { + SVNException cause = (SVNException)e.getCause(); + + // skip errors on unversioned resources. + if (cause.getErrorMessage().getErrorCode() != SVNErrorCode.WC_NOT_DIRECTORY) { + myExceptions.add(e); } + } else { + myExceptions.add(e); } } } @@ -294,18 +306,17 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { } private void revertFileOrDir(File file) throws SVNException, VcsException { - final SVNWCClient wcClient = mySvnVcs.createWCClient(); - SVNInfo info = wcClient.doInfo(file, SVNRevision.UNDEFINED); + SVNInfo info = mySvnVcs.getInfo(file); if (info != null) { if (info.getKind() == SVNNodeKind.FILE) { - wcClient.doRevert(file, false); + doRevert(file, false); } else { if (SVNProperty.SCHEDULE_ADD.equals(info.getSchedule())) { - wcClient.doRevert(file, true); + doRevert(file, true); } else { boolean under17Copy = isUnder17Copy(file, info); if (under17Copy) { - wcClient.doRevert(file, true); + doRevert(file, true); } else { // do update to restore missing directory. mySvnVcs.createUpdateClient().doUpdate(file, SVNRevision.HEAD, true); @@ -317,10 +328,16 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { } } + private void doRevert(@NotNull File path, boolean recursive) throws VcsException { + mySvnVcs.getFactory(path).createRevertClient().revert(new File[]{path}, SVNDepth.fromRecurse(recursive), null); + } + private boolean isUnder17Copy(final File file, final SVNInfo info) throws VcsException { final RootsToWorkingCopies copies = mySvnVcs.getRootsToWorkingCopies(); WorkingCopy copy = copies.getMatchingCopy(info.getURL()); if (copy == null) { + // TODO: Why null could be here? + // TODO: Think we could just rewrite it with mySvnVcs.getWorkingCopyFormat(file) SVNStatus status = null; try { status = mySvnVcs.createStatusClient().doStatus(file, false); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/AbstractSvnUpdatePanel.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/AbstractSvnUpdatePanel.java index ecc3fa6b5204..d6bdf63cafc8 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/AbstractSvnUpdatePanel.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/AbstractSvnUpdatePanel.java @@ -109,17 +109,8 @@ public abstract class AbstractSvnUpdatePanel { @Nullable private SVNURL getUrlFor(@NotNull final FilePath root) { - try { - SVNWCClient wcClient = myVCS.createWCClient(); - final SVNInfo info = wcClient.doInfo(root.getIOFile(), SVNRevision.UNDEFINED); - if (info != null) { - return info.getURL(); - } - return null; - } - catch (SVNException e) { - return null; - } + final SVNInfo info = myVCS.getInfo(root.getIOFile()); + return info != null ? info.getURL() : null; } protected abstract JComponent getPanel(); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/AbstractUpdateIntegrateCrawler.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/AbstractUpdateIntegrateCrawler.java index e9e7b0b5f897..826f7532b3a0 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/AbstractUpdateIntegrateCrawler.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/AbstractUpdateIntegrateCrawler.java @@ -59,7 +59,7 @@ public abstract class AbstractUpdateIntegrateCrawler implements SvnWCRootCrawler SVNUpdateClient client = myVcs.createUpdateClient(); client.setEventHandler(myHandler); - long rev = doUpdate(root, client); + long rev = doUpdate(root); if (rev < 0 && !isMerge()) { throw new SVNException(SVNErrorMessage.create(SVNErrorCode.UNKNOWN, SvnBundle.message("exception.text.root.was.not.properly.updated", root))); @@ -73,9 +73,7 @@ public abstract class AbstractUpdateIntegrateCrawler implements SvnWCRootCrawler protected abstract void showProgressMessage(ProgressIndicator progress, File root); - protected abstract long doUpdate( - File root, - SVNUpdateClient client) throws SVNException; + protected abstract long doUpdate(File root) throws SVNException; protected abstract boolean isMerge(); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/MergeRootInfo.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/MergeRootInfo.java index b8ea0520ebc2..96701b519111 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/MergeRootInfo.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/MergeRootInfo.java @@ -18,6 +18,7 @@ package org.jetbrains.idea.svn.update; import org.jetbrains.idea.svn.SvnVcs; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.wc.SVNInfo; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc.SVNWCClient; @@ -33,17 +34,9 @@ public class MergeRootInfo { myRevision1 = SVNRevision.HEAD; myRevision2 = SVNRevision.HEAD; - try { - SVNWCClient wcClient = vcs.createWCClient(); - final SVNURL url = wcClient.doInfo(file, SVNRevision.UNDEFINED).getURL(); - myUrl1 = url.toString(); - myUrl2 = url.toString(); - } - catch (SVNException e) { - myUrl1 = ""; - myUrl2 = ""; - } - + SVNInfo info = vcs.getInfo(file); + myUrl1 = info != null && info.getURL() != null ? info.getURL().toString() : ""; + myUrl2 = myUrl1; } public SVNURL getUrl1() { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnIntegrateEnvironment.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnIntegrateEnvironment.java index 9e8a5991f41c..9f4a95f7016f 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnIntegrateEnvironment.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnIntegrateEnvironment.java @@ -91,10 +91,7 @@ public class SvnIntegrateEnvironment extends AbstractSvnUpdateIntegrateEnvironme } } - protected long doUpdate( - final File root, - final SVNUpdateClient client) throws - SVNException { + protected long doUpdate(final File root) throws SVNException { final SvnConfiguration svnConfig = SvnConfiguration.getInstance(myVcs.getProject()); MergeRootInfo info = svnConfig.getMergeRootInfo(root, myVcs); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateEnvironment.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateEnvironment.java index 80e43aaf7828..5d2d4153be50 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateEnvironment.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateEnvironment.java @@ -33,7 +33,6 @@ import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.wc.SVNInfo; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc.SVNUpdateClient; -import org.tmatesoft.svn.core.wc.SVNWCClient; import java.io.File; import java.util.ArrayList; @@ -76,10 +75,7 @@ public class SvnUpdateEnvironment extends AbstractSvnUpdateIntegrateEnvironment progress.setText(SvnBundle.message("progress.text.updating", root.getAbsolutePath())); } - protected long doUpdate( - final File root, - final SVNUpdateClient client) throws - SVNException { + protected long doUpdate(final File root) throws SVNException { final long rev; final SvnConfiguration configuration = SvnConfiguration.getInstance(myVcs.getProject()); @@ -104,13 +100,15 @@ public class SvnUpdateEnvironment extends AbstractSvnUpdateIntegrateEnvironment private SvnUpdateClientI createUpdateClient(SvnConfiguration configuration, File root, boolean isSwitch, SVNURL sourceUrl) { final SvnUpdateClientI updateClient; + boolean is18Format = myVcs.getWorkingCopyFormat(root) == WorkingCopyFormat.ONE_DOT_EIGHT; + // do not do from command line for switch now - if (! isSwitch && SvnConfiguration.UseAcceleration.commandLine.equals(configuration.myUseAcceleration) && + if (! isSwitch && (is18Format || SvnConfiguration.UseAcceleration.commandLine.equals(configuration.myUseAcceleration) && Svn17Detector.is17(myVcs.getProject(), root) && ( SvnAuthenticationManager.HTTP.equals(sourceUrl.getProtocol()) || SvnAuthenticationManager.HTTPS.equals(sourceUrl.getProtocol()) - )) { - updateClient = new SvnCommandLineUpdateClient(myVcs.getProject(), null); + ))) { + updateClient = new SvnCommandLineUpdateClient(myVcs, null); } else { updateClient = new SvnSvnkitUpdateClient(myVcs.createUpdateClient()); } @@ -129,18 +127,8 @@ public class SvnUpdateEnvironment extends AbstractSvnUpdateIntegrateEnvironment @Nullable private static SVNURL getSourceUrl(final SvnVcs vcs, final File root) { - try { - SVNWCClient wcClient = vcs.createWCClient(); - final SVNInfo svnInfo = wcClient.doInfo(root, SVNRevision.UNDEFINED); - if (svnInfo != null) { - return svnInfo.getURL(); - } else { - return null; - } - } - catch (SVNException e) { - return null; - } + final SVNInfo svnInfo = vcs.getInfo(root); + return svnInfo != null ? svnInfo.getURL() : null; } public boolean validateOptions(final Collection<FilePath> roots) { @@ -193,9 +181,8 @@ public class SvnUpdateEnvironment extends AbstractSvnUpdateIntegrateEnvironment // false - do not do update private boolean checkAncestry(final File sourceFile, final SVNURL targetUrl, final SVNRevision targetRevision) throws SVNException { - final SVNWCClient client = myVcs.createWCClient(); - final SVNInfo sourceSvnInfo = client.doInfo(sourceFile, SVNRevision.UNDEFINED); - final SVNInfo targetSvnInfo = client.doInfo(targetUrl, SVNRevision.UNDEFINED, targetRevision); + final SVNInfo sourceSvnInfo = myVcs.getInfo(sourceFile); + final SVNInfo targetSvnInfo = myVcs.getInfo(targetUrl, targetRevision); if (sourceSvnInfo == null || targetSvnInfo == null) { // cannot check diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/UpdateRootInfo.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/UpdateRootInfo.java index 4852e9f6b95f..201260e232f2 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/UpdateRootInfo.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/UpdateRootInfo.java @@ -31,20 +31,9 @@ public class UpdateRootInfo { public UpdateRootInfo(File file, SvnVcs vcs) { myRevision = SVNRevision.HEAD; - try { - SVNWCClient wcClient = vcs.createWCClient(); - SVNInfo info = wcClient.doInfo(file, SVNRevision.UNDEFINED); - if (info != null) { - final SVNURL url = info.getURL(); - myUrl = url.toString(); - } else { - myUrl = ""; - } - } - catch (SVNException e) { - myUrl = ""; - } + SVNInfo info = vcs.getInfo(file); + myUrl = info != null && info.getURL() != null ? info.getDepth().toString() : ""; } public SVNURL getUrl() { diff --git a/plugins/svn4idea/svn4idea-tests.iml b/plugins/svn4idea/svn4idea-tests.iml index 9a537c253abf..0b826ed87489 100644 --- a/plugins/svn4idea/svn4idea-tests.iml +++ b/plugins/svn4idea/svn4idea-tests.iml @@ -38,7 +38,6 @@ </SOURCES> </library> </orderEntry> - <orderEntry type="module" module-name="bindSvn" /> </component> </module> diff --git a/plugins/svn4idea/svn4idea.iml b/plugins/svn4idea/svn4idea.iml index 4f0d931cd13f..53dd66166d64 100644 --- a/plugins/svn4idea/svn4idea.iml +++ b/plugins/svn4idea/svn4idea.iml @@ -76,16 +76,6 @@ </SOURCES> </library> </orderEntry> - <orderEntry type="module" module-name="bindSvn" /> - <orderEntry type="module-library"> - <library> - <CLASSES> - <root url="jar://$MODULE_DIR$/bindSvn/lib/javahl.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES /> - </library> - </orderEntry> </component> </module> diff --git a/plugins/svn4idea/testData18/svn/bin/windows/svn.exe b/plugins/svn4idea/testData18/svn/bin/windows/svn.exe Binary files differnew file mode 100644 index 000000000000..827753f442aa --- /dev/null +++ b/plugins/svn4idea/testData18/svn/bin/windows/svn.exe diff --git a/plugins/svn4idea/testData18/svn/newrepo.zip b/plugins/svn4idea/testData18/svn/newrepo.zip Binary files differnew file mode 100644 index 000000000000..fd9a577a39a0 --- /dev/null +++ b/plugins/svn4idea/testData18/svn/newrepo.zip diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/SvnTestCase.java b/plugins/svn4idea/testSource/org/jetbrains/idea/SvnTestCase.java index 14d1b4c42897..d5a55520adae 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/SvnTestCase.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/SvnTestCase.java @@ -78,13 +78,19 @@ import static org.junit.Assert.assertTrue; * @author yole */ public abstract class SvnTestCase extends AbstractJunitVcsTestCase { + + public static String ourGlobalTestDataDir; + public static Boolean ourGlobalUseNativeAcceleration; + protected TempDirTestFixture myTempDirFixture; protected String myRepoUrl; protected TestClientRunner myRunner; protected String myWcRootName; - protected boolean myUseNativeAcceleration = new GregorianCalendar().get(Calendar.HOUR_OF_DAY) % 2 == 0; + // TODO: Change this to explicitly run either with native acceleration or not. + // properties set through run configurations or different runners (like Suite) could be used + private boolean myUseNativeAcceleration = new GregorianCalendar().get(Calendar.HOUR_OF_DAY) % 2 == 0; - protected final String myTestDataDir; + private String myTestDataDir; private File myRepoRoot; private File myWcRoot; private ChangeListManagerGate myGate; @@ -118,7 +124,13 @@ public abstract class SvnTestCase extends AbstractJunitVcsTestCase { @Before public void setUp() throws Exception { - System.out.println("Native client for status: " + myUseNativeAcceleration); + System.out.println("Native client for status: " + isUseNativeAcceleration()); + + String property = System.getProperty("svn.test.data.directory"); + if (!StringUtil.isEmpty(property)) { + myTestDataDir = property; + } + UIUtil.invokeAndWaitIfNeeded(new Runnable() { @Override public void run() { @@ -138,7 +150,7 @@ public abstract class SvnTestCase extends AbstractJunitVcsTestCase { myPluginRoot = new File(rootPath).getParentFile().getParentFile().getParentFile(); } - File svnBinDir = new File(myPluginRoot, myTestDataDir + "/svn/bin"); + File svnBinDir = new File(myPluginRoot, getTestDataDir() + "/svn/bin"); File svnExecutable = null; if (SystemInfo.isWindows) { svnExecutable = new File(svnBinDir, "windows/svn.exe"); @@ -156,7 +168,7 @@ public abstract class SvnTestCase extends AbstractJunitVcsTestCase { ? createClientRunner(Collections.singletonMap("DYLD_LIBRARY_PATH", myClientBinaryPath.getPath())) : createClientRunner(); - ZipUtil.extract(new File(myPluginRoot, myTestDataDir + "/svn/newrepo.zip"), myRepoRoot, null); + ZipUtil.extract(new File(myPluginRoot, getTestDataDir() + "/svn/newrepo.zip"), myRepoRoot, null); myWcRoot = new File(myTempDirFixture.getTempDirPath(), myWcRootName); assert myWcRoot.mkdir() || myWcRoot.isDirectory() : myWcRoot; @@ -205,7 +217,7 @@ public abstract class SvnTestCase extends AbstractJunitVcsTestCase { @Override protected void projectCreated() { - if (myUseNativeAcceleration) { + if (isUseNativeAcceleration()) { SvnConfiguration.getInstance(myProject).myUseAcceleration = SvnConfiguration.UseAcceleration.commandLine; SvnApplicationSettings.getInstance().setCommandLinePath(myClientBinaryPath + File.separator + "svn"); } @@ -319,6 +331,22 @@ public abstract class SvnTestCase extends AbstractJunitVcsTestCase { //clManager.ensureUpToDate(false); } + public String getTestDataDir() { + return StringUtil.isEmpty(ourGlobalTestDataDir) ? myTestDataDir : ourGlobalTestDataDir; + } + + public void setTestDataDir(String testDataDir) { + myTestDataDir = testDataDir; + } + + public boolean isUseNativeAcceleration() { + return ourGlobalUseNativeAcceleration != null ? ourGlobalUseNativeAcceleration : myUseNativeAcceleration; + } + + public void setUseNativeAcceleration(boolean useNativeAcceleration) { + myUseNativeAcceleration = useNativeAcceleration; + } + protected class SubTree { public VirtualFile myRootDir; public VirtualFile mySourceDir; diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/IgnoredFilesTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/IgnoredFilesTest.java index 270e6d49c607..bded7affdf2c 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/IgnoredFilesTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/IgnoredFilesTest.java @@ -54,6 +54,7 @@ public class IgnoredFilesTest extends Svn17TestCase { @Before public void setUp() throws Exception { + super.setUp(); UIUtil.invokeAndWaitIfNeeded(new Runnable() { @Override public void run() { diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnChangesCorrectlyRefreshedNativeTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnChangesCorrectlyRefreshedNativeTest.java index a9c1074a3d94..6153d658767b 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnChangesCorrectlyRefreshedNativeTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnChangesCorrectlyRefreshedNativeTest.java @@ -34,7 +34,7 @@ public class SvnChangesCorrectlyRefreshedNativeTest extends Svn17TestCase { @Override public void setUp() throws Exception { - myUseNativeAcceleration = true; + setUseNativeAcceleration(true); super.setUp(); clManager = ChangeListManager.getInstance(myProject); diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCommitTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCommitTest.java index f4da04ea201a..f18b6a578ecb 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCommitTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCommitTest.java @@ -465,7 +465,7 @@ public class SvnCommitTest extends Svn17TestCase { return feedback; } - private void checkinFile(VirtualFile file, FileStatus status) { + protected void checkinFile(VirtualFile file, FileStatus status) { final Change change = myChangeListManager.getChange(file); Assert.assertNotNull(change); Assert.assertEquals(status, change.getFileStatus()); @@ -478,6 +478,7 @@ public class SvnCommitTest extends Svn17TestCase { } protected void run2variants(final MyRunner runner) throws Exception { + // TODO: Change this to run different variants separately. See SvnTestCase.myUseAcceleration. setNativeAcceleration(false); runner.run(); runner.cleanup(); diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnExternalTests.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnExternalTests.java index 500acbdff4c9..3e3b2fda1665 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnExternalTests.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnExternalTests.java @@ -27,6 +27,7 @@ import com.intellij.openapi.vcs.changes.ChangeListManagerImpl; import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.ui.UIUtil; import junit.framework.Assert; import org.junit.Test; @@ -171,8 +172,7 @@ public class SvnExternalTests extends Svn17TestCase { private void updatedCreatedExternalFromIDEAImpl() { final File sourceDir = new File(myWorkingCopyDir.getPath(), "source"); - ProjectLevelVcsManager.getInstance(myProject).setDirectoryMappings( - Arrays.asList(new VcsDirectoryMapping(FileUtil.toSystemIndependentName(sourceDir.getPath()), myVcs.getName()))); + setNewDirectoryMappings(sourceDir); imitUpdate(myProject); final File externalFile = new File(sourceDir, "external/t11.txt"); @@ -180,6 +180,16 @@ public class SvnExternalTests extends Svn17TestCase { Assert.assertNotNull(externalVf); } + private void setNewDirectoryMappings(final File sourceDir) { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + ProjectLevelVcsManager.getInstance(myProject).setDirectoryMappings( + Arrays.asList(new VcsDirectoryMapping(FileUtil.toSystemIndependentName(sourceDir.getPath()), myVcs.getName()))); + } + }); + } + @Test public void testUncommittedExternalStatus() throws Exception { prepareExternal(false, true, false); @@ -221,8 +231,7 @@ public class SvnExternalTests extends Svn17TestCase { private void uncommittedExternalCopyIsDetectedImpl() { final File sourceDir = new File(myWorkingCopyDir.getPath(), "source"); - ProjectLevelVcsManager.getInstance(myProject).setDirectoryMappings( - Arrays.asList(new VcsDirectoryMapping(FileUtil.toSystemIndependentName(sourceDir.getPath()), myVcs.getName()))); + setNewDirectoryMappings(sourceDir); imitUpdate(myProject); refreshSvnMappingsSynchronously(); diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnLockingTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnLockingTest.java index 171bb1c35cb8..7c32af921f56 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnLockingTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnLockingTest.java @@ -22,6 +22,7 @@ import com.intellij.util.concurrency.Semaphore; import junit.framework.Assert; import junit.framework.TestCase; import org.junit.Before; +import org.junit.Ignore; import org.tmatesoft.sqljet.core.SqlJetException; import org.tmatesoft.sqljet.core.table.ISqlJetBusyHandler; import org.tmatesoft.sqljet.core.table.ISqlJetTransaction; @@ -42,6 +43,8 @@ import java.util.concurrent.atomic.AtomicBoolean; * Date: 10/23/12 * Time: 2:27 PM */ +// TODO: Locking functionality which is tested by this test is not required anymore. Likely test needs to be removed. +@Ignore public class SvnLockingTest extends TestCase { private File myWorkingCopyRoot; private SvnTestWriteOperationLocks myLocks; diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnNativeClientAuthTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnNativeClientAuthTest.java index 61aca5670867..b6d6ae104bbc 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnNativeClientAuthTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnNativeClientAuthTest.java @@ -72,7 +72,7 @@ public class SvnNativeClientAuthTest extends Svn17TestCase { @Before public void setUp() throws Exception { super.setUp(); - final File certFile = new File(myPluginRoot, myTestDataDir + "/svn/____.pfx"); + final File certFile = new File(myPluginRoot, getTestDataDir() + "/svn/____.pfx"); setNativeAcceleration(true); myVcs = SvnVcs.getInstance(myProject); // replace authentication provider so that pass credentials without dialogs diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnChangesCorrectlyRefreshedNativeTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnChangesCorrectlyRefreshedNativeTest.java index 205733657075..8939f1cbf164 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnChangesCorrectlyRefreshedNativeTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnChangesCorrectlyRefreshedNativeTest.java @@ -49,7 +49,7 @@ public class SvnChangesCorrectlyRefreshedNativeTest extends Svn17TestCase { @Override public void setUp() throws Exception { - myUseNativeAcceleration = true; + setUseNativeAcceleration(true); super.setUp(); clManager = ChangeListManager.getInstance(myProject); diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn18/Svn18TestSuite.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn18/Svn18TestSuite.java new file mode 100644 index 000000000000..a7019738b9b5 --- /dev/null +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn18/Svn18TestSuite.java @@ -0,0 +1,33 @@ +package org.jetbrains.idea.svn18; + +import org.jetbrains.idea.SvnTestCase; +import org.jetbrains.idea.svn.*; +import org.junit.ClassRule; +import org.junit.rules.ExternalResource; +import org.junit.rules.TestRule; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +/** + * @author Konstantin Kolosovsky. + */ +// TODO: Add svn clients for mac and linux +@RunWith(Suite.class) +@Suite.SuiteClasses({SvnAddTest.class, SvnDeleteTest.class}) +public class Svn18TestSuite { + + @ClassRule + public static TestRule configuration = new ExternalResource() { + @Override + protected void before() throws Throwable { + SvnTestCase.ourGlobalTestDataDir = "testData18"; + SvnTestCase.ourGlobalUseNativeAcceleration = true; + } + + @Override + protected void after() { + SvnTestCase.ourGlobalTestDataDir = null; + SvnTestCase.ourGlobalUseNativeAcceleration = null; + } + }; +} |