summaryrefslogtreecommitdiff
path: root/plugins/svn4idea/src/org/jetbrains/idea
diff options
context:
space:
mode:
authorTor Norbye <tnorbye@google.com>2013-08-29 10:40:28 -0700
committerTor Norbye <tnorbye@google.com>2013-08-29 10:40:35 -0700
commit932259520ebaedeb2ccf4b7594bad50c700963d7 (patch)
tree2b52334b8a019d9652625b1432deac11a7c4b31e /plugins/svn4idea/src/org/jetbrains/idea
parente47d04f1f804b9e725b768725da436af6788f19b (diff)
downloadidea-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/src/org/jetbrains/idea')
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/ForNestedRootChecker.java2
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/RootsToWorkingCopies.java51
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAbstractWriteOperationLocks.java1
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationNotifier.java2
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties1
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProvider.java4
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java24
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangelistListener.java6
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnConfigurable.form27
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnContentRevision.java4
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnDiffProvider.java159
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java171
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileUrlMappingImpl.java9
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFormatSelector.java16
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnProxies.java1
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java45
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnTestWriteOperationLocks.java1
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java265
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java170
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/SvnWriteOperationLocks.java1
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/WorkingCopyFormat.java14
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AddAction.java10
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/actions/MarkResolvedAction.java22
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnMergeProvider.java83
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/add/AddClient.java23
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/add/CmdAddClient.java69
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/add/SvnKitAddClient.java39
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/AnnotateClient.java24
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/CmdAnnotateClient.java126
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnAnnotationProvider.java117
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnKitAnnotateClient.java42
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/api/BaseSvnClient.java22
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/api/ClientFactory.java99
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/api/CmdClientFactory.java38
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/api/FileStatusResultParser.java68
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/api/SvnClient.java15
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/api/SvnKitClientFactory.java38
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/branchConfig/DefaultConfigLoader.java2
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/branchConfig/SvnBranchConfigurationNew.java5
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaCommitHandler.java4
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/IdeaSvnkitBasedAuthenticationCallback.java17
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/checkin/SvnCheckinEnvironment.java100
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/AuthenticationCallback.java124
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandUtil.java218
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommitEventHandler.java (renamed from plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnPropDetailsProvider.java)17
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommitEventType.java49
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/LineCommandListener.java40
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindClient.java67
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindException.java36
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindUtil.java82
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java272
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineInfoClient.java107
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineStatusClient.java89
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandLineUpdateClient.java62
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java59
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommitRunner.java209
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoHandler.java130
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnInfoStructure.java1
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java605
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java110
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/UpdateOutputLineConverter.java28
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/CmdConflictClient.java33
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/ConflictClient.java15
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/conflict/SvnKitConflictClient.java25
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/content/CmdContentClient.java39
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/content/ContentClient.java17
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/content/FileTooBigRuntimeException.java7
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/content/SvnKitContentClient.java61
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/copy/CmdCopyMoveClient.java30
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/copy/CopyMoveClient.java15
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/copy/SvnKitCopyMoveClient.java28
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/delete/CmdDeleteClient.java29
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/delete/DeleteClient.java15
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/delete/SvnKitDeleteClient.java24
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/CreateBranchOrTagDialog.java27
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/history/CmdHistoryClient.java223
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/history/HistoryClient.java28
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedChangesProvider.java5
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnEditCommitMessageAction.java2
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnFileRevision.java3
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java70
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistorySession.java32
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnKitHistoryClient.java40
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRepositoryContentRevision.java29
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/IntegratedSelectedOptionsDialog.java15
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java7
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/portable/PortableStatus.java25
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java65
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java23
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java68
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/revert/CmdRevertClient.java79
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/revert/RevertClient.java18
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/revert/SvnKitRevertClient.java31
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java49
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/update/AbstractSvnUpdatePanel.java13
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/update/AbstractUpdateIntegrateCrawler.java6
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/update/MergeRootInfo.java15
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnIntegrateEnvironment.java5
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateEnvironment.java33
-rw-r--r--plugins/svn4idea/src/org/jetbrains/idea/svn/update/UpdateRootInfo.java15
100 files changed, 4594 insertions, 912 deletions
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/src/org/jetbrains/idea/svn/commandLine/AuthenticationCallback.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/AuthenticationCallback.java
new file mode 100644
index 000000000000..1944749f3608
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/AuthenticationCallback.java
@@ -0,0 +1,124 @@
+/*
+ * 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 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;
+import java.net.URISyntaxException;
+
+/**
+ * Passed for authentication purpose to SvnLineCommand
+ * Called when svn command indicates that it needs credentials. It also means that credential was not found in standard place
+ * (Subversion config directory)
+ *
+ * Implementations should 1) ask credential from user or take it from any other storage (memory, for instance)
+ * 2) write credential in Subversion standard form into
+ * a) standard config directory if user allowed to save *all* credentials
+ * b) TMP directory and return path to the directory from getSpecialConfigDir() - if user rejected at least one credential storing
+ *
+ * Please note that several credentials could be asked during the command and therefore implementation class is used as
+ * keeping its state, and that TMP directory should be reused for all written credentials
+ *
+ * User: Irina.Chernushina
+ * Date: 2/26/13
+ * Time: 1:05 PM
+ */
+public interface AuthenticationCallback {
+ /**
+ * Authenticate for realm and base file belonging to corresponding working copy
+ *
+ * @param realm - realm that should be used for credential retrieval/storage.
+ * @param base - file target of the operation
+ * @param previousFailed - whether previous credentials were correct
+ * @param passwordRequest - if true, password should be asked. Otherwise that may be a certificate (determined by the protocol)
+ * @return false if authentication canceled or was unsuccessful
+ */
+ 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
+ File getSpecialConfigDir();
+
+ /**
+ * Ask user or read from memory storage whether server certificate should be accepted
+ *
+ * @param url - that we used for request
+ * @param realm - realm that should be used for credential retrieval/storage.
+ * @return true is certificate was accepted
+ */
+ boolean acceptSSLServerCertificate(String url, final String realm);
+
+ /**
+ * Ask user or read from memory storage whether server certificate should be accepted
+ *
+ * @param file - that we used for request
+ * @param realm - realm that should be used for credential retrieval/storage.
+ * @return true is certificate was accepted
+ */
+ boolean acceptSSLServerCertificate(File file, final String realm);
+
+ /**
+ * Clear credentials stored anywhere - in case they were not full, wrong or anything else
+ *
+ * @param realm - required that credential
+ * @param base - file used in command
+ * @param password - whether password credential should be deleted or certificate, if protocol might demand certificate
+ */
+ void clearPassiveCredentials(String realm, File base, boolean password);
+
+ /**
+ * @return true if there's something from IDEA config that should be persisted into Subversion tmp config directory
+ * for successful call
+ * (now it's IDEA proxy settings)
+ */
+ boolean haveDataForTmpConfig();
+
+ /**
+ * writes IDEA config settings (that should be written) into tmp config directory
+ * (now it's IDEA proxy settings)
+ * @return true if have written data, false if wasn't able to determine parameters etc
+ * @throws IOException
+ * @throws URISyntaxException
+ */
+ boolean persistDataToTmpConfig(File baseFile) throws IOException, URISyntaxException;
+
+ /**
+ * Ask for IDEA-defined proxy credentials, using standard authenticator
+ * Store data into tmp config
+ *
+ * @return false if authentication was canceled or related calculations were unsuccessful
+ */
+ boolean askProxyCredentials(File base);
+
+ void reset();
+}
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/src/org/jetbrains/idea/svn/properties/SvnPropDetailsProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommitEventHandler.java
index 683024679277..d74884073863 100644
--- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnPropDetailsProvider.java
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommitEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2012 JetBrains s.r.o.
+ * 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.
@@ -13,13 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.jetbrains.idea.svn.properties;
+package org.jetbrains.idea.svn.commandLine;
+
+import java.io.File;
/**
- * Created with IntelliJ IDEA.
+ * Used to listen to commit events to display progress to user
+ *
* User: Irina.Chernushina
- * Date: 2/12/12
- * Time: 8:40 PM
+ * Date: 2/26/13
+ * Time: 10:12 AM
*/
-public class SvnPropDetailsProvider {
+public interface CommitEventHandler {
+ void commitEvent(final CommitEventType type, final File target);
+ void committedRevision(final long revNum);
}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommitEventType.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommitEventType.java
new file mode 100644
index 000000000000..87dc212d0341
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommitEventType.java
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 2/25/13
+ * Time: 6:51 PM
+ */
+public enum CommitEventType {
+ adding("Adding"),
+ deleting("Deleting"),
+ sending("Sending"),
+ replacing("Replacing"),
+ transmittingDeltas("Transmitting file data"),
+ committedRevision("Committed revision");
+
+ private final String myText;
+
+ CommitEventType(String text) {
+ myText = text;
+ }
+
+ public String getText() {
+ return myText;
+ }
+
+ public static CommitEventType create(String text) {
+ text = text.trim();
+ for (CommitEventType value : CommitEventType.values()) {
+ if (value.getText().equals(text)) return value;
+ }
+ return null;
+ }
+}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/LineCommandListener.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/LineCommandListener.java
new file mode 100644
index 000000000000..ccbc5c37db61
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/LineCommandListener.java
@@ -0,0 +1,40 @@
+/*
+ * 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.LineProcessEventListenerAdapter;
+
+import java.io.File;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 2/26/13
+ * Time: 10:38 AM
+ */
+public abstract class LineCommandListener extends LineProcessEventListenerAdapter {
+ private boolean myCanceled;
+
+ public abstract void baseDirectory(final File file);
+
+ public void cancel() {
+ myCanceled = true;
+ }
+
+ protected boolean isCanceled() {
+ return myCanceled;
+ }
+}
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/src/org/jetbrains/idea/svn/commandLine/SvnBindException.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindException.java
new file mode 100644
index 000000000000..524b928eae92
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindException.java
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 2/25/13
+ * Time: 5:57 PM
+ *
+ * Marker exception
+ */
+public class SvnBindException extends VcsException {
+ public SvnBindException(String message) {
+ super(message);
+ }
+
+ public SvnBindException(Throwable throwable) {
+ super(throwable);
+ }
+}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindUtil.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindUtil.java
new file mode 100644
index 000000000000..7d663bae65b5
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindUtil.java
@@ -0,0 +1,82 @@
+/*
+ * 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 java.io.File;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 2/5/13
+ * Time: 4:56 PM
+ */
+public class SvnBindUtil {
+
+ private final static List<DateFormat> ourFormats = new ArrayList<DateFormat>();
+
+ static {
+ ourFormats.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"));
+ ourFormats.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'000Z'"));
+ ourFormats.add(new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z", Locale.US));
+ ourFormats.add(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z (EE, d MMM yyyy)", Locale.getDefault()));
+ ourFormats.add(new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss' 'ZZZZ' ('E', 'dd' 'MMM' 'yyyy')'"));
+ ourFormats.add(new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss'Z'"));
+ ourFormats.add(new SimpleDateFormat("EEE' 'MMM' 'dd' 'HH:mm:ss' 'yyyy"));
+ ourFormats.add(new SimpleDateFormat("MM' 'dd' 'yyyy"));
+ ourFormats.add(new SimpleDateFormat("MM' 'dd' 'HH:mm"));
+ ourFormats.add(new SimpleDateFormat("MM' 'dd' 'HH:mm:ss"));
+ }
+
+ public static Date parseDate(final String date) {
+ for (DateFormat format : ourFormats) {
+ try {
+ return format.parse(date);
+ }
+ catch (ParseException e) {
+ continue;
+ }
+ }
+ return new Date(0);
+ }
+
+ public static void changelistsToCommand(String[] changeLists, final List<String> list) {
+ if (changeLists != null) {
+ for (String name : changeLists) {
+ list.add("--cl");
+ list.add(name);
+ }
+ }
+ }
+
+ public static String getDepthName(final int i) {
+ return org.tigris.subversion.javahl.Depth.toADepth(i).name();
+ }
+
+ public static File correctUpToExistingParent(File base) {
+ while (base != null) {
+ if (base.exists() && base.isDirectory()) return base;
+ base = base.getParentFile();
+ }
+ return null;
+ }
+}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java
new file mode 100644
index 000000000000..9e355e56f4d6
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommand.java
@@ -0,0 +1,272 @@
+/*
+ * 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.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;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.vcs.ProcessEventListener;
+import com.intellij.util.EventDispatcher;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 1/25/12
+ * Time: 12:58 PM
+ */
+public abstract class SvnCommand {
+ static final Logger LOG = Logger.getInstance(SvnCommand.class.getName());
+ private final File myConfigDir;
+
+ private boolean myIsDestroyed;
+ private int myExitCode;
+ protected final GeneralCommandLine myCommandLine;
+ 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);
+ private final SvnCommandName myCommandName;
+
+ public SvnCommand(File workingDirectory, @NotNull SvnCommandName commandName, @NotNull @NonNls String exePath) {
+ this(workingDirectory, commandName, exePath, null);
+ }
+
+ 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());
+ }
+ 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() {
+ synchronized (myLock) {
+ checkNotStarted();
+
+ try {
+ myProcess = myCommandLine.createProcess();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(myCommandLine.toString());
+ }
+ myHandler = new OSProcessHandler(myProcess, myCommandLine.getCommandLineString());
+ startHandlingStreams();
+ } catch (Throwable t) {
+ myListeners.getMulticaster().startFailed(t);
+ }
+ }
+ }
+
+ private void startHandlingStreams() {
+ final ProcessListener processListener = new ProcessListener() {
+ public void startNotified(final ProcessEvent event) {
+ // do nothing
+ }
+
+ public void processTerminated(final ProcessEvent event) {
+ final int exitCode = event.getExitCode();
+ try {
+ setExitCode(exitCode);
+ SvnCommand.this.processTerminated(exitCode);
+ } finally {
+ listeners().processTerminated(exitCode);
+ }
+ }
+
+ public void processWillTerminate(final ProcessEvent event, final boolean willBeDestroyed) {
+ // do nothing
+ }
+
+ public void onTextAvailable(final ProcessEvent event, final Key outputType) {
+ SvnCommand.this.onTextAvailable(event.getText(), outputType);
+ }
+ };
+
+ outputAdapter = new CapturingProcessAdapter();
+ myHandler.addProcessListener(outputAdapter);
+ myHandler.addProcessListener(processListener);
+ myHandler.startNotify();
+ }
+
+ public String getOutput() {
+ return outputAdapter.getOutput().getStdout();
+ }
+
+ /**
+ * Wait for process termination
+ * @param timeout
+ */
+ public boolean waitFor(int timeout) {
+ checkStarted();
+ final OSProcessHandler handler;
+ synchronized (myLock) {
+ if (myIsDestroyed) return true;
+ handler = myHandler;
+ }
+ if (timeout == -1) {
+ return handler.waitFor();
+ }
+ else {
+ return handler.waitFor(timeout);
+ }
+ }
+
+ protected abstract void processTerminated(int exitCode);
+ protected abstract void onTextAvailable(final String text, final Key outputType);
+
+ public void cancel() {
+ synchronized (myLock) {
+ checkStarted();
+ destroyProcess();
+ }
+ }
+
+ protected void setExitCode(final int code) {
+ synchronized (myLock) {
+ myExitCode = code;
+ }
+ }
+
+ public void addListener(final ProcessEventListener listener) {
+ synchronized (myLock) {
+ myListeners.addListener(listener);
+ }
+ }
+
+ protected ProcessEventListener listeners() {
+ synchronized (myLock) {
+ return myListeners.getMulticaster();
+ }
+ }
+
+ public void addParameters(@NonNls @NotNull String... parameters) {
+ synchronized (myLock) {
+ checkNotStarted();
+ myCommandLine.addParameters(parameters);
+ }
+ }
+
+ public void addParameters(List<String> parameters) {
+ synchronized (myLock) {
+ checkNotStarted();
+ myCommandLine.addParameters(parameters);
+ }
+ }
+
+ public void destroyProcess() {
+ synchronized (myLock) {
+ if (! myIsDestroyed) {
+ myIsDestroyed = true;
+ myHandler.destroyProcess();
+ }
+ }
+ }
+
+ public String getCommandText() {
+ synchronized (myLock) {
+ return myCommandLine.getCommandLineString();
+ }
+ }
+
+ public String getExePath() {
+ synchronized (myLock) {
+ return myCommandLine.getExePath();
+ }
+ }
+
+ /**
+ * check that process is not started yet
+ *
+ * @throws IllegalStateException if process has been already started
+ */
+ private void checkNotStarted() {
+ if (isStarted()) {
+ throw new IllegalStateException("The process has been already started");
+ }
+ }
+
+ /**
+ * check that process is started
+ *
+ * @throws IllegalStateException if process has not been started
+ */
+ protected void checkStarted() {
+ if (! isStarted()) {
+ throw new IllegalStateException("The process is not started yet");
+ }
+ }
+
+ /**
+ * @return true if process is started
+ */
+ public boolean isStarted() {
+ synchronized (myLock) {
+ return myProcess != null;
+ }
+ }
+
+ protected int getExitCode() {
+ synchronized (myLock) {
+ return myExitCode;
+ }
+ }
+
+ 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/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java
new file mode 100644
index 000000000000..c19ea283bbd1
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommandName.java
@@ -0,0 +1,59 @@
+/*
+ * 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;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 1/25/12
+ * 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),
+ 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;
+
+ private SvnCommandName(String name, boolean writeable) {
+ myName = name;
+ myWriteable = writeable;
+ }
+
+ public String getName() {
+ return myName;
+ }
+
+ public boolean isWriteable() {
+ return myWriteable;
+ }
+}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommitRunner.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommitRunner.java
new file mode 100644
index 000000000000..83f6ec156cf1
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnCommitRunner.java
@@ -0,0 +1,209 @@
+/*
+ * 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.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.tmatesoft.svn.core.SVNURL;
+
+import java.io.File;
+import java.util.*;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 2/25/13
+ * Time: 4:56 PM
+ */
+public class SvnCommitRunner {
+ private final String myExePath;
+ @Nullable private final AuthenticationCallback myAuthenticationCallback;
+ 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) {
+ myExePath = path;
+ myCommandListener = new CommandListener(handler);
+ myAuthenticationCallback = authenticationCallback;
+ }
+
+ public long commit(String[] paths,
+ String message,
+ int depth,
+ boolean noUnlock,
+ boolean keepChangelist,
+ String[] changelists,
+ Map revpropTable, Convertor<String[], SVNURL> urlProvider) throws VcsException {
+ if (paths.length == 0) return Revision.SVN_INVALID_REVNUM;
+
+ final List<String> parameters = new ArrayList<String>();
+ parameters.add("--depth");
+ parameters.add(SvnBindUtil.getDepthName(depth));
+ if (noUnlock) {
+ parameters.add("--no-unlock");
+ }
+ if (keepChangelist) {
+ parameters.add("--keep-changelists");
+ }
+ if (changelists != null && changelists.length > 0) {
+ SvnBindUtil.changelistsToCommand(changelists, parameters);
+ }
+ if (revpropTable != null && ! revpropTable.isEmpty()) {
+ final Set<Map.Entry<Object, Object>> set = revpropTable.entrySet();
+ for (Map.Entry<Object, Object> entry : set) {
+ parameters.add("--with-revprop");
+ parameters.add(entry.getKey() + "=" + entry.getValue());
+ }
+ }
+ parameters.add("-m");
+ parameters.add(message);
+ Arrays.sort(paths);
+ parameters.addAll(Arrays.asList(paths));
+
+ SvnLineCommand.runWithAuthenticationAttempt(myExePath, new File(paths[0]), urlProvider.convert(paths), SvnCommandName.ci,
+ myCommandListener, myAuthenticationCallback, ArrayUtil.toStringArray(parameters));
+ myCommandListener.throwExceptionIfOccurred();
+
+ return myCommandListener.getCommittedRevision();
+ }
+
+ public static class CommandListener extends LineCommandListener {
+ @Nullable private final CommitEventHandler myHandler;
+ private SvnBindException myException;
+ private long myCommittedRevision = Revision.SVN_INVALID_REVNUM;
+ private File myBase;
+
+ public CommandListener(@Nullable CommitEventHandler handler) {
+ myHandler = handler;
+ }
+
+ public void throwExceptionIfOccurred() throws VcsException {
+ if (myException != null) {
+ throw myException;
+ }
+ }
+
+ private long getCommittedRevision() {
+ return myCommittedRevision;
+ }
+
+ @Override
+ public void baseDirectory(File file) {
+ myBase = file;
+ }
+
+ @Override
+ public void onLineAvailable(String line, Key outputType) {
+ final String trim = line.trim();
+ if (ProcessOutputTypes.STDOUT.equals(outputType)) {
+ try {
+ parseLine(trim);
+ }
+ catch (SvnBindException e) {
+ myException = e;
+ }
+ }
+ }
+
+ private void parseLine(String line) throws SvnBindException {
+ if (StringUtil.isEmptyOrSpaces(line)) return;
+ if (line.startsWith(CommitEventType.transmittingDeltas.getText())) {
+ if (myHandler != null) {
+ myHandler.commitEvent(CommitEventType.transmittingDeltas, myBase);
+ }
+ return;
+ }
+ if (line.startsWith(CommitEventType.committedRevision.getText())) {
+ final String substring = line.substring(CommitEventType.committedRevision.getText().length());
+ int cnt = 0;
+ while (StringUtil.isWhiteSpace(substring.charAt(cnt))) {
+ ++ cnt;
+ }
+ final StringBuilder num = new StringBuilder();
+ while (Character.isDigit(substring.charAt(cnt))) {
+ num.append(substring.charAt(cnt));
+ ++ cnt;
+ }
+ if (num.length() > 0) {
+ try {
+ myCommittedRevision = Long.parseLong(num.toString());
+ if (myHandler != null) {
+ myHandler.committedRevision(myCommittedRevision);
+ }
+ } catch (NumberFormatException e) {
+ final String message = "Wrong committed revision number: " + num.toString() + ", string: " + line;
+ LOG.info(message, e);
+ throw new SvnBindException(message);
+ }
+ } else {
+ final String message = "Missing committed revision number: " + num.toString() + ", string: " + line;
+ LOG.info(message);
+ throw new SvnBindException(message);
+ }
+ } else {
+ if (myHandler == null) return;
+ final int idxSpace = line.indexOf(' ');
+ if (idxSpace == -1) {
+ LOG.info("Can not parse event type: " + line);
+ return;
+ }
+ final CommitEventType type = CommitEventType.create(line.substring(0, idxSpace));
+ if (type == null) {
+ LOG.info("Can not parse event type: " + line);
+ return;
+ }
+ final File target = new File(myBase, new String(line.substring(idxSpace + 1).trim()));
+ myHandler.commitEvent(type, target);
+ }
+ }
+ }
+
+/*C:\TestProjects\sortedProjects\Subversion\local2\preRelease\mod2\src\com\test>sv
+ n st
+ D gggG
+ D gggG\Rrr.java
+ D gggG\and555.txt
+ D gggG\test.txt
+ A + gggGA
+ D + gggGA\Rrr.java
+ A + gggGA\RrrAA.java
+ D + gggGA\and.txt
+ M + gggGA\and555.txt
+ A gggGA\someNewFile.txt
+
+ --- Changelist 'New changelistrwerwe':
+ A ddd.jpg
+
+ C:\TestProjects\sortedProjects\Subversion\local2\preRelease\mod2\src\com\test>sv
+ n ci -m 123
+ Adding ddd.jpg
+ Deleting gggG
+ Adding gggGA
+ Deleting gggGA\Rrr.java
+ Adding gggGA\RrrAA.java
+ Sending gggGA\and555.txt
+ Adding gggGA\someNewFile.txt
+ Transmitting file data ....
+ Committed revision 165.*/
+}
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/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java
new file mode 100644
index 000000000000..c5e3b9e6861c
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnLineCommand.java
@@ -0,0 +1,605 @@
+/*
+ * 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.execution.process.ProcessOutputTypes;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ApplicationNamesInfo;
+import com.intellij.openapi.util.Key;
+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.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.
+ * User: Irina.Chernushina
+ * Date: 1/25/12
+ * Time: 4:05 PM
+ *
+ * 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:";
+ /**
+ * the partial line from stdout stream
+ */
+ private final StringBuilder myStdoutLine = new StringBuilder();
+ /**
+ * the partial line from stderr stream
+ */
+ private final StringBuilder myStderrLine = new StringBuilder();
+ 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);
+ }
+
+ public SvnLineCommand(File workingDirectory, @NotNull SvnCommandName commandName, @NotNull @NonNls String exePath, File configDir) {
+ super(workingDirectory, commandName, exePath, configDir);
+ myLineListeners = EventDispatcher.create(LineProcessEventListener.class);
+ myExitCode = new AtomicReference<Integer>();
+ myErr = new StringBuffer();
+ myStdOut = new StringBuffer();
+ }
+
+ @Override
+ protected void processTerminated(int exitCode) {
+ // force newline
+ if (myStdoutLine.length() != 0) {
+ onTextAvailable("\n\r", ProcessOutputTypes.STDOUT);
+ }
+ else if (myStderrLine.length() != 0) {
+ onTextAvailable("\n\r", ProcessOutputTypes.STDERR);
+ }
+ }
+
+ 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);
+
+ File configDir = null;
+
+ try {
+ // for IDEA proxy case
+ if (authenticationCallback != null) {
+ writeIdeaConfig2SubversionConfig(authenticationCallback, base);
+ configDir = authenticationCallback.getSpecialConfigDir();
+ }
+
+ while (true) {
+ final SvnLineCommand command = runCommand(exePath, commandName, listener, base, configDir, parameters);
+ 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;
+ }
+ }
+ }
+ throw new SvnBindException(errText);
+ } else {
+ throw new SvnBindException("Svn process exited with error code: " + exitCode);
+ }
+ } else if (command.myErr.length() > 0) {
+ LOG.info("Detected warning - " + command.myErr);
+ }
+ return command;
+ }
+ } finally {
+ if (authenticationCallback != null) {
+ authenticationCallback.reset();
+ }
+ }
+ }
+
+ 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 {
+ if (! authenticationCallback.persistDataToTmpConfig(base)) {
+ throw new SvnBindException("Can not persist " + ApplicationNamesInfo.getInstance().getProductName() +
+ " HTTP proxy information into tmp config directory");
+ }
+ }
+ catch (IOException e) {
+ throw new SvnBindException(e);
+ }
+ catch (URISyntaxException e) {
+ throw new SvnBindException(e);
+ }
+ assert authenticationCallback.getSpecialConfigDir() != null;
+ }
+ }
+
+ 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);
+ }
+ if (errText.startsWith(AUTHENTICATION_REALM)) {
+ return new CredentialsCallback(callback, base);
+ }
+ if (errText.startsWith(PASSPHRASE_FOR)) {
+ return new PassphraseCallback(callback, base);
+ }
+ 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);
+ }
+
+ @Override
+ boolean getCredentials(String errText) throws SvnBindException {
+ return myAuthenticationCallback.askProxyCredentials(myBase);
+ }
+ }
+
+ private static class CredentialsCallback extends AuthCallbackCase {
+ protected CredentialsCallback(AuthenticationCallback callback, File base) {
+ super(callback, base);
+ }
+
+ @Override
+ boolean getCredentials(String errText) throws SvnBindException {
+ 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);
+ }
+ myTried = true;
+ if (myAuthenticationCallback.authenticateFor(realm, myBase, myAuthenticationCallback.getSpecialConfigDir() != null, isPassword)) {
+ return true;
+ }
+ throw new SvnBindException("Authentication canceled for realm: " + realm);
+ }
+ }
+
+ private static String cutFirstLine(final String text) {
+ final int idx = text.indexOf('\n');
+ if (idx == -1) return text;
+ return text.substring(0, idx);
+ }
+
+ 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 {
+ // 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);
+ }
+ final int idx2 = realm.indexOf('\'', idx1 + 1);
+ if (idx2== -1) {
+ throw new SvnBindException("Can not detect authentication realm name: " + errText);
+ }
+ 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 {
+ protected boolean myTried = false;
+ protected final AuthenticationCallback myAuthenticationCallback;
+ protected final File myBase;
+
+ protected AuthCallbackCase(AuthenticationCallback callback, final File base) {
+ myAuthenticationCallback = callback;
+ myBase = base;
+ }
+
+ abstract boolean getCredentials(final String errText) throws SvnBindException;
+
+ public void updateParameters(List<String> parameters) {
+ }
+ }
+
+ 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());
+ }
+
+ final SvnSimpleCommand cleanupCommand = new SvnSimpleCommand(wcRoot, SvnCommandName.cleanup, exePath);
+ try {
+ cleanupCommand.run();
+ }
+ catch (VcsException e) {
+ throw new SvnBindException(e);
+ }
+ }
+ }
+
+ /*svn: E170001: Commit failed (details follow):
+ svn: E170001: Unable to connect to a repository at URL 'htt../svn/secondRepo/local2/trunk/mod2/src/com/test/gggGA'
+ svn: E170001: OPTIONS of 'htt.../svn/secondRepo/local2/trunk/mod2/src/com/test/gggGA': authorization failed: Could not authenticate to server: rejected Basic challenge (ht)*/
+ private final static String ourAuthFailed = "authorization failed";
+ private final static String ourAuthFailed2 = "Could not authenticate to server";
+
+ private static boolean isAuthenticationFailed(String s) {
+ return s.trim().startsWith(AUTHENTICATION_REALM);
+ //return s.contains(ourAuthFailed) && s.contains(ourAuthFailed2);
+ }
+
+ private static SvnLineCommand runCommand(String exePath,
+ SvnCommandName commandName,
+ final LineCommandListener listener,
+ File base, File configDir,
+ String... parameters) throws SvnBindException {
+ final AtomicBoolean errorReceived = new AtomicBoolean(false);
+ final SvnLineCommand command = new SvnLineCommand(base, commandName, exePath, configDir) {
+ int myErrCnt = 0;
+
+ @Override
+ protected void onTextAvailable(String text, Key outputType) {
+
+ // we won't stop right now if got "authentication realm" -> since we want to get "password" string (that would mean password is expected
+ // or certificate maybe is expected
+ // but for certificate passphrase we get just "Passphrase for ..." - one line without line feed
+
+ // for client certificate (when no path in servers file) we get
+ // Authentication realm: <text>
+ // Client certificate filename:
+ if (ProcessOutputTypes.STDERR.equals(outputType)) {
+ ++ myErrCnt;
+ final String trim = text.trim();
+ // should end in 1 second
+ errorReceived.set(true);
+ if (trim.startsWith(UNABLE_TO_CONNECT)) {
+ // wait for 3 lines of text then
+ if (myErrCnt >= 3) {
+ destroyProcess();
+ }
+ } else if (trim.startsWith(PASSPHRASE_FOR) || myErrCnt >= 2) {
+ destroyProcess();
+ }
+ }
+ super.onTextAvailable(text, outputType);
+ }
+ };
+
+ //command.addParameters("--non-interactive");
+ command.addParameters(parameters);
+ final AtomicReference<Throwable> exceptionRef = new AtomicReference<Throwable>();
+ // several threads
+ 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);
+ }
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ System.out.println("==> " + line);
+ }
+ listener.onLineAvailable(line, outputType);
+ if (listener.isCanceled()) {
+ command.destroyProcess();
+ return;
+ }
+ if (ProcessOutputTypes.STDERR.equals(outputType)) {
+ if (command.myErr.length() > 0) {
+ command.myErr.append('\n');
+ }
+ command.myErr.append(line);
+ }
+ }
+
+ @Override
+ public void processTerminated(int exitCode) {
+ listener.processTerminated(exitCode);
+ command.myExitCode.set(exitCode);
+ }
+
+ @Override
+ public void startFailed(Throwable exception) {
+ listener.startFailed(exception);
+ exceptionRef.set(exception);
+ }
+ });
+ command.start();
+ boolean finished;
+ do {
+ finished = command.waitFor(500);
+ if (!finished && errorReceived.get()) {
+ command.waitFor(1000);
+ command.destroyProcess();
+ break;
+ }
+ }
+ while (!finished);
+
+ if (exceptionRef.get() != null) {
+ throw new SvnBindException(exceptionRef.get());
+ }
+ return command;
+ }
+
+ @Override
+ protected void onTextAvailable(String text, Key outputType) {
+ Iterator<String> lines = LineHandlerHelper.splitText(text).iterator();
+ if (ProcessOutputTypes.STDOUT == outputType) {
+ notifyLines(outputType, lines, myStdoutLine);
+ }
+ else if (ProcessOutputTypes.STDERR == outputType) {
+ notifyLines(outputType, lines, myStderrLine);
+ }
+ }
+
+ private void notifyLines(final Key outputType, final Iterator<String> lines, final StringBuilder lineBuilder) {
+ if (!lines.hasNext()) return;
+ if (lineBuilder.length() > 0) {
+ lineBuilder.append(lines.next());
+ if (lines.hasNext()) {
+ // line is complete
+ final String line = lineBuilder.toString();
+ notifyLine(line, outputType);
+ lineBuilder.setLength(0);
+ }
+ }
+ while (true) {
+ String line = null;
+ if (lines.hasNext()) {
+ line = lines.next();
+ }
+
+ if (lines.hasNext()) {
+ notifyLine(line, outputType);
+ }
+ else {
+ if (line != null && line.length() > 0) {
+ lineBuilder.append(line);
+ }
+ break;
+ }
+ }
+ }
+
+ private void notifyLine(final String line, final Key outputType) {
+ String trimmed = LineHandlerHelper.trimLineSeparator(line);
+ myLineListeners.getMulticaster().onLineAvailable(trimmed, outputType);
+ }
+
+ public void addLineListener(LineProcessEventListener listener) {
+ myLineListeners.addListener(listener);
+ super.addListener(listener);
+ }
+
+ private static class PassphraseCallback extends AuthCallbackCase {
+ public PassphraseCallback(AuthenticationCallback callback, File base) {
+ super(callback, base);
+ }
+
+ @Override
+ boolean getCredentials(String errText) throws SvnBindException {
+ // try to get from file
+ /*if (myTried) {
+ myAuthenticationCallback.clearPassiveCredentials(null, myBase);
+ }*/
+ myTried = true;
+ if (myAuthenticationCallback.authenticateFor(null, myBase, myAuthenticationCallback.getSpecialConfigDir() != null, false)) {
+ return true;
+ }
+ throw new SvnBindException("Authentication canceled for : " + errText.substring(PASSPHRASE_FOR.length()));
+ }
+ }
+}
diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java
new file mode 100644
index 000000000000..a088da0334a3
--- /dev/null
+++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnSimpleCommand.java
@@ -0,0 +1,110 @@
+/*
+ * 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.execution.process.ProcessOutputTypes;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.vcs.ProcessEventListener;
+import com.intellij.openapi.vcs.VcsException;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 1/25/12
+ * Time: 4:04 PM
+ */
+public class SvnSimpleCommand extends SvnCommand {
+ private final StringBuilder myStderr;
+ private final StringBuilder myStdout;
+ private VcsException myException;
+ private final Object myDataLock;
+
+ public SvnSimpleCommand(File workingDirectory, @NotNull SvnCommandName commandName, @NotNull @NonNls String exePath) {
+ super(workingDirectory, commandName, exePath);
+
+ myDataLock = new Object();
+ myStderr = new StringBuilder();
+ myStdout = new StringBuilder();
+ }
+
+ @Override
+ protected void processTerminated(int exitCode) {
+ //
+ }
+
+ @Override
+ protected void onTextAvailable(String text, Key outputType) {
+ synchronized (myDataLock) {
+ if (ProcessOutputTypes.STDOUT.equals(outputType)) {
+ myStdout.append(text);
+ } else if (ProcessOutputTypes.STDERR.equals(outputType)) {
+ myStderr.append(text);
+ }
+ }
+ }
+
+ public StringBuilder getStderr() {
+ synchronized (myDataLock) {
+ return myStderr;
+ }
+ }
+
+ public StringBuilder getStdout() {
+ synchronized (myDataLock) {
+ return myStdout;
+ }
+ }
+
+ public String run() throws VcsException {
+ addListener(new ProcessEventListener() {
+ @Override
+ public void processTerminated(int exitCode) {
+ }
+
+ @Override
+ public void startFailed(Throwable exception) {
+ synchronized (myDataLock) {
+ myException = new VcsException("Process failed to start (" + myCommandLine.getCommandLineString() + "): " + exception.toString(), exception);
+ }
+ }
+ });
+ start();
+ if (isStarted()) {//if wasn't started, exception is stored into a field, don't wait for process
+ waitFor(-1);
+ }
+
+ synchronized (myDataLock) {
+ if (myException != null) throw myException;
+ final int code = getExitCode();
+ if (code == 0) {
+ 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())
+ .append("\nCommand was: ").append(myCommandLine.getCommandLineString()).toString();
+ throw new VcsException(msg);
+ }
+ }
+ }
+}
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/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() {